Skip to content

Commit

Permalink
Merge branch 'develop' into chores/nextjs-v13-refactor-apis-into-serv…
Browse files Browse the repository at this point in the history
…er-actions
  • Loading branch information
tednguyendev committed Jun 23, 2023
2 parents 254c301 + aa04905 commit 5923673
Show file tree
Hide file tree
Showing 56 changed files with 969 additions and 429 deletions.
11 changes: 9 additions & 2 deletions nextjs-13/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ OAUTH_GOOGLE_CLIENT_SECRET=
NEXTAUTH_SECRET=
NEXTAUTH_URL=http://localhost:3300
MAILGUN_DOMAIN=
MAILGUN_SMTP_HOST=
MAILGUN_SMTP_PORT=

# set the credentials like this if you want to use mailgun
MAILGUN_SMTP_HOST=smtp.mailgun.org
MAILGUN_SMTP_PORT=587

# set the credentials like this if you want to use mailcatcher
MAILGUN_SMTP_HOST=0.0.0.0
MAILGUN_SMTP_PORT=1025

MAILGUN_SMTP_USERNAME=
MAILGUN_SMTP_PASSWORD=
11 changes: 11 additions & 0 deletions nextjs-13/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.PHONY: dev migrate_db

dev:
make migrate_db &
yarn dev &
redis-server &
yarn worker &
maildev

