Skip to content

Commit

Permalink
feat: ASAP-415 Add Working-Group Public API endpoint (#4266)
Browse files Browse the repository at this point in the history
* add new public endpoints
* add publish date and version to the responses
  • Loading branch information
peterstarling authored May 9, 2024
1 parent b82c520 commit d8c3ee8
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ export const parseWorkingGroupToDataObject = (
primaryEmail: workingGroup.primaryEmail ?? undefined,
secondaryEmail: workingGroup.secondaryEmail ?? undefined,
leadingMembers: workingGroup.leadingMembers ?? '',
publishDate: workingGroup.sys.publishedAt,
systemPublishedVersion: workingGroup.sys.publishedVersion || undefined,
tags,
members,
milestones,
Expand Down
20 changes: 19 additions & 1 deletion apps/gp2-server/src/publicApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import UserController from './controllers/user.controller';
import { userRouteFactory } from './routes/public/user.route';
import { UserContentfulDataProvider } from './data-providers/user.data-provider';
import { AssetContentfulDataProvider } from './data-providers/asset.data-provider';
import WorkingGroupController from './controllers/working-group.controller';
import { workingGroupRouteFactory } from './routes/public/working-group.route';
import { WorkingGroupContentfulDataProvider } from './data-providers/working-group.data-provider';

export const publicAppFactory = (
dependencies: PublicAppDependencies = {},
Expand Down Expand Up @@ -70,12 +73,20 @@ export const publicAppFactory = (
const assetDataProvider = new AssetContentfulDataProvider(
getContentfulRestClientFactory,
);
const workingGroupDataProvider = new WorkingGroupContentfulDataProvider(
contentfulGraphQLClient,
getContentfulRestClientFactory,
);

const userController =
dependencies.userController ||
new UserController(userDataProvider, assetDataProvider);
const outputController =
dependencies.outputController ||
new OutputController(outputDataProvider, externalUserDataProvider);
const workingGroupController =
dependencies.workingGroupController ||
new WorkingGroupController(workingGroupDataProvider);

const basicRoutes = Router();

Expand All @@ -86,9 +97,15 @@ export const publicAppFactory = (

const outputRoutes = outputRouteFactory(outputController);
const userRoutes = userRouteFactory(userController);
const workingGroupRoutes = workingGroupRouteFactory(workingGroupController);

// add routes
app.use('/public', [basicRoutes, outputRoutes, userRoutes]);
app.use('/public', [
basicRoutes,
outputRoutes,
userRoutes,
workingGroupRoutes,
]);

// Catch all
app.get('*', async (_req, res) => {
Expand Down Expand Up @@ -117,6 +134,7 @@ type PublicAppDependencies = {
userDataProvider?: UserDataProvider;
userController?: UserController;
externalUserDataProvider?: ExternalUserDataProvider;
workingGroupController?: WorkingGroupController;
sentryErrorHandler?: typeof Sentry.Handlers.errorHandler;
sentryRequestHandler?: typeof Sentry.Handlers.requestHandler;
sentryTransactionIdHandler?: RequestHandler;
Expand Down
49 changes: 49 additions & 0 deletions apps/gp2-server/src/routes/public/working-group.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Router, Response } from 'express';
import { gp2 as gp2Model } from '@asap-hub/model';
import WorkingGroupController from '../../controllers/working-group.controller';

export const workingGroupRouteFactory = (
workingGroupController: WorkingGroupController,
): Router => {
const workingGroupRoutes = Router();

workingGroupRoutes.get(
'/working-groups',
async (_req, res: Response<gp2Model.ListPublicWorkingGroupResponse>) => {
const result = await workingGroupController.fetch();

res.json({
total: result.total,
items: result.items.map(mapWorkingGroupToPublicWorkingGroup),
});
},
);

workingGroupRoutes.get(
'/working-groups/:workingGroupId',
async (req, res: Response<gp2Model.PublicWorkingGroupResponse>) => {
const { workingGroupId } = req.params;

const workingGroup =
await workingGroupController.fetchById(workingGroupId);

res.json(mapWorkingGroupToPublicWorkingGroup(workingGroup));
},
);

return workingGroupRoutes;
};

const mapWorkingGroupToPublicWorkingGroup = (
workingGroup: gp2Model.WorkingGroupResponse,
): gp2Model.PublicWorkingGroupResponse => ({
id: workingGroup.id,
description: workingGroup.description,
members: workingGroup.members,
shortDescription: workingGroup.shortDescription,
title: workingGroup.title,
primaryEmail: workingGroup.primaryEmail,
secondaryEmail: workingGroup.secondaryEmail,
publishDate: workingGroup.publishDate,
systemPublishedVersion: workingGroup.systemPublishedVersion,
});
38 changes: 38 additions & 0 deletions apps/gp2-server/test/fixtures/working-group.fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ export const getWorkingGroupDataObject =
name: 'working group calendar',
},
tags: [{ id: '42', name: 'tag-1' }],
publishDate: '2023-05-17T13:39:03.250Z',
systemPublishedVersion: 14,
});

export const getWorkingGroupUpdateDataObject =
Expand Down Expand Up @@ -94,6 +96,39 @@ export const getListWorkingGroupsResponse =
items: [getWorkingGroupResponse()],
});

export const getPublicWorkingGroupResponse =
(): gp2Model.PublicWorkingGroupResponse => {
const {
id,
description,
title,
shortDescription,
members,
primaryEmail,
secondaryEmail,
publishDate,
systemPublishedVersion,
} = getWorkingGroupResponse();

return {
id,
description,
title,
shortDescription,
primaryEmail,
secondaryEmail,
members,
publishDate,
systemPublishedVersion,
};
};

export const getListPublicWorkingGroupResponse =
(): gp2Model.ListPublicWorkingGroupResponse => ({
total: 1,
items: [getPublicWorkingGroupResponse()],
});

export const getContentfulGraphqlWorkingGroupMembers = () => ({
total: 1,
items: [
Expand Down Expand Up @@ -148,6 +183,9 @@ export const getContentfulGraphqlWorkingGroup = (
> => ({
sys: {
id: '11',
publishedAt: '2023-05-17T13:39:03.250Z',
publishedVersion: 14,
firstPublishedAt: '2021-01-01T13:39:03.250Z',
},
title: 'a working group title',
shortDescription: 'Short description',
Expand Down
82 changes: 82 additions & 0 deletions apps/gp2-server/test/routes/public/working-group.route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { NotFoundError } from '@asap-hub/errors';
import supertest from 'supertest';
import { publicAppFactory } from '../../../src/publicApp';
import {
getListPublicWorkingGroupResponse,
getListWorkingGroupsResponse,
getPublicWorkingGroupResponse,
getWorkingGroupResponse,
} from '../../fixtures/working-group.fixtures';
import { workingGroupControllerMock } from '../../mocks/working-group.controller.mock';

describe('/working-groups/ route', () => {
const publicApp = publicAppFactory({
workingGroupController: workingGroupControllerMock,
});

afterEach(jest.clearAllMocks);

describe('GET /working-groups', () => {
test('Should return 200 when no working-group exists', async () => {
workingGroupControllerMock.fetch.mockResolvedValueOnce({
items: [],
total: 0,
});
const response = await supertest(publicApp).get('/public/working-groups');

expect(response.status).toBe(200);
expect(response.body).toEqual({
total: 0,
items: [],
});
});

test('Should return the results correctly', async () => {
const listWorkingGroupResponse = getListWorkingGroupsResponse();
const listPublicWorkingGroupResponse =
getListPublicWorkingGroupResponse();

workingGroupControllerMock.fetch.mockResolvedValueOnce(
listWorkingGroupResponse,
);

const response = await supertest(publicApp).get('/public/working-groups');

expect(response.body).toEqual(listPublicWorkingGroupResponse);
});
});

describe('GET /working-groups/:workingGroupId', () => {
test('Should return 404 when working-group does not exist', async () => {
workingGroupControllerMock.fetchById.mockRejectedValueOnce(
new NotFoundError(undefined, 'working-group with id not found'),
);

const response = await supertest(publicApp).get(
'/public/working-groups/working-group-id',
);

expect(response.status).toBe(404);
expect(response.body).toEqual({
error: 'Not Found',
message: 'working-group with id not found',
statusCode: 404,
});
});

test('Should return the working-group correctly', async () => {
const workingGroupResponse = getWorkingGroupResponse();
const publicWorkingGroupResponse = getPublicWorkingGroupResponse();

workingGroupControllerMock.fetchById.mockResolvedValueOnce(
workingGroupResponse,
);

const response = await supertest(publicApp).get(
`/public/working-groups/${workingGroupResponse!.id}`,
);

expect(response.body).toEqual(publicWorkingGroupResponse);
});
});
});
1 change: 1 addition & 0 deletions packages/fixtures/src/gp2/working-groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const mockedWorkingGroup: gp2.WorkingGroupResponse = {
},
],
tags: [],
publishDate: '2023-05-17T13:39:03.250Z',
};

export const createWorkingGroupResponse = (
Expand Down
17 changes: 17 additions & 0 deletions packages/model/src/gp2/working-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,30 @@ export type WorkingGroupDataObject = {
resources?: Resource[];
calendar?: Calendar;
tags: TagDataObject[];
publishDate: string;
systemPublishedVersion?: number;
};

export type ListWorkingGroupDataObject = ListResponse<WorkingGroupDataObject>;

export type WorkingGroupResponse = WorkingGroupDataObject;

export type PublicWorkingGroupResponse = Pick<
WorkingGroupResponse,
| 'id'
| 'title'
| 'description'
| 'shortDescription'
| 'primaryEmail'
| 'secondaryEmail'
| 'members'
| 'publishDate'
| 'systemPublishedVersion'
>;

export type ListWorkingGroupResponse = ListResponse<WorkingGroupResponse>;
export type ListPublicWorkingGroupResponse =
ListResponse<PublicWorkingGroupResponse>;

export type WorkingGroupUpdateDataObject = Partial<
Pick<WorkingGroupDataObject, 'resources'>
Expand Down

0 comments on commit d8c3ee8

Please sign in to comment.