Skip to content

Latest commit

 

History

History
2958 lines (2212 loc) · 116 KB

File metadata and controls

2958 lines (2212 loc) · 116 KB

#100DaysOfCode Round 3

⭐ Round 3 Projects ⭐

  • Continue through Javascript30
  • freeCodeCamp
    • Information Security and QA overview section
      • Day 05: Finish new Javascript Algorithm challenges, began ChaiJS
      • Day 06: Continue testing with ChaiJS
      • Day 07: Finish testing with ChaiJS ✓
      • Day 08: Complete HelmetJS & bcrypt ✓
      • Day 11: Pug/Passport - Node & Express
      • Day 12: Passport - Node & Express
      • Day 13: Passport - Node & Express
    • Infosec/QA - Metric-Imperial Converter ✓
      • Day 14: Metric-Imperial Converter
      • Day 15: Metric-Imperial Converter
      • Day 16: Metric-Imperial Converter
      • Day 17: Metric-Imperial Converter ✓
    • Infosec/QA - Issue Tracker ✓
      • Day 18: POST with fake data
      • Day 19: POST with form data, GET route
      • Day 20: DELETE route
      • Day 21: PUT route
      • Day 22: Functional tests (POST)
      • Day 35: Functional tests (POST)
      • Day 36: Researching API HTTP Responses
      • Day 37: Sending MongoDB boolean from deconstructed array
      • Day 38: Functional tests ✓
    • Data Structures
      • Day 56
  • Node.js: Testing and Code Quality - Jon Peck ✓
    • Day 23: Intro
    • Day 24: Testing and Code Quality Fundamentals
    • Day 25: Finding Errors with Linting
    • Day 26: Finding Errors with Linting & Validating Correctness with Unit Tests
    • Day 28: Validating Correctness with Unit Testing
    • Day 29: Replacing and Inspecting with Stubs, Spies, and Mocks
    • Day 30: Reporting on Your Entire Codebase ✓
  • Front-end Masters
  • Read Code Complete
    • Day 10: History of programming languages
    • Day 34: Programming into the language
  • Codepen
  • Portfolio & Server updates
    • Day 31: Updated to support LetsEncrypt ACMEv2 and to Debian 10 ✓
    • Day 54: Explore docker droplets, SSH config
    • Day 62: Added schema to satinflame.com
  • IBM Design Thinking
    • Day 31: Practitioner Badge ✓
  • Stylelint
    • Day 32: Initial stylelint with ordering
  • Codecademy Game Development with Phaser.JS
    • Day 44: Learn Phaser: Basics
    • Day 45: Learn Phaser: Basics ✓
    • Day 46: Learn Phaser: Color a Pegasus
    • Day 47: Learn Phaser: Color a Pegasus ✓
    • Day 48: Learn Phaser: Physics
    • Day 49: Learn Phaser: Physics
    • Day 50: Learn Phaser: Physics
    • Day 51: Learn Phaser: Physics
    • Day 52: Learn Phaser: Physics ✓
  • Meetup
    • Day 53: GDG AMP Website Workshop
    • Day 83: GDG Go with James Perkins
    • Dya 90: JS Bozeman Deno
  • Microservices with Node JS and React
    • Day 57: S1✓: Fundamental Ideas Around Microservices
    • Day 58: S2: A Mini-Microservices App (setup and services)
    • Day 59: S2: A Mini-Microservices App (begin React components)
    • Day 60: S2: A Mini-Microservices App (PostCreate)
    • Day 61: S2: A Mini-Microservices App (CORS support)
    • Day 63: S2: A Mini-Microservices App (React components)
    • Day 65: S2: A Mini-Microservices App (Emitting events)
    • Day 67: S2: A Mini-Microservices App (Implement query service)
    • Day 69: S2: A Mini-Microservices App (Comment moderation)
    • Day 70: S2: A Mini-Microservices App (Moderation service and CommentUpdated event)
    • Day 71: S2✓: A Mini-Microservices App (Event bus data store)
    • Day 72: S3: Running services with Docker
    • Day 74: S24: Basics of Docker
    • Day 75: S24: Basics of Docker
    • Day 76: S24: Basics of Docker (Docker images)
    • Day 78: S24✓: Basics of Docker (Running Node.js in a Docker Container)
    • Day 79: S3✓: Running services with Docker (Create Dockerfile for posts)
    • Day 80: S4: Orchestrating Collections of Services with Kubernetes (Getting started: Kubernetes)
    • Day 81: S4: Orchestrating Collections... (Deployments)
    • Day 82: S4: Orchestrating Collections... (Updating deployments, creating services)
    • Day 84: S4: Orchestrating Collections... (Intro to ClusterIPs)
    • Day 85: S4: Orchestrating Collections... (Setup ClusterIPs for Posts & Events)
    • Day 87: S4: Orchestrating Collections... (Update all services to use Docker)
    • Day 88: S4: Orchestrating Collections... (Begin adding Ingress)
    • Day 89: S4: Orchestrating Collections... (Ingress)
    • Day 91: S4✓: Orchestrating Collections... (Skaffold)
    • Day 92: S5: Architecture of Multi-Service Apps
    • Day 93: S25: Typescript
    • Day 94: S25: Typescript: Functions and Objects
    • Day 95: S25: Typescript: Arrays, tuples, interfaces
    • Day 96: S25: Typescript: Classes
    • Day 97: S25: Typescript: Constructors and App Setup
    • Day 98: S25: Typescript: App w/ faker, Google map
    • Day 99: S25✓: Typescript: Google map CustomMap and Mappable interface
    • Day 100: S5: Architecture... (Auth)
  • Develop out Knight University using TailwindCSS
    • Day 55: Hero, subfeature
  • Other projects/APIs
    • Day 64: Test ESPN APi
    • Day 68: Sendgrid handlebars email templates
    • Day 73: Netlify Jamdocs
  • Blue Array Academy SEO Manager Certification✓
    • Day 46: Welcome
    • Day 46: How does search work?
    • Day 47: SEO strategies
    • Day 47: Site audit: Checklist & requirements
    • Day 48, 49: Technical audit of your site (part 1)
    • Day 50: Technical audit of your site (part 2)
    • Day 51: Producing a technical backlog
    • Day 51, 52, 66: Benchmarking against your competitors
    • Day 66, 76, 77: SEO common pitfalls
    • Day 81, 86: Creating a Keyword Universe
    • Day 91: Blue Array certification

Today I finished R3 D100 of 100DaysOfCode! 🎉 Thanks to all the amazing content creators including freeCodeCamp, Codecademy, FrontendMasters, FluxSauce, ste_grider, and wesbos! https://virtual.github.io/100daysofcode/

Favorite course of the year from ste_grider: Microservices with Node JS and React. Great lessons on learning Docker and Typescript and actually integrating them into projects. I love the hands-on approach and "improving code" progression. 👏 https://www.udemy.com/course/microservices-with-node-js-and-react/

Interested in test-driven design TDD? There's so much information in this course: Node.js: Testing and Code Quality from FluxSauce! I learned about tools I didn't even know existed for auditing and reporting on code coverage. https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html

If you're interested in doing 100DaysOfCode, start with a list of possible projects you're interested in working on. It's helpful when you finish something (or are a bit bored) & need a change of pace. Great options: javascript30 freecodecamp

R3 Day 100: 2020-06-28 Sunday

Auth service

  • New auth folder
  • npm init -y
  • npm install typescript ts-node-dev express @types/express
  • tsc --init
  • create auth/src/index.ts (ts!)
  • update package.json: "start": "ts-node-dev src/index.ts"

Setup K8s early on!

  • Make sure we can build an image, we'll need a Dockerfile (FROM node:alpine...)
  • We'll also add .dockerignore to avoid copying over node_modules folder to image
  • Try building image
  • Now add k8s info in infra/k8s
  • Create auth-depl.yaml
  • Everytime we create a deployment, we'll want to create a service to go along with it
  • Create service information inside the same auth-depl.yaml file

Skaffold

  • From root of project, create skaffold.yaml and fill in
  • Test run with skaffold dev
  • Note on -poll (I use poll a lot for other projects!)
    • If you did not see your server restart after changing the index.ts file, do the following:
    • Open the package.json file in the ‘auth’ directory
    • Find the ‘start’ script
    • Update the start script to the following:
    • ts-node-dev --poll src/index.ts

R3 Day 99: 2020-06-27 Saturday

addMarker(mappable: User | Company): void { }

  • To allow for multiple type as an argument, use a bar as an OR operator
  • This compares the available properties in each
  • Only properties that exist in both (and have the same types) can be used within this function call
  • This can get messy as we scale, as we need to manage the types, eg: User | Company | Parks | CarLots...
  • Instead, invert the setup and make the class types work with the CustomMap class needs
interface Mappable {
  location: {
    lat: number;
    lng: number;
  };
}
// Ideal things we can do with the map in index.ts
export class CustomMap {
  // addMarker(mappable: User | Company): void {
  // Instead of maintaing list types, we can use the Mappable type
  addMarker(mappable: Mappable): void {}
}

Typescript does an implicit check of User and Company to make sure they pass as type Mappable

To help identify errors in classes we can use export:

// by adding export, we can use this to help with error checking in User
// and Company classees
export interface Mappable {}

And then add implements to the Class itself

import { Mappable } from './CustomMap';
// Since we export Mappable, we can use implements
// (not extends!) Mappable to help id errors
// Not required, but helpful :)
export class User implements Mappable {}

Now errors will show inside the User class showing expectations of Mappable

Typical Typescript file

  • Interface definitions for working with this class
  • Class definition

Review of why this project used Parcel: "Parcel is a Web Application Bundler. It's in the same tool category of webpack, with a different value proposition. Parcel promises to do many things without any configuration at all, and be fast too."

R3 Day 98: 2020-06-26 Friday

  • npm install faker - allows generation for fake data
  • Error: 'could not find declaration file for module': if you include a file that isn't Typescript, it can't determine TS types
  • Type definition files (name.d.ts) tells the TS compiler the functions that are in a file and what kind of types it takes/uses
  • Precreated type definition files can be found from "Definitely typed", eg @types/faker: use install npm install @types/faker
  • After install, the error should magically go away :)
  • Command click on import variable to view typescript def file

Example of User.js that creates a user with faker data; uses the export keyword:

import faker from 'faker';

export class User {
  name: string;
  location: {
    lat: number;
    lng: number;
  };

  constructor() {
    // generate random info using faker
    this.name = faker.name.firstName();
    // this.location.lat -- this is undefined!
    this.location = {
      lat: parseFloat(faker.address.latitude()), // return a number to match definition above
      lng: parseFloat(faker.address.longitude())
    };
  }
}
import { User } from './User';
// anything we export something from a file, we use curly braces
// this allows us to export mulitple items with diff names
// different from `export default` as that will be the default
// export default is rarely used in TS
const user = new User();
  • By adding the following into our index.html, we have created a global variable google: <script src="https://maps.googleapis.com/maps/api/js?key=API_KEY"></script>
  • However, TS files do not understand that this global var is available
  • We can use the type definition files for these situations as well
  • We can reference this in the top of the file as /// <reference types="@types/googlemaps" />
  • Command+shift+P inside a TS file and choose Fold Level 2 to fold all level-2 headings
  • opts? is a flag for optional argument

Limit access to methods on third-party plugins:

// Instead of creating and calling this directly in index.ts, let's
// create a Map class to limit the functionality allowed in index.ts
const map = new google.maps.Map(document.getElementById('map'), {
  zoom: 2,
  center: {
    lat: 0,
    lng: 0
  }
});

R3 Day 97: 2020-06-25 Thursday

Constructors

// way 1
color: string;
// a constructor is instantly executed and uses arguments
constructor(color: string) {
  this.color = color; // if you use a constructor, then don't define it above
}
// end way 1

// way 2 -- add public, equivalent to way 1!
constructor(public color: string) { }
// end way 2

constructors that extend a parent class need super() calls

Going to use npm install -g parcel-bundler for packaging TS code going forward

R3 Day 96: 2020-06-24 Wednesday

Typescript Classes

  • A blueprint to create an object with some fields (values) and methods (functions) to represent a 'thing'
  • Inheritence: (Car is a type of Vehicle) extends a class to use another Class's methods
