Skip to content

Commit

Permalink
fix: make all docs consistent with version prefix and grouping in sid…
Browse files Browse the repository at this point in the history
…ebar (#659)

# Problem

Docs sidebar for different services/controllers was inconsistent;
showing the version prefix path for some, and not others.

# Solution
Utilize Redocly `x-tagGroups` extension to group controllers with
different versions nested under the controller group. Only affects docs.

## Steps to Verify:

1. Run `npm run generate:openapi`
2. Run `npm run generate:swagger-ui`
3. Inspect docs for each service to see changes
4. Run each service & browse to the live Swagger UI; should be no change
from previous (Swagger UI doesn't support tag groups)

## Screenshots (optional):

![image](https://github.com/user-attachments/assets/59427902-a2b8-46d0-bdee-c8dfda0e579e)


![image](https://github.com/user-attachments/assets/fe16b5bb-e4fc-4f14-bd79-d5470bc5ac50)


![image](https://github.com/user-attachments/assets/9c7e6487-c3ca-4788-8981-02c705b017e6)


![image](https://github.com/user-attachments/assets/1c69e55e-21c6-406b-ba55-e37910545a33)


Closes #658

---------

Co-authored-by: Wil Wade <[email protected]>
  • Loading branch information
JoeCap08055 and wilwade authored Oct 24, 2024
1 parent db34494 commit 026a80d
Show file tree
Hide file tree
Showing 43 changed files with 526 additions and 216 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ Contributions, issues, and feature requests are welcome!

- [Contributing Guidelines](./CONTRIBUTING.md)
- [Open Issues](https://github.com/ProjectLibertyLabs/gateway/issues)
- [Contributor Notes](./developer-docs/CONTRIBUTOR-NOTES.md)

<p align="right">(<a href="#-table-of-contents">back to top</a>)</p>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { RetireMsaRequestDto, TransactionResponse } from '#types/dtos/account';
import { AccountIdDto, MsaIdDto } from '#types/dtos/common';
import blockchainConfig, { IBlockchainConfig } from '#blockchain/blockchain.config';

@Controller('v1/accounts')
@Controller({ version: '1', path: 'accounts' })
@ApiTags('v1/accounts')
export class AccountsControllerV1 {
private readonly logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { ApiCreatedResponse, ApiOkResponse, ApiOperation, ApiTags } from '@nestj
import { AccountIdDto, MsaIdDto, ProviderMsaIdDto } from '#types/dtos/common';

@Controller({ version: '1', path: 'delegation' })
@ApiTags('delegation')
@ApiTags('v1/delegation')
@UseGuards(ReadOnlyGuard) // Apply guard at the controller level
export class DelegationControllerV1 {
private readonly logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { u8aToHex } from '@polkadot/util';
import { TransactionType } from '#types/account-webhook';
import { HandleDto, MsaIdDto } from '#types/dtos/common';

@Controller('v1/handles')
@Controller({ version: '1', path: 'handles' })
@ApiTags('v1/handles')
@UseGuards(ReadOnlyGuard)
export class HandlesControllerV1 {
Expand Down
2 changes: 1 addition & 1 deletion apps/account-api/src/controllers/v1/keys-v1.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
import { TransactionType } from '#types/account-webhook';
import { MsaIdDto } from '#types/dtos/common';

@Controller('v1/keys')
@Controller({ version: '1', path: 'keys' })
@ApiTags('v1/keys')
@UseGuards(ReadOnlyGuard) // Apply guard at the controller level
export class KeysControllerV1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ import { hasChainSubmissions } from '@projectlibertylabs/siwfv2';
// - `authorizationPayload`: A base64url encoded, JSON stringification of the SIWF Response data structure.
//

@Controller('v2/accounts')
@Controller({ version: '2', path: 'accounts' })
@ApiTags('v2/accounts')
export class AccountsControllerV2 {
private readonly logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { IDelegationResponseV2 } from '#types/interfaces/account/delegations.int
import { Controller, Get, HttpCode, HttpStatus, Logger, Param, UseGuards } from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';

@Controller({ version: ['2'], path: '/delegations' })
@ApiTags('delegations')
@Controller({ version: '2', path: 'delegations' })
@ApiTags('v2/delegations')
@UseGuards(ReadOnlyGuard) // Apply guard at the controller level
export class DelegationsControllerV2 {
private readonly logger: Logger;
Expand Down
14 changes: 13 additions & 1 deletion apps/account-api/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,19 @@ async function bootstrap() {
const swaggerDoc = await generateSwaggerDoc(app, {
title: 'Account Service',
description: 'Account Service API',
version: '1.0',
version: '2.0',
extensions: new Map<string, any>([
[
'x-tagGroups',
[
{ name: 'accounts', tags: ['v1/accounts', 'v2/accounts'] },
{ name: 'delegations', tags: ['v1/delegation', 'v2/delegations'] },
{ name: 'handles', tags: ['v1/handles'] },
{ name: 'health', tags: ['health'] },
{ name: 'keys', tags: ['v1/keys'] },
],
],
]),
});

const args = process.argv.slice(2);
Expand Down
25 changes: 21 additions & 4 deletions apps/account-api/test/accounts.controller.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-undef */
import { HttpStatus, INestApplication, ValidationPipe } from '@nestjs/common';
import { HttpStatus, ValidationPipe, VersioningType } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { EventEmitter2 } from '@nestjs/event-emitter';
import request from 'supertest';
Expand All @@ -12,9 +12,13 @@ import { u8aToHex } from '@polkadot/util';
import { cryptoWaitReady } from '@polkadot/util-crypto';
import { CacheMonitorService } from '#cache/cache-monitor.service';
import { WalletLoginRequestDto, RetireMsaPayloadResponseDto, RetireMsaRequestDto } from '#types/dtos/account';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { TimeoutInterceptor } from '#utils/interceptors/timeout.interceptor';
import { NestExpressApplication } from '@nestjs/platform-express';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';

describe('Account Controller', () => {
let app: INestApplication;
let app: NestExpressApplication;
let module: TestingModule;
let currentBlockNumber: number;
let users: ChainUser[];
Expand Down Expand Up @@ -58,14 +62,27 @@ describe('Account Controller', () => {
}).compile();

app = module.createNestApplication();

// Uncomment below to see logs when debugging tests
// module.useLogger(new Logger());

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
app.enableVersioning({ type: VersioningType.URI });
app.enableShutdownHooks();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, enableDebugMessages: true }));
app.useGlobalInterceptors(new TimeoutInterceptor(config.apiTimeoutMs));
app.useBodyParser('json', { limit: config.apiBodyJsonLimit });

const eventEmitter = app.get<EventEmitter2>(EventEmitter2);
eventEmitter.on('shutdown', async () => {
await app.close();
});
app.useGlobalPipes(new ValidationPipe());
app.enableShutdownHooks();
await app.init();

// Make sure we're connected to the chain before running tests
const blockchainService = app.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
await blockchainService.isReady();

httpServer = app.getHttpServer();

// Redis timeout keeping test suite alive for too long; disable
Expand Down
25 changes: 21 additions & 4 deletions apps/account-api/test/accounts.v2.controller.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-undef */
import { HttpStatus, INestApplication, ValidationPipe } from '@nestjs/common';
import { HttpStatus, ValidationPipe, VersioningType } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { cryptoWaitReady } from '@polkadot/util-crypto';
Expand All @@ -14,9 +14,13 @@ import { CacheMonitorService } from '#cache/cache-monitor.service';
import { WalletV2RedirectRequestDto } from '#types/dtos/account/wallet.v2.redirect.request.dto';
import { SCHEMA_NAME_TO_ID } from '#types/constants/schemas';
import { validSiwfV2Create } from './e2e-setup.mock.spec';
import { NestExpressApplication } from '@nestjs/platform-express';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';
import { TimeoutInterceptor } from '#utils/interceptors/timeout.interceptor';

describe('Accounts v2 Controller', () => {
let app: INestApplication;
let app: NestExpressApplication;
let module: TestingModule;
let httpServer: any;
let mockSiwfServer: Server;
Expand All @@ -33,14 +37,27 @@ describe('Accounts v2 Controller', () => {
}).compile();

app = module.createNestApplication();

// Uncomment below to see logs when debugging tests
// module.useLogger(new Logger());

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
app.enableVersioning({ type: VersioningType.URI });
app.enableShutdownHooks();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, enableDebugMessages: true }));
app.useGlobalInterceptors(new TimeoutInterceptor(config.apiTimeoutMs));
app.useBodyParser('json', { limit: config.apiBodyJsonLimit });

const eventEmitter = app.get<EventEmitter2>(EventEmitter2);
eventEmitter.on('shutdown', async () => {
await app.close();
});
app.useGlobalPipes(new ValidationPipe({ transform: true }));
app.enableShutdownHooks();
await app.init();

// Make sure we're connected to the chain before running tests
const blockchainService = app.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
await blockchainService.isReady();

httpServer = app.getHttpServer();

// Redis timeout keeping test suite alive for too long; disable
Expand Down
25 changes: 21 additions & 4 deletions apps/account-api/test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { INestApplication, ValidationPipe } from '@nestjs/common';
import { ValidationPipe, VersioningType } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import request from 'supertest';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ApiModule } from '../src/api.module';
import { CacheMonitorService } from '#cache/cache-monitor.service';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';
import { TimeoutInterceptor } from '#utils/interceptors/timeout.interceptor';
import { NestExpressApplication } from '@nestjs/platform-express';

describe('Account Service E2E request verification!', () => {
let app: INestApplication;
let app: NestExpressApplication;
let module: TestingModule;
let httpServer: any;

Expand All @@ -16,14 +20,27 @@ describe('Account Service E2E request verification!', () => {
}).compile();

app = module.createNestApplication();

// Uncomment below to see logs when debugging tests
// module.useLogger(new Logger());

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
app.enableVersioning({ type: VersioningType.URI });
app.enableShutdownHooks();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, enableDebugMessages: true }));
app.useGlobalInterceptors(new TimeoutInterceptor(config.apiTimeoutMs));
app.useBodyParser('json', { limit: config.apiBodyJsonLimit });

const eventEmitter = app.get<EventEmitter2>(EventEmitter2);
eventEmitter.on('shutdown', async () => {
await app.close();
});
app.useGlobalPipes(new ValidationPipe());
app.enableShutdownHooks();
await app.init();

// Make sure we're connected to the chain before running tests
const blockchainService = app.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
await blockchainService.isReady();

httpServer = app.getHttpServer();

// Redis timeout keeping test suite alive for too long; disable
Expand Down
29 changes: 21 additions & 8 deletions apps/account-api/test/delegations.controller.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable no-undef */
import { HttpStatus, INestApplication, ValidationPipe, VersioningType } from '@nestjs/common';
import { HttpStatus, ValidationPipe, VersioningType } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { EventEmitter2 } from '@nestjs/event-emitter';
import request from 'supertest';
Expand All @@ -12,6 +12,10 @@ import { u8aToHex } from '@polkadot/util';
import Keyring from '@polkadot/keyring';
import { RevokeDelegationPayloadRequestDto, RevokeDelegationPayloadResponseDto } from '#types/dtos/account';
import { KeyringPair } from '@polkadot/keyring/types';
import apiConfig, { IAccountApiConfig } from '#account-api/api.config';
import { BlockchainRpcQueryService } from '#blockchain/blockchain-rpc-query.service';
import { TimeoutInterceptor } from '#utils/interceptors/timeout.interceptor';
import { NestExpressApplication } from '@nestjs/platform-express';

let users: ChainUser[];
let revokedUser: ChainUser;
Expand All @@ -29,7 +33,7 @@ let msaNonProviderId: string;
let nonMsaKeypair: KeyringPair;

describe('Delegation Controller', () => {
let app: INestApplication;
let app: NestExpressApplication;
let module: TestingModule;

beforeAll(async () => {
Expand All @@ -50,18 +54,27 @@ describe('Delegation Controller', () => {
}).compile();

app = module.createNestApplication();

// Uncomment below to see logs when debugging tests
// module.useLogger(new Logger());

const config = app.get<IAccountApiConfig>(apiConfig.KEY);
app.enableVersioning({ type: VersioningType.URI });
app.enableShutdownHooks();
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, enableDebugMessages: true }));
app.useGlobalInterceptors(new TimeoutInterceptor(config.apiTimeoutMs));
app.useBodyParser('json', { limit: config.apiBodyJsonLimit });

const eventEmitter = app.get<EventEmitter2>(EventEmitter2);
eventEmitter.on('shutdown', async () => {
await app.close();
});
app.useGlobalPipes(new ValidationPipe());
app.enableShutdownHooks();
// Enable URL-based API versioning
app.enableVersioning({
type: VersioningType.URI,
});
await app.init();

// Make sure we're connected to the chain before running tests
const blockchainService = app.get<BlockchainRpcQueryService>(BlockchainRpcQueryService);
await blockchainService.isReady();

httpServer = app.getHttpServer();

// Redis timeout keeping test suite alive for too long; disable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { FilesInterceptor } from '@nestjs/platform-express';
import { ApiBody, ApiConsumes, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { ApiService } from '../../api.service';

@Controller('v1/asset')
@Controller({ version: '1', path: 'asset' })
@ApiTags('v1/asset')
export class AssetControllerV1 {
private readonly logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { AnnouncementTypeName } from '#types/enums';
import { MsaIdDto } from '#types/dtos/common';

@Controller('v1/content')
@Controller({ version: '1', path: 'content' })
@ApiTags('v1/content')
export class ContentControllerV1 {
private readonly logger: Logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ProfileDto, AnnouncementResponseDto, AssetIncludedRequestDto } from '#t
import { AnnouncementTypeName } from '#types/enums';
import { MsaIdDto } from '#types/dtos/common';

@Controller('v1/profile')
@Controller({ version: '1', path: 'profile' })
@ApiTags('v1/profile')
export class ProfileControllerV1 {
private readonly logger: Logger;
Expand Down
18 changes: 17 additions & 1 deletion apps/content-publishing-api/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NestFactory } from '@nestjs/core';
import { Logger, ValidationPipe } from '@nestjs/common';
import { Logger, ValidationPipe, VersioningType } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ApiModule } from './api.module';
import apiConfig, { IContentPublishingApiConfig } from './api.config';
Expand Down Expand Up @@ -33,10 +33,26 @@ async function bootstrap() {
rawBody: true,
});

// Enable URL-based API versioning
app.enableVersioning({
type: VersioningType.URI,
});

const swaggerDoc = await generateSwaggerDoc(app, {
title: 'Content Publishing Service API',
description: 'Content Publishing Service API',
version: '1.0',
extensions: new Map<string, any>([
[
'x-tagGroups',
[
{ name: 'asset', tags: ['v1/asset'] },
{ name: 'content', tags: ['v1/content'] },
{ name: 'health', tags: ['health'] },
{ name: 'profile', tags: ['v1/profile'] },
],
],
]),
});

const args = process.argv.slice(2);
Expand Down
Loading

0 comments on commit 026a80d

Please sign in to comment.