migrate_db:
npx prisma migrate dev --name init
35 changes: 3 additions & 32 deletions nextjs-13/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,13 @@ cp .env.sample .env
yarn install
```

2. Migrate database:
2. Run the development process:

```bash
prisma generate
prisma migrate dev
```

3. Run the development server:

```bash
npm run dev
# or
yarn dev
```

4. Start maildev

```bash
maildev
# then go to http://0.0.0.0:1080/#/
```

5. Start redis + worker

```bash
# install redis if not already, port 6379
redis-server
make dev
```

```bash
yarn worker
```


6. Open [http://localhost:3300](http://localhost:3300) with your browser to see the result.
3. Open [http://localhost:3300](http://localhost:3300) with your browser to see the result.

## Tests

Expand Down
5 changes: 2 additions & 3 deletions nextjs-13/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"workerr": "dotenv -e .env.local -- npx tsx --watch src/workers/sample.worker.ts",
"worker": "npx tsx --watch src/workers/sample.worker.ts"
"worker": "npx tsx --watch src/workers/email.worker.ts"
},
"dependencies": {
"@faker-js/faker": "^8.0.1",
Expand All @@ -25,8 +24,8 @@
"@types/react": "18.2.6",
"@types/react-dom": "18.2.4",
"axios": "^1.4.0",
"classnames": "^2.3.2",
"bullmq": "^3.15.2",
"classnames": "^2.3.2",
"eslint": "8.40.0",
"eslint-config-next": "13.4.3",
"handlebars": "^4.7.7",
Expand Down
43 changes: 15 additions & 28 deletions nextjs-13/src/app/actions/actions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
* @jest-environment node
*/
import { Prisma } from '@prisma/client';
import { StatusCodes } from 'http-status-codes';

import { newsletterFactory } from '@test/factories/newsletter.factory';
import { userFactory } from '@test/factories/user.factory';
import { invalidParamsMessage } from 'lib/request/getInvalidParamsError';
import { errorMessageList } from 'lib/request/error';
import withAuth from 'lib/withAuth/withAuth';
import {
createNewsletter as createRecord,
updateNewsletter as updateRecord,
deleteNewsletter as deleteRecord,
countNewsletters,
} from 'repositories/newsletter.repository';
import { sendMailQueue } from 'workers/email.worker';

Expand All @@ -29,7 +29,7 @@ jest.mock('lib/withAuth/withAuth');
jest.mock('repositories/newsletter.repository');
jest.mock('workers/email.worker', () => ({
sendMailQueue: {
add: jest.fn(),
addBulk: jest.fn(),
},
}));

Expand Down Expand Up @@ -75,7 +75,7 @@ describe('createNewsletter', () => {
throw new Prisma.PrismaClientValidationError();
});
await expect(createNewsletter(requestBody)).rejects.toThrow(
invalidParamsMessage
errorMessageList[StatusCodes.UNPROCESSABLE_ENTITY]
);

expect(createRecord).toHaveBeenCalledWith({
Expand Down Expand Up @@ -193,23 +193,6 @@ describe('sendNewsletter', () => {
});
});

describe('given invalid newsletter ids', () => {
it('returns invalid newsletter error', async () => {
const user = { id: '1' };
const requestBody = {
email: 'dev@nimblehq.co',
ids: ['1'],
};

withAuth.mockImplementation((callback) => callback(user));
countNewsletters.mockResolvedValue(0);

await expect(sendNewsletter(requestBody)).rejects.toThrow(
'Invalid newsletters'
);
});
});

describe('given valid newsletter ids', () => {
it('triggers sendMail', async () => {
const user = { id: '1', name: 'Dave' };
Expand All @@ -219,16 +202,20 @@ describe('sendNewsletter', () => {
};

withAuth.mockImplementation((callback) => callback(user));
countNewsletters.mockResolvedValue(requestBody.ids.length);

await sendNewsletter(requestBody);

expect(sendMailQueue.add).toHaveBeenCalledWith('sendMail', {
ids: requestBody.ids,
to: requestBody.email,
senderId: user.id,
senderName: user.name,
});
expect(sendMailQueue.addBulk).toHaveBeenCalledWith([
{
data: {
id: requestBody.ids[0],
to: requestBody.email,
senderId: user.id,
senderName: user.name,
},
name: 'sendMail',
},
]);
});
});
});
Expand Down
41 changes: 22 additions & 19 deletions nextjs-13/src/app/actions/actions.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import RequestError from 'lib/request/error';
import { invalidParamsMessage } from 'lib/request/getInvalidParamsError';
import { StatusCodes } from 'http-status-codes';

import RequestError, { errorMessageList } from 'lib/request/error';
import withAuth from 'lib/withAuth/withAuth';
import {
deleteNewsletter as deleteRecord,
updateNewsletter as updateRecord,
createNewsletter as createRecord,
countNewsletters as countRecords,
} from 'repositories/newsletter.repository';
import { sendMailQueue } from 'workers/email.worker';

export async function deleteNewsletter(id: string) {
return withAuth(async (currentUser) => {
const result = await deleteRecord(id, currentUser.id);

if (result.count === 0) {
if (!result.count) {
throw new RequestError({ message: 'Newsletter could not be deleted' });
}
});
Expand Down Expand Up @@ -41,7 +41,7 @@ export async function updateNewsletter({
data,
});

if (result.count === 0) {
if (!result.count) {
throw new RequestError({ message: 'Newsletter could not be updated' });
}
});
Expand All @@ -64,7 +64,9 @@ export async function createNewsletter({

await createRecord(attributes);
} catch (err) {
throw new RequestError({ message: invalidParamsMessage });
throw new RequestError({
message: errorMessageList[StatusCodes.UNPROCESSABLE_ENTITY],
});
}
});
}
Expand All @@ -89,21 +91,22 @@ export async function sendNewsletter({
throw new RequestError({ message: 'Invalid email' });
}

if (ids.length === 0) {
throw new RequestError({ message: 'Invalid newsletters' });
}

const newslettersCount = await countRecords(currentUser.id, ids);
const allIdsAreValid = newslettersCount === ids.length;
if (!allIdsAreValid) {
if (!ids.length) {
throw new RequestError({ message: 'Invalid newsletters' });
}

sendMailQueue.add('sendMail', {
ids,
to: email,
senderId: currentUser.id,
senderName: currentUser.name,
});
sendMailQueue.addBulk(
ids.map((id) => {
return {
name: 'sendMail',
data: {
id,
to: email,
senderId: currentUser.id,
senderName: currentUser.name,
},
};
})
);
});
}
4 changes: 2 additions & 2 deletions nextjs-13/src/app/api/v1/newsletter/[id]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StatusCodes } from 'http-status-codes';
import { NextResponse, NextRequest } from 'next/server';

import getInvalidParamsError from 'lib/request/getInvalidParamsError';
import { invalidParamsResponseError } from 'lib/request/error';
import { findNewsletter } from 'repositories/newsletter.repository';

export async function GET(
Expand All @@ -19,6 +19,6 @@ export async function GET(

return NextResponse.json({ record }, { status: StatusCodes.OK });
} catch (err) {
return getInvalidParamsError();
return invalidParamsResponseError();
}
}
6 changes: 3 additions & 3 deletions nextjs-13/src/app/api/v1/newsletter/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { StatusCodes } from 'http-status-codes';

import { newsletterFactory } from '@test/factories/newsletter.factory';
import appHandler from 'lib/handler/app.handler';
import { queryNewsletters } from 'repositories/newsletter.repository';
import { queryNewsletterList } from 'repositories/newsletter.repository';

import { GET } from './route';

Expand All @@ -19,12 +19,12 @@ describe('GET /v1/newsletter', () => {
const newsletterAttributes = { id: '1', userId: user.id };
const newsletter = { ...newsletterFactory, ...newsletterAttributes };
appHandler.mockImplementation((req, callback) => callback(user, {}));
queryNewsletters.mockResolvedValue([newsletter]);
queryNewsletterList.mockResolvedValue([newsletter]);

const response = await GET({});
const responseBody = await response.json();

expect(queryNewsletters).toHaveBeenCalledWith(user.id);
expect(queryNewsletterList).toHaveBeenCalledWith(user.id);
expect(response.status).toBe(StatusCodes.OK);
expect(responseBody.records[0]).toMatchObject<Newsletter>({
id: newsletterAttributes.id,
Expand Down
4 changes: 2 additions & 2 deletions nextjs-13/src/app/api/v1/newsletter/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { StatusCodes } from 'http-status-codes';
import { NextResponse, NextRequest } from 'next/server';

import appHandler from 'lib/handler/app.handler';
import { queryNewsletters } from 'repositories/newsletter.repository';
import { queryNewsletterList } from 'repositories/newsletter.repository';

export async function GET(req: NextRequest) {
return appHandler(req, async (currentUser, _) => {
const records = await queryNewsletters(currentUser.id);
const records = await queryNewsletterList(currentUser.id);

return NextResponse.json({ records: records }, { status: StatusCodes.OK });
});
Expand Down
Loading

0 comments on commit 5923673

Please sign in to comment.