// refer to as the Super Class
class Vehicle {
  drive(): void {
    console.log('chugga chugga');
  }
  honk(): void {
    console.log('beep');
  }
}
// refer to as the Child Class
class CarClass extends Vehicle {
  // copies all props from Vehicle
  // if we want to override, we can
  drive(): void {
    console.log('vroom');
  }
}
const carInstance = new CarClass();
carInstance.drive();
carInstance.honk();

Modifers: keywords we can place on methods and props inside a class

  • public--can be called anywhere, anytime
  • private--can only be called by other methods in this class
  • protected--can be called by other methods in this class, or by other methods in child classes

Notes:

  • If you override the method in a child class, you cannot change the modifier

R3 Day 95: 2020-06-23 Tuesday

Tuples:

  • array-like structure where each element represents one property about a record
  • contains multiple properties to describe one thing
  • drink example [color, carbonated, sugarContent]
  • order is critical
  • could be useful for CSV files (rows)
const pepsiArray = ['brown', true, 40];
const pepsiTuple: [string, boolean, number] = ['brown', true, 40];

// Can also be done as a "type alias"
// not an array--> defines the tuple!
type Drink = [string, boolean, number];
const coke: Drink = ['brown', true, 39];
const tea: Drink = ['brown', false, 15];

Interfaces in Typescript

  • Interfaces + Classes = strong TS code
  • Creates a new type, describing the property names and value types of an object
  • Used for 'gatekeeping' and ensuring an object has the right type (good for code reuse!)
  • Create functions that accept arguments that are typed with interfaces
  • Object/classes can decide to do 'implement' a given interface to work with a function
interface Vehicle {
  name: string;
  year: number;
  broken: boolean;
}
const listVehicle = (vehicle: Vehicle): void => {
  console.log(`Name: ${vehicle.name}`);
  console.log(`Year: ${vehicle.year}`);
  console.log(`Broken: ${vehicle.broken}`);
};

// Refactoring
// Q: But what happens to all the type checking for the vehicles?
// Q: Can we have optional, but defined, variables in the Interface?
interface Reportable {
  summary(): string;
}
const car1 = {
  prop1: 'vw',
  summary(): string {
    return `This ${this.prop1} is awesome.`;
  }
};
const printSummary = (item: Reportable): void => {
  console.log(item.summary());
};
printSummary(car1);

R3 Day 94: 2020-06-22 Monday

  • You can assign multiple types to a variable
  • Type inference on functions only works for the return type, not the argument type(s)

let numberAboveZero: boolean | number = false;

Destructured function example:

const logWeatherDestr = ({
  date,
  weather
}: {
  date: Date;
  weather: string;
}): void => {
  console.log(date, weather);
};

Object example:

const {
  coords: { lat, lng }
}: { coords: { lat: number; lng: number } } = profile;

R3 Day 93: 2020-06-21 Sunday

Typescript

What is it? Basically:

  • JavaScript
    • arrays
    • arrow functions
    • destructuring
  • plus a type system
    • uses 'type annotations' to analyze our code

Typescript:

  • Helps identify bugs in development
  • Only active during dev
  • Compiles as normal javascript
  • Does not change speed/performance
  • Add to environment npm install -g typescript ts-node
  • test with tsc --help (typescript compiler)

Resources:

FetchJSON app

  • compile with tsc index.ts (creates compiled js)
  • run with node index.js
  • ts-node compiles and executes in one command
  • Create an interface and tell TS that an object uses that interface as Todo

Primitive types:

  • number
  • boolean
  • void
  • undefined
  • string
  • symbol
  • null

Object types: (we can tell TS to treat as a diff Object type)

  • functions
  • arrays
  • classes
  • objects

Inferences vs. annotation

  • If we declaration and initialization are done on the same line, Typescript can infer the type for us: const color = 'red'
  • If on two separate lines, let apples; & apples = 5, typescript gives apples a type of any and is not able to infer the type
  • Avoid variables with type 'any' at all costs!

R3 Day 92: 2020-06-20 Saturday

Reviewing the setup of our first Microservices app and setup for next

  • Biggest challenge: handling data
  • Focus on async communication
  • Async communication focuses on communicating changes using events sent to an event bus
  • Async encourages each service to be 100% self-sufficient
  • Docker makes it easier to package services
  • Kubernetes makes it easy to deploy and scale microservices

Review upcoming ticket app

Data

  • User
  • Order
  • Ticket
  • Charge

Services

  • auth

  • tickets

  • orders

  • expiration

  • payments

  • Client: Will be using React with Next.js -- Next.js is a server-side rendering React framework

  • Service: node & mongodb; node & Redis (expiration)

  • services will use a common library

  • in place of event bus, we'll use NATS streaming server

R3 Day 91: 2020-06-19 Friday

Microservices: Skaffold

  • Current workflow includes building each image, pushing to Docker hub and running kubectl rollout restart deployment [depl_name]
  • While this is a good process for rolling out updates to production, is there an easier way to make changes while doing development?

Skaffold

  • Automates many tasks in a k8s dev envionrment
  • Makes it easy to update code in a running pod
  • Makes it easy to create/delete all objects tied to a project at once
  • Skaffold runs outside of cluster
  • skaffold.yaml (in root folder) is a config file only used by Skaffold
  • run with skaffold dev
  • Quit with ctrl+c, Skaffold will remove objects (services, deployments, etc)

R3 Day 90: 2020-06-18 Thursday

Exploring Deno with Bozeman JS group :)

R3 Day 89: 2020-06-17 Wednesday

Adding Ingress config:

Create ingress-srv.yaml and apply file

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress-srv
  annotations: # helps ingress understand that we are sending it routing rules
    kubernetes.io/ingress.class: nginx
spec:
  rules: # all the routing rules to teach ingress how to route incoming traffic
    - host: posts.com # ingress assumes you may be hosting multiple domains
      http:
        paths:
          - path: /post # match what the posts/index.js expects traffic on
            backend:
              serviceName: posts-clusterip-srv # send requests to this service
              servicePort: 4000 # matches what is in posts-depl.yaml port

Ingress host

  • Since Ingress requires a host (eg post.com), we need to trick our localhost to thinking it's post.com
  • Edit /etc/hosts file (Mac)
  • # kubernetes microservices-blog
    # connect to localhost
    127.0.0.1 posts.com
  • Now you can go to http://posts.com/posts in your browser
  • Gather a list of all axios requests, POST, GET, etc
  • For each of these requests, update ingress-depl.yaml with these route handler
  • Ingress cannot choose based on method (POST, GET) so every method using the same route will need a unique ID: POST /posts or GET /posts are not unique enough

Getting React to work in cluster

  • Update all references to localhost to posts.com (or whatever you called your dev site); eg. http://localhost:4000/posts becomes http://posts.com/posts
  • Create a Docker image of the client folder
  • Push to Docker hub and create deployment & cluster ip configs

Q: Issue with adding a post and refreshing; need to rebuild query service in order for new posts to show up?

  • A: Routes in event-bus/index.js needed to refer to the service :)

R3 Day 88: 2020-06-16 Tuesday

Outside world -> Load Balancer -> Ingress Controller -> Cluster (Pod)

  • We will be using ingress-nginx
  • Install per instructions
  • Confirm the ingress-nginx-controller deployment exists: kubectl get pods -n ingress-nginx

R3 Day 87: 2020-06-15 Monday

  • Microservices: update remaining services to use event-bus
  • After creating all docker images and pushing them to Docker hub, create K8 depl files for each image
  • Rebuild the folder of infra/k8s using kubectl apply -f .
  • Test using postman

Integrating React portion

  • We'll use a Load Balancer Service to give the browser access to the posts, comments, etc services
  • Load Balancer Service: tell K8s to reach out to its provider and provision a load balancer. Gets traffic in a single pod
  • Ingress (Controller): A pod with a set of routing rules to distribute traffic to other services

R3 Day 86: 2020-06-14 Sunday

Keyword Universe clusters

  • More clusters! But this time in SEO.
  • Import all keyword info and competitor keyword info into a sheet with keyword, monthly search volume and keyword difficulty (and later cluster, modifier and sub-category)
  • Create keyword clusters using the free Keyword Grouper Tool
  • Bring these keywords into your sheet and use VLOOKUP to match your terms with their cluster
  • Also create a modifier column and filter (How, What, When, Why, Where) to get a better understanding of possible questions
  • Create sub-categories on your cluster groups if they make sense
  • This information also helps with understanding the Information Architecture (IA) for your website

R3 Day 85: 2020-06-13 Saturday

Setting up Cluster IP service

Add cluster IP service to existing deployment file for each pod config to make it easier to manage incoming requests

