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 <https://cla.developers.google.com/> 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/<PROJECT_ID>/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/<PROJECT_ID>/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/<PROJECT_ID>/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/<PROJECT_ID>/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.<br>
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
The page will reload if you make edits.<br>
You will also see any lint errors in the console.
### `npm test`
Launches the test runner in the interactive watch mode.<br>
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.<br>
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.<br>
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
================================================
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>Fancy Store</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
================================================
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 <source> <destination>");
}
//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 <ClippedDrawer />;
}
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 (
<Box sx={{ display: "flex" }}>
<Router>
<CssBaseline />
<AppBar
position="fixed"
sx={{
zIndex: (theme) => theme.zIndex.drawer + 1,
}}
>
<Toolbar>
<Typography variant="h6" noWrap>
Fancy Store
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
sx={{
width: drawerWidth,
flexShrink: 0,
[`& .MuiDrawer-paper`]: {
width: drawerWidth,
boxSizing: "border-box",
},
}}
>
<Toolbar />
<List>
<ListItem
component={NavLink}
exact
sx={{ color: "rgba(0, 0, 0, 0.54)" }}
activeClassName="Mui-selected"
to="/"
>
<ListItemText primary="Home" />
</ListItem>{" "}
<ListItem
component={NavLink}
exact
sx={{ color: "rgba(0, 0, 0, 0.54)" }}
activeClassName="Mui-selected"
to="/products"
>
<ListItemText primary="Products" />
</ListItem>{" "}
<ListItem
component={NavLink}
sx={{ color: "rgba(0, 0, 0, 0.54)" }}
activeClassName="Mui-selected"
to="/orders"
>
<ListItemText primary="Orders" />
</ListItem>
</List>
</Drawer>
<Box component="main" sx={{ flexGrow: 1, p: 3 }}>
<Toolbar />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route exact path="/products">
<Products />
</Route>
<Route path="/orders/:id">
<OrderDetails />
</Route>
<Route path="/orders">
<Orders />
</Route>
<Route>
<NotFound />
</Route>
</Switch>
</Box>
</Router>
</Box>
);
}
================================================
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(<App />, 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 (
<Box sx={{ flexGrow: 1 }}>
<Paper
elevation={3}
sx={{
width: "800px",
margin: "0 auto",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography variant="h5">Welcome to the Fancy Store!</Typography>
<br />
<Typography variant="body1">
Take a look at our wide variety of products.
</Typography>
</Paper>
</Box>
);
}
================================================
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 (
<Box sx={{ flexGrow: 1 }}>
<Paper
elevation={3}
sx={{
width: "800px",
margin: "0 auto",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography variant="h5">Fancy Fashion & Style Online</Typography>
<br />
<Typography variant="body1">
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!
</Typography>
</Paper>
</Box>
);
}
================================================
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 (
<Box sx={{ flexGrow: 1 }}>
<Paper elevation={3} sx={{ padding: (theme) => theme.spacing(3, 2) }}>
<Typography component="p">Page not found</Typography>
</Paper>
</Box>
);
}
================================================
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 (
<Box sx={{ flexGrow: 1 }}>
{hasErrors && (
<Paper
elevation={3}
sx={{
background: "#f99",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography component="p">
An error has occurred, please try reloading the page.
</Typography>
</Paper>
)}
{!hasErrors && (
<Paper
elevation={3}
sx={{
maxWidth: "800px",
margin: "0 auto",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Grid
container
spacing={3}
justifyContent="flex-start"
alignItems="stretch"
>
<Grid item xs={12}>
<Typography variant="h5">{order.id}</Typography>
</Grid>
<Grid item md={6} xs={12}>
<Typography component="p">
<b>Date: </b>
{order.date}
</Typography>
<Typography component="p">
<b>Cost: </b>${order.cost}
</Typography>
</Grid>
<Grid item md={6} xs={12}>
<Typography component="p">
<b>Order Items: </b>
</Typography>
{order.items &&
order.items.map((item) => (
<Typography key={item}>{item}</Typography>
))}
</Grid>
</Grid>
</Paper>
)}
</Box>
);
}
================================================
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 (
<Box sx={{ flexGrow: 1 }}>
{hasErrors && (
<Paper
elevation={3}
sx={{
background: "#f99",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography component="p">
An error has occurred, please try reloading the page.
</Typography>
</Paper>
)}
{!hasErrors && (
<Paper
elevation={3}
sx={{
maxWidth: "800px",
margin: "0 auto",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography variant="h5">Orders</Typography>
<Table sx={{ minWidth: "650px" }}>
<TableHead>
<TableRow>
<TableCell>Order Id</TableCell>
<TableCell>Date</TableCell>
<TableCell>Total Items</TableCell>
<TableCell>Cost</TableCell>
</TableRow>
</TableHead>
<TableBody>
{orders.map((order) => (
<TableRow
hover
sx={{ cursor: "pointer" }}
key={order.id}
onClick={() => {
history.push(`/orders/${order.id}`);
}}
>
<TableCell component="th" scope="row">
{order.id}
</TableCell>
<TableCell>{order.date}</TableCell>
<TableCell>
{(order.items && order.items.length) || 0}
</TableCell>
<TableCell>${order.cost}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Paper>
)}
</Box>
);
}
================================================
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 (
<Box sx={{ flexGrow: 1 }}>
{hasErrors && (
<Paper
elevation={3}
sx={{
background: "#f99",
padding: (theme) => theme.spacing(3, 2),
}}
>
<Typography component="p">
An error has occurred, please try reloading the page.
</Typography>
</Paper>
)}
{!hasErrors && (
<Grid
sx={{ maxWidth: "1000px", margin: "0 auto" }}
container
spacing={3}
justify="flex-start"
alignItems="stretch"
>
{products.map((product) => {
return (
<Grid key={product.id} item md={4} xs={12}>
<Card>
<CardMedia
sx={{ height: 0, paddingTop: "56.25%" }}
image={product.picture}
title={product.name}
/>
<CardContent>
<Typography variant="body1">
{product.name} - ${product.cost}
</Typography>
</CardContent>
</Card>
</Grid>
);
})}
</Grid>
)}
</Box>
);
}
================================================
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
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
SYMBOL INDEX (7 symbols across 7 files)
FILE: react-app/src/App.js
function App (line 21) | function App() {
FILE: react-app/src/components/ClippedDrawer/index.js
function ClippedDrawer (line 45) | function ClippedDrawer() {
FILE: react-app/src/pages/Home/index.js
function Home (line 19) | function Home() {
FILE: react-app/src/pages/NotFound/index.js
function Orders (line 19) | function Orders() {
FILE: react-app/src/pages/OrderDetails/index.js
function OrderDetails (line 20) | function OrderDetails() {
FILE: react-app/src/pages/Orders/index.js
function Orders (line 29) | function Orders() {
FILE: react-app/src/pages/Products/index.js
function Products (line 27) | function Products() {
Condensed preview — 59 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (63K chars).
[
{
"path": ".gitignore",
"chars": 140,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# m"
},
{
"path": "CONTRIBUTING.md",
"chars": 1100,
"preview": "# How to Contribute\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guid"
},
{
"path": "LICENSE",
"chars": 552,
"preview": "Copyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file exc"
},
{
"path": "README.md",
"chars": 3481,
"preview": "# Monolith to Microservices\n\n## NOTE: This is not an officially supported Google product\n\n## Introduction\n\n### This proj"
},
{
"path": "deploy-monolith.sh",
"chars": 1305,
"preview": "#!/bin/bash\n\n# Copyright 2019 Google LLC\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may "
},
{
"path": "microservices/.gitignore",
"chars": 134,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n\n# "
},
{
"path": "microservices/package.json",
"chars": 640,
"preview": "{\n \"name\": \"microservices\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\":"
},
{
"path": "microservices/src/frontend/.dockerignore",
"chars": 27,
"preview": "node_modules\nnpm-debug.log\n"
},
{
"path": "microservices/src/frontend/.gcloudignore",
"chars": 606,
"preview": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": "microservices/src/frontend/.gitignore",
"chars": 141,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/pub"
},
{
"path": "microservices/src/frontend/Dockerfile",
"chars": 961,
"preview": "# Copyright 2019 Google LLC\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this "
},
{
"path": "microservices/src/frontend/k8s/deployment.yml",
"chars": 400,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: frontend\n labels:\n app: frontend\nspec:\n replicas: 1\n select"
},
{
"path": "microservices/src/frontend/k8s/service.yml",
"chars": 173,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: frontend\nspec:\n type: LoadBalancer\n ports:\n - port: 80\n targetPort:"
},
{
"path": "microservices/src/frontend/package.json",
"chars": 311,
"preview": "{\n \"name\": \"frontend\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"server.js\",\n \"scripts\": {\n \"test\": \"ec"
},
{
"path": "microservices/src/frontend/server.js",
"chars": 1117,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "microservices/src/orders/.dockerignore",
"chars": 27,
"preview": "node_modules\nnpm-debug.log\n"
},
{
"path": "microservices/src/orders/.gitignore",
"chars": 133,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# m"
},
{
"path": "microservices/src/orders/Dockerfile",
"chars": 961,
"preview": "# Copyright 2019 Google LLC\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this "
},
{
"path": "microservices/src/orders/data/orders.json",
"chars": 683,
"preview": "{\n \"orders\": [\n {\n \"id\": \"ORD-000001-MICROSERVICE\",\n \"date\": \"7/01/2019\",\n \"cost\": 67.99,\n \"item"
},
{
"path": "microservices/src/orders/k8s/deployment.yml",
"chars": 388,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: orders\n labels:\n app: orders\nspec:\n replicas: 1\n selector:\n"
},
{
"path": "microservices/src/orders/k8s/service.yml",
"chars": 169,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: orders\nspec:\n type: LoadBalancer\n ports:\n - port: 80\n targetPort: 8"
},
{
"path": "microservices/src/orders/package.json",
"chars": 331,
"preview": "{\n \"name\": \"orders\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"server.js\",\n \"scripts\": {\n \"test\": \"echo"
},
{
"path": "microservices/src/orders/server.js",
"chars": 1096,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "microservices/src/products/.dockerignore",
"chars": 27,
"preview": "node_modules\nnpm-debug.log\n"
},
{
"path": "microservices/src/products/.gitignore",
"chars": 133,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n\n# m"
},
{
"path": "microservices/src/products/Dockerfile",
"chars": 961,
"preview": "# Copyright 2019 Google LLC\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this "
},
{
"path": "microservices/src/products/data/products.json",
"chars": 2915,
"preview": "{\n \"products\": [\n {\n \"id\": \"OLJCESPC7Z\",\n \"name\": \"MS - Vintage Typewriter\",\n "
},
{
"path": "microservices/src/products/k8s/deployment.yml",
"chars": 400,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: products\n labels:\n app: products\nspec:\n replicas: 1\n select"
},
{
"path": "microservices/src/products/k8s/service.yml",
"chars": 173,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: products\nspec:\n type: LoadBalancer\n ports:\n - port: 80\n targetPort:"
},
{
"path": "microservices/src/products/package.json",
"chars": 333,
"preview": "{\n \"name\": \"products\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"server.js\",\n \"scripts\": {\n \"test\": \"ec"
},
{
"path": "microservices/src/products/server.js",
"chars": 1121,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "monolith/.dockerignore",
"chars": 27,
"preview": "node_modules\nnpm-debug.log\n"
},
{
"path": "monolith/.gcloudignore",
"chars": 606,
"preview": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this f"
},
{
"path": "monolith/.gitignore",
"chars": 141,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/pub"
},
{
"path": "monolith/Dockerfile",
"chars": 965,
"preview": "# Copyright 2019 Google LLC\n# \n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this "
},
{
"path": "monolith/data/orders.json",
"chars": 618,
"preview": "{\n \"orders\": [\n {\n \"id\": \"ORD-000001\",\n \"date\": \"7/01/2019\",\n \"cost\": 67.99,\n \"items\": [\"OLJCESP"
},
{
"path": "monolith/data/products.json",
"chars": 2861,
"preview": "{\n \"products\": [\n {\n \"id\": \"OLJCESPC7Z\",\n \"name\": \"Vintage Typewriter\",\n \"des"
},
{
"path": "monolith/k8s/deployment.yml",
"chars": 400,
"preview": "apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: monolith\n labels:\n app: monolith\nspec:\n replicas: 1\n select"
},
{
"path": "monolith/k8s/service.yml",
"chars": 174,
"preview": "apiVersion: v1\nkind: Service\nmetadata:\n name: monolith\nspec:\n type: LoadBalancer\n ports:\n - port: 80\n targetPort:"
},
{
"path": "monolith/package.json",
"chars": 346,
"preview": "{\n \"name\": \"monolith\",\n \"version\": \"1.0.0\",\n \"description\": \"\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"ech"
},
{
"path": "monolith/src/server.js",
"chars": 1714,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/.gitignore",
"chars": 299,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": "react-app/README.md",
"chars": 2867,
"preview": "This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n## Available Scrip"
},
{
"path": "react-app/package.json",
"chars": 1187,
"preview": "{\n \"name\": \"frontend\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"dependencies\": {\n \"@emotion/react\": \"^11.7.1\",\n "
},
{
"path": "react-app/public/index.html",
"chars": 1719,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"shortcut icon\" href=\"%PUBLIC_URL%/"
},
{
"path": "react-app/public/manifest.json",
"chars": 306,
"preview": "{\n \"short_name\": \"React App\",\n \"name\": \"Create React App Sample\",\n \"icons\": [\n {\n \"src\": \"favicon.ico\",\n "
},
{
"path": "react-app/public/robots.txt",
"chars": 57,
"preview": "# https://www.robotstxt.org/robotstxt.html\nUser-agent: *\n"
},
{
"path": "react-app/public/static/img/products/credits.txt",
"chars": 529,
"preview": "film-camera.jpg,CC0 Public Domain,https://pxhere.com/en/photo/829555\ncamera-lens.jpg,CC0 Public Domain,https://pxhere.co"
},
{
"path": "react-app/scripts/post-build.js",
"chars": 1542,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/App.js",
"chars": 712,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/components/ClippedDrawer/index.js",
"chars": 3267,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/index.js",
"chars": 703,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/Home/index.js",
"chars": 1126,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/Home/index.js.new",
"chars": 1317,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/NotFound/index.js",
"chars": 891,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/OrderDetails/index.js",
"chars": 2799,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/Orders/index.js",
"chars": 3025,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "react-app/src/pages/Products/index.js",
"chars": 2394,
"preview": "/*\nCopyright 2019 Google LLC\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file "
},
{
"path": "setup.sh",
"chars": 2363,
"preview": "#!/usr/bin/env bash\n\n# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# y"
}
]
About this extraction
This page contains the full source code of the googlecodelabs/monolith-to-microservices GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 59 files (54.6 KB), approximately 15.3k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.