Repository: googlecodelabs/monolith-to-microservices Branch: master Commit: deeb3099f6a2 Files: 59 Total size: 54.6 KB Directory structure: gitextract_1dpbqcof/ ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── deploy-monolith.sh ├── microservices/ │ ├── .gitignore │ ├── package.json │ └── src/ │ ├── frontend/ │ │ ├── .dockerignore │ │ ├── .gcloudignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── k8s/ │ │ │ ├── deployment.yml │ │ │ └── service.yml │ │ ├── package.json │ │ └── server.js │ ├── orders/ │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── data/ │ │ │ └── orders.json │ │ ├── k8s/ │ │ │ ├── deployment.yml │ │ │ └── service.yml │ │ ├── package.json │ │ └── server.js │ └── products/ │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── data/ │ │ └── products.json │ ├── k8s/ │ │ ├── deployment.yml │ │ └── service.yml │ ├── package.json │ └── server.js ├── monolith/ │ ├── .dockerignore │ ├── .gcloudignore │ ├── .gitignore │ ├── Dockerfile │ ├── data/ │ │ ├── orders.json │ │ └── products.json │ ├── k8s/ │ │ ├── deployment.yml │ │ └── service.yml │ ├── package.json │ └── src/ │ └── server.js ├── react-app/ │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public/ │ │ ├── index.html │ │ ├── manifest.json │ │ ├── robots.txt │ │ └── static/ │ │ └── img/ │ │ └── products/ │ │ └── credits.txt │ ├── scripts/ │ │ └── post-build.js │ └── src/ │ ├── App.js │ ├── components/ │ │ └── ClippedDrawer/ │ │ └── index.js │ ├── index.js │ └── pages/ │ ├── Home/ │ │ ├── index.js │ │ └── index.js.new │ ├── NotFound/ │ │ └── index.js │ ├── OrderDetails/ │ │ └── index.js │ ├── Orders/ │ │ └── index.js │ └── Products/ │ └── index.js └── setup.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules # misc .DS_Store /logs ================================================ FILE: CONTRIBUTING.md ================================================ # How to Contribute We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. ## Contributor License Agreement Contributions to this project must be accompanied by a Contributor License Agreement. You (or your employer) retain the copyright to your contribution; this simply gives us permission to use and redistribute your contributions as part of the project. Head over to to see your current agreements on file or to sign a new one. You generally only need to submit a CLA once, so if you've already submitted one (even if it was for a different project), you probably don't need to do it again. ## Code reviews All submissions, including submissions by project members, require review. We use GitHub pull requests for this purpose. Consult [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more information on using pull requests. ## Community Guidelines This project follows [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/). ================================================ FILE: LICENSE ================================================ Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # Monolith to Microservices ## NOTE: This is not an officially supported Google product ## Introduction ### This project is used by the Google Cloud Platform team to demonstrate different services within Google Cloud. This project contains two versions of the same application, one architected as a monolith and the other as a set of microservices ## Setup ### **NOTE:** Make sure you have a newer version of NodeJS (16.13.0) or newer (in Cloud Shell you can run `nvm install --lts`) ```bash git clone https://github.com/googlecodelabs/monolith-to-microservices cd monolith-to-microservices ./setup.sh ``` ## Monolith ### To run the monolith project use the following commands from the top level directory ```bash cd monolith npm start ``` You should see output similar to the following ```text Monolith listening on port 8080! ``` #### That's it! You now have a perfectly functioning monolith running on your machine ### Docker - Monolith #### To create a Docker image for the monolith, execute the following commands ```bash cd monolith docker build -t monolith:1.0.0 . ``` To run the Docker image, execute the following commands ```bash docker run --rm -p 8080:8080 monolith:1.0.0 ``` ## Microservices ### To run the microservices project use the following commands from the top level directory ```bash cd microservices npm start ``` You should see output similar to the following ```text [0] Frontend microservice listening on port 8080! [2] Orders microservice listening on port 8081! [1] Products microservice listening on port 8082! ``` ### That's it! You now have a perfectly functioning set of microservices running on your machine ### Docker - Microservices ### To create a Docker image for the microservices, you will have to create a Docker image for each service. Execute the following commands for each folder under the microservices folder ```bash cd microservices/src/frontend docker build -t frontend:1.0.0 . cd ../products docker build -t products:1.0.0 . cd ../orders docker build -t orders:1.0.0 . ``` To run the Docker image, execute the following commands ```bash docker run -d --rm -p 8080:8080 monolith:1.0.0 docker run -d --rm -p 8081:8081 orders:1.0.0 docker run -d --rm -p 8082:8082 products:1.0.0 ``` #### To stop the containers, you will need to find the CONTAINER ID for each and stop them individually. See the steps below ```bash docker ps -a CONTAINER ID IMAGE COMMAND CREATED 4c01db0b339c frontend:1.0.0 bash 17 seconds ago d7886598dbe2 orders:1.0.0 bash 17 seconds ago d85756f57265 products:1.0.0 bash 17 seconds ago docker stop 4c01db0b339c docker stop d7886598dbe2 docker stop d85756f57265 ``` ## React App ### The react-app folder contains a React application created from `create-react-app`. You can modify this fronted, but afterwards, you will need to build and move the static files to the monolith and microservices project. You can do this by running the standard create-react-app build command below ```bash npm run build ``` #### This will run the build script to create the static files two times. The first will build with relative URLs and copy the static files to the monolith/public folder. The second run will build with the standard microservices URLs and copy the static files to the microservices/src/frontend/public folder ================================================ FILE: deploy-monolith.sh ================================================ #!/bin/bash # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. printf "Enabling Cloud Build APIs...\n" gcloud services enable cloudbuild.googleapis.com printf "Completed.\n\n" printf "Building Monolith Container...\n" cd ~/monolith-to-microservices/monolith gcloud builds submit --tag gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 . printf "Completed.\n\n" printf "Deploying Monolith To GKE Cluster...\n" kubectl create deployment monolith --image=gcr.io/${GOOGLE_CLOUD_PROJECT}/monolith:1.0.0 kubectl expose deployment monolith --type=LoadBalancer --port 80 --target-port 8080 printf "Completed.\n\n" printf "Please run the following command to find the IP address for the monolith service: kubectl get service monolith\n\n" printf "Deployment completed successfully!\n" ================================================ FILE: microservices/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules # misc .DS_Store ================================================ FILE: microservices/package.json ================================================ { "name": "microservices", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "concurrently \"npm run frontend\" \"npm run products\" \"npm run orders\"", "frontend": "node ./src/frontend/server.js", "products": "node ./src/products/server.js", "orders": "node ./src/orders/server.js", "install-all": "npm-recursive-install" }, "keywords": [], "author": "", "license": "Apache-2.0", "devDependencies": { "concurrently": "^7.6.0" }, "dependencies": { "cors": "^2.8.5", "express": "^4.18.2" } } ================================================ FILE: microservices/src/frontend/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: microservices/src/frontend/.gcloudignore ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .git .gitignore node_modules/ ================================================ FILE: microservices/src/frontend/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /public # misc .DS_Store ================================================ FILE: microservices/src/frontend/Dockerfile ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # A wildcard is used to ensure both package.json AND package-lock.json are copied # where available (npm@5+) COPY package*.json ./ RUN npm install # If you are building your code for production # RUN npm ci --only=production # Bundle app source COPY . . EXPOSE 8080 CMD [ "node", "server.js" ] ================================================ FILE: microservices/src/frontend/k8s/deployment.yml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: frontend labels: app: frontend spec: replicas: 1 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: gcr.io//frontend:1.0.0 imagePullPolicy: Always ports: - containerPort: 8080 ================================================ FILE: microservices/src/frontend/k8s/service.yml ================================================ apiVersion: v1 kind: Service metadata: name: frontend spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP selector: app: frontend ================================================ FILE: microservices/src/frontend/package.json ================================================ { "name": "frontend", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "keywords": [], "author": "", "license": "Apache-2.0", "dependencies": { "express": "^4.17.2" } } ================================================ FILE: microservices/src/frontend/server.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const express = require("express"); const path = require("path"); const app = express(); const port = process.env.PORT || 8080; //Serve website app.use(express.static(path.join(__dirname, "public"))); //Client side routing fix on page refresh or direct browsing to non-root directory app.get("/*", (req, res) => { res.sendFile(path.join(__dirname, "public", "index.html"), err => { if (err) { res.status(500).send(err); } }); }); //Start the server app.listen(port, () => console.log(`Frontend microservice listening on port ${port}!`)); ================================================ FILE: microservices/src/orders/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: microservices/src/orders/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules # misc .DS_Store ================================================ FILE: microservices/src/orders/Dockerfile ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # A wildcard is used to ensure both package.json AND package-lock.json are copied # where available (npm@5+) COPY package*.json ./ RUN npm install # If you are building your code for production # RUN npm ci --only=production # Bundle app source COPY . . EXPOSE 8081 CMD [ "node", "server.js" ] ================================================ FILE: microservices/src/orders/data/orders.json ================================================ { "orders": [ { "id": "ORD-000001-MICROSERVICE", "date": "7/01/2019", "cost": 67.99, "items": ["OLJCESPC7Z"] }, { "id": "ORD-000002-MICROSERVICE", "date": "7/24/2019", "cost": 124, "items": ["1YMWWN1N4O"] }, { "id": "ORD-000003-MICROSERVICE", "date": "8/03/2019", "cost": 12.49, "items": ["66VCHSJNUP"] }, { "id": "ORD-000004-MICROSERVICE", "date": "8/14/2019", "cost": 89.83, "items": ["0PUK6V6EV0", "LS4PSXUNUM"] }, { "id": "ORD-000005-MICROSERVICE", "date": "8/29/2019", "cost": 12.30, "items": ["6E92ZMYYFZ"] } ] } ================================================ FILE: microservices/src/orders/k8s/deployment.yml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: orders labels: app: orders spec: replicas: 1 selector: matchLabels: app: orders template: metadata: labels: app: orders spec: containers: - name: orders image: gcr.io//orders:1.0.0 imagePullPolicy: Always ports: - containerPort: 8080 ================================================ FILE: microservices/src/orders/k8s/service.yml ================================================ apiVersion: v1 kind: Service metadata: name: orders spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP selector: app: orders ================================================ FILE: microservices/src/orders/package.json ================================================ { "name": "orders", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "keywords": [], "author": "", "license": "Apache-2.0", "dependencies": { "cors": "^2.8.5", "express": "^4.17.2" } } ================================================ FILE: microservices/src/orders/server.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const express = require("express"); const cors = require("cors"); const app = express(); const port = process.env.PORT || 8081; //Load orders for pseudo database const orders = require("./data/orders.json").orders; //Enable cors app.use(cors()); //Get all orders app.get("/api/orders", (req, res) => res.json(orders)); //Get orders by ID app.get("/api/orders/:id", (req, res) => res.json(orders.find(order => order.id === req.params.id)) ); app.listen(port, () => console.log(`Orders microservice listening on port ${port}!`) ); ================================================ FILE: microservices/src/products/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: microservices/src/products/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules # misc .DS_Store ================================================ FILE: microservices/src/products/Dockerfile ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # A wildcard is used to ensure both package.json AND package-lock.json are copied # where available (npm@5+) COPY package*.json ./ RUN npm install # If you are building your code for production # RUN npm ci --only=production # Bundle app source COPY . . EXPOSE 8082 CMD [ "node", "server.js" ] ================================================ FILE: microservices/src/products/data/products.json ================================================ { "products": [ { "id": "OLJCESPC7Z", "name": "MS - Vintage Typewriter", "description": "This typewriter looks good in your living room.", "picture": "static/img/products/typewriter.jpg", "cost": 67.99, "categories": ["vintage"] }, { "id": "66VCHSJNUP", "name": "MS - Vintage Camera Lens", "description": "You won't have a camera to use it and it probably doesn't work anyway.", "picture": "static/img/products/camera-lens.jpg", "cost": 12.49, "categories": ["photography", "vintage"] }, { "id": "1YMWWN1N4O", "name": "MS - Home Barista Kit", "description": "Always wanted to brew coffee with Chemex and Aeropress at home?", "picture": "static/img/products/barista-kit.jpg", "cost": 124, "categories": ["cookware"] }, { "id": "L9ECAV7KIM", "name": "MS - Terrarium", "description": "This terrarium will looks great in your white painted living room.", "picture": "static/img/products/terrarium.jpg", "cost": 36.45, "categories": ["gardening"] }, { "id": "2ZYFJ3GM2N", "name": "MS - Film Camera", "description": "This camera looks like it's a film camera, but it's actually digital.", "picture": "static/img/products/film-camera.jpg", "cost": 2245, "categories": ["photography", "vintage"] }, { "id": "0PUK6V6EV0", "name": "MS - Vintage Record Player", "description": "It still works.", "picture": "static/img/products/record-player.jpg", "cost": 65.50, "categories": ["music", "vintage"] }, { "id": "LS4PSXUNUM", "description": "You probably don't go camping that often but this is better than plastic cups.", "name": "MS - Metal Camping Mug", "picture": "static/img/products/camp-mug.jpg", "cost": 24.33, "categories": ["cookware"] }, { "id": "9SIQT8TOJO", "name": "MS - City Bike", "description": "This single gear bike probably cannot climb the hills of San Francisco.", "picture": "static/img/products/city-bike.jpg", "cost": 789.50, "categories": ["cycling"] }, { "id": "6E92ZMYYFZ", "name": "MS - Air Plant", "description": "Have you ever wondered whether air plants need water? Buy one and figure out.", "picture": "static/img/products/air-plant.jpg", "cost": 12.30, "categories": ["gardening"] } ] } ================================================ FILE: microservices/src/products/k8s/deployment.yml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: products labels: app: products spec: replicas: 1 selector: matchLabels: app: products template: metadata: labels: app: products spec: containers: - name: products image: gcr.io//products:1.0.0 imagePullPolicy: Always ports: - containerPort: 8080 ================================================ FILE: microservices/src/products/k8s/service.yml ================================================ apiVersion: v1 kind: Service metadata: name: products spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP selector: app: products ================================================ FILE: microservices/src/products/package.json ================================================ { "name": "products", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "keywords": [], "author": "", "license": "Apache-2.0", "dependencies": { "cors": "^2.8.5", "express": "^4.17.2" } } ================================================ FILE: microservices/src/products/server.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const express = require("express"); const cors = require("cors"); const app = express(); const port = process.env.PORT || 8082; //Load product for pseudo database const products = require("./data/products.json").products; //Enable cors app.use(cors()); //Get all products app.get("/api/products", (req, res) => res.json(products)); //Get products by ID app.get("/api/products/:id", (req, res) => res.json(products.find(product => product.id === req.params.id)) ); app.listen(port, () => console.log(`Products microservice listening on port ${port}!`) ); ================================================ FILE: monolith/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: monolith/.gcloudignore ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. .git .gitignore node_modules/ ================================================ FILE: monolith/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /public # misc .DS_Store ================================================ FILE: monolith/Dockerfile ================================================ # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM node:16 # Create app directory WORKDIR /usr/src/app # Install app dependencies # A wildcard is used to ensure both package.json AND package-lock.json are copied # where available (npm@5+) COPY package*.json ./ RUN npm install # If you are building your code for production # RUN npm ci --only=production # Bundle app source COPY . . EXPOSE 8080 CMD [ "node", "src/server.js" ] ================================================ FILE: monolith/data/orders.json ================================================ { "orders": [ { "id": "ORD-000001", "date": "7/01/2019", "cost": 67.99, "items": ["OLJCESPC7Z"] }, { "id": "ORD-000002", "date": "7/24/2019", "cost": 124, "items": ["1YMWWN1N4O"] }, { "id": "ORD-000003", "date": "8/03/2019", "cost": 12.49, "items": ["66VCHSJNUP"] }, { "id": "ORD-000004", "date": "8/14/2019", "cost": 89.83, "items": ["0PUK6V6EV0", "LS4PSXUNUM"] }, { "id": "ORD-000005", "date": "8/29/2019", "cost": 12.30, "items": ["6E92ZMYYFZ"] } ] } ================================================ FILE: monolith/data/products.json ================================================ { "products": [ { "id": "OLJCESPC7Z", "name": "Vintage Typewriter", "description": "This typewriter looks good in your living room.", "picture": "static/img/products/typewriter.jpg", "cost": 67.99, "categories": ["vintage"] }, { "id": "66VCHSJNUP", "name": "Vintage Camera Lens", "description": "You won't have a camera to use it and it probably doesn't work anyway.", "picture": "static/img/products/camera-lens.jpg", "cost": 12.49, "categories": ["photography", "vintage"] }, { "id": "1YMWWN1N4O", "name": "Home Barista Kit", "description": "Always wanted to brew coffee with Chemex and Aeropress at home?", "picture": "static/img/products/barista-kit.jpg", "cost": 124, "categories": ["cookware"] }, { "id": "L9ECAV7KIM", "name": "Terrarium", "description": "This terrarium will looks great in your white painted living room.", "picture": "static/img/products/terrarium.jpg", "cost": 36.45, "categories": ["gardening"] }, { "id": "2ZYFJ3GM2N", "name": "Film Camera", "description": "This camera looks like it's a film camera, but it's actually digital.", "picture": "static/img/products/film-camera.jpg", "cost": 2245, "categories": ["photography", "vintage"] }, { "id": "0PUK6V6EV0", "name": "Vintage Record Player", "description": "It still works.", "picture": "static/img/products/record-player.jpg", "cost": 65.50, "categories": ["music", "vintage"] }, { "id": "LS4PSXUNUM", "name": "Metal Camping Mug", "description": "You probably don't go camping that often but this is better than plastic cups.", "picture": "static/img/products/camp-mug.jpg", "cost": 24.33, "categories": ["cookware"] }, { "id": "9SIQT8TOJO", "name": "City Bike", "description": "This single gear bike probably cannot climb the hills of San Francisco.", "picture": "static/img/products/city-bike.jpg", "cost": 789.50, "categories": ["cycling"] }, { "id": "6E92ZMYYFZ", "name": "Air Plant", "description": "Have you ever wondered whether air plants need water? Buy one and figure out.", "picture": "static/img/products/air-plant.jpg", "cost": 12.30, "categories": ["gardening"] } ] } ================================================ FILE: monolith/k8s/deployment.yml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: monolith labels: app: monolith spec: replicas: 1 selector: matchLabels: app: monolith template: metadata: labels: app: monolith spec: containers: - name: monolith image: gcr.io//monolith:1.0.0 imagePullPolicy: Always ports: - containerPort: 8080 ================================================ FILE: monolith/k8s/service.yml ================================================ apiVersion: v1 kind: Service metadata: name: monolith spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 protocol: TCP selector: app: monolith ================================================ FILE: monolith/package.json ================================================ { "name": "monolith", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node ./src/server.js" }, "keywords": [], "author": "", "license": "Apache-2.0", "dependencies": { "body-parser": "^1.20.2", "express": "^4.18.2" } } ================================================ FILE: monolith/src/server.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const express = require("express"); const path = require("path"); const app = express(); const port = process.env.PORT || 8080; //Load orders and products for pseudo database const orders = require("../data/orders.json").orders; const products = require("../data/products.json").products; //Serve website app.use(express.static(path.join(__dirname, "..", "public"))); //Get all products app.get("/service/products", (req, res) => res.json(products)); //Get products by ID app.get("/service/products/:id", (req, res) => res.json(products.find((product) => product.id === req.params.id)) ); //Get all orders app.get("/service/orders", (req, res) => res.json(orders)); //Get orders by ID app.get("/service/orders/:id", (req, res) => res.json(orders.find((order) => order.id === req.params.id)) ); //Client side routing fix on page refresh or direct browsing to non-root directory app.get("/*", (req, res) => { res.sendFile(path.join(__dirname, "..", "public", "index.html"), (err) => { if (err) { res.status(500).send(err); } }); }); //Start the server app.listen(port, () => console.log(`Monolith listening on port ${port}!`)); ================================================ FILE: react-app/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # production /build # misc .DS_Store .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: react-app/README.md ================================================ This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). ## Available Scripts In the project directory, you can run: ### `npm start` Runs the app in the development mode.
Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits.
You will also see any lint errors in the console. ### `npm test` Launches the test runner in the interactive watch mode.
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. ### `npm run build` Builds the app for production to the `build` folder.
It correctly bundles React in production mode and optimizes the build for the best performance. The build is minified and the filenames include the hashes.
Your app is ready to be deployed! See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. ### `npm run eject` **Note: this is a one-way operation. Once you `eject`, you can’t go back!** If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. ## Learn More You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). To learn React, check out the [React documentation](https://reactjs.org/). ### Code Splitting This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting ### Analyzing the Bundle Size This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size ### Making a Progressive Web App This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app ### Advanced Configuration This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration ### Deployment This section has moved here: https://facebook.github.io/create-react-app/docs/deployment ### `npm run build` fails to minify This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify ================================================ FILE: react-app/package.json ================================================ { "name": "frontend", "version": "0.1.0", "private": true, "dependencies": { "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@mui/material": "^5.3.1", "@mui/styles": "^5.3.0", "env-cmd": "^10.1.0", "express": "^4.21.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router-dom": "^5.3.0", "react-scripts": "^5.0.0" }, "scripts": { "start": "react-scripts start", "prebuild": "npm run build:monolith", "build": "react-scripts build", "postbuild": "node scripts/post-build.js ./build ../microservices/src/frontend/public", "build:monolith": "env-cmd -f .env.monolith react-scripts build", "postbuild:monolith": "node scripts/post-build.js ./build ../monolith/public", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "ncp": "^2.0.0", "rimraf": "^3.0.2" } } ================================================ FILE: react-app/public/index.html ================================================ Fancy Store
================================================ FILE: react-app/public/manifest.json ================================================ { "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } ================================================ FILE: react-app/public/robots.txt ================================================ # https://www.robotstxt.org/robotstxt.html User-agent: * ================================================ FILE: react-app/public/static/img/products/credits.txt ================================================ film-camera.jpg,CC0 Public Domain,https://pxhere.com/en/photo/829555 camera-lens.jpg,CC0 Public Domain,https://pxhere.com/en/photo/670041 air-plant.jpg,,https://unsplash.com/photos/uUwEAW5jFLE camp-mug.jpg,,https://unsplash.com/photos/h9VhRlMfVkg record-player.jpg,,https://unsplash.com/photos/pEEHFSX1vak city-bike.jpg,,https://unsplash.com/photos/Lpe9u9etwMU typewriter.jpg,,https://unsplash.com/photos/mk7D-4UCfmg barista-kit.jpg,,https://unsplash.com/photos/ZiRyGGIpRCw terrarium.jpg,,https://unsplash.com/photos/E9QYLj0724Y ================================================ FILE: react-app/scripts/post-build.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const path = require("path"); const ncp = require("ncp"); const rimraf = require("rimraf"); if(process.argv.length < 4){ console.error("Invalid arguments, two arguments are required "); } //const sourceFolder = path.join(__dirname, "..", "build"); //const destFolder = path.join(__dirname, "..", "..", "..", "monolith", "public"); const sourceFolder = process.argv[2]; const destFolder = process.argv[3]; console.log(`Deleting stale folder: ${destFolder}`); rimraf(destFolder, err => { if (err) { console.log(`Failed to delete stale destination folder: ${destFolder}`); return; } console.log(`Deleted stale destination folder: ${destFolder}`); console.log(`Copying files from ${sourceFolder} to ${destFolder}`) ncp(sourceFolder, destFolder, err => { if (err) { return console.error(`Failed to copy ${sourceFolder} to ${destFolder}!`); } console.log(`Copied ${sourceFolder} to ${destFolder} successfully!`); }); }); ================================================ FILE: react-app/src/App.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; import ClippedDrawer from "./components/ClippedDrawer"; function App() { return ; } export default App; ================================================ FILE: react-app/src/components/ClippedDrawer/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; import { Drawer, AppBar, CssBaseline, Toolbar, List, Typography, ListItem, ListItemText, Box, } from "@mui/material"; import { BrowserRouter as Router, Route, Switch, NavLink, } from "react-router-dom"; //Import Pages import Home from "../../pages/Home"; import Products from "../../pages/Products"; import Orders from "../../pages/Orders"; import OrderDetails from "../../pages/OrderDetails"; import NotFound from "../../pages/NotFound"; const drawerWidth = 200; export default function ClippedDrawer() { return ( theme.zIndex.drawer + 1, }} > Fancy Store {" "} {" "} ); } ================================================ FILE: react-app/src/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(, document.getElementById('root')); ================================================ FILE: react-app/src/pages/Home/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; import { Box, Paper, Typography } from "@mui/material"; export default function Home() { return ( theme.spacing(3, 2), }} > Welcome to the Fancy Store!
Take a look at our wide variety of products.
); } ================================================ FILE: react-app/src/pages/Home/index.js.new ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; import { Box, Paper, Typography } from "@mui/material"; export default function Home() { return ( theme.spacing(3, 2), }} > Fancy Fashion & Style Online
Tired of mainstream fashion ideas, popular trends and societal norms? This line of lifestyle products will help you catch up with the Fancy trend and express your personal style. Start shopping Fancy items now!
); } ================================================ FILE: react-app/src/pages/NotFound/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React from "react"; import { Box, Paper, Typography } from "@mui/material"; export default function Orders() { return ( theme.spacing(3, 2) }}> Page not found ); } ================================================ FILE: react-app/src/pages/OrderDetails/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React, { useState, useEffect } from "react"; import { useRouteMatch } from "react-router-dom"; import { Box, Paper, Grid, Typography } from "@mui/material"; export default function OrderDetails() { const match = useRouteMatch(); const [hasErrors, setErrors] = useState(false); const [order, setOrder] = useState({}); const orderId = match.params.id; async function fetchOrder(orderId) { try { const response = await fetch( `${process.env.REACT_APP_ORDERS_URL}/${orderId}` ); const order = await response.json(); setOrder(order); } catch (err) { setErrors(true); } } useEffect(() => { fetchOrder(orderId); }, [orderId]); return ( {hasErrors && ( theme.spacing(3, 2), }} > An error has occurred, please try reloading the page. )} {!hasErrors && ( theme.spacing(3, 2), }} > {order.id} Date: {order.date} Cost: ${order.cost} Order Items: {order.items && order.items.map((item) => ( {item} ))} )} ); } ================================================ FILE: react-app/src/pages/Orders/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React, { useState, useEffect } from "react"; import { useHistory } from "react-router-dom"; import { Box, Table, TableBody, TableCell, TableHead, TableRow, Paper, Typography, } from "@mui/material"; export default function Orders() { const history = useHistory(); const [hasErrors, setErrors] = useState(false); const [orders, setOrders] = useState([]); async function fetchOrders() { try { const response = await fetch(`${process.env.REACT_APP_ORDERS_URL}`); const orders = await response.json(); setOrders(orders); } catch (err) { setErrors(true); } } useEffect(() => { fetchOrders(); }, []); return ( {hasErrors && ( theme.spacing(3, 2), }} > An error has occurred, please try reloading the page. )} {!hasErrors && ( theme.spacing(3, 2), }} > Orders Order Id Date Total Items Cost {orders.map((order) => ( { history.push(`/orders/${order.id}`); }} > {order.id} {order.date} {(order.items && order.items.length) || 0} ${order.cost} ))}
)}
); } ================================================ FILE: react-app/src/pages/Products/index.js ================================================ /* Copyright 2019 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ import React, { useState, useEffect } from "react"; import { Box, Paper, Card, CardMedia, CardContent, Grid, Typography, } from "@mui/material"; export default function Products() { const [hasErrors, setErrors] = useState(false); const [products, setProducts] = useState([]); async function fetchData() { try { const response = await fetch(`${process.env.REACT_APP_PRODUCTS_URL}`); const products = await response.json(); setProducts(products); } catch (err) { setErrors(true); } } useEffect(() => { fetchData(); }, []); return ( {hasErrors && ( theme.spacing(3, 2), }} > An error has occurred, please try reloading the page. )} {!hasErrors && ( {products.map((product) => { return ( {product.name} - ${product.cost} ); })} )} ); } ================================================ FILE: setup.sh ================================================ #!/usr/bin/env bash # Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -eEo pipefail if [ -z "$CLOUD_SHELL" ]; then printf "Checking for required npm version...\n" npm install -g npm > /dev/null 2>&1 printf "Completed.\n\n" printf "Setting up NVM...\n" export NVM_DIR="/usr/local/nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion printf "Completed.\n\n" printf "Updating nodeJS version...\n" nvm install --lts printf "Completed.\n\n" fi printf "Installing monolith dependencies...\n" cd ./monolith npm install printf "Completed.\n\n" printf "Installing microservices dependencies...\n" cd ../microservices npm install printf "Completed.\n\n" printf "Installing React app dependencies...\n" cd ../react-app npm install printf "Completed.\n\n" printf "Building React app and placing into sub projects...\n" npm run build printf "Completed.\n\n" printf "Setup completed successfully!\n" if [ -z "$CLOUD_SHELL" ]; then printf "\n" printf "###############################################################################\n" printf "# NOTICE #\n" printf "# #\n" printf "# Make sure you have a compatible nodeJS version with the following command: #\n" printf "# #\n" printf "# nvm install --lts #\n" printf "# #\n" printf "###############################################################################\n" fi