How to Communicate between services

  • Reference other Cluster IP services based on their name (as shown when you do kubectl get services)
  • For example, await axios.post('http://localhost:4005/events' would become await axios.post('http://event-bus-srv:4005/events'
  • Redeploy (see R3D82 Updating deployments)
  • Test a Post in postman to localhost:30594/posts

R3 Day 84: 2020-06-12 Friday

Setting up Cluster IP service

Will allow posts and event bus to talk to eachother

  • Build an image for the Event Bus
  • Push the image to Docker Hub
  • Create a deployment for Event Bus
  • Create a Cluster IP service for Event Bus and Posts
  • Wire it all up

R3 Day 83: 2020-06-11 Thursday

Made a quick Go program for a GDG meetup with James Perkins!

madlibs/main.go

package main

import "fmt"

func main() {
	var exclamation string = ""
	var adverb string = ""
	var noun = ""
	var adjective string = ""

	fmt.Print("Enter a exclamation!")
	fmt.Scanln(&exclamation)
	fmt.Print("Enter an adverb!")
	fmt.Scanln(&adverb)
	fmt.Print("Enter a noun!")
	fmt.Scanln(&noun)
	fmt.Print("Enter an adjective!")
	fmt.Scanln(&adjective)

	fmt.Println(exclamation+"!", "She said", adverb, "as she jumped into her",
		adjective, noun, "and drove off with her", adjective, "dog.")
}

R3 Day 82: 2020-06-10 Wednesday

Updating deployments

Recommended method using Docker Hub (no need for hardcoded versions)

  1. The deployment must be using the 'latest' tag in the pod spec sections
  2. Make the updates to your code
  3. Build the image
  4. Push the image to docker hub: docker push satinflame/posts
  5. Run the command: kubectl rollout restart deployment [depl_name] (depl_name should refer to the deployment NAME when you view kubectl get deployments)

Services

K8s Services provide networking between pods

Types of services:

  • Cluster IP: sets up an easy-to-use URL to access a pod. Only exposes pods in the cluster.
  • Node port: allows access to a pod outside the cluster; used for dev only.
  • Load Balancer: makes a pod accessible outside the cluster; this is the right way to expose a pod to the outside world.
  • External Name: redirects an in-cluster request to a CNAME URL.

Creating a NodePort service in posts-srv.yaml:

apiVersion: v1
kind: Service
metadata:
  name: posts-srv
spec:
  type: NodePort
  selector:
    app: posts
  ports:
    - name: posts
      protocol: TCP
      port: 4000
      targetPort: 4000
k apply -f posts-srv.yaml
service/posts-srv created

k get services
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP          2d19h
posts-srv    NodePort    10.102.129.168   <none>        4000:30595/TCP   19s

NodePort now available (30595) and you can access this endpoint at http://localhost:30595/posts

R3 Day 81: 2020-06-09 Tuesday

Understanding a Pod Spec

  • apiVersion v1 is the basic "pool" of K8s objects
  • a dash (-) means the item is an array entry
  • if the image is "latest", K8s will try to find the image from docker hub

Common Kubectl Commands

  • kubectl get pods - list running pods
  • kubectl exec -it [pod_name] [cmd] - execute a command in a running pod
  • kubectl logs [pod_name] - print pod logs
  • kubectl delete pod [pod_name] - Use delete to destroy and recreate (restart) pod
  • kubectl apply -f [config.yaml] - tell K8s to process config (start pod)
  • kubectl describe pod [pod_name] - print info about running pod (debug)

Bash Alias

alias k="kubectl"
  • enable with source ~/.bashrc
  • for Mac, add to ~/.bash_profile:
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

Deployments

  • A pod manager
  • Typically we run deployments instead of configs for a single pod
  • Deployments run multiple identical pods with the same config file
  • Recreates pods if they crash or disappear
  • Creates pods for a new version and sunsets old pods
  • replicas: the number of pods to be created by deployment

posts-depl.yaml (No longer going to use posts.yaml for single pod)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: posts-depl
spec:
  replicas: 1
  selector:
    matchLabels:
      app: posts
  template:
    metadata:
      labels:
        app: posts
    spec:
      containers:
        - name: posts
          image: satinflame/posts:0.0.1
  • Now if you delete a running pod, the deployment will automatically recreate a new one

Common Deployment Commands

  • kubectl get deployments - list running deployments
  • kubectl describe deployment [deployment_name] - print out info on a deployment
  • kubectl apply -f [config.yaml] - create deployment from config
  • kubectl delete deployment [deployment_name] - Use delete to destroy a deployment (and delete related pods)

R3 Day 80: 2020-06-08 Monday

Kubernetes

  • Container: an instance of a Docker image
  • kubectl - the command to interact with a K8s cluster
  • K8s will look at your local machine first to see if a Docker image is available

Kubernetes vocab:

  • Cluster: a master of nodes and a master to manage them
  • Node: virtual machine; the computer that will run some number of containers for us
  • Pod: the smallest deployable units of computing that can be created and managed in Kubernetes; Pods run a single container (could have multi)
  • Deployment for a service - monitors a set of pods/containers created and responsible for recreating pods when a service crashes, etc.
  • Service - provides an simple URL to access a running container; gives us access to running pods inside our cluster, handles networking

Always use config files to create Objects--Deployments, Pods, and Services; do not type direct commands

Config

  • Start by creating a new versioned tag: docker build -t satinflame/posts:0.0.1 .
  • Create /infra/k8s/posts.yaml:
apiVersion: v1
kind: Pod
metadata:
  name: posts
spec:
  containers:
    - name: posts
      image: satinflame/posts:0.0.1
  • cd to k8s folder and run kubectl apply -f posts.yaml
  • kubectl get pods - return a list of running pods

R3 Day 79: 2020-06-07 Sunday

Create Dockerfile for posts

  • Use .dockerignore file to ignore node_modules and other directories you don't want copied to the Docker image

Common commands:

  • docker build -t stephengrinder/posts . - build an image based on the dockerfile in the current directory, tag it as 'stephengrinder/posts'
  • docker run[image id or image tag] - Create and start a container based on the provided image id or tag
  • docker run -it [image id or image tag][cmd] - Create and start container, but also override the default command
  • docker ps - Print out information about all of the running containers
  • docker exec -it [container id][cmd] - Execute the given command in a running container
  • docker logs [container id] - Print out logs from the given container

Kubernetes is a tool for running many different containers

  • Docker for Mac; preferences, enable Kubernetes
  • kubectl version
  • When you want to use K8s, you should already have all the Docker images ready

R3 Day 78: 2020-06-06 Saturday

Running Node.js in a Docker Container

Initial Attempt:

# base image
FROM alpine
# Install dependencies
RUN npm install
# Set start command
CMD [ "npm", "start" ]

docker build .

Possible errors:

  • npm not found
    • there is no copy of npm available (npm is not part of the alpine base image)
    • find a different base image (search docker hub for "node")
    • or run a different command to add in npm
    • if you want a certain tag, you could tell it FROM node:6.14 or FROM node:alpine in the Dockerfile
    • "alpine" is a very stripped down version of an image
  • npm WARN saveError ENOENT: no such file or directory, open '/package.json'
    • by default, new container doesn't see files that are on your local filesystem
    • you'll specifically need to tell the container to see files from your hard drive
    • COPY command to copy files from local file system to container
    • COPY ./ ./ (pwd)
  • Docker ID is long
    • Let's rebuild using tagging: docker build -t satinflame/simpleweb .
    • Now we can run docker run satinflame/simpleweb
  • Why is the app not showing on localhost:8080?
    • By default no outside services can access ports on your computer (incoming requests)
    • We have to set up an explicit port mapping, forward
    • We can only make this change when we run a docker container (fwd requests to my machine at port 8080 to port 8080 on docker)
    • docker run -p 5000:8080 satinflame/simpleweb

Cleaning up working directory

  • When you run the COPY command in the Dockerfile, let's create a subdirectory so we're not copying files into the base folder
  • WORKDIR, once set, any following commands will be executed relative to this folder
  • Since we changed Dockerfile, rebuild!
  • Now when you run docker run -it satinflame/simpleweb sh you'll start in the WORKDIR

Making changes to the local files

  • When you run COPY you are making snapshot of the files
  • New changes are not automatically reflected
  • Need to rebuild--but this means we need to re-copy and re-npm install all items
  • Let's split COPY into two steps:
    • COPY ./package.json ./
    • COPY ./ ./

Our final, awesome Dockerfile:

# base image
FROM node:alpine
# Create work directory
# If not exists, this folder will be created
WORKDIR /usr/app
# Split COPY into two steps to avoid having to npm install each rebuild
COPY ./package.json ./
# Install dependencies
RUN npm install
# Copy local file system to container
# Move this after npm install so npm install doesn't need to be rerun
COPY ./ ./
# Set start command
CMD [ "npm", "start" ]

R3 Day 77: 2020-06-05 Friday

SEO reviewing common pitfalls

R3 Day 76: 2020-06-04 Thursday

Docker images

  • docker build . runs build on the Dockerfile
  • each build item is a "step"
  • each step creates a new intermediate/temporary container
  • If rerunning a build that hasn't changed (up to a step), it can use a cache
  • put changes as low (last) in the Dockerfile as possible to keep rebuilding fast

Tagging an image

  • When building tag in order to reference a name instead of id <yourDockerId>/<reponame>:<version>, e.g. docker build -t stephengrinder/redis:latest .
  • <reponame> is what you want to reference it as
  • "tag" refers to the version
  • commit -c - for manually creating

R3 Day 75: 2020-06-03 Wednesday

Microservices: Basics of Docker

More about -it

  • Every process inside a Docker (linux) environment has 3 communication channels attached to it:
    1. STDIN - things you type into
    2. STDOUT - output that shows on your console (from the process)
    3. STDERR - similar to STDOUT but specifically for show process errors
  • -it is really -i and -t
    • -i - attach the terminal to the STDIN when executing this command
    • -t - formats input/output so it shows up nicely and allows for autocomplete

Running a terminal inside your running container

  • docker exec -it 9320 sh
  • allows for Unix commands like cd ~/ or ls
  • if you need to exit and CTRL+C isn't working, try CTRL+D or exit
  • sh is the default command processor (or bash)
  • containers do not share filesystems by default

Docker Images

Creating an Image:

  • Setup a dockerfile - plain text config that defines how the container should behave
    • Specify base image
    • Run some command to install additional programs
    • Set the command to run on container startup
    • Base image, Analogy: adding Chrome to a computer with no OS
# Use existing image (alpine) as base
FROM alpine

# Download and Install depedency
RUN apk add --update redis

# Tell image what to do with in starts as a container
CMD ["redis-server"]

FROM, RUN, CMD are docker commands used in the Dockerfile

R3 Day 74: 2020-06-02 Tuesday

Microservices: Basics of Docker

  • Image: a snapshot of the filesystem and a start command
  • Namespacing: isolating resources per process (or group of processes)
  • Control groups (cgroup): manages the RAM, hard disk, network bandwidth, etc needed by the container/per process (linux)
  • Docker (for Win/Mac) is a Linux VM, the Linux kernal manages access from containers to computer hardware

docker run <image name> command

  • Creates and runs a container docker run = docker create + docker start
  • Since docker containers have a run command by default, if you add a command, it will override the default run command.
  • If we try to execute a command inside a container (ls or echo hi) that doesn't exist in image's filesystem, we receive an error.
  • docker ps - show running images
  • docker ps --all - show any image ever created one machine
  • docker start -a <containerid> - -a (attach), watch it and tell it to output to our console from the container
  • By default: docker run shows all output; docker start does not

Lifecycles

  • When a container is exited, it can still be easily restarted
  • Images save your start command override; you cannot override it
  • docker system prune - clean existing images
  • docker stop <id> - gives running processes time to shut down (10s)
  • docker kill <id> - forces it to stop immediately (such as ping)

Seeing what was run:

  • docker create busybox echo hi there
  • b895a109c17c51e05967ac463c0d2bb77f82a1be79d2ef0e99ed135dd4cfe02c
  • docker start b895 (if you forgot the -a flag)
  • b895
  • docker logs b895
  • hi there

Redis:

  • a in-memory data store that's commonly used for web applications
  • docker run redis - starts Redis server
  • redis-cli --> need to start it inside the container
  • Use docker exec -it <containerid> <command> to execute a second command inside a running container (-it allows us to provide input to the container)
  • e.g.: docker exec -it 8ca6 redis-cli

R3 Day 73: 2020-06-01 Monday

Playing with Jamdocs on Netlify, uses gridsome and Forestry: https://virtual-jamdocs.netlify.app/

R3 Day 72: 2020-05-31 Sunday

Microservices: Running services with Docker

  • Load balance: randomize which server the incoming request goes to
  • Docker container: instance of an image; one container for each service (eg Posts, Comments, Query, Event Bus)
  • Kubernetes: a tool for running a bunch of Docker containers together
  • K8s cluster: a set of virtual machines (called nodes) managed by master
  • K8s master: a program that manages everything in the cluster, reads config file

Basics of Docker

  • docker run hello-world - A simple app that shows that docker is running successfully in your environment
  • Docker CLI - aka the Docker client
  • docker run <IMGNAME> creates a container of an image from your local machine or Docker Hub

R3 Day 71: 2020-05-30 Saturday

  • Fix type check in moderation
  • Turn off moderation service in order to mimick the "pending approval"
  • When we turn moderation service back on, the comment will not get approved :(
  • How do we deal with missing events and event syncing?
  • How do we retroactively enable a new feature in the future?
    1. Sync requests - for first run only
    2. Direct DB access - for first run only
    3. Store events - new service needs access to all events that were emitted in the past

Storing events

  • Use event bus, emits event for all services and then stores the event internally in the event bus data store
  • New service can determine what needs to be done for tasks
  • No extra processing code needs to be written (eg sync and direct db code)

Steps:

  1. Every time an event occurs we store it inside an array
  2. Add an endpoint to event bus that allows us to retrieve all the events that ever occurred

R3 Day 70: 2020-05-29 Friday

Microservices Query Service: Add moderation

Best practice: Query service only listens for update events:

  • The query service is all about presentation logic
  • Does it make sense for a presentation service to understand how to process a very precise update?
  • What happens when (in the future) there are 10 ways to update it? (Upvote, add photos, advertised)
  • The Comment service is in charge of all business logic around a comment
    • When comment updated (moderated, upvoted, downvoted, etc) Comment service emits one generic event Comment Updated to Query service
    • Query service can then take the emitted data attrs and copy them

R3 Day 69: 2020-05-28 Thursday

Beginning adding comment moderation to Udemy Microservices Blog feature

R3 Day 68: 2020-05-27 Wednesday

Added handlesbars for implementing conditional content in sendgrid email templates--hopefully can reduce the large amount of email templates down to a few!

R3 Day 67: 2020-05-26 Tuesday

Implement query service for listing posts and comments. GitHub changes

R3 Day 66: 2020-05-25 Monday

A little break from programming; working on SEO Gap analysis for my website and some complex Excel functions.

  • Sum of search volume for all Keywords appearing positions 1-3: =SUMIFS('Overall Aggregate'!$F:$F,'Overall Aggregate'!$C:$C,"<4",'Overall Aggregate'!$C:$C,">0")
  • If site has a position for a certain keyword: =IFERROR(VLOOKUP(A14,'Our site (Pos 1-100, non-brand)'!A:B,2,FALSE),"GAP")

R3 Day 65: 2020-05-24 Sunday

Microservices blog

Current data flow:

  • Get posts, show each post, get the comments for each posts (each making its own request)
  • Very inefficient

Proposed: Asynchronous call using an Event bus

  • Types of event buses: RabbitMQ, Kafka, NATS
  • Adds in a Query service that assembles all of the blogs and comments into an efficient structure
  • Listens to any time a post or comment is created
  • Posts and Comments services will emit an event when new item created

Emitting events

  • Each event should have two properties: type (eg 'PostCreated') and data (object of new item)

Notes:

  • Why do you not need to explicitly install body-parser and why do we need body-parser?
    • To handle HTTP POST request in Express.js version 4 and above, you need to install middleware module called body-parser.
    • body-parser extract the entire body portion of an incoming request stream and exposes it on req.body.
    • The middleware was a part of Express.js earlier but now you have to install it separately.
    • This body-parser module parses the JSON, buffer, string and URL encoded data submitted using HTTP POST request. Install body-parser using NPM.
    • As of April 2019: in express@4.16.0 the body-parser middleware bundled with express.

R3 Day 64: 2020-05-23 Saturday

Playing with football APIs. ESPN seems to have a good--albeit undocumented--API.

Notes:

  • When do we use const vs. import for certain libraries?
    • require is the node.js way to load modules
    • In 2016 it makes sense to stick with the import since that's the part of the standard. There is no technical reason to prefer import over require though: everything that can be done using require can be done with import and vice versa. In some cases one will be more concise, in another - the other.
  • Using Axios on Node

R3 Day 63: 2020-05-22 Friday

Microservices react components for (listing and adding) Posts and Comments complete.

R3 Day 62: 2020-05-21 Thursday

Inspired by Scott Mathson's SEO on the Jamstack presentation - VirtuaCon (Jekyll) to add schema to a website using Hugo.

Notes:

  • My implemented schema for my portfolio
  • To see multiple structures on Google Structured Data Testing, you have to wrap each structure in its own <script> tag.
  • It doesn't appear that BlogPost is an enhancement type shown in Google Search Console.

R3 Day 61: 2020-05-20 Wednesday

Different ways to define a React component

You have to extend React.Component to create a stateful component which then will need a constructor and you'll be able to use the state.

Create Component using Class

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      title: 'Hello React'
    };
  }
  render() {
    return <div>{this.state.title}</div>;
  }
}

