Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
npx validate-branch-name
npm run validate-branch-name
npm run pretty
npm run lint
npm run test:e2e
Expand Down
16 changes: 15 additions & 1 deletion cypress/e2e/lambda-endpoint-spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@ describe('Lambda demo endpoint works as expected', () => {
});
});

it('check test Lambda function error handling is working with no body data', () => {
it('check test Lambda function error handling is working with no userQuestion in request body data', () => {
cy.request({
method: 'POST',
url: '/api/lambda',
body: {},
failOnStatusCode: false,
}).then((response) => {
expect(response.status).to.eq(500);
Expand All @@ -38,4 +39,17 @@ describe('Lambda demo endpoint works as expected', () => {
);
});
});

it('check test Lambda function error handling is working with no body object in request', () => {
cy.request({
method: 'POST',
url: '/api/lambda',
failOnStatusCode: false,
}).then((response) => {
expect(response.status).to.eq(500);
expect(response.body).to.contain(
'Invalid Payload : must contain a body property',
);
});
});
});
821 changes: 237 additions & 584 deletions package-lock.json

Large diffs are not rendered by default.

37 changes: 17 additions & 20 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,35 @@
"pretty-fix": "prettier 'src/**/*.{js,jsx,ts,tsx,css,scss,md}' --write",
"test:e2e": "start-server-and-test dev tcp:8000 cypress:run",
"cypress:open": "cypress open",
"cypress:run": "cypress run --e2e"
"cypress:run": "cypress run --e2e",
"validate-branch-name": "bash validate-branch-name.sh"
},
"dependencies": {
"@faker-js/faker": "^9.0.1",
"@faker-js/faker": "^9.3.0",
"@mswjs/data": "^0.16.2",
"@mswjs/http-middleware": "^0.10.1",
"@mswjs/http-middleware": "^0.10.2",
"@types/chai-json-schema": "^1.4.10",
"@types/express": "^4.17.21",
"@types/express": "^5.0.0",
"@types/markdown-it": "^14.1.2",
"chai-json-schema": "^1.5.1",
"dotenv": "^16.4.5",
"dotenv": "^16.4.6",
"highlight.js": "^11.10.0",
"markdown-it": "^14.1.0",
"msw": "^2.4.7",
"tsx": "^4.19.1",
"typescript": "^5.6.2"
"msw": "^2.6.6",
"tsx": "^4.19.2",
"typescript": "^5.7.2",
"zod": "^3.23.8"
},
"devDependencies": {
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@types/aws-lambda": "^8.10.145",
"@types/node": "^22.5.5",
"cypress": "^13.14.2",
"husky": "^9.1.6",
"@commitlint/cli": "^19.6.0",
"@commitlint/config-conventional": "^19.6.0",
"@types/aws-lambda": "^8.10.146",
"@types/node": "^22.10.1",
"cypress": "^13.16.0",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"prettier": "3.3.3",
"prettier": "3.4.1",
"start-server-and-test": "^2.0.8",
"validate-branch-name": "^1.3.1",
"xo": "^0.59.3"
},
"lint-staged": {
Expand All @@ -52,9 +53,5 @@
"extends": [
"@commitlint/config-conventional"
]
},
"validate-branch-name": {
"pattern": "^(main|dev){1}$|^(feat|fix|hotfix|release|chore)/.+$",
"errorMsg": "INVALID BRANCH NAME: use format 'feat|fix|hotfix|release|core/your-branch-name'"
}
}
1 change: 1 addition & 0 deletions src/api/bikes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { http, HttpResponse } from 'msw';
// Add any http handler here (get, push , delete etc., and middleware as needed)

