Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: CI/CD Showcase Pipeline

permissions:
id-token: write
contents: read

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

jobs:
test:
runs-on: ${{ matrix.os }}
timeout-minutes: 10
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [18,20]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}

- name: cache npm dependencies
uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-{{ matrix.node-version }}-

- name: Install dependencies
run: npm ci

- name: Run linting
run: npm run lint

- name: Run unit tests
run: npm test -- --forceExit

build:
needs: test
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20.x'

- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-node-20.x-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-20.x-

- name: Install dependencies
run: npm ci

- name: Run build
run: npm run build

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-build
path: dist/

deploy-staging:
needs: build
runs-on: ubuntu-latest
environment: staging

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download artifact
uses: actions/download-artifact@v4
with:
name: app-build
path: dist/

- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Deploy to Staging
uses: azure/webapps-deploy@v3
with:
app-name: dipendra-staging-app-hthxe6e5cpdgg6dd.centralindia-01.azurewebsites.net
package: dist/

- name: Debug - List App Services in RG
run: az webapp list --resource-group dipendra-rg --query "[].name" -o table

- name: Debug - List all App Services in subscription
run: az webapp list --query "[].{Name:name, ResourceGroup:resourceGroup, Location:location}" -o table

deploy-prod:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Download artifact
uses: actions/download-artifact@v4
with:
name: app-build
path: dist/

- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}

- name: Deploy to Production
uses: azure/webapps-deploy@v3
with:
app-name: dipendra-production-app-cxe9eff4e2atbjhq.centralindia-01.azurewebsites.net
package: dist/
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ node_modules
jspm_packages

# Optional npm cache directory
.npm


# Optional REPL history
.node_repl_history
package-lock.json
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ This sample demonstrates a tiny Hello World node.js app for [App Service Web App
## Contributing

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

what can do hahaha
220 changes: 220 additions & 0 deletions dist/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// ***************************************************************************
// Bank API code from Web Dev For Beginners project
// https://github.com/microsoft/Web-Dev-For-Beginners/tree/main/7-bank-project/api
// ***************************************************************************

// Cache test - this should hit cache

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const crypto = require('crypto');
const pkg = require('./package.json');

// App constants
const port = process.env.PORT || 3000;
const apiPrefix = '/api';

// Store data in-memory, not suited for production use!
const db = {
test: {
user: 'test',
currency: '$',
description: `Test account`,
balance: 75,
transactions: [
{ id: '1', date: '2020-10-01', object: 'Pocket money', amount: 50 },
{ id: '2', date: '2020-10-03', object: 'Book', amount: -10 },
{ id: '3', date: '2020-10-04', object: 'Sandwich', amount: -5 },
],
},
jondoe: {
user: 'jondoe',
currency: '$',
description: `Second test account`,
balance: 150,
transactions: [
{ id: '1', date: '2022-10-01', object: 'Gum', amount: -2 },
{ id: '2', date: '2022-10-03', object: 'Book', amount: -10 },
{ id: '3', date: '2022-10-04', object: 'Restaurant', amount: -45 },
],
},
};

// Create the Express app & setup middlewares
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors({ origin: /http:\/\/(127(\.\d){3}|localhost)/ }));
app.options('*', cors());

// ***************************************************************************

// Configure routes
const router = express.Router();

// Hello World for index page
app.get('/', function (req, res) {
return res.send('Hello World!');
});

app.get('/api', function (req, res) {
return res.send('Fabrikam Bank API');
});

// ----------------------------------------------
// Create an account
router.post('/accounts', (req, res) => {
// Check mandatory request parameters
if (!req.body.user || !req.body.currency) {
return res.status(400).json({ error: 'Missing parameters' });
}

// Check if account already exists
if (db[req.body.user]) {
return res.status(409).json({ error: 'User already exists' });
}

// Convert balance to number if needed
let balance = req.body.balance;
if (balance && typeof balance !== 'number') {
balance = parseFloat(balance);
if (isNaN(balance)) {
return res.status(400).json({ error: 'Balance must be a number' });
}
}

// Create account
const account = {
user: req.body.user,
currency: req.body.currency,
description: req.body.description || `${req.body.user}'s budget`,
balance: balance || 0,
transactions: [],
};
db[req.body.user] = account;

return res.status(201).json(account);
});

// ----------------------------------------------

// Get all data for the specified account
router.get('/accounts/:user', (req, res) => {
const account = db[req.params.user];

// Check if account exists
if (!account) {
return res.status(404).json({ error: 'User does not exist' });
}

return res.json(account);
});

// ----------------------------------------------

// Remove specified account
router.delete('/accounts/:user', (req, res) => {
const account = db[req.params.user];

// Check if account exists
if (!account) {
return res.status(404).json({ error: 'User does not exist' });
}

// Removed account
delete db[req.params.user];

res.sendStatus(204);
});

// ----------------------------------------------

// Add a transaction to a specific account
router.post('/accounts/:user/transactions', (req, res) => {
const account = db[req.params.user];

// Check if account exists
if (!account) {
return res.status(404).json({ error: 'User does not exist' });
}

// Check mandatory requests parameters
if (!req.body.date || !req.body.object || !req.body.amount) {
return res.status(400).json({ error: 'Missing parameters' });
}

// Convert amount to number if needed
let amount = req.body.amount;
if (amount && typeof amount !== 'number') {
amount = parseFloat(amount);
}

// Check that amount is a valid number
if (amount && isNaN(amount)) {
return res.status(400).json({ error: 'Amount must be a number' });
}

// Generates an ID for the transaction
const id = crypto
.createHash('md5')
.update(req.body.date + req.body.object + req.body.amount)
.digest('hex');

// Check that transaction does not already exist
if (account.transactions.some((transaction) => transaction.id === id)) {
return res.status(409).json({ error: 'Transaction already exists' });
}

// Add transaction
const transaction = {
id,
date: req.body.date,
object: req.body.object,
amount,
};
account.transactions.push(transaction);

// Update balance
account.balance += transaction.amount;

return res.status(201).json(transaction);
});

// ----------------------------------------------

// Remove specified transaction from account
router.delete('/accounts/:user/transactions/:id', (req, res) => {
const account = db[req.params.user];

// Check if account exists
if (!account) {
return res.status(404).json({ error: 'User does not exist' });
}

const transactionIndex = account.transactions.findIndex(
(transaction) => transaction.id === req.params.id
);

// Check if transaction exists
if (transactionIndex === -1) {
return res.status(404).json({ error: 'Transaction does not exist' });
}

// Remove transaction
account.transactions.splice(transactionIndex, 1);

res.sendStatus(204);
});

// ***************************************************************************

// Add 'api` prefix to all routes
app.use(apiPrefix, router);

// Start the server
app.listen(port, () => {
console.log(`Server listening on port ${port}`);
});

module.exports = app; // ← This exports the Express app instance (NOT the server)
Loading