Implicit

export default function Foo(props) {
  return <div>My component</div>;
}

Using arrow functions

Using an arrow function with a class property ensures that the method is always invoked with the component as the value for this, meaning that the manual rebinding here is redundant:

this.handleUpdateInput = this.handleUpdateInput.bind (this);

export default (details) => {
  // code
};

or from Dan Abramov: "Arrows work just fine if you give them implicit name first."

const RenderDetails = (details) => {
  // code
};
export default RenderDetails;
  • Review using the Network tab (Inspect Elements) and focus on XHR
  • What does XHR stand for? XMLHttpRequest is an API in the form of an object whose methods transfer data between a web browser and a web server. The object is provided by the browser's JavaScript environment.

R3 Day 60: 2020-05-19 Tuesday

Determine React component hierarchy

Begin adding React components

  • App

    • PostList
      • CommentList
      • CommentCreate
    • PostCreate

R3 Day 59: 2020-05-18 Monday

  • Find what is running on a port: sudo lsof -i :3000
  • Prevent returning undefined with an or-statement, e.g. res.send(commentsByPostId[req.params.id] || [])

R3 Day 58: 2020-05-17 Sunday

Section 2: A Mini-Microservices App

  • which services will we recreate? One separate service for every resource in our app: Posts and Comments
  • Consider the goals and responsibilities of each service
  • Dependencies: comments will have to know to tie to certain (existing) post

App steps

  1. Create a new app via create react app
  2. Create an express-based project for the posts service
  3. Create an express-based project for the comments service

R3 Day 57: 2020-05-16 Saturday

Microservices built with Node, React, Docker and Kubernetes

  • A monolith has routing, middleware, business logic, and database access to implement all features of the app
  • A single microservice has routing, middleware, business logic, and database access to implement one features of the app
  • Data management between services is a large problem for microservices
  • Each service gets its own database
  • Services will never, ever reach into another service's database

Communicating between services (not the same meaning as JavaScript)

  • Sync - Services communicate with eachother using direct requests
    • Service D requests (req/res) directly from Service A, B and C
    • Easy to understand
    • Service D won't need its own database
  • Async - Services communicate with eachother using events
    • Async #1 - uses an Event bus - handles little notifications/objects that come from each service
      • Once a service is connected, it can either emit events or receive events
      • Event example: type: UserQuery, data: {id: 1}
      • Uncommon; requires the same dependencies as Sync, but has additional downsides
    • Async #2
      • Start by creating a more specific expectation for req/res and what service should do: what is the minimum information needed for the database to return that information?
      • May seem inefficient
      • Services A, B, and C need to tell Service D when there is a related update to the data in database D
      • Service D relies on others to emit an event (that flows into the event bus) in order to know to add information to its db
      • Service D has zero dependencies
      • Extremely fast

R3 Day 56: 2020-05-15 Friday

Typed arrays are actually objects, and they don't have access to array methods like push and pop. 🤷‍♀️ But they can help with performance by only allocating the memory space you need. Learning data structures from #freecodecamp! #womenintech #codenewbie R3D56/#100DaysOfCode

VSCode: Alt + Shift to edit multiple lines

FCC Data Structures

  • Typed Arrays: allow you to define how much memory you want to give an array.
Type Each element size in bytes
Int8Array 1
Uint8Array 1
Uint8ClampedArray 1
Int16Array 2
Uint16Array 2
Int32Array 4
Uint32Array 4
Float32Array 4
Float64Array 8

Typed arrays do not have some of the methods traditional arrays have such as .pop() or .push(). Typed arrays are type object.

  • Learn how a Stack Works

Last-In-First-Out using .push() and .pop()

  • Create a Stack Class
  • Create a Queue Class
  • Create a Priority Queue Class
  • Create a Circular Queue
  • Create a Set Class
  • Perform a Union on Two Sets
  • Perform an Intersection on Two Sets of Data
  • Perform a Difference on Two Sets of Data
  • Perform a Subset Check on Two Sets of Data
  • Create and Add to Sets in ES6
  • Remove items from a set in ES6
  • Use .has and .size on an ES6 Set
  • Use Spread and Notes for ES5 Set() Integration
  • Create a Map Data Structure
  • Create an ES6 JavaScript Map
  • Create a Hash Table
  • Work with Nodes in a Linked List
  • Create a Linked List Class
  • Remove Elements from a Linked List
  • Search within a Linked List
  • Remove Elements from a Linked List by Index
  • Add Elements at a Specific Index in a Linked List
  • Create a Doubly Linked List
  • Reverse a Doubly Linked List
  • Add a New Element to a Binary Search Tree
  • Find the Minimum and Maximum Value in a Binary - Search Tree
  • Check if an Element is Present in a Binary - Search Tree
  • Check if Tree is Binary Search Tree
  • Find the Minimum and Maximum Height of a Binary - Search Tree
  • Use Depth First Search in a Binary Search Tree
  • Use Breadth First Search in a Binary Search Tree
  • Delete a Leaf Node in a Binary Search Tree
  • Delete a Node with One Child in a Binary Search - Tree
  • Delete a Node with Two Children in a Binary - Search Tree
  • Invert a Binary Tree
  • Create a Trie Search Tree
  • Insert an Element into a Max Heap
  • Remove an Element from a Max Heap
  • Implement Heap Sort with a Min Heap
  • Adjacency List
  • Adjacency Matrix
  • Incidence Matrix
  • Breadth-First Search
  • Depth-First Search

R3 Day 55: 2020-05-14 Thursday

Working on mocking up my faux Knight University website using TailwindCSS. I'm finding maybe it's best to componentize all the things (like buttons for example.) Maybe even headers (h2, h3)? Curious how others manage this. https://github.com/virtual/knightu R3D55/#100DaysOfCode

R3 Day 54: 2020-05-13 Wednesday

Set up my SSH config on my Mac so I don't have to type crazy-long SSH lines! Logged into DigitalOcean to look into setting up a droplet to try out Docker, TDD & CI/CD and realized I still have no idea what I'm doing. https://www.ostechnix.com/how-to-create-ssh-alias-in-linux/ R3D54/#100DaysOfCode

R3 Day 53: 2020-05-12 Tuesday

A little DGD while I code an AMP example with GDG! Thanks for teaching the #AMPstudygroup benmorss! R3D53/#100DaysOfCode https://virtual-gdg-amp-study-group.glitch.me

Feeling motivated an inspired today. Morning yoga, Toastmasers, and an AMP workshop with GDG!

AMP Project: Cheesey Bikes

AMP Notes

Tell the browser your page is AMP using one of two ways:

<html amp ...>
  <html  ...></html>
</html>
  • web vitals
  • AMP adds tags to HTML
  • Supported by Bing and IE
  • AMP cache optimizes images for you (for mobile only)
  • In the future, "AMP-like" pages might also be given AMP treatment
  • Can validate AMP pages with chrome amp validator plugin
  • if you add #development=1 to the end of the URL, errors will log to the console

Types implemented:

  • amp-img
  • amp-youtube - Some videos require scripts
  • amp-carousel
  • amp-sidebar

Required scripts have to go before the call to the element.

Extra:

Docker

Ports

  • When starting the container, you define which ports you want to bind using the -p <host-port>:<container-port> option. The Redis container exposes the service on port 6379. If you wanted to map this port directly on the host, we'd use the option -p 6379:6379. docker run -d --name redisHostPort -p 6379:6379 redis:latest
  • Binding directories (also known as volumes) in Docker is similar to binding ports using the option -v <host-dir>:<container-dir>. When a directory is mounted, the files which exist in that directory on the host can be accessed by the container and any data changed/written to the directory inside the container will be stored on the host.
  • Docker allows you to use $PWD as a placeholder for the current directory.
  • If we wanted to interact with the container instead of just seeing the output, we'd include the options -ti.

? Maybe

R3 Day 52: 2020-05-11 Monday

Finished Learn Phaser: Physics from Codecademy; working on technical audit for SEO

R3 Day 51: 2020-05-10 Sunday

I've been continuing to play with bugs in Phaser.JS game tutorials and also working out SEO issues while going through Blue Array Academy's course. (Good info--free through May.) R3D47-51/#100DaysOfCode

R3 Day 50: 2020-05-09 Saturday

Continuing Codecademy: Phaser.js Physics

SEO

  • Check Google Search Console for mobile issues
  • Are robots.txt blocking rendering resources (CSS) that would support mobile-rendering?
  • What are the errors/warnings in GSC?

R3 Day 49: 2020-05-08 Friday

Continuing Codecademy: Phaser.js Physics

SEO

What are the ideal lengths for meta?

R3 Day 48: 2020-05-07 Thursday

Continuing Codecademy: Phaser.js Physics

SEO

Questions

  • What backlinks do my competitors have?
  • How do I remove results for that I don't want users landing on - robots?
  • Force redirect * to www?
  • Can I add more links within my content to my other content?
  • Can I get redirects working for older links?
  • Rel next/prev for pagination?
  • Can we add lastmod to OU Campus sitemaps?

R3 Day 47: 2020-05-06 Wednesday

Continuing Codecademy: Phaser.js Physics

SEO

4 pillars:

  • How your site currently performs: strengths and weaknesses
  • Who your real competitors are
  • What are your business goals
  • Identify your key enablers

SEO Roadmap document

For new sites, focus on content with low competition keywords

R3 Day 46: 2020-05-05 Tuesday

A little more fun with Phaser.js today; also trying it out on my own with a little Maplestory panda and some physics like bounce and gravity. R3D46/#100DaysOfCode https://cdpn.io/virtual/debug/QWjaRRo/ZoMBaznVqdEk

R3 Day 45: 2020-05-04 Monday

Finished the first section on learning Phaser.js for game development--managing state and taking input. I'm also pretty excited for the next section (it involves coloring a pegasus!) 🦄🎨 R3D45/#100DaysOfCode https://www.codecademy.com/courses/learn-phaser/lessons/learn-phaser-basics

Phaser.JS

  • 3 ways for Storing State
    1. Create global variables for everything.
    2. Attach important variables to the Scene itself by creating a new property for this from within a Scene method.
    3. Create a gameState object and keep track of the state there.
  • In order to interact with a GameObject, we need to call the setInteractive() method on it. The setInteractive() method tells Phaser to listen in for interactive events on the GameObject.

SEO

R3 Day 44: 2020-05-03 Sunday

Today I'm learning the basics of Phaser.js for game development--including preload, create, and update functions. R3D44/#100DaysOfCode https://www.codecademy.com/learn/learn-phaser

Game Development with Phaser.JS

Phaser games are composed of Scenes that we define and pass to Phaser in the config! A Phaser Scene can have any of the following functions:

  • preload(), where we load in external files (or “assets”) to our game.
  • create(), where we define the GameObjects that are necessary at the start of our game.
  • update() where we define animation and interaction in our game (more on this in later exercises!)
  • Phaser does its best to call update() 60 times per second, so delta‘s values will normally be around 16.6.
