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

N21-1257 refactoring tool module #4434

Merged
merged 47 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
b62e126
N21-1269 wip
arnegns Sep 13, 2023
5f3d4c9
add pseudonym controller
IgorCapCoder Sep 13, 2023
064a684
Merge branch 'N21-1269-roster-adaptation-temp' into N21-1269-roster-a…
IgorCapCoder Sep 13, 2023
5f0f69a
N21-1269 logs
arnegns Sep 14, 2023
1d5c2e3
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 14, 2023
e17ff65
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 14, 2023
3e33484
N21-1269 logs 2
arnegns Sep 14, 2023
4fddaf4
N21-1269 changes loading user
arnegns Sep 14, 2023
7902c8c
N21-1269 changes response
arnegns Sep 14, 2023
5e4ac9f
N21-1269 logging
arnegns Sep 14, 2023
6b05d35
N21-1269 changed repo
arnegns Sep 14, 2023
d8d7296
N21-1269 changes
arnegns Sep 14, 2023
a5e96f5
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 14, 2023
bc2c525
pseudonyms endpoint
IgorCapCoder Sep 14, 2023
ed20ee1
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
IgorCapCoder Sep 14, 2023
de27649
N21-1269 pseudonym endpoint with pagination implemented
arnegns Sep 14, 2023
be61610
N21-1269 changed endpoint to get with id
arnegns Sep 14, 2023
5b7e88e
N21-1269 moved iframe code from id-token service to pseudonym service
arnegns Sep 15, 2023
a71f75b
N21-1269 wip
arnegns Sep 15, 2023
ff095f5
check permission, uc.spec update
IgorCapCoder Sep 15, 2023
69c9214
api test WIP
IgorCapCoder Sep 15, 2023
06f820b
Merge branch 'main' into N21-1269-roster-adaptation
arnegns Sep 18, 2023
1b8197d
N21-1269 getUserGroups changes
arnegns Sep 18, 2023
17c2130
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
57430ed
N21-1269 adds feathers test
arnegns Sep 18, 2023
185c423
N21-1269 adds some nest tests
arnegns Sep 18, 2023
40080e8
api test
IgorCapCoder Sep 18, 2023
0c149e3
N21-1269 adds loggable exception for too many pseudonyms
arnegns Sep 18, 2023
d842aeb
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
32ac9e8
refactor controller and uc to use a pseudonym string directly
IgorCapCoder Sep 18, 2023
7409893
N21-1269 changes feathers test
arnegns Sep 18, 2023
6635d08
Merge remote-tracking branch 'origin/N21-1269-roster-adaptation' into…
arnegns Sep 18, 2023
04bf666
N21-1269 fixes feathers test with resetting sinon stubs
arnegns Sep 18, 2023
35e5e07
code readabilty enhancement
IgorCapCoder Sep 18, 2023
59cc0e5
code readabilty enhancement
IgorCapCoder Sep 18, 2023
6fdcbc7
unit test fix
IgorCapCoder Sep 18, 2023
3be3eea
fix import
IgorCapCoder Sep 18, 2023
3668879
add unit test
IgorCapCoder Sep 18, 2023
8b224bf
test structure in uc
IgorCapCoder Sep 21, 2023
dd7b374
get key and secret in private function
IgorCapCoder Sep 21, 2023
b1a6685
request changes
IgorCapCoder Sep 22, 2023
67ce0e6
Merge branch 'main' into N21-1257-refactoring-tool-module
IgorCapCoder Sep 25, 2023
796a934
Merge branch 'main' into N21-1257-refactoring-tool-module
IgorCapCoder Sep 25, 2023
713de00
request changes
IgorCapCoder Sep 25, 2023
ae18871
coverage up
IgorCapCoder Sep 25, 2023
6cbaf21
Merge branch 'main' into N21-1257-refactoring-tool-module
IgorCapCoder Sep 25, 2023
1f43126
Merge branch 'main' into N21-1257-refactoring-tool-module
IgorCapCoder Sep 26, 2023
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
1 change: 1 addition & 0 deletions apps/server/src/modules/pseudonym/loggable/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './too-many-pseudonyms.loggable-exception';
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { TooManyPseudonymsLoggableException } from './too-many-pseudonyms.loggable-exception';

