Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content-publisher api: more validations #518

Merged
merged 2 commits into from
Sep 24, 2024
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
46 changes: 23 additions & 23 deletions apps/content-publishing-api/k6-test/script.k6.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ export const options = {
};

export default function () {
group('/v1/content/{userDsnpId}', () => {
let userDsnpId = '1';
group('/v1/content/{msaId}', () => {
let msaId = '1';
// Request No. 1: ApiController_update with no assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}`;
let url = BASE_URL + `/v1/content/${msaId}`;
const body = {
targetContentHash: '0x7653423447AF',
targetContentHash: 'bdyqdua4t4pxgy37mdmjyqv3dejp5betyqsznimpneyujsur23yubzna',
targetAnnouncementType: 'broadcast',
content: validContentNoUploadedAssets,
};
Expand All @@ -56,9 +56,9 @@ export default function () {
}
// Request No. 2: ApiController_update with assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}`;
let url = BASE_URL + `/v1/content/${msaId}`;
const body = {
targetContentHash: '0x7653423447AF',
targetContentHash: 'bdyqdua4t4pxgy37mdmjyqv3dejp5betyqsznimpneyujsur23yubzna',
targetAnnouncementType: 'broadcast',
content: createContentWithAsset(BASE_URL),
};
Expand All @@ -72,9 +72,9 @@ export default function () {

// Request No. 3: ApiController_delete
{
let url = BASE_URL + `/v1/content/${userDsnpId}`;
let url = BASE_URL + `/v1/content/${msaId}`;
let body = {
targetContentHash: '0x7653423447AF',
targetContentHash: 'bdyqdua4t4pxgy37mdmjyqv3dejp5betyqsznimpneyujsur23yubzna',
targetAnnouncementType: 'broadcast',
};
let params = { headers: { 'Content-Type': 'application/json', Accept: 'application/json' } };
Expand All @@ -99,12 +99,12 @@ export default function () {
}
});

group('/v1/profile/{userDsnpId}', () => {
let userDsnpId = '1'; // specify value as there is no example value for this parameter in OpenAPI spec
group('/v1/profile/{msaId}', () => {
let msaId = '1'; // specify value as there is no example value for this parameter in OpenAPI spec

// Request No. 1: ApiController_profile with no assets
{
let url = BASE_URL + `/v1/profile/${userDsnpId}`;
let url = BASE_URL + `/v1/profile/${msaId}`;
let body = { profile: validProfileNoUploadedAssets };
let params = { headers: { 'Content-Type': 'application/json', Accept: 'application/json' } };
let request = http.put(url, JSON.stringify(body), params);
Expand All @@ -115,7 +115,7 @@ export default function () {
}
// Request No. 2: ApiController_profile with asset
{
let url = BASE_URL + `/v1/profile/${userDsnpId}`;
let url = BASE_URL + `/v1/profile/${msaId}`;
const referenceId = getReferenceId(BASE_URL);
let profile = Object.assign({}, validProfileNoUploadedAssets, {
icon: [
Expand All @@ -136,12 +136,12 @@ export default function () {
}
});

group('/v1/content/{userDsnpId}/broadcast', () => {
let userDsnpId = '1';
group('/v1/content/{msaId}/broadcast', () => {
let msaId = '1';

// Request No. 1: ApiController_broadcast no assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}/broadcast`;
let url = BASE_URL + `/v1/content/${msaId}/broadcast`;
const body = {
content: validContentNoUploadedAssets,
};
Expand All @@ -154,7 +154,7 @@ export default function () {
}
// Request No. 2: ApiController_broadcast with assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}/broadcast`;
let url = BASE_URL + `/v1/content/${msaId}/broadcast`;
const body = {
content: createContentWithAsset(BASE_URL),
};
Expand All @@ -167,12 +167,12 @@ export default function () {
}
});

group('/v1/content/{userDsnpId}/reaction', () => {
let userDsnpId = '1';
group('/v1/content/{msaId}/reaction', () => {
let msaId = '1';

// Request No. 1: ApiController_reaction
{
let url = BASE_URL + `/v1/content/${userDsnpId}/reaction`;
let url = BASE_URL + `/v1/content/${msaId}/reaction`;
let body = validReaction;
let params = { headers: { 'Content-Type': 'application/json', Accept: 'application/json' } };
let request = http.post(url, JSON.stringify(body), params);
Expand All @@ -183,12 +183,12 @@ export default function () {
}
});

group('/v1/content/{userDsnpId}/reply', () => {
let userDsnpId = '1';
group('/v1/content/{msaId}/reply', () => {
let msaId = '1';

// Request No. 1: ApiController_reply no assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}/reply`;
let url = BASE_URL + `/v1/content/${msaId}/reply`;
let body = validReplyNoUploadedAssets;
let params = { headers: { 'Content-Type': 'application/json', Accept: 'application/json' } };
let request = http.post(url, JSON.stringify(body), params);
Expand All @@ -199,7 +199,7 @@ export default function () {
}
// Request No. 2: ApiController_reply with assets
{
let url = BASE_URL + `/v1/content/${userDsnpId}/reply`;
let url = BASE_URL + `/v1/content/${msaId}/reply`;
let body = Object.assign({}, validReplyNoUploadedAssets, { content: createContentWithAsset(BASE_URL) });
let params = { headers: { 'Content-Type': 'application/json', Accept: 'application/json' } };
let request = http.post(url, JSON.stringify(body), params);
Expand Down
12 changes: 12 additions & 0 deletions apps/content-publishing-api/src/api.config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe('Content Publishing API Config', () => {
API_PORT: undefined,
// API_TIMEOUT_MS: undefined,
FILE_UPLOAD_MAX_SIZE_IN_BYTES: undefined,
FILE_UPLOAD_COUNT_LIMIT: undefined,
aramikm marked this conversation as resolved.
Show resolved Hide resolved
};

beforeAll(() => {
Expand All @@ -28,8 +29,13 @@ describe('Content Publishing API Config', () => {

it('missing file upload limit should fail', async () => validateMissing(ALL_ENV, 'FILE_UPLOAD_MAX_SIZE_IN_BYTES'));

it('missing file upload count limit should fail', async () => validateMissing(ALL_ENV, 'FILE_UPLOAD_COUNT_LIMIT'));

it('invalid file upload limit should fail', async () =>
shouldFailBadValues(ALL_ENV, 'FILE_UPLOAD_MAX_SIZE_IN_BYTES', [-1]));

it('invalid file upload count limit should fail', async () =>
shouldFailBadValues(ALL_ENV, 'FILE_UPLOAD_COUNT_LIMIT', [-1]));
});

describe('valid environment', () => {
Expand All @@ -52,6 +58,12 @@ describe('Content Publishing API Config', () => {
);
});

it('should get file upload count limit', () => {
expect(contentPublishingServiceConfig.fileUploadCountLimit).toStrictEqual(
parseInt(ALL_ENV.FILE_UPLOAD_COUNT_LIMIT as string, 10),
);
});

// it('should get api timeout limit milliseconds', () => {
// expect(contentPublishingServiceConfig.apiTimeoutMs).toStrictEqual(parseInt(ALL_ENV.API_TIMEOUT_MS as string, 10));
// });
Expand Down
5 changes: 5 additions & 0 deletions apps/content-publishing-api/src/api.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface IContentPublishingApiConfig {
apiPort: number;
// apiTimeoutMs: number;
fileUploadMaxSizeBytes: number;
fileUploadCountLimit: number;
}

export default registerAs('content-publishing-api', (): IContentPublishingApiConfig => {
Expand All @@ -27,6 +28,10 @@ export default registerAs('content-publishing-api', (): IContentPublishingApiCon
value: process.env.FILE_UPLOAD_MAX_SIZE_IN_BYTES,
joi: Joi.number().min(1).required(),
},
fileUploadCountLimit: {
value: process.env.FILE_UPLOAD_COUNT_LIMIT,
joi: Joi.number().min(1).required(),
},
};

return JoiUtils.validate<IContentPublishingApiConfig>(configs);
Expand Down
1 change: 1 addition & 0 deletions apps/content-publishing-api/src/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import ipfsConfig from '#content-publishing-lib/config/ipfs.config';
useFactory: async (apiConf: IContentPublishingApiConfig) => ({
limits: {
fileSize: apiConf.fileUploadMaxSizeBytes,
files: apiConf.fileUploadCountLimit,
},
}),
inject: [apiConfig.KEY],
Expand Down
1 change: 1 addition & 0 deletions apps/content-publishing-api/src/build-openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ process.env.CAPACITY_LIMIT = '{"type":"amount","value":"80"}';
process.env.IPFS_ENDPOINT = 'http://127.0.0.1';
process.env.IPFS_GATEWAY_URL = 'http://127.0.0.1';
process.env.FILE_UPLOAD_MAX_SIZE_IN_BYTES = '100';
process.env.FILE_UPLOAD_COUNT_LIMIT = '10';
process.env.ASSET_EXPIRATION_INTERVAL_SECONDS = '100';
process.env.BATCH_INTERVAL_SECONDS = '100';
process.env.BATCH_MAX_COUNT = '100';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { Body, Controller, Delete, HttpCode, Logger, Param, Post, Put } from '@n
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiService } from '../../api.service';
import {
DsnpUserIdParam,
BroadcastDto,
AnnouncementResponseDto,
AssetIncludedRequestDto,
Expand All @@ -12,6 +11,7 @@ import {
TombstoneDto,
} from '#types/dtos/content-publishing';
import { AnnouncementTypeName } from '#types/enums';
import { MsaIdDto } from '#types/dtos/common';

@Controller('v1/content')
@ApiTags('v1/content')
Expand All @@ -22,60 +22,46 @@ export class ContentControllerV1 {
this.logger = new Logger(this.constructor.name);
}

@Post(':userDsnpId/broadcast')
@Post(':msaId/broadcast')
@ApiOperation({ summary: 'Create DSNP Broadcast for user' })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async broadcast(
@Param() userDsnpId: DsnpUserIdParam,
@Body() broadcastDto: BroadcastDto,
): Promise<AnnouncementResponseDto> {
async broadcast(@Param() { msaId }: MsaIdDto, @Body() broadcastDto: BroadcastDto): Promise<AnnouncementResponseDto> {
const metadata = await this.apiService.validateAssetsAndFetchMetadata(broadcastDto as AssetIncludedRequestDto);
return this.apiService.enqueueRequest(
AnnouncementTypeName.BROADCAST,
userDsnpId.userDsnpId,
broadcastDto,
metadata,
);
return this.apiService.enqueueRequest(AnnouncementTypeName.BROADCAST, msaId, broadcastDto, metadata);
}

@Post(':userDsnpId/reply')
@Post(':msaId/reply')
@ApiOperation({ summary: 'Create DSNP Reply for user' })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async reply(@Param() userDsnpId: DsnpUserIdParam, @Body() replyDto: ReplyDto): Promise<AnnouncementResponseDto> {
async reply(@Param() { msaId }: MsaIdDto, @Body() replyDto: ReplyDto): Promise<AnnouncementResponseDto> {
const metadata = await this.apiService.validateAssetsAndFetchMetadata(replyDto as AssetIncludedRequestDto);
return this.apiService.enqueueRequest(AnnouncementTypeName.REPLY, userDsnpId.userDsnpId, replyDto, metadata);
return this.apiService.enqueueRequest(AnnouncementTypeName.REPLY, msaId, replyDto, metadata);
}

@Post(':userDsnpId/reaction')
@Post(':msaId/reaction')
@ApiOperation({ summary: 'Create DSNP Reaction for user' })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async reaction(
@Param() userDsnpId: DsnpUserIdParam,
@Body() reactionDto: ReactionDto,
): Promise<AnnouncementResponseDto> {
return this.apiService.enqueueRequest(AnnouncementTypeName.REACTION, userDsnpId.userDsnpId, reactionDto);
async reaction(@Param() { msaId }: MsaIdDto, @Body() reactionDto: ReactionDto): Promise<AnnouncementResponseDto> {
return this.apiService.enqueueRequest(AnnouncementTypeName.REACTION, msaId, reactionDto);
}

@Put(':userDsnpId')
@Put(':msaId')
@ApiOperation({ summary: 'Update DSNP Content for user' })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async update(@Param() userDsnpId: DsnpUserIdParam, @Body() updateDto: UpdateDto): Promise<AnnouncementResponseDto> {
async update(@Param() { msaId }: MsaIdDto, @Body() updateDto: UpdateDto): Promise<AnnouncementResponseDto> {
const metadata = await this.apiService.validateAssetsAndFetchMetadata(updateDto as AssetIncludedRequestDto);
return this.apiService.enqueueRequest(AnnouncementTypeName.UPDATE, userDsnpId.userDsnpId, updateDto, metadata);
return this.apiService.enqueueRequest(AnnouncementTypeName.UPDATE, msaId, updateDto, metadata);
}

@Delete(':userDsnpId')
@Delete(':msaId')
@ApiOperation({ summary: 'Delete DSNP Content for user' })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async delete(
@Param() userDsnpId: DsnpUserIdParam,
@Body() tombstoneDto: TombstoneDto,
): Promise<AnnouncementResponseDto> {
return this.apiService.enqueueRequest(AnnouncementTypeName.TOMBSTONE, userDsnpId.userDsnpId, tombstoneDto);
async delete(@Param() { msaId }: MsaIdDto, @Body() tombstoneDto: TombstoneDto): Promise<AnnouncementResponseDto> {
return this.apiService.enqueueRequest(AnnouncementTypeName.TOMBSTONE, msaId, tombstoneDto);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { Body, Controller, HttpCode, Logger, Param, Put } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiService } from '../../api.service';
import {
DsnpUserIdParam,
ProfileDto,
AnnouncementResponseDto,
AssetIncludedRequestDto,
} from '#types/dtos/content-publishing';
import { ProfileDto, AnnouncementResponseDto, AssetIncludedRequestDto } from '#types/dtos/content-publishing';
import { AnnouncementTypeName } from '#types/enums';
import { MsaIdDto } from '#types/dtos/common';

@Controller('v1/profile')
@ApiTags('v1/profile')
Expand All @@ -18,15 +14,12 @@ export class ProfileControllerV1 {
this.logger = new Logger(this.constructor.name);
}

@Put(':userDsnpId')
@Put(':msaId')
@ApiOperation({ summary: "Update a user's Profile" })
@HttpCode(202)
@ApiResponse({ status: '2XX', type: AnnouncementResponseDto })
async profile(
@Param() userDsnpId: DsnpUserIdParam,
@Body() profileDto: ProfileDto,
): Promise<AnnouncementResponseDto> {
async profile(@Param() { msaId }: MsaIdDto, @Body() profileDto: ProfileDto): Promise<AnnouncementResponseDto> {
const metadata = await this.apiService.validateAssetsAndFetchMetadata(profileDto as AssetIncludedRequestDto);
return this.apiService.enqueueRequest(AnnouncementTypeName.PROFILE, userDsnpId.userDsnpId, profileDto, metadata);
return this.apiService.enqueueRequest(AnnouncementTypeName.PROFILE, msaId, profileDto, metadata);
}
}
Loading