scene: {
  preload, create;
}

Note that we are using JavaScript’s property-value shorthand, the code above would be the same if we passed { preload: preload, create: create } to the scene instead.

R3 Day 43: 2020-05-02 Saturday

Worked a little more on my minecraft drawing tool, fixing the grid on load and touch on mobile; but mostly just watching Dance Gavin Dance music videos. R3D43/#100DaysOfCode

R3 Day 42: 2020-05-01 Friday

Ready for the weekend! Made a silly little Minecraft doodle tool with jQuery. Interesting challenge using mousedown / mousemove to keep drawing. Happy May Day! 🌼 R3D42/#100DaysOfCode https://codepen.io/virtual/full/vYNeajo

R3 Day 41: 2020-04-30 Thursday

Learning about continuous integration with TravisCI and automatically building from GitHub to DockerHub. All interesting pieces--will be neat to figure this out with my own server! R3D41/#100DaysOfCode

Lynda Cert

Continuous integration

  • Travis file: .travis.yml indicates to TravisCI what environment we need to run the test on, where do we deploy the app
  • Name the docker apps using your docker hub username :) docker build -t satinflame/lyndanode
  • Create github repo
  • Activate in TravisCI
  • Trigger a build from dashboard
  • You can set ENV vars directly in travis-ci.org

Other builds

  • Swarms, allows you to cluser, manage and schedule containers
  • Kubernetes, similar to swarm but is industry standard

R3 Day 40: 2020-04-29 Wednesday

Set up docker-compose files and now know a little more about running Docker containers. 🤗 The VSCode Docker plugin is also helpful for seeing active containers and writing out Dockerfiles! (https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) https://www.lynda.com/Docker-tutorials/Leverage-power-Docker/2211315/2221492-4.html R3D40/#100DaysOfCode

🛥️ Bonus: Ahoy! Docker containers are named after shipping containers. More nautical analogies can be found in this article on demystifying Docker and Kubernetes. https://apifriends.com/api-management/docker-and-kubernetes/

Docker for Developers

Getting started

  • In a project, create a new Dockerfile (no extension) and .dockerignore (ignores files to place in container)
  • In .dockerignore:
    node_modules
    npm-debug.log
    
  • Dockerfile is the blueprint for a container
  • VSCode plugin: ms-azuretools.vscode-docker
  • To build image: docker build -t folder/backend . (-t = Name and optionally a tag in the ‘name:tag’ format), (. runs the command)
  • docker images - see all images you've created or used
  • docker rmi folder/backend - rm an image
  • docker run -p 4000:4000 folder/backend (-p is for port)
  • while docker is running docker ps to list running images (process status)
  • docker stop cc3cc or docker start cc3cc - force stop/start
  • docker pull / docker push

Develop with Docker

  • Goal: Separate the front end and back end on different containers with a volume for data
  • docker-compose.yml - tells Docker which applications we want to start
  • links - listed services must run before this; they are defined in the file (eg mongo)
  • docker-compose build to build images in the docker-compose file
  • docker logs cc3cc if you want to view logs for a particular service
  • docker-compose stop - turn off all
  • docker run -p 3000:3000 lynda/frontend

Fullstack--putting it together

  • Created fullstack folder. Put frontend and backend folders in and rename to client and api, respectively.
  • Move the backend/docker-compose.yml to main folder
  • Update dirs for backend/Dockerfile
  • Add version to docker-compose.yml, to indicate the version of docker-compose
  • links vs. depends_on?
  • recommend to start each image separately to prevent crashes:
    1. docker-compose up -d mongo
    2. docker-compose up -d app
    3. docker-compose up -d client

R3 Day 39: 2020-04-28 Tuesday

Labs: Introduction to Containers and Docker

Introduction to Containers

History of servers

  • Dedicated Server -- Maintain the hardware (physical servers), OS Kernal, Dependencies and Application code.

  • Virtual Machine -- Abstract the hardware, maintain Kernal, Dependencies and Application code.

  • Container -- Abstract the hardware, virtualize the Kernal (decoupled), maintain Dependencies and Application code.

  • CGroup (consistency group) - allows you to manage these containers and keep them consistent 2006

  • Docker - you can only edit the top layer "container layer" 2013

Introduction to Docker

IRL: you push and pull images from a registry

What are advantages of containers versus virtual machines?

  • They share parts of the OS, but not common dependencies, so they start fast and can run anywhere with the same kernel.

Which types of actions can you perform in a container Dockerfile?

  • CMD - what command to run in the container
  • FROM - creates a layer a Docker image

What is the default hostname to publish a Docker image to the Google Registry?

  • gcr.io

Clusters, Nodes, and Pods

Kubernetes -- an open-source orchestrator for a container environment, it manages and delegates jobs

  • Users
  • Master cluster server - controls jobs, scheduling, etcd (a cluster is a set of computers that works as an instance that manages nodes); masters manage jobs by scheduling jobs on nodes based on loads (move to nodes with free-space or move jobs away from nodes that pulling a lot of resources), Master runs K8s
  • Nodes (Kubelets, a K8s engine)
  • Pod -- analogous to "VM", they share networking and storage separate from the node, yaml config (containers, ports), can have redundant pods as failsafes

Services, Labels, and Selectors

  • Service assigns a fixed IP to your pod replicas, acts as communication beween pods, acts as a load balancer, defined in yaml

  • Labels -- metadata you can assign to any API object and represent identity, used for filtering/searching pods; e.g.

    Pod1;
    App: MyApp;
    Phase: prod;
    Role: FE;
    
    Pod2;
    App: MyApp;
    Phase: test;
    Role: BE;

Volumes

  • Docker provides data storage for containers, volumes do not share data
  • K8s volume allow containers in pods to share data and be stateful
  • Volume -- a directory

Why use Kubernetes (i.e. what benefit does it provide to containers?)

  • It provides a set of APIs that you can use to deploy containers on a set of nodes.

What does a pod specify?

  • A set of containers sharing networking and storage.

Which component do you use to send requests to API servers on masters to configure the cluster?

  • kubectl

Deployments and Rolling Updates

  • Deployments rely on replicate sets to manage and run pods
  • allow you to name a set of pods, and a number, to make sure pods run evenly
  • if one node (pod) is unhealthy, the deployment will spin up a new pod to keep the number correct
  • rolling updates allow you to gradually update from one image version to another, moves one pod from one ReplicaSet to another ReplicaSet with a newer image version until they've all been moved

Canary and Blue-Green Deployments

  • Canary deployment relies on a service to load-balance the traffic to primary pods based on label selectors
  • Blue-Green deployment uses the service label selector to switch all traffic from one deployment to another

Lab: Deploying to Kubernetes

What is the purpose of the ReplicaSet?

  • To create a desired number of pods.

When are rolling deployments triggered?

  • If the deployment’s pod template changes.

How does Kubernetes choose instances in the second deployment of a canary deployment?

  • The service points to instances in both deployments with a common selector.

Creating a Continuous Delivery Pipeline

Deploying a continuous delivery pipeline enables you to build, stage, test, and deploy your application, using automated triggers and manual approvals. (e.g. Spinnaker or Jenkins)

  • Build steps - analogous to running commands in a script (Spinnaker)
  • Pushing to a certain branch may update a dev environment or later prod (Jenkins)
  • Spinnaker and Jenkins are deployed as K8s applications, they are not stand-alone services

Why is it useful to set up continuous delivery with Kubernetes?

  • Continuous delivery is one of the main advantages of containers because containers are so portable.

Which 3 deployments are used in our instance of Kubernetes continuous delivery?

  • Development, canary, and production.

Which of the following best describes why we used Jenkins for continous delivery with Kubernetes?

  • You can use any tool, but it’s good to try one that’s well documented in a controlled environment first.

R3 Day 38: 2020-04-27 Monday

Finished the second infosec challenge for #freecodecamp! I learned a lot about returning statuses, errors, and messages from the API and more types of assertions with #ChaiJS. R3D38/#100DaysOfCode https://glitch.com/~virtual-fcc-issue-tracker

------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------
File                    | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------
All files               |   43.84 |    42.11 |   30.77 |   47.83 |
 app                    |   23.23 |     3.13 |       0 |   26.74 |
  assertion-analyser.js |    1.54 |        0 |       0 |    1.92 | 31-128
  server.js             |   64.71 |       25 |       0 |   64.71 | 26,32,43,52-61
 app/controllers        |   57.14 |    57.69 |   44.44 |   57.14 |
  dataHandler.js        |   57.14 |    57.69 |   44.44 |   57.14 | 16-17,20-21,24-25,28-29,32-33,47,50,54,57,60
 app/routes             |   54.93 |    51.06 |   41.38 |   59.09 |
  api.js                |   76.67 |    65.75 |     100 |   77.53 | 27,74,77,83,86,89,105-110,159,191,198,208,211,214,217,220-222,232,265,278
  fcctesting.js         |   17.31 |        0 |    5.56 |   20.93 | 38-41,46-49,54-57,63-73,77-102
------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------

R3 Day 37: 2020-04-26 Sunday

Oof.

¯_(ツ)_/¯ 3 hours on deconstructing a boolean to send in a MongoDB query. Still no idea. https://glitch.com/edit/#!/virtual-fcc-issue-tracker?path=routes/api.js:128:22 R3D37/#100DaysOfCode

R3 Day 36: 2020-04-25 Saturday

Reviewing HTTP response statuses sent in APIs. It seems that there's not even agreement over the correct status for GET for an element that does not exist in DB (404/204/null object/500?), but I now have plenty of references! R3D36/#100DaysOfCode https://nordicapis.com/best-practices-api-error-handling

Example of 404 response:

// GET (BY ID)
app.get('/api/tasks/:id', (request, response) => {
  const taskId = request.params.id;
  const task = tasks.find((task) => task.id === parseInt(taskId));
  if (!task)
    return response
      .status(404)
      .send('The task with the provided ID does not exist.');
  response.send(task);
});

Recommended example (ref RFC 7807) from Best Practices for REST API Error Handling:

This schema is composed of five parts:

  1. type — A URI identifier that categorizes the error
  2. title — A brief, human-readable message about the error
  3. status — The HTTP response code (optional)
  4. detail — A human-readable explanation of the error
  5. instance — A URI that identifies the specific occurrence of the error
{
  "type": "/errors/incorrect-user-pass",
  "title": "Incorrect username or password.",
  "status": 403,
  "detail": "Authentication failed due to incorrect username or password.",
  "instance": "/login/log/abc123"
}

For example, in HTML, a problem could be embedded by encapsulating JSON in a script tag (pg 16):

<script type="application/problem+json">
  {
    "type": "https://example.com/probs/out-of-credit",
    "title": "You do not have enough credit.",
    "detail": "Your current balance is 30, but that costs 50.",
    "instance": "/account/12345/msgs/abc",
    "balance": 30,
    "accounts": ["/account/12345",
                "/account/67890"]
  }
</script>

Example from twitter API:

{
  "errors": [
    {
      "message": "Sorry, that page does not exist",
      "code": 34
    }
  ]
}

Example from facebook API:

{
  "error": {
    "message": "An active access token must be used to query information about the current user.",
    "type": "OAuthException",
    "code": 2500,
    "fbtrace_id": "A6S6sVDcfVJd7p7vcOABR0d"
  }
}

Example from Twilio API:

{
  "code": 21211,
  "message": "The 'To' number 5551234567 is not a valid phone number.",
  "more_info": "https://www.twilio.com/docs/errors/21211",
  "status": 400
}

Questions

  • What is the proper REST response code for a valid request but an empty data? 204, 404? empty object?

R3 Day 35: 2020-04-24 Friday

Back to #freecodecamp's 2nd InfoSec challenge--I'm still not understanding how best to write functional tests. It appears that I need to learn more about returning error statuses from the API. (Any recommendations on related reading?) 🤔 R3D35/#100DaysOfCode #chaijs

Add coverage to FCC Issue Tracker Glitch project:

  • Add test/mocha.opts:
    tests
    --recursive
  • Add coverage script to package.json: "coverage": "nyc --reporter=text --reporter=html mocha -u tdd"
