Skip to content

Commit 3886d45

Browse files
flopez7portuu3dnechay
authored
[Reputation Oracle] NDA (#3132)
* add NDA module and related functionality * refactor: rename nda_signed to nda_signed_url and update related logic * [Human App] NDA (#3146) * feat: add NDA module with service, controller, and related functionality * feat: add HTTP status code to signNDA endpoint and update tests to use ndaServiceMock * Move user within the signNDA tests as it is not being modified * Clean user instance for each test * Remove async from beforeEach Co-authored-by: Dmitry Nechay <lelekkofe@gmail.com> --------- Co-authored-by: portuu3 <61605646+portuu3@users.noreply.github.com> Co-authored-by: Dmitry Nechay <lelekkofe@gmail.com>
1 parent 64de1ac commit 3886d45

30 files changed

+574
-2
lines changed

packages/apps/human-app/server/src/app.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ import { EnvironmentConfigService } from './common/config/environment-config.ser
4343
import { ForbidUnauthorizedHostMiddleware } from './common/middleware/host-check.middleware';
4444
import { HealthModule } from './modules/health/health.module';
4545
import { UiConfigurationModule } from './modules/ui-configuration/ui-configuration.module';
46+
import { NDAModule } from './modules/nda/nda.module';
47+
import { NDAController } from './modules/nda/nda.controller';
4648

4749
const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
4850

@@ -125,6 +127,7 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
125127
CronJobModule,
126128
HealthModule,
127129
UiConfigurationModule,
130+
NDAModule,
128131
],
129132
controllers: [
130133
AppController,
@@ -137,6 +140,7 @@ const JOI_BOOLEAN_STRING_SCHEMA = Joi.string().valid('true', 'false');
137140
HCaptchaController,
138141
RegisterAddressController,
139142
TokenRefreshController,
143+
NDAController,
140144
],
141145
exports: [HttpModule],
142146
providers: [EnvironmentConfigService],

packages/apps/human-app/server/src/common/config/gateway-config.service.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ export class GatewayConfigService {
110110
method: HttpMethod.GET,
111111
headers: this.JSON_HEADER,
112112
},
113+
[ReputationOracleEndpoints.GET_LATEST_NDA]: {
114+
endpoint: '/nda/latest',
115+
method: HttpMethod.GET,
116+
headers: this.JSON_HEADER,
117+
},
118+
[ReputationOracleEndpoints.SIGN_NDA]: {
119+
endpoint: '/nda/sign',
120+
method: HttpMethod.POST,
121+
headers: this.JSON_HEADER,
122+
},
113123
} as Record<ReputationOracleEndpoints, GatewayEndpointConfig>,
114124
},
115125
[ExternalApiName.HCAPTCHA_LABELING_STATS]: {

packages/apps/human-app/server/src/common/enums/reputation-oracle-endpoints.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export enum ReputationOracleEndpoints {
1616
KYC_ON_CHAIN = 'kyc_on_chain',
1717
REGISTRATION_IN_EXCHANGE_ORACLE = 'registration_in_exchange_oracle',
1818
GET_REGISTRATION_IN_EXCHANGE_ORACLES = 'get_registration_in_exchange_oracles',
19+
GET_LATEST_NDA = 'GET_LATEST_NDA',
20+
SIGN_NDA = 'SIGN_NDA',
1921
}
2022
export enum HCaptchaLabelingStatsEndpoints {
2123
USER_STATS = 'user_stats',

packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.gateway.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@ import {
7676
SigninOperatorData,
7777
SigninOperatorResponse,
7878
} from '../../modules/user-operator/model/operator-signin.model';
79+
import {
80+
GetNDACommand,
81+
GetNDAResponse,
82+
SignNDACommand,
83+
SignNDAData,
84+
SignNDAResponse,
85+
} from '../../modules/nda/model/nda.model';
7986

8087
@Injectable()
8188
export class ReputationOracleGateway {
@@ -329,6 +336,25 @@ export class ReputationOracleGateway {
329336
return this.handleRequestToReputationOracle<TokenRefreshResponse>(options);
330337
}
331338

339+
async getLatestNDA(command: GetNDACommand) {
340+
const options = this.getEndpointOptions(
341+
ReputationOracleEndpoints.GET_LATEST_NDA,
342+
undefined,
343+
command.token,
344+
);
345+
return this.handleRequestToReputationOracle<GetNDAResponse>(options);
346+
}
347+
348+
async sendSignedNDA(command: SignNDACommand) {
349+
const data = this.mapper.map(command, SignNDACommand, SignNDAData);
350+
const options = this.getEndpointOptions(
351+
ReputationOracleEndpoints.SIGN_NDA,
352+
data,
353+
command.token,
354+
);
355+
return this.handleRequestToReputationOracle<SignNDAResponse>(options);
356+
}
357+
332358
sendKycOnChain(token: string) {
333359
const options = this.getEndpointOptions(
334360
ReputationOracleEndpoints.KYC_ON_CHAIN,

packages/apps/human-app/server/src/integrations/reputation-oracle/reputation-oracle.mapper.profile.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
SigninOperatorCommand,
5858
SigninOperatorData,
5959
} from '../../modules/user-operator/model/operator-signin.model';
60+
import { SignNDACommand, SignNDAData } from '../../modules/nda/model/nda.model';
6061

6162
@Injectable()
6263
export class ReputationOracleProfile extends AutomapperProfile {
@@ -143,6 +144,15 @@ export class ReputationOracleProfile extends AutomapperProfile {
143144
destination: new SnakeCaseNamingConvention(),
144145
}),
145146
);
147+
createMap(
148+
mapper,
149+
SignNDACommand,
150+
SignNDAData,
151+
namingConventions({
152+
source: new CamelCaseNamingConvention(),
153+
destination: new SnakeCaseNamingConvention(),
154+
}),
155+
);
146156
};
147157
}
148158
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { AutoMap } from '@automapper/classes';
2+
import { IsString, IsUrl } from 'class-validator';
3+
import { ApiProperty } from '@nestjs/swagger';
4+
5+
export class GetNDACommand {
6+
token: string;
7+
}
8+
9+
export class GetNDAResponse {
10+
url: string;
11+
}
12+
13+
export class SignNDADto {
14+
@AutoMap()
15+
@IsUrl()
16+
@IsString()
17+
@ApiProperty({ example: 'string' })
18+
url: string;
19+
}
20+
21+
export class SignNDACommand {
22+
@AutoMap()
23+
url: string;
24+
token: string;
25+
}
26+
27+
export class SignNDAData {
28+
@AutoMap()
29+
url: string;
30+
}
31+
32+
export class SignNDAResponse {
33+
message: string;
34+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { Mapper } from '@automapper/core';
2+
import { InjectMapper } from '@automapper/nestjs';
3+
import {
4+
Body,
5+
Controller,
6+
Get,
7+
HttpCode,
8+
Post,
9+
UsePipes,
10+
ValidationPipe,
11+
} from '@nestjs/common';
12+
import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
13+
import { Authorization } from '../../common/config/params-decorators';
14+
import { GetNDACommand, SignNDACommand, SignNDADto } from './model/nda.model';
15+
import { NDAService } from './nda.service';
16+
17+
@Controller('/nda')
18+
@ApiTags('NDA')
19+
@UsePipes(new ValidationPipe())
20+
@ApiBearerAuth()
21+
export class NDAController {
22+
@InjectMapper() private readonly mapper: Mapper;
23+
24+
constructor(
25+
private readonly ndaService: NDAService,
26+
@InjectMapper() mapper: Mapper,
27+
) {
28+
this.mapper = mapper;
29+
}
30+
31+
@ApiOperation({
32+
summary: 'Retrieves latest NDA URL',
33+
description:
34+
'Retrieves the latest NDA URL that users must sign to join the oracle',
35+
})
36+
@Get('/')
37+
async getLatestNDA(@Authorization() token: string) {
38+
const command = new GetNDACommand();
39+
command.token = token;
40+
return this.ndaService.getLatestNDA(command);
41+
}
42+
43+
@ApiOperation({
44+
summary: 'Sign NDA',
45+
description:
46+
'Signs the NDA with the provided URL. The URL must match the latest NDA URL.',
47+
})
48+
@HttpCode(200)
49+
@Post('sign')
50+
async signNDA(@Body() dto: SignNDADto, @Authorization() token: string) {
51+
const command = this.mapper.map(dto, SignNDADto, SignNDACommand);
52+
command.token = token;
53+
await this.ndaService.signNDA(command);
54+
return { message: 'NDA signed successfully' };
55+
}
56+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Injectable } from '@nestjs/common';
2+
import {
3+
CamelCaseNamingConvention,
4+
createMap,
5+
Mapper,
6+
namingConventions,
7+
SnakeCaseNamingConvention,
8+
} from '@automapper/core';
9+
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
10+
import { SignNDACommand, SignNDADto } from './model/nda.model';
11+
12+
@Injectable()
13+
export class SignNDAProfile extends AutomapperProfile {
14+
constructor(@InjectMapper() mapper: Mapper) {
15+
super(mapper);
16+
}
17+
18+
override get profile() {
19+
return (mapper: Mapper) => {
20+
createMap(
21+
mapper,
22+
SignNDADto,
23+
SignNDACommand,
24+
namingConventions({
25+
source: new SnakeCaseNamingConvention(),
26+
destination: new CamelCaseNamingConvention(),
27+
}),
28+
);
29+
};
30+
}
31+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { Module } from '@nestjs/common';
2+
import { NDAService } from 'src/modules/nda/nda.service';
3+
import { ReputationOracleModule } from '../../integrations/reputation-oracle/reputation-oracle.module';
4+
import { SignNDAProfile } from './nda.mapper.profile';
5+
6+
@Module({
7+
imports: [ReputationOracleModule],
8+
providers: [NDAService, SignNDAProfile],
9+
exports: [NDAService],
10+
})
11+
export class NDAModule {}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Injectable } from '@nestjs/common';
2+
import { ReputationOracleGateway } from '../../integrations/reputation-oracle/reputation-oracle.gateway';
3+
import {
4+
GetNDACommand,
5+
GetNDAResponse,
6+
SignNDACommand,
7+
SignNDAResponse,
8+
} from './model/nda.model';
9+
10+
@Injectable()
11+
export class NDAService {
12+
constructor(private readonly gateway: ReputationOracleGateway) {}
13+
14+
async getLatestNDA(command: GetNDACommand): Promise<GetNDAResponse> {
15+
return this.gateway.getLatestNDA(command);
16+
}
17+
18+
async signNDA(command: SignNDACommand): Promise<SignNDAResponse> {
19+
return await this.gateway.sendSignedNDA(command);
20+
}
21+
}

0 commit comments

Comments
 (0)