function handler(pathName: string) {
console.log('bikes path');
return [
http.get(`/${pathName}`, ({ request }) => {
const url = new URL(request.url);
Expand Down
1 change: 0 additions & 1 deletion src/api/lambda/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ function handler(pathName: string) {
// Make POST requests to this route with the JSON body data with {"userQuestion": "some test text"} to test
http.post(`/${pathName}`, async ({ request }) => {
const event = await requestToApiGatewayProxyEvent(request);

return HttpResponse.json(await demoHandler(event));
}),
];
Expand Down
4 changes: 1 addition & 3 deletions src/api/posts/api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { db } from '../../models/db.js';
import { prefix } from '../../utilities/env.js';

// Example of msw data auto REST handler generation
function handler(pathName: string) {
// Need to add a prefix here for automatic REST handler generation to a specific path
const prefix = process.env?.USE_API_URL_PREFIX
? '/' + process.env.USE_API_URL_PREFIX
: '';
return [...db.post.toHandlers('rest', prefix)];
}

Expand Down
4 changes: 1 addition & 3 deletions src/api/users/api.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { db } from '../../models/db.js';
import { prefix } from '../../utilities/env.js';

// Example of msw data auto REST handler generation
function handler(pathName: string) {
// Need to add a prefix here for automatic REST handler generation to a specific path
const prefix = process.env?.USE_API_URL_PREFIX
? '/' + process.env.USE_API_URL_PREFIX
: '';
return [...db.user.toHandlers('rest', prefix)];
}

Expand Down
2 changes: 1 addition & 1 deletion src/lambdas/test-lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const handler = async (
): Promise<APIGatewayProxyResult> => {
// On aws its JSON.parse (event.body) to get the body as an object

if (!event.body) {
if (!event?.body) {
throw new Error('Invalid Payload : must contain a body property');
}

Expand Down
7 changes: 4 additions & 3 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@ import { createServer } from '@mswjs/http-middleware';
import * as seeders from './seeders/index.js';
import getApiPaths from './utilities/file-scan.js';
import serverPage from './utilities/server-page.js';
import { env } from './utilities/env.js';

const { apiHandlers, apiPaths } = await getApiPaths();

const httpServer = createServer(...apiHandlers, ...serverPage(apiPaths));
const httpServer = createServer({}, ...apiHandlers, ...serverPage(apiPaths));

httpServer.listen(process.env.SERVER_PORT);
httpServer.listen(env.SERVER_PORT);

// Execute dB seeder functions
for (const seeder of Object.values(seeders)) {
seeder();
}

console.log(`SERVER UP AND RUNNING ON LOCALHOST:${process.env.SERVER_PORT}`);
console.log(`SERVER UP AND RUNNING ON LOCALHOST:${env.SERVER_PORT}`);
9 changes: 8 additions & 1 deletion src/utilities/aws-apigw-convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ const requestToApiGatewayProxyEvent = async (
// On aws its JSON.parse (event.body) to get the body as an object

const extractBodyPayload = async () => {
return JSON.stringify(await request.json());
let payload: DefaultBodyType;
try {
payload = await request.json();
} catch {
throw new Error('Invalid Payload : must contain a body property');
}

return JSON.stringify(payload);
};

const extractQueryStringParameters = async () => {
Expand Down
14 changes: 14 additions & 0 deletions src/utilities/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* eslint-disable @typescript-eslint/naming-convention -- we want env vars to be all caps not strict camelcase */
import { z } from 'zod';

const envSchema = z.object({
PROJECT_NAME: z.string(),
SERVER_PORT: z.string(),
USE_API_URL_PREFIX: z.string(),
});

export const env = envSchema.parse(process.env);

export const prefix = env?.USE_API_URL_PREFIX
? env.USE_API_URL_PREFIX + '/'
: '';
5 changes: 1 addition & 4 deletions src/utilities/file-scan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { type HttpHandler } from 'msw';
import { prefix } from './env';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand All @@ -19,10 +20,6 @@ export default async function getApiPaths() {

const files = fs.readdirSync(apiFolder);

const prefix = process.env?.USE_API_URL_PREFIX
? process.env.USE_API_URL_PREFIX + '/'
: '';

for (const file of files) {
const filePath = path.join(apiFolder, file);
const stats = fs.statSync(filePath);
Expand Down
9 changes: 3 additions & 6 deletions src/utilities/server-page.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
import 'dotenv/config';
import { http, HttpResponse } from 'msw';

const prefix = process.env?.USE_API_URL_PREFIX
? process.env.USE_API_URL_PREFIX + '/'
: '';
import { env, prefix } from './env';

const homePage = (apiPaths: string[]) => {
const htmlString = `
Expand All @@ -16,8 +13,8 @@ const homePage = (apiPaths: string[]) => {

<div style="text-align:left;width:clamp(650px, 800px, 90%)); ">
<h3 class="info">Server Address: <span class="highlight" cy-data="server_address">localhost</span> </h3>
<h3 class="info">Server Port: <span class="highlight" cy-data="server_port">${process.env?.SERVER_PORT?.toUpperCase() ?? 'NONE'}</span> </h3>
<h3 class="info">Server URL Prefix: <span class="highlight" cy-data="url_prefix">${process.env?.USE_API_URL_PREFIX?.toLowerCase() ?? 'NONE'}</span> </h3>
<h3 class="info">Server Port: <span class="highlight" cy-data="server_port">${env?.SERVER_PORT?.toUpperCase() ?? 'NONE'}</span> </h3>
<h3 class="info">Server URL Prefix: <span class="highlight" cy-data="url_prefix">${env?.USE_API_URL_PREFIX?.toLowerCase() ?? 'NONE'}</span> </h3>

<h3 class="info">API endpoints*:</h3>
<div>
Expand Down
5 changes: 2 additions & 3 deletions templates/handlers/api.rest.template.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// // Copy and save as api.ts in the api path folder in your project using the correct path to models dir
import { db } from '../../src/models/db.js';
import { env } from '../../src/utilities/env.js';

// Example of msw data auto REST handler generation
function handler(pathName: string) {
// Need to add a prefix here for automatic REST handler generation to a specific path
const prefix = process.env?.USE_API_URL_PREFIX
? '/' + process.env.USE_API_URL_PREFIX
: '';
const prefix = env?.USE_API_URL_PREFIX ? '/' + env.USE_API_URL_PREFIX : '';

// This will generate all REST handlers for the /api/posts path - GET, POST, PUT and DELETE
return [...db.post.toHandlers('rest', prefix)];
Expand Down
14 changes: 14 additions & 0 deletions validate-branch-name.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Validate branch name format
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)

if ! [[ $BRANCH_NAME =~ ^(main|dev){1}$|^(feat|fix|hotfix|release|chore)/.+$ ]]; then
echo "Error: INVALID BRANCH NAME: use format 'feature|fix|hotfix|release|core/your-branch-name'"
exit 1
fi
echo "Validated branch: $BRANCH_NAME - all OK :)"


## you can set other regex patterns here such as :
## ^(feature|fix|hotfix|release)/.+ - branch has to start with feature/, fix/, release/ or hotfix/
## (feature|release|hotfix)/(JIRA-\d+) - it should look like feature/JIRA-1234
## (feature|release|hotfix)/(JIRA-\d+/)?[a-z-]+ - it should look like feature/branch-name or include JIRA's code like feature/JIRA-1234/branch-name
Loading