------------------------|---------|----------|---------|---------|--------------------------------
File                    | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------|---------|----------|---------|---------|--------------------------------
All files               |   29.29 |     4.85 |      24 |   32.41 |
 app                    |   23.23 |     3.13 |       0 |   26.74 |
  assertion-analyser.js |    1.54 |        0 |       0 |    1.92 | 31-128
  server.js             |   64.71 |       25 |       0 |   64.71 | 26,32,43,52-61
 app/controllers        |   51.72 |        0 |   71.43 |   51.72 |
  dataHandler.js        |   51.72 |        0 |   71.43 |   51.72 | 36-40,43-65
 app/routes             |   28.83 |     7.84 |   24.14 |   31.68 |
  api.js                |   38.98 |    13.33 |   54.55 |   39.66 | 27,88,133,147-255
  fcctesting.js         |   17.31 |        0 |    5.56 |   20.93 | 38-41,46-49,54-57,63-73,77-102
------------------------|---------|----------|---------|---------|--------------------------------

FCC Issue Tracker Reference

R3 Day 34: 2020-04-23 Thursday

Digging into CSS & JS SourceMaps; add ".sourceMaps()" to webpack mix.

I'll be adding these into my clients' websites as I work on them—an easier method to debug code without needing to ship an unminified version to production! R3D34/#100DaysOfCode https://css-tricks.com/should-i-use-source-maps-in-production/

Add sourceMaps() to webpack.mix.js:

  mix.js('source/_assets/js/main.js', 'js')
    .sourceMaps(productionToo = true,
      type = 'eval-source-map')
    ...

R3 Day 33: 2020-04-22 Wednesday

Checkbox ranges using Shift! And now that I know how to incorporate them, I'll need to make sure more of my projects use them.

☑️ Challenge 10/#javascript30 R3D33/#100DaysOfCode https://virtual-javascript30.herokuapp.com/

R3 Day 32: 2020-04-21 Tuesday

After learning more about ESLinting, I added similar functionality for .scss to a project using stylelint! Lots of information and plugins out there, but I'd love to see examples of your .stylelintrc files too if you use it. R3D32/#100DaysOfCode https://github.com/stylelint/awesome-stylelint

One of my goals for incorporating style linting is to be better at arranging my properties in a coherent manner.

"If you can always count on certain properties being in the same place, you can understand the CSS a bit faster (less scanning)." https://css-tricks.com/poll-results-how-do-you-order-your-css-properties/

WIP Stylelinting

"stylelint": "^13.3.3",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-declaration-block-no-ignored-properties": "^2.3.0",
"stylelint-order": "^4.0.0",
  • All .scss comments should be on their own line to prevent bugs
  • npx stylelint source/_assets/sass/FILE.scss --fix to autofix

Stylelint tips:

Glad you're learning to use stylelint! The 50+ rules that limit language features are often overlooked. They are *-pattern, *-blacklist, *-whitelist and *-max-* rules that you can use to enforce non-stylistic conventions in your CSS.

{
  "declaration-no-important": true,
  "media-feature-name-whitelist": ["min-width"],
  "selector-class-pattern": "^[a-z][a-zA-Z0-9]+$",
  "selector-max-id": 0,
  "unit-whitelist": ["rem"]
}

To enforce rem units, min-width media queries, camelCase selectors and so on.

R3 Day 31: 2020-04-20 Monday

Been putting this off since January—updated my webserver to the next OS version.

Killed off MariaDB on recommended purge/cleanup, but thankfully when I reinstalled it, all my databases were still there. Small miracles. 🌟 R3D31/#100DaysOfCode

IBM Practitioner

Restless reinvention

  • “The last best experience that anyone has anywhere, becomes the minimum expectation for the experience they want everywhere.” - Bridget Van Kralingen
  • restless reinvention a principle of Enterprise Design Thinking that represents active continuous testing and learning in order to improve the solution to a problem
  • Visualize your ideas, so that your team and users can understand them clearly. It does need to communicate the idea enough for someone else to understand it and give you feedback.
  • The next time you have a new idea to share with your team, before you tell them about it, draw it on paper. Think about how a diagram or visual representation might convey something that words couldn’t.
  • Ideate!
  • Prompts like “...on the moon” automatically trigger non-obvious ideas. And that’s how you get to something no one else does yet.Try thinking of your users’ problem in a different context, like “without modern technology,” or “in the year 3019.”
  • Take risks: Let’s talk about failure

Include a variety of voices

  • Enterprise Design Thinking asks that you collaborate as a whole team to get the job done.
  • Diverse Empowered Teams an Enterprise Design Thinking Principle that represents that a group of people with varied perspectives more successfully make decisions together and work toward shared goals
  • Ideate
  • Cluster like ideas
  • Build off eachother's ideas
  • Come to some alignment on a path forward
  • Build alignment across your team, alignment a state of agreement and understanding on a group decision that includes follow-through on that decision
    • Next time you’re in a meeting where the conversation spins in circles, ask everyone to grab something to write with, visualize their thoughts, and then take turns sharing.
    • Next time you’re in a meeting where only one or two people share their opinion, hold a silent and anonymous voting session to expose everyone’s viewpoints.
  • Start sharing stories
  • Playbacks an Enterprise Design Thinking Key, story-based presentations that bring stakeholders and whole teams together in a safe space to exchange feedback
  • Alignment:
    • Starting a new project or initiative. Answer questions like: Who will be the users and stakeholders? What experience are we trying to improve and why?
    • Deciding as a team on a future experience for your users. Answer questions like: What do we think our users need to be successful? How are we going to serve those needs?
    • Reviewing progress as you deliver. Answer questions like: Do we successfully deliver value to our users? Are we still aligned as a team?

Planning

  • In design thinking, to solve for an actual need that exists in the world, you must take the time to write clear problem statements around your intent: What problem are you solving, for whom, and why?

R3 Day 30: 2020-04-19 Sunday

Spent the day learning https://istanbul.js.org/ and code coverage. What is it? The measure (%) of how much code is executed with a given operation. (Goal: 100% coverage!) R3D30/#100DaysOfCode

  • Executed statements
  • Branches
  • Called, defined functions
  • Executed lines

🎵 Bonus: why do you run the Istanbul test coverage library with nyc instead of istanbul? It's a reference to the song "Istanbul (not Constantinople)" from They Might Be Giants! https://www.youtube.com/watch?v=xo0X77OBJUg

What is code coverage?

  • Code coverage: the measure of how much code is executed with a given operation (%)
  • Four types, the proportion of:
    1. Executed statements
    2. Branches
    3. Called, defined functions
    4. Executed lines
  • The percentages will often be pretty close to each other, but you can have 100% line execution but miss out on some branches.
  • Reports also show uncovered lines, which are not covered by any test

Statements--perform an action in code

  • can be many lines
  • consists of a group of keywords
  • examples:
    • content in an if... else block
    • declaring a variable const...
    • what executes in a while loop

Branches--each statement within a conditional

  • include conditions, logical ors, ands and not (||, &&, !), ternary operator
  • examples:
if (condition) {
  statement1(); //branch 1
} else {
  statement2(); //branch 2
  statement3(); //branch 2
}

Function--a function that is never called

Line--a line that is never executed

How do we implement code coverage?

Code coverage libraries:

Install:

  • add nyc to dev dependencies: npm install nyc -D
  • add coverage to package.json: "coverage": "cross-env DEBUG=nadia:* nyc --reporter=text --reporter=html mocha"
  • Give test runner as argument (mocha): npm run -s coverage
  • Ignore coverage files from .eslintignore

Functional Testing

  • Based on business requirements
  • Describes what a function does, not how
  • Libraries:
    • Phantom.js (Casper.js)
    • Selenium WebDriver (Nightwatch, webdriver io)
    • SuperAgent (Chai HTTP)

Example user stories for Functional Testing:

As a user, I want to:

  • See a reservation form
  • Book a table.
  • Submit a valid reservation
  • Be thanked upon success.
  • Submit an invalid reservation
  • Be informed that there's a problem.

Installing Chai HTTP: npm install chai-http -D

Chai docs on using .get(), end

Because the end function is passed a callback, assertions are run asynchronously. Therefore, a mechanism must be used to notify the testing framework that the callback has completed. Otherwise, the test will pass before the assertions are checked.

For example, in the Mocha test framework, this is accomplished using the done callback, which signal that the callback has completed, and the assertions can be verified:

it('fails, as expected', function (done) {
  // <= Pass in done callback
  chai
    .request('http://localhost:8080')
    .get('/')
    .end(function (err, res) {
      expect(res).to.have.status(123);
      done(); // <= Call done to signal callback end
    });
});

it('succeeds silently!', function () {
  // <= No done callback
  chai
    .request('http://localhost:8080')
    .get('/')
    .end(function (err, res) {
      expect(res).to.have.status(123); // <= Test completes before this runs
    });
});

When done is passed in, Mocha will wait until the call to done(), or until the timeout expires. done also accepts an error parameter when signaling completion.

Need to parse HTML to check for valid output? Check out cheerio.js

"100% Test" coverage:

  • Does not mean that your application is bug free.
  • Does not mean that you wrote good tests, it just means that your tests didn't fail.
  • Tests could not be properly isolated so unexpected behavior could still occur.
  • Does not deliver new functionality
  • Not a substitute for peer code review.

Open source continuous integration:

  • Buildbot
  • Jenkins
  • Strider CD

Continuous Integration as a Service:

  • CircleCI
  • Gitlab
  • TravisCI
  • Heroku (I think?)

CI Features to look for:

  • Matches your prod environment
  • Easy to configure and use
  • Scalable
  • Integrates with your workflow

Establishing Code Standards

  • try a eslint config
  • jsdocs to require good docs, require-jsdoc, valid-jsdoc
  • Mocha rules: eslint-program-mocha

Links:

Mine are here: https://satinflame.com/uses

Brad Traversy

  • Live Server
  • Live Sass Compiler
  • ES7/React/Redux/GraphQL Snippets
  • Vetur
  • Auto Tag Rename
  • Bracket Colorizer
  • Chrome Debugger
  • Git Lens
  • Dotenv
  • Quokka
  • Python
  • PHP Intellisense
  • Docker
  • Auto Markdown Preview

R3 Day 29: 2020-04-18 Saturday

Learning how to test code and watch if it successfully creates entries & which actions are performed. Test doubles, stubs, spies & mocks. Not understanding everything... but more info can also be found in this 800+ page book! http://xunitpatterns.com 🙈 R3D29/#100DaysOfCode

  • Test Doubles
    • A replacement for any component of executable code (module or method)
    • Simplified
    • May be hard-coded
    • Isolated, consistent, and fast
    • Sinon.js is one library, sinon-chai is a module we can use with Chai for readability: npm install sinon@2 sinon-chai -D
  • A test stub verifies indirect inputs by providing a controlled response.
  • A test spy records indirect outputs by reporting on how the request was made for later verification.
  • A mock object verifies indirect outputs by setting up expectations and throwing an exception if it's used unexpectedly.
  • A fake object just runs faster with less functionality.
  • And a dummy object specifies values used for testing.
  • xunitpatterns.com and martinfowler.com for more info

