Skip to content

Commit

Permalink
feat: add basic lambda deployment
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardobridge committed Jan 23, 2024
1 parent 6d80b60 commit 3a578c2
Show file tree
Hide file tree
Showing 7 changed files with 269,403 additions and 0 deletions.
68 changes: 68 additions & 0 deletions .github/workflows/serverless-deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: Deploy to AWS

on:
workflow_dispatch:
inputs:
ENVIRONMENT:
description: 'Environment to deploy to'
required: true
default: 'master'
type: string
SHOULD_SEED_DB:
description: 'Seed the database?'
required: false
default: false
type: boolean
workflow_call:
inputs:
ENVIRONMENT:
description: 'Environment to deploy to'
required: true
default: 'master'
type: string
SHOULD_SEED_DB:
description: 'Seed the database?'
required: false
default: false
type: boolean
outputs:
SERVICE_URL:
description: 'API Gateway endpoints'
value: ${{ jobs.deploy_to_env.outputs.SERVICE_URL }}

permissions:
id-token: write
contents: read
jobs:
deploy_to_env:
runs-on: ubuntu-latest
env:
ENVIRONMENT: ${{ github.event.inputs.ENVIRONMENT }}
outputs:
SERVICE_URL: ${{ steps.set_service_url.outputs.SERVICE_URL }}
steps:
- uses: actions/checkout@v3
- name: Use Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
- run: npm ci
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-region: eu-west-2
audience: sts.amazonaws.com
role-to-assume: ${{ secrets.AWS_DEPLOYER_ROLE }}
role-session-name: OIDCSession
mask-aws-account-id: true
- name: serverless_deploy
run: npm run deploy
- name: seed_db
if: ${{ github.event.inputs.SHOULD_SEED_DB == true }}
run: npm run seed-db
- id: set_service_url
name: Generate, mask, and output a secret
run: |
SERVICE_URL="$(npx serverless info --verbose | grep ServiceEndpoint | sed s/ServiceEndpoint\:\ //g)"
# echo "::add-mask::$SERVICE_URL"
echo "SERVICE_URL=$SERVICE_URL" >> "$GITHUB_OUTPUT"
13 changes: 13 additions & 0 deletions constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const SERVICE_NAME = "movies-service";
const REGION = process.env.DEPLOYMENT_REGION || "eu-west-2";

if(!process.env.DDB_TABLE_NAME || (!process.env.SERVICE_NAME && !process.env.ENVIRONMENT)) {
throw new Error('TABLE_NAME environment variable not set');
}

const TABLE_NAME = process.env.DDB_TABLE_NAME || `${SERVICE_NAME}-${ENVIRONMENT}`;

module.exports = {
REGION,
TABLE_NAME,
}
37 changes: 37 additions & 0 deletions handlers/get-all-movies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, ScanCommand } = require("@aws-sdk/lib-dynamodb");
const { REGION, TABLE_NAME } = require('../constants');

const ddbClient = new DynamoDBClient({ region: REGION });
const docClient = DynamoDBDocumentClient.from(ddbClient);

const getAllMovies = async () => {
const params = {
TableName: TABLE_NAME
};

try {
const data = await docClient.send(new ScanCommand(params));
return {
statusCode: 200,
body: JSON.stringify(data.Items.map(movie => {
return {
id: movie.id,
title: movie.title,
year: movie.year,
overview: movie.overview,
vote_average: movie.vote_average,
poster_path: `https://image.tmdb.org/t/p/w45${movie.poster_path}`,
}
}))
};
} catch (error) {
console.error("Error getting all movies:", error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Could not fetch movies' })
};
}
};

module.exports = { getAllMovies };
40 changes: 40 additions & 0 deletions handlers/get-movie-by-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

const { REGION, TABLE_NAME } = require('../constants');

const ddbClient = new DynamoDBClient({ region: REGION });
const docClient = DynamoDBDocumentClient.from(ddbClient);

const getMovieById = async (event) => {

const params = {
TableName: TABLE_NAME,
Key: {
id: Number(event.pathParameters.id),
},
};

try {
const data = await docClient.send(new GetCommand(params));
if (!data.Item) {
return {
statusCode: 404,
body: JSON.stringify({ error: 'Movie not found' }),
};
}

return {
statusCode: 200,
body: JSON.stringify(data.Item),
};
} catch (error) {
console.error("Error getting movie by ID:", error);
return {
statusCode: 500,
body: JSON.stringify({ error: 'Could not fetch movie' }),
};
}
};

module.exports = { getMovieById };
28 changes: 28 additions & 0 deletions seed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const AWS = require('aws-sdk');
const { REGION, TABLE_NAME } = require('./constants');

const dynamoDb = new AWS.DynamoDB.DocumentClient({ region: REGION });
const movies = require('./tmdb_movies.json');

async function uploadMovie(movie) {
const params = {
TableName: TABLE_NAME,
Item: movie,
};

try {
await dynamoDb.put(params).promise();
console.log(`Movie added: ${movie.title}`);
} catch (error) {
console.error(`Error adding movie: ${movie.title}`, error);
}
}

async function uploadAllMovies() {
for (const movie of movies) {
await uploadMovie(movie);
await new Promise(resolve => setTimeout(resolve, 100));
}
}

uploadAllMovies().then(() => console.log('All movies uploaded'));
59 changes: 59 additions & 0 deletions serverless.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
service: movie-service

provider:
name: aws
runtime: nodejs18.x
stage: ${opt:stage}
region: eu-west-2
environment:
DDB_TABLE_NAME: ${self:custom.tableName}
DEPLOYMENT_REGION: ${self:provider.region}
deploymentBucket:
name: bernardo-demo-deployment-bucket
serverSideEncryption: AES256
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Scan
- dynamodb:GetItem
Resource: arn:aws:dynamodb:${self:provider.region}:*:table/${self:custom.tableName}

custom:
tableName: ${self:service}-${self:provider.stage}

plugins:
- serverless-deployment-bucket
- serverless-better-credentials

functions:
getAllMovies:
handler: handlers/get-all-movies.getAllMovies
events:
- http:
path: movies
method: get
cors: true

getMovieById:
handler: handlers/get-movie-by-id.getMovieById
events:
- http:
path: movies/{id}
method: get
cors: true

resources:
Resources:
MovieTable:
Type: 'AWS::DynamoDB::Table'
Properties:
TableName: ${self:custom.tableName}
AttributeDefinitions:
- AttributeName: id
AttributeType: N
KeySchema:
- AttributeName: id
KeyType: HASH
BillingMode: PAY_PER_REQUEST
Loading

0 comments on commit 3a578c2

Please sign in to comment.