describe('TooManyPseudonymsLoggableException', () => {
describe('constructor', () => {
const setup = () => {
const pseudonym = 'pseudonym';

return { pseudonym };
};

it('should create an instance of TooManyPseudonymsLoggableException', () => {
const { pseudonym } = setup();

const loggable = new TooManyPseudonymsLoggableException(pseudonym);

expect(loggable).toBeInstanceOf(TooManyPseudonymsLoggableException);
});
});

describe('getLogMessage', () => {
const setup = () => {
const pseudonym = 'pseudonym';
const loggable = new TooManyPseudonymsLoggableException(pseudonym);

return { loggable, pseudonym };
};

it('should return a loggable message', () => {
const { loggable, pseudonym } = setup();

const message = loggable.getLogMessage();

expect(message).toEqual({
type: 'PSEUDONYMS_TOO_MANY_PSEUDONYMS_FOUND',
message: 'Too many pseudonyms where found.',
stack: loggable.stack,
data: {
pseudonym,
},
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { HttpStatus } from '@nestjs/common';
import { BusinessError } from '@shared/common';
import { Loggable } from '@src/core/logger/interfaces';
import { ErrorLogMessage, LogMessage, ValidationErrorLogMessage } from '@src/core/logger/types';

export class TooManyPseudonymsLoggableException extends BusinessError implements Loggable {
constructor(private readonly pseudonym: string) {
super(
{
type: 'PSEUDONYMS_TOO_MANY_PSEUDONYMS_FOUND',
title: 'Too many pseudonyms where found.',
defaultMessage: 'Too many pseudonyms where found.',
},
HttpStatus.BAD_REQUEST
);
}

getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage {
return {
type: 'PSEUDONYMS_TOO_MANY_PSEUDONYMS_FOUND',
message: 'Too many pseudonyms where found.',
stack: this.stack,
data: {
pseudonym: this.pseudonym,
},
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ToolStatusOutdatedLoggableException } from './tool-status-outdated.loggable-exception';

describe('ToolStatusOutdatedLoggableException', () => {
describe('getLogMessage', () => {
const setup = () => {
const toolId = 'toolId';
const userId = 'userId';

const exception = new ToolStatusOutdatedLoggableException(userId, toolId);

return {
exception,
};
};

it('should log the correct message', () => {
const { exception } = setup();

const result = exception.getLogMessage();

expect(result).toEqual({
type: 'TOOL_STATUS_OUTDATED',
message: expect.any(String),
stack: expect.any(String),
data: {
userId: 'userId',
toolId: 'toolId',
},
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ const toolConfigTypeToToolLaunchDataTypeMapping: Record<ToolConfigType, ToolLaun
[ToolConfigType.OAUTH2]: ToolLaunchDataType.OAUTH2,
};

const toolLaunchDataTypeToToolConfigTypeMapping: Record<ToolLaunchDataType, ToolConfigType> = Object.entries(
toolConfigTypeToToolLaunchDataTypeMapping
).reduce((acc: Record<ToolLaunchDataType, ToolConfigType>, [key, value]) => {
return { ...acc, [value]: key as ToolConfigType };
}, {} as Record<ToolLaunchDataType, ToolConfigType>);
const toolLaunchDataTypeToToolConfigTypeMapping: Record<ToolLaunchDataType, ToolConfigType> = {
[ToolLaunchDataType.BASIC]: ToolConfigType.BASIC,
[ToolLaunchDataType.LTI11]: ToolConfigType.LTI11,
[ToolLaunchDataType.OAUTH2]: ToolConfigType.OAUTH2,
};

export class ToolLaunchMapper {
static mapToParameterLocation(location: CustomParameterLocation): PropertyLocation {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Authorization } from 'oauth-1.0a';
import { LtiRole } from '../../../common/enum';
import { ExternalTool } from '../../../external-tool/domain';
import { LtiRoleMapper } from '../../mapper';
import { LaunchRequestMethod, PropertyData, PropertyLocation } from '../../types';
import { LaunchRequestMethod, PropertyData, PropertyLocation, AuthenticationValues } from '../../types';
import { Lti11EncryptionService } from '../lti11-encryption.service';
import { AbstractLaunchStrategy } from './abstract-launch.strategy';
import { IToolLaunchParams } from './tool-launch-params.interface';
Expand Down Expand Up @@ -129,6 +129,19 @@ export class Lti11ToolLaunchStrategy extends AbstractLaunchStrategy {
payload[property.name] = property.value;
}

const authentication: AuthenticationValues = this.getAuthenticationValues(properties);

const signedPayload: Authorization = this.lti11EncryptionService.sign(
authentication.keyValue,
authentication.secretValue,
url,
payload
);

return JSON.stringify(signedPayload);
}

private getAuthenticationValues(properties: PropertyData[]): AuthenticationValues {
const key: PropertyData | undefined = properties.find((property: PropertyData) => property.name === 'key');
const secret: PropertyData | undefined = properties.find((property: PropertyData) => property.name === 'secret');

Expand All @@ -138,9 +151,9 @@ export class Lti11ToolLaunchStrategy extends AbstractLaunchStrategy {
);
}

const signedPayload: Authorization = this.lti11EncryptionService.sign(key.value, secret.value, url, payload);
const authentication = { keyValue: key.value, secretValue: secret.value };

return JSON.stringify(signedPayload);
return authentication;
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ describe('OAuth2ToolLaunchStrategy', () => {
});

describe('buildToolLaunchRequestPayload', () => {
describe('when always', () => {
describe('whenever it is called', () => {
it('should return undefined', () => {
const payload: string | null = strategy.buildToolLaunchRequestPayload('url', []);

Expand All @@ -43,7 +43,7 @@ describe('OAuth2ToolLaunchStrategy', () => {
});

describe('buildToolLaunchDataFromConcreteConfig', () => {
describe('when always', () => {
describe('whenever it is called', () => {
const setup = () => {
const externalTool: ExternalTool = externalToolFactory.build();
const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build();
Expand All @@ -69,7 +69,7 @@ describe('OAuth2ToolLaunchStrategy', () => {
});

describe('determineLaunchRequestMethod', () => {
describe('when always', () => {
describe('whenever it is called', () => {
it('should return GET', () => {
const result: LaunchRequestMethod = strategy.determineLaunchRequestMethod([]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,32 +181,6 @@ describe('ToolLaunchService', () => {
new InternalServerErrorException('Unknown tool config type')
);
});

it('should call getSchoolExternalToolById', async () => {
const { launchParams } = setup();

try {
await service.getLaunchData('userId', launchParams.contextExternalTool);
} catch (exception) {
// Do nothing
}

expect(schoolExternalToolService.getSchoolExternalToolById).toHaveBeenCalledWith(
launchParams.schoolExternalTool.id
);
});

it('should call findExternalToolById', async () => {
const { launchParams } = setup();

try {
await service.getLaunchData('userId', launchParams.contextExternalTool);
} catch (exception) {
// Do nothing
}

expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(launchParams.schoolExternalTool.toolId);
});
});

describe('when tool configuration status is not LATEST', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class AuthenticationValues {
keyValue: string;

secretValue: string;

constructor(props: AuthenticationValues) {
this.keyValue = props.keyValue;
this.secretValue = props.secretValue;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/tool/tool-launch/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './property-location';
export * from './tool-launch-request';
export * from './tool-launch-data-type';
export * from './launch-request-method';
export * from './authentication-values';
20 changes: 9 additions & 11 deletions apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Test, TestingModule } from '@nestjs/testing';
import { contextExternalToolFactory } from '@shared/testing';
import { ObjectId } from 'bson';
import { ToolLaunchService } from '../service';
import { ToolLaunchData, ToolLaunchDataType, ToolLaunchRequest } from '../types';
import { ToolLaunchUc } from './tool-launch.uc';
Expand Down Expand Up @@ -62,7 +63,11 @@ describe('ToolLaunchUc', () => {
properties: [],
});

const userId = 'userId';
const userId: string = new ObjectId().toHexString();

toolPermissionHelper.ensureContextPermissions.mockResolvedValueOnce();
contextExternalToolService.getContextExternalToolById.mockResolvedValueOnce(contextExternalTool);
toolLaunchService.getLaunchData.mockResolvedValueOnce(toolLaunchData);

return {
userId,
Expand All @@ -82,31 +87,24 @@ describe('ToolLaunchUc', () => {

it('should call service to get data', async () => {
const { userId, contextExternalToolId, contextExternalTool } = setup();
toolPermissionHelper.ensureContextPermissions.mockResolvedValue();
contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool);

await uc.getToolLaunchRequest(userId, contextExternalToolId);

expect(toolLaunchService.getLaunchData).toHaveBeenCalledWith(userId, contextExternalTool);
});

it('should call service to generate launch request', async () => {
const { userId, contextExternalToolId, contextExternalTool, toolLaunchData } = setup();
toolPermissionHelper.ensureContextPermissions.mockResolvedValue();
contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool);
const { userId, contextExternalToolId, toolLaunchData } = setup();

toolLaunchService.getLaunchData.mockResolvedValue(toolLaunchData);
toolLaunchService.getLaunchData.mockResolvedValueOnce(toolLaunchData);

await uc.getToolLaunchRequest(userId, contextExternalToolId);

expect(toolLaunchService.generateLaunchRequest).toHaveBeenCalledWith(toolLaunchData);
});

it('should return launch request', async () => {
const { userId, contextExternalToolId, toolLaunchData, contextExternalTool } = setup();
toolPermissionHelper.ensureContextPermissions.mockResolvedValue();
contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool);
toolLaunchService.getLaunchData.mockResolvedValue(toolLaunchData);
const { userId, contextExternalToolId } = setup();

const toolLaunchRequest: ToolLaunchRequest = await uc.getToolLaunchRequest(userId, contextExternalToolId);

Expand Down
Loading