Overriding dependencies

  • Testing dependencies is integration, not unit testing
  • 3 main dependency maniuplation libaries:
    1. Mockery (easiest, shows warnings)
    2. Proxyquire (offers more granular control): npm install proxyquire -D
    3. Rewire (can override modules, but doesn't support es6 or transpilers like Babel)
  • Stub update in package.json: "test": "cross-env DEBUG=nadia:* mocha"

Process of Stubbing a Custom Response

  • Replicate how the method responds:
    • Is it returning a value, throwing an exception, or is it a promise that resolves or rejects?
    • Once you know how the response will be made you'll need to replicate what is being returned. The goal should be to match the structure.
    • If it returns an integer, the stub will need to return an integer, and so forth.
  • The easiest way to discover this is to console.log the actual response if the API documentation isn't available or you're not sure.

Example: testing a module that updates the database:

  • How can we use a wrapped stub?
  • In our application, the best target will be create, which uses save to insert a reservations record into the database. Create uses that response to return an ID.
  • To do this, we'll wrap the database, replace the run method, and return a resolved promise that has the correct structure.

Observing interactions with Spies

  • spies record information about function calls
  • How are we going to use spies in our application? -It'd be useful to check how the validator was called when creating a reservation.
  • The validator should only be called once. When called through create, it should be called with a transformed reservation.

Sinon Mocks

  • Control how a unit of code is being used
  • Gives no responses
  • Tests how many times called
  • Tests with which arguments
  • Example, ensure db is only called once when saving

Book ref: xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros

R3 Day 28: 2020-04-17 Friday

Back to more testing with Mocha and Chai. Set up example tests for unit tests, callbacks and promises. Mocha doesn't use arrow functions in order to keep the correct binding for this. R3D28/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html

  • Unit test:
    • Setup test with describe
    • Arrow functions in mocha are discouraged because they break the binding to this
    • Setup suite of tests w/ context
    • mocha is not recursive by default, need to pass recursive flag npm test -- --recursive
    • you can add test/mocha.opts (deprecated) to override default setting; add to file: --recursive or see .mocharc.json
  • Testing asynchronous code
    • Add a callback to it, usually called done()
  • Testing promises
    • return the promise
    • then put the assertion in .then or .catch
    • do not use callback (do not use done())
describe('Reservation Suite', function () {
  context('Date and Time Combination', function () {
    it('should return a ISO 8601 date and time with valid input', function () {
      const date = '2020/04/17';
      const time = '04:53 PM';

      Reservation.combineDateTime(date, time).should.equal(
        '2020-04-17T16:53:00.000Z'
      );
    });
  });
});

R3 Day 27: 2020-04-16 Thursday

Code things that make you happy! 😄

Saw examples of snaking timelines from #smashingconf and haven't stopped thinking about them since! Snake

Haml, CSS, and JS for reordering cards in the responsive design. R3D27/#100DaysOfCode #WomenWhoCode https://codepen.io/virtual/pen/bGVpdyN

IBM Design thinking

Notes

  • Identify your users and their problems
  • a focus on user outcomes an Enterprise Design Thinking Principle that represents putting your users at the center of your work and solving for their needs
  • Ask the 5 Whys
  • Possible questions
    • What does Joe do at work?
    • What do they like and dislike?
    • How could their current experiences be improved?
  • Sponsor users: 1-2/month for 6-9 months
  • design research the practice of inquiry and discovery that builds knowledge, insight, and empathy for your users
  • Questions during research
    • What’s one thing you learned that you didn’t know before?
    • What pain points did you see the gate agents experience
    • What was difficult about the exercise?

R3 Day 26: 2020-04-15 Wednesday

Today I customized ESLint to keep code tidy and standardized within a project.

You can also customize your own config and add it to npm for others to use as a dependency—as Google and Airbnb have done for their teams. Neat! R3D26/#100DaysOfCode https://medium.com/@natterstefan/how-to-create-your-own-shared-eslint-prettier-and-stylelint-configuration-3930dd764de3

Validate correctness using Unit Testing

Node.js Testing frameworks:

  • AVA
  • Jasmine
  • Jest
  • mocha -- oldest and most used; does not include assertsions, but supports plugins (Chai)
  • tape

Assertion Libraries (that work with any testing framework):

  • Assert (built into Node.js)
  • Chai - can be used for Node.js and browser testing
    • assert: TDD-style
    • expect/should: BDD-style
  • code - simplified expect functions from Chai only
  • should.js

Installing

  • npm install mocha chai -D
  • add "test": "mocha" to scripts
  • add ESLint env vars for mocha globals--add test/.eslintrc.js
module.exports = {
  env: {
    mocha: true
  },
  rules: {
    'no-unused-vars': ['error', { varsIgnorePattern: 'should|expect' }]
  }
};

ESLint Configs

R3 Day 25: 2020-04-14 Tuesday

Learning how to set up and configure EditorConfig and ESLint in order to maintain consistent coding standards in my projects! R3D25/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html

Linter:

  • root: true only in root
  • built-in checker: node --check jsfile.js
  • JSLint, JSHint, and ESLint (most popular)

ESLint

  • don't install globally, package antipattern
  • npm install eslint -D
  • add "eslint": "eslint", to the scripts in package.json due to the placement of eslint
  • npm run eslint or npm run eslint .
  • need to configure ESLint eslint init
  • don't put ESLint configs in package.json, use .eslintrc.js instead
  • try putting flags before eslint, npm run -h eslint
  • check a file npm run -s eslint file.js

R3 Day 24: 2020-04-13 Monday

Following along on a @lynda course from @FluxSauce on Node.js testing. Great info on testing libraries vs. assertion libraries, TDD vs. BDD, and soon will be digging into setting up better linting tools! R3D24/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html

Node.js: Testing and Code Quality - Jon Peck

  • Quality code is deficiency free
    • Useful: What does it do, can it be extended to work for similar operations?
    • Maintainable
      • Can you maintain the code?
      • Can someone else maintain it without help?
      • Can someone else read the code and understand the design and intent?
  • Code standards are the easiest way to improve code quality
    • Use good conventions: spacing, whitespace, naming
    • Use linters to check for good code standards
    • Run lint before peer review

Different types of testing:

  • Unit testing: the smallest testable part of an application, ie a function
    • tests an isolated unit via API
    • safe to run repeatedly
    • fast
    • validates an assertion, returns true or false
    • simulate dependencies
    • do not test third-party code
  • Integrations testing: builds upon unit tests, combines and tests resulting combinations (APIs, UIs, results)
    • more complex
    • harder to maintain
    • checks more steps (register success, validation, etc)
  • Functional testing: tests how the system works together, against the requirements
    • focus is on the result, not the code
    • slower to execute
    • tests user interface, eg. click button, enter input, see UI message
  • System testing: tests the whole system, security issues, usability, etc
    • any test that requires the whole system to be running
  • Regression testing: "regression"-software bug

TDD vs BDD

  • TDD: terminology about testing
    • describes when something works and when it doesn't
    • too literal
    • can lead to overly specific tests
  • BDD: terminology about behavior examples
    • describe how and why with examples
    • acceptance criteria as a scenario:
      • given some initial context (the givens)
      • when an event occurs
      • then ensures some outcomes
    • describe/context - creates a group of specifications (tests)
    • specify/it - create a specification, test, or test-spec

R3 Day 23: 2020-04-12 Sunday

Happy Easter! 🌼 Taking a step back from freeCodeCamp projects to learn JavaScript testing a bit more thoroughly on Lynda. Are there any recommendations for good walkthroughs on TDD/BDD (Test/Behavior-Driven Development) using Node.js/ChaiJS? R3D23/#100DaysOfCode

Reviewing Testing

Node.js: Testing and Code Quality - Jon Peck

Notes:

TDD General Notes:

  • Mocha is the testing library that allows us to run tests, and Chai is an assertion library that contains some helpful functions that we’ll use to verify our test results.
  • describe is used to group individual tests
  • In TDD, you write your unit test first, watch it fail, and then implement code changes until the test passes.

Five steps to TDD (also known as Red-Green-Refactoring):

  1. Read, understand, and process the feature or bug request.
  2. Translate the requirement by writing a unit test. If you have hot reloading set up, the unit test will run and fail as no code is implemented yet.
  3. Write and implement the code that fulfills the requirement. Run all tests and they should pass, if not repeat this step.
  4. Clean up your code by refactoring.
  5. Rinse, lather and repeat.

Questions:

  • What is BDD? I keep seeing it. In TDD (Test Driven Development), the test is written to check the implementation of functionality, but as the code evolves, tests can give false results. BDD (Behavior Driven Development) is also a test-first approach, but differs by testing the actual behavior of the system from the end users perspective.

R3 Day 22: 2020-04-11 Saturday

R3D22/#100DaysOfCode Working out how to use expect functions in addition to assert in Chai, but the doc's examples aren't even working in my app. Need to check version support.

Slow going with adding testing to the 2nd infosec challenge for #freecodecamp.

Questions:

  • Which tests do I need for functional tests?
  • Why aren't expect tests working?

IBM Design thinking

Notes

  • Design thinking is the mindset that aims to improve the situation of people through the experiences they have. If you’re interested in solving problems for people, then you can practice design thinking.
  • The Loops - Understand the present and envision the future in a continuous cycle of:
    • observing: Immerse yourself in the real world with design research. Interview users, watch them work, and test your ideas with the people who matter most to inform your decision-making and understanding.
    • reflecting: Come together and look within to synchronize your movements, synthesize what you’ve learned, and share your “aha” moments with each other. Decide together and move forward with confidence.
    • making: Give concrete form to abstract ideas. The earlier you make the faster you learn. Put your ideas out there before they’re complete and improve them as you go.
  • Enterprise Design Thinking - a tailor-made approach for large, distributed teams to help them quickly deliver human-centered outcomes to the market
  • Principles
    1. A focus on user outcomes
    2. Restless reinvention
    3. Diverse Empowered Teams
  • Keys
    1. 🔺 Hills: Hills are statements of intent written as user enablements. They follow a format of Who, What, and Wow.
    2. 🔲 Playbacks: Playbacks are story-based presentations that share insights, ideas, and updates to a user experience.
    3. *️⃣ Sponsor Users: Sponsor Users are external clients, future clients, or end users that represent your target user, who regularly contribute domain expertise to your team.

R3 Day 21: 2020-04-10 Friday

CRUD Working! Post, Get, Put, and Delete routes for #freecodecamp's 2nd infosec project now setup.

Next to figure out is the testing and learn what I really set up incorrectly. 😆 R3D21/#100DaysOfCode https://virtual-fcc-issue-tracker.glitch.me/apitest/

Questions:

  • What's the difference in creating an Update statement (versus Find) in MongoDB? add { $set: projectObj } with value-pairs that need to be updated.
db.collection("issues").update(
  { _id: ObjectId(issueID) },
  { $set: projectObj },
  function(err, result) {
    ...

R3 Day 20: 2020-04-09 Thursday

Today I created the delete route for #freecodecamp's 2nd Infosec project.

Added a check to see if the ID actually exists prior to deleting it. Using findOne() seems to be better as you don't need to parse an array. R3D20/#100DaysOfCode https://virtual-fcc-issue-tracker.glitch.me/apitest/

Questions:

  • What format do I send the req.body info as in Postman? x-www-form-encoded

  • How do I reference the _id? req.body._id

  • How do I get the result of a promise?

    db.collection('issues').findOne({ _id: ObjectId(req.body._id) }, function (
      err,
      result
    ) {
      if (err) {
        throw err;
      } else {
        // do things
      }
    });

R3 Day 19: 2020-04-08 Wednesday

Spent another day digging into MongoDB for #freecodecamp's 2nd Security project. Updated my post route to use real form input and worked out the response for the get route as well.

Half of the CRUD is working now! 😋 https://virtual-fcc-issue-tracker.glitch.me/apitest/ R3D19/#100DaysOfCode

Questions:

  • How do I return the result from a MongoDB query? Use .toArray()
  • How do I pass the serialized form fields to the route? Use req.body
  • How can I pass the current time into MongoDB? new Date(Date.now()) May also be able to setup a defined field in MongoDB that defaults to current time

Started IBM's Design Thinking Practitioner Course

R3 Day 18: 2020-04-07 Tuesday

Started on the second security challenge for @freeCodeCamp, beginning with a bit of CRUD!

I'm really rusty on MongoDB, but I finally got my database connection set up and a fake object for Project Mew inserted in a collection! 🎉 R3D18/#100DaysOfCode

Glitch Project

Questions:

  • Where do I put the database connection? (I put it in api.js)
  • How do I insert in mongoDB? Create db in mlab, create user, create collection, insert into db
  • Does the insert into DB return an _id?
  • What should the res return in the API?

R3 Day 17: 2020-04-06 Monday

✅ Metric-Imperial Converter completed for @freecodecamp!

🔹 Built out controller functions
🔹 Verified correct return types
🔹 Added HelmetJS for security
🔹 Created unit and functional tests
🔹 Wrote API routes

https://virtual-fcc-mi-convert.glitch.me R3D17/#100DaysOfCode #womenintech

R3 Day 16: 2020-04-05 Sunday

Finished the unit tests for @freecodecamp's M/I converter challenge. Tomorrow: functional tests!

I learned that Chai's approximately only works if you actually return numbers—I will need to pay attention to my return types! R3D16/#100DaysOfCode

Enjoy your weekend!

R3 Day 15: 2020-04-04 Saturday

Building the first project for @freecodecamp's security challenges. Added controller functions to process input (32mi) and calc output (51.5kg). @getpostman helps to debug. Love these projects, they really make things click! R3D15/#100DaysOfCode https://virtual-fcc-mi-convert.glitch.me

"Controllers" — functions that separate out the code to route requests from the code that actually processes requests.

Since I always forget the MVC setup:

  • Models are the heart of any JavaScript application, containing the interactive data as well as a large part of the logic surrounding it: conversions, validations, computed properties, and access control.
  • Routes to forward the supported requests (and any information encoded in request URLs) to the appropriate controller functions.
  • Controller functions to get the requested data from the models, create an HTML page displaying the data, and return it to the user to view in the browser.
  • Views (templates) used by the controllers to render the data.

R3 Day 14: 2020-04-03 Friday

R3D14/#100DaysOfCode Started working out the first @freeCodeCamp security challenge for a Metric-Imperial Converter. Reviewing how to use regex to split strings and integrate HelmetJS for security.

Glitch project

R3 Day 13: 2020-04-02 Thursday

Continuing @freecodecamp's passport authentication and trying to debug errors preventing tests to pass even though it works. Frustrating! 😣

I hope all your coding adventures have been more rewarding today! R3D13/#100DaysOfCode

There are so many issues and gotchas with this chapter outlined in a myriad of forum posts and GitHub issues.

Time for me to leave this challenge and hope when I get back to it, it's been given some TLC. I tried a GitHub PR for the page title issues, but it was rejected.

R3 Day 12: 2020-04-01 Wednesday

Just like any time I do server setup, spending way too much time debugging silly things like page titles...

Is there anyone with @freecodecamp approval access that can review the PRs for this repo? R3D12/#100DaysOfCode https://github.com/freeCodeCamp/boilerplate-advancednode/pulls

Glitch project

Serialization of a User Object

To serialize an object means to convert its contents into a small key essentially that can then be deserialized into the original object. This is what allows us to know whos communicated with the server without having to send the authentication data like username and password at each request for a new page.

Authentication Strategies

A strategy is a way of authenticating a user.

R3 Day 11: 2020-03-31 Tuesday

Markdown links—someone once shared that "Brackets" comes before "Parenthesis" alphabetically, and I haven't screwed up creating a link since! 💙 (Thank you!)

Working on @freeCodeCamp's Node & Express section, starting with 🐶 #PugJS templates. R3D11/#100DaysOfCode

Pug code example:

doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) bar(1 + 5)
  body
    h1 Pug - node template engine
    #container.col
      if youAreUsingPug
        p You are amazing
      else
        p Get on it!
      p.
        Pug is a terse and simple templating language with a
        strong focus on performance and powerful features.

Passport

It's time to set up Passport so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the key to access the data stored on the server.

const session = require('express-session');
const passport = require('passport');

// ...

app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: true,
    saveUninitialized: true
  })
);

