Skip to content

Commit 85849cd

Browse files
authored
Merge pull request #8 from davidsilva/chore/clean-up
Chore/clean up
2 parents 848da49 + c3b0df7 commit 85849cd

File tree

11 files changed

+193
-2045
lines changed

11 files changed

+193
-2045
lines changed

.devcontainer/devcontainer.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,18 @@
2727
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
2828
// Allow devcontainer to use the host's AWS credentials and configuration
2929
"source=${localEnv:HOME}/.aws,target=/root/.aws,type=bind,consistency=cached"
30-
]
30+
],
31+
"remoteEnv": {
32+
"CHROME_BIN": "/usr/bin/chromium",
33+
"HOST_PROJECT_PATH": "${localWorkspaceFolder}"
34+
},
3135

3236
// Use 'forwardPorts' to make a list of ports inside the container available locally.
3337
// "forwardPorts": [],
3438

3539
// Use 'postCreateCommand' to run commands after the container is created.
36-
// "postCreateCommand": "yarn install",
40+
"postCreateCommand": "apt-get update && apt-get install -y chromium"
41+
3742

3843
// Configure tool-specific properties.
3944
// "customizations": {},

.env.example

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
PORT=3000
2-
DATABASE_URL=postgres://your_db_user:your_db_password@localhost:5432/your_db_name
1+
DATABASE_URL=postgres://<USERNAME>:<PASSWORD>@db:5432/interviewprepdbinstance
2+
POSTGRES_USER=<USERNAME>
3+
POSTGRES_PASSWORD=<PASSWORD>
4+
POSTGRES_DB=interviewprepdbinstance

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
uses: actions/setup-node@v2
4646
with:
4747
node-version: '22'
48-
48+
4949
- name: Create .pgpass file
5050
run: |
5151
echo "localhost:5432:postgres:postgres:postgres" > ~/.pgpass &&

README.md

+88-173
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This project grew out of wanting to prepare for an interview for a job that woul
44

55
* Angular instead of React/NextJS
66
* Angular Material instead of AWS Amplify's UI library. (I do, however, use Tailwind CSS in both.)
7-
* Postgres instead of DynamoDB (Sql vs NoSQL)
7+
* Postgres instead of DynamoDB (SQL vs NoSQL)
88
* REST (using a NodeJS/Express backend) vs GraphQL (using Amplify and AppSync)
99
* This project has nothing in regard to authentication (yet), while the Amplify project implemented authentication right at the beginning.
1010

@@ -26,217 +26,132 @@ I'm using the [Node.js & TypeScript (typescript-node)](https://github.com/devcon
2626
* [Terraform](https://github.com/devcontainers/features/tree/main/src/terraform)
2727
* [PostgreSQL Client](https://github.com/robbert229/devcontainer-features/blob/main/src/postgresql-client/README.md)
2828

29-
In devcontainer.json you'll see that I'm specifying a network. That's so the devcontainer and the Docker services can communicate with one another. For instance, from a shell within the devcontainer I can connect to the service running the Postgres test database.
29+
In `devcontainer.json` you'll see that I'm specifying a network. That's so the devcontainer and the Docker services can communicate with one another. For instance, from a shell within the devcontainer I can connect to the service running the Postgres test database.
3030

3131
I also have some mounts so that
3232

3333
* git can authenticate with GitHub using the SSH agent on my laptop (the host machine)
3434
* devcontainer can communicate with the host's Docker daemon
3535
* the AWS credentials and configuration are available within the devcontainer.
3636

37+
### NOTE
3738

38-
39-
### Frontend
40-
41-
How to run the frontend app:
42-
43-
1. `cd frontend`
44-
1. `npm run start`
45-
1. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.
46-
47-
### Backend
48-
49-
How to run the backend app:
50-
51-
1. `cd backend`
52-
1. `npm run dev` (The backend uses port 3000.)
53-
54-
## Building for Production
55-
56-
### Frontend
57-
58-
1. `cd frontend`
59-
1. `npm run build` (The build artifacts will be stored in the `dist/` directory.)
60-
61-
### Backend
62-
63-
1. `cd backend`
64-
1. `npm run build` (compiled app will be in `dist/`)
65-
66-
## Running unit tests for frontend
67-
68-
TO COME. Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
69-
70-
## Running end-to-end tests
71-
72-
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
73-
74-
## Further help
75-
76-
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
77-
78-
## Run SSH Tunnel in Background
79-
80-
- Using port 5433 in order not to conflict with local db using standard 5432.
81-
- 107.22.66.121 is Elastic IP for bastion host
82-
`ssh -f -i <PATH TO KEY-PAIR PEM> -L 5433:interviewprepdbinstance.c92egeoumrf1.us-east-1.rds.amazonaws.com:5432 [email protected] -N`
83-
- To keep SSH tunnel alive: `ssh -f -i <PATH TO KEY-PAIR PEM> -L 5433:interviewprepdbinstance.c92egeoumrf1.us-east-1.rds.amazonaws.com:5432 [email protected] -N -o ServerAliveInterval=60 -o ServerAliveCountMax=3`
84-
85-
## Migrations
86-
87-
DB credentials and URLs are in `.env.local` and `.env.development`. Set the environment to be used: `export NODE_ENV=local` or `export NODE_ENV=production`.
88-
89-
Maybe obsolete:
90-
91-
You need an SSH tunnel to be able to run the migrations on the db in AWS. Check for a tunnel by using `ps aux | grep ssh`. It will look something like
92-
39+
I'm having a peculiar issue with bind mounts in `docker-compose.yml` when I try to use a relative reference. For example, the following *should* (I believe) bind the `backend` directory within root directory of my app (`interview-prep`) in the devcontainer to the `/app/backend` directory within the backend service:
40+
```yaml
41+
services:
42+
backend:
43+
volumes:
44+
- ./backend:/app/backend
9345
```
94-
davidsilva 69319 0.0 0.0 410379280 1904 ?? Ss 12:34PM 0:00.01 ssh -f -i /Users/davidsilva/Downloads/OnyxKeyPair.pem -L 5433:interviewprepdbinstance.c92egeoumrf1.us-east-1.rds.amazonaws.com:5432 [email protected] -N
46+
Unfortunately, I get an error saying...
47+
```
48+
Attaching to frontend-1
49+
Error response from daemon: Mounts denied:
50+
The path /workspaces/interview-prep/backend is not shared from the host and is not known to Docker.
51+
You can configure shared paths from Docker -> Preferences... -> Resources -> File Sharing.
52+
See https://docs.docker.com/desktop/mac for more info.
53+
```
54+
That error isn't helpful, as `workspaces` isn't even a "real" directory and can't be added to shared paths. In any event, I wouldn't want every user of a project to have to specify a directory on his or her host machine; a devcontainer shouldn't require that. I can get around the error by supplying a full, absolute path, e.g., `/Users/davidsilva/Dev/interview-prep/backend:/app/backend`, or by doing what I'm doing until I can figure out the relative directory thing -- namely, setting an environment variable in `devcontainer.json`,
55+
```json
56+
"remoteEnv": {
57+
"HOST_PROJECT_PATH": "${localWorkspaceFolder}"
58+
},
59+
```
60+
and using `HOST_PROJECT_PATH` in `docker-compose.yml`:
61+
```yaml
62+
services:
63+
backend:
64+
volumes:
65+
- ${HOST_PROJECT_PATH}/backend:/app/backend
9566
```
9667
97-
# SSH into Bastion Host
98-
99-
The security group only allows SSH connections from my VPN IP address.
100-
101-
- `ssh -i <PATH TO KEY-PAIR PEM> [email protected]`, where `107.22.66.121` is the IP of the bastion host.
102-
103-
# Run psql from Bastion Host
104-
105-
- `psql -h interviewprepdbinstance.c92egeoumrf1.us-east-1.rds.amazonaws.com -p 5432 -U dbadmin -d interviewprepdbinstance`. Supply password when prompted.
106-
- Example command: `\d products` (output the structure of the "products" table).
107-
108-
# SSH into `interview-prep-app-instance` via Bastion Host
109-
110-
- Start ssh agent if it isn't already running: `eval "$(ssh-agent -s)"`.
111-
- Add private key to the ssh agent: `ssh-add <PATH TO KEY-PAIR PEM>`.
112-
- SSH into the bastion host with agent forwarding: `ssh -A -i <PATH TO KEY-PAIR PEM> [email protected]`, where `107.22.66.121` is the Elastic IP of the bastion host.
113-
- SSH into app_instance `ssh [email protected]`, where `10.0.5.216` is the private IP address of `interview-prep-app-instance` (`i-06ce4967201932411`).
114-
115-
# How to Find Private IP Address of an EC2 Instance
116-
117-
`aws ec2 describe-instances --filters "Name=tag:Name,Values=<NAME OF YOUR INSTANCE>" --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text`
118-
119-
# Steps to Dockerize Angular App, Push to AWS ECR Using AWS Copilot
120-
121-
- User/profile with AdministratorAccess. Options: Temporarily give the default user AdministratorAccess; create such a user for occassions like this; or (this would be hard) figure out exactly all the individual permissions you would need and create a user just with those permissions.
122-
123-
# Create the ECR repository
124-
125-
`aws ecr create-repository --repository-name interview-prep --region us-east-1 --profile aws-cli-user`
126-
127-
# Verify the repository
128-
129-
`aws ecr describe-repositories --region us-east-1 --profile aws-cli-user`
130-
131-
# Authenticate Docker to ECR
132-
133-
`aws ecr get-login-password --region us-east-1 --profile aws-cli-user | docker login --username AWS --password-stdin 909500381447.dkr.ecr.us-east-1.amazonaws.com`
134-
135-
# Build the Docker images
136-
137-
To run locally:
138-
139-
1. Authenticate Docker to ECR if necessary.
140-
1. From the app root directory (parent of frontend and backend) run `docker build -t interview-prep-frontend:latest -f frontend/Dockerfile .`
141-
1. `docker build -t interview-prep-backend:latest -f backend/Dockerfile .`
142-
143-
# Tag the Docker image
144-
145-
`docker tag interview-prep:latest 909500381447.dkr.ecr.us-east-1.amazonaws.com/interview-prep:latest`
146-
147-
# Push the Docker image to ECR
148-
149-
`docker push 909500381447.dkr.ecr.us-east-1.amazonaws.com/interview-prep:latest`
150-
151-
# Get IP Address of a Docker Container
152-
153-
`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container_id>`
154-
E.g.,
155-
`docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' c611004f5595`
156-
157-
# Check Docker Network Settings
158-
159-
`docker network ls`
160-
161-
# Inspect Docker Container
162-
163-
`docker inspect interview-prep-frontend-1`
164-
165-
# Check the disk space usage by Docker resources
166-
167-
`docker system df`
168-
169-
# Remove Unused Docker Images
170-
171-
`docker image prune -a`
172-
173-
# Remove Unused Docker Containers
174-
175-
`docker container prune`
176-
177-
# Remove Unused Docker Volumes
68+
## Frontend
17869
179-
`docker volume prune`
70+
The frontend app, which uses Angular 18, is pretty simple: featurewise, it allows listing, creating and editing users and products.
18071
181-
# Remove Unused Docker Volume Usage
72+
What are some technical aspects of Angular, Angular Material and RxJS that it demonstrates?
18273
183-
`docker network prune`
74+
* [Built-in flow control](https://blog.angular.dev/introducing-angular-v17-4d7033312e4b), e.g., @if vs *ngIf. It's more readable and supposed to be more performant. New in Angular 17.
75+
* [Angular Material 3](https://material.angular.io/guides) components.
76+
* [Standalone components](https://angular.dev/guide/components/importing#standalone-components).
77+
* [Dependency injection using `inject()`](https://angular.dev/tutorials/learn-angular/20-inject-based-di). (Introduced in Angular 14.)
78+
* [Reactive forms](https://angular.dev/guide/forms/reactive-forms) and form validation.
79+
* [Signal inputs](https://angular.dev/guide/signals/inputs). (In developer preview as of 2024-11-06.)
80+
* [Observables](https://rxjs.dev/guide/overview).
81+
* [Custom pipes](https://angular.dev/tutorials/learn-angular/24-create-a-pipe).
82+
* An implementation of an Angular Material's [ErrorStateMatcher](https://material.angular.io/components/core/api#ErrorStateMatcher).
83+
* [Injectable services](https://angular.dev/guide/di).
84+
* [Routing](https://angular.dev/guide/routing).
18485

185-
# List Docker Volumes
86+
Jasmine and Karma are used for testing.
18687

187-
`docker volume ls`
88+
## Backend
18889

189-
# Inspect a Particular Docker Volume
90+
The backend app uses NodeJS and Express to provide a REST API for getting, creating, updating and deleting products and users in a Postgres database. It uses Joi for validation. I added some middleware for handling errors and logging.
19091

191-
`docker volume inspect <VOLUME NAME>`
92+
The PostgreSQL database is created using the official PostgreSQL Docker image. Environment variables can be provided from `.env.local` following the example of `.env.example`.
19293

193-
# Run Frontend Tests
94+
Jest is used for testing.
19495

195-
`docker-compose --env-file .env.local up frontend-tests`
96+
## Shared
19697

197-
Or to do a build, too:
98+
Both the frontend and backend apps use an NPM package `@onyxdevtutorials/interview-prep-shared` that defines TypeScript interfaces for `product` and `user`. The source code for the package is in `shared`.
19899

199-
`docker-compose --env-file .env.local up --build frontend-tests`
100+
## Workflow and Continuous Integration (CI)
200101

201-
Or:
102+
`.gihub/workflows/ci.yml` runs the frontend and backend tests when there are pushes to or pull requests on specified branches. The results can be viewed in the GitHub UI.
202103

203-
`docker-compose --env-file .env.local build --no-cache frontend-tests`
104+
## Building the App
204105

205-
`docker-compose --env-file .env.local up frontend-tests`
106+
Prerequisites:
206107

207-
# Run Backend Tests
108+
* [Docker desktop app](https://www.docker.com/) installed and running.
109+
* [VSCode](https://code.visualstudio.com/) with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
208110

209-
`docker-compose --env-file .env.test build backend-tests`
210-
`docker-compose --env-file .env.test up backend-tests`
111+
1. Command-P and choose Dev Containers: Clone Repository in Container Volume...
112+
1. Specify this repo and choose the Development branch.
113+
1. Create `.env.local` and `.env.test` files based on `.env.example` and customize the values as necessary.
114+
1. Open a terminal and run `docker-compose --env-file .env.local build`.
115+
1. Run migrations to set up the database tables: `docker-compose --env-file .env.local up migrate`
211116

212-
Run outside of Docker container:
117+
## Run the App
213118

214-
`npm run test`
119+
1. `docker-compose --env-file .env.local up frontend` (The dependent services should come up automatically.)
120+
1. Load `http://localhost:4200/` in a web browser. You should be able to add new users and products, edit them, and list them.
215121

216-
Could run in the background or in separate shell to have HTML reports loaded (and reloaded) in web browser:
122+
## Running Tests
217123

218-
`npm run serve:report`
124+
### Backend
219125

220-
# Shared
126+
1. `docker-compose --env-file .env.test up backend-tests`
221127

222-
## Update, Build and Publish
128+
To view test and coverage reports in a web browser:
223129

224-
1. Increase version number in `package.json`.
225-
1. `npm run build`
226-
1. `npm publish`
130+
1. Bring up the backend with `docker-compose --env-file .env.test up backend`
131+
1. In a separate terminal window, `cd` into `backend`.
132+
1. `npm run test:coverage`. That will run all the tests and a coverage report.
133+
1. `npm run serve:report`. This will bring up a pretty web page at `http://localhost:8080/` where you should be able to look at test and coverage reports.
227134

228-
# Run the Whole App in Docker Locally
135+
#### Note
229136

230-
Everything except the tests will run because frontend is dependent upon backend, which is dependent upon db.
231-
`docker-compose --env-file .env.local up --build frontend`
137+
I thought it would be nice to have a *live* web display of test results and coverage but that's problematic. Practically speaking it's probably best or easiest just to run `npm run test:watch` as you make code changes; that will present the familiar terminal based interface to give you feedback on particular code you change and tests you write.
232138

233-
# Database
139+
### Frontend
234140

235-
With `interview-prep-db-1` up and running, you can access the db via psql using...
141+
1. `docker-compose --env-file .env.test up frontend-tests` (At the moment I don't have any pretty web reports set up.)
236142

237-
`docker exec -it interview-prep-db-1 psql -U interviewprep_admin -d interviewprepdbinstance`
143+
Tests can also be run in watch mode so that they will be re-run as you make changes to code and tests:
238144

239-
## Run Migrations
145+
1. `docker-compose --env-file .env.local up backend` (to bring up the backend app and the db).
146+
1. `cd frontend`
147+
1. `npm run test:watch`
148+
1. In another terminal, `cd frontend` and `npm run start` (now tests will be re-run and you can see changes reflected in the web browser as you make changes to code and tests).
240149

241-
`docker-compose --env-file .env.local up migrate`
150+
## Version History
242151

152+
### 0.0.0
153+
- Created a frontend app with views for adding and updating users and products, with services to talk to the REST API of the backend.
154+
- Created a backend app to persist users and products in a PostgreSQL database and provide a REST API for the frontend.
155+
- Created unit and integration tests for frontend and backend.
156+
- Dockerized the application and set it up to use a devcontainer.
157+
- Created a GitHub workflow to run tests when there push to or pull request on development, stage or production branches.

0 commit comments

Comments
 (0)