// passport. initialize() is a middle-ware that initialises Passport. Middlewares are functions that have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle
app.use(passport.initialize());

// passport.session() acts as a middleware to alter the req object and change the 'user' value that is currently the session id (from the client cookie) into the true deserialized user object.
// `app.use(passport.session());` is equivalent to `app.use(passport.authenticate('session'));`
app.use(passport.session());

R3 Day 10: 2020-03-30 Monday

Making R3D10/#100DaysOfCode an easier day, reading through the history of languages on Code Complete.

Amused by this tidbit: "The acronym PHP originally stood for Personal Home Page."

Language History from Code Complete (ed.2, pp 63-66)

LL = Low-level ML = Mid-level HL = High-level OOL = Object-oriented

  • Ada (HL) - embedded systems, named after Adam Lovelace
  • Assembly language (LL) - assembler for a single machine instruction, used in processors
  • C (ML) - originally associated with UNIX OS, extensive use of pointers and addresses, used for microcomputers 1980-1990s
  • C++ (OOL) - classes, exception handling, templates, compatible with C
  • C# (OOL) - programming environment developed by Microsoft, used for dev on Microsoft platforms
  • Cobol - DOD, mathematical and object-oriented. Stands for COmmon Business-Oriented Language
  • Fortran (HL) - Scientific and engineering applications, stands for FORmula TRANslation
  • Java (OOL) - Sun Microsystems, platforms convert Java source code to bye code and runs in a virtual machine
  • JavaScript - Client-side programming
  • Perl - used for administration tasks, building scripts, report generation. Stands for Pratical Extraction and Report Language
  • PHP - Can be embedded in web pages and present database info, originally stood for Personal Home Page but now stands for PHP: Hypertext Processor
  • Python (OOL) - Interpreted, used for writing scripts
  • SQL - Structured Query Language - is declarative language meaning that it does not define a sequence of operations, but rather the result of some operations
  • Visual Basic (HL) - Developed at Dartmouth College in 1960s, stands for Beginner's All-purpose Symbolic Instruction Code

R3 Day 9: 2020-03-29 Sunday

Another #javascript30 down! Neat console logging ideas like formatting as errors, custom styles & grouping. Added @prismjs for syntax highlights.

I return for the intro riff 🎶 & stay for the great content from Wes Bos. 😉
https://virtual-javascript30.herokuapp.com/09-dev-tools/index.html R3D9/#100DaysOfCode

R3 Day 8: 2020-03-28 Saturday

R3D8/#100DaysOfCode Completed #Helmetjs/bcrypt for @freeCodeCamp's Information Security and QA section.

Have any of you finished this certification? Thinking face I'm curious about what the projects are like and would love to see your GitHub repos/examples! #womenintech

Applied InfoSec Challenges

HelmetJS

HelmetJS is a type of middleware for Express-based applications that automatically sets HTTP headers to prevent sensitive information from unintentionally being passed between the server and client. While HelmetJS does not account for all situations, it does include support for common ones like Content Security Policy, XSS Filtering, and HTTP Strict Transport Security, among others. HelmetJS can be installed on an Express project from npm, after which each layer of protection can be configured to best fit the project.

  • contentSecurityPolicy for setting Content Security Policy
  • dnsPrefetchControl controls browser DNS prefetching
  • frameguard to prevent clickjacking
  • hidePoweredBy to remove the X-Powered-By header
  • hsts for HTTP Strict Transport Security
  • ieNoOpen sets X-Download-Options for IE8+
  • noSniff to keep clients from sniffing the MIME type
  • referrerPolicy to hide the Referer header
  • xssFilter adds some small XSS protections

Resources

BCrypt

BCrypt hashes are very secure. A hash is basically a fingerprint of the original data- always unique. This is accomplished by feeding the original data into an algorithm and returning a fixed length result. To further complicate this process and make it more secure, you can also salt your hash. Salting your hash involves adding random data to the original data before the hashing process which makes it even harder to crack the hash.

BCrypt hashes will always looks like $2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm which does have a structure. The first small bit of data $2a is defining what kind of hash algorithm was used. The next portion $13 defines the cost. Cost is about how much power it takes to compute the hash. It is on a logarithmic scale of 2^cost and determines how many times the data is put through the hashing algorithm. For example, at a cost of 10 you are able to hash 10 passwords a second on an average computer, however at a cost of 15 it takes 3 seconds per hash... and to take it further, at a cost of 31 it would takes multiple days to complete a hash. A cost of 12 is considered very secure at this time. The last portion of your hash $ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm, looks like one large string of numbers, periods, and letters but it is actually two separate pieces of information. The first 22 characters is the salt in plain text, and the rest is the hashed password!

R3 Day 7: 2020-03-27 Friday

Finished Unit and Functional testing for @freecodecamp.

I was always curious about how to test an actual form. #ChaiJS can mimic filling in a form & submitting it, and test the output on the screen.

Assertions cheat sheet: https://devhints.io/chai R3D7/#100DaysOfCode

Chai Testing Repo running on Heroku

suite('Functional Tests', function () {
  test('submit "surname" : "Vespucci" - write your e2e test...', function (done) {
    browser
      // fill the form, and submit.
      .fill('surname', 'Vespucci')
      .pressButton('submit', function () {
        // assert that status is OK 200
        browser.assert.success(); // 200
        // assert that the text inside the element 'span#name' is 'Amerigo'
        browser.assert.text('span#name', 'Amerigo', 'name should be Amerigo');
        // assert that the text inside the element 'span#surname' is 'Vespucci'
        browser.assert.text('span#surname', 'Vespucci', 'surname is Vespucci');
        // assert that the element(s) 'span#dates' exist and their count is 1
        browser.assert.element('span#dates', 1, 'dates element exists');
        // assert.fail();
      });
    done();
  });
});

R3 Day 6: 2020-03-26 Thursday

R3D6/#100DaysOfCode Another day working with Chai on @freeCodeCamp. I'm impressed with the flexibility of tests like "approximately."

Spent a while debugging my testing scripts until I realized I had the wrong branch set to be automatically deployed on Heroku--oops! 🙄

R3 Day 5: 2020-03-25 Wednesday

Happy to now be a monthly supporter for @freeCodeCamp!

R3D5/#100DaysOfCode Started working through the testing curriculum; worked out how to manage via GitHub and Heroku (instead of Glitch) so my workflow is much happier. 🤗 https://github.com/virtual/boilerplate-mochachai

Chai Testing Repo running on Heroku

Note on CSS Update for reduced motion:

@​media (prefers-reduced-motion: reduce) {
  *,
  *:after,
  *:before {
    animation: none !important;
    transition: none !important;
  }
}

R3 Day 4: 2020-03-24 Tuesday

R3D4/#100DaysOfCode In-range but still invalid? Here's a pen to help you understand the difference between :invalid and :out-of-range CSS psuedo-selectors. Example from @FrontendMasters CSS In-Depth. https://codepen.io/virtual/pen/abORwjw?editors=0100

Psuedo-selectors

Selectors that match based on the current state of the UI

:valid

:invalid (out of range and/or unmatched step value)

:required

:optional

:in-range

:out-of-range (out of range, ignores step)

:user-invalid Similar to on blur

R3 Day 3: 2020-03-23 Monday

R3D3/#100DaysOfCode I'll admit, I don't use these much (aside from form styling), and many have been around since CSS2! Do you ever style your elements this way?

Reviewing selectors, specificity, and gotchas with @estelle —you know, for the next CSS trivia night. Winking face

Selectors

Selector: "The part that comes before the declaration block"

Pseudo-classes have the same selector weight as classes (0 - 1 - 0)

Attribute selectors

[attribute] Has the attribute (e.g. [type])

[attribute='value'] Equal to the exact value only

[attribute~='value'] Includes the full word 'value'

[attribute|='en'] Equal to "en" specifically or "en-" followed by anything

[attribute^='val'] Starts with (e.g. 'https:')

[attribute$='lue'] Ends with (e.g. ".pdf")

[attribute*='alu'] Has anywhere

R3 Day 2: 2020-03-22 Sunday

Interesting information on CSS failures and faux-commenting while you're testing code as shown from CSS in Depth (v3) with @estelle, @FrontendMasters.

If you style CSS, take a peek at all these less common selectors! https://drafts.csswg.org/selectors-4/#overview R3D2/#100DaysOfCode

Began CSS In-Depth, v3, Estelle Weyl (FEM)

  • ✓ Introduction
  • Selectors
  • Specificity
  • Generated Content
  • Media Queries
  • Units and Values
  • Colors & Transparency
  • Math (variables, calc, etc)
  • Text and fonts
  • Box Model
  • Float & Flexbox
  • Tables
  • CSS Grids
  • Backgrounds & Images
  • Gradients
  • Transforms
  • Transitions and Animations
  • Other features
  • File organization

R3 Day 1: 2020-03-21 Saturday

Scrubbed my toaster, cleaned my knife set, cut my finger, and then decided maybe I should start #100daysofcode back up instead.

Challenge 8/#javascript30--now enjoying this mindnumbingly fun canvas board I can now doodle on like it's 2002. R3D1 https://virtual-javascript30.herokuapp.com/08-canvas/index.html

Starting Round 3 of #100DaysOfCode--honestly for a mental health break from reading #covid19 info; getting back into Javascript30


⭐ ⭐ Round 3 begins ⭐ ⭐

Ideas tabled for later (see R3 Day 100 for projects worked on!)

  • Animations / manipulations with SVG
  • Create a game with an isometric view
  • Create a virtual pet site
  • Mod a game (Minecraft)
  • Continue developing out Libworx
  • Consider apps that might help Toastmaster groups online (Timer, Feedback forms)
  • FCC AWS Certification