Skip to content

Commit

Permalink
N21-1319 ctl showing tool usage (#4543)
Browse files Browse the repository at this point in the history
* N21-1319 externaltool metadata
* N21-1319 schoolexternaltool metadata
* N21-1319 add ExternalToolMetadataService
* N21-1319 add SchoolExternalToolMetadataService
* N21-1319 loggable
  • Loading branch information
mrikallab authored Nov 20, 2023
1 parent 84c267d commit caf63ea
Show file tree
Hide file tree
Showing 33 changed files with 1,010 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ToolContextType } from '../enum';
import { ContextExternalToolType } from '../../context-external-tool/entity';

export class ToolContextMapper {
static contextMapping: Record<ToolContextType, ContextExternalToolType> = {
[ToolContextType.COURSE]: ContextExternalToolType.COURSE,
[ToolContextType.BOARD_ELEMENT]: ContextExternalToolType.BOARD_ELEMENT,
};
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Loaded } from '@mikro-orm/core';
import { EntityManager, ObjectId } from '@mikro-orm/mongodb';
import { ServerTestModule } from '@modules/server';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { Permission } from '@shared/domain';
import { Permission, SchoolEntity } from '@shared/domain';
import {
cleanupCollections,
contextExternalToolEntityFactory,
externalToolEntityFactory,
externalToolFactory,
schoolExternalToolEntityFactory,
schoolFactory,
TestApiClient,
UserAndAccountTestFactory,
} from '@shared/testing';
import { ServerTestModule } from '@modules/server';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { Response } from 'supertest';
Expand All @@ -20,8 +23,16 @@ import {
CustomParameterTypeParams,
ToolConfigType,
} from '../../../common/enum';
import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity';
import { SchoolExternalToolEntity } from '../../../school-external-tool/entity';
import { ExternalToolMetadata } from '../../domain';
import { ExternalToolEntity } from '../../entity';
import { ExternalToolCreateParams, ExternalToolResponse, ExternalToolSearchListResponse } from '../dto';
import {
ExternalToolCreateParams,
ExternalToolResponse,
ExternalToolSearchListResponse,
ExternalToolMetadataResponse,
} from '../dto';

describe('ToolController (API)', () => {
let app: INestApplication;
Expand Down Expand Up @@ -617,4 +628,82 @@ describe('ToolController (API)', () => {
});
});
});

describe('[GET] tools/external-tools/:externalToolId/metadata', () => {
describe('when user is not authenticated', () => {
const setup = () => {
const toolId: string = new ObjectId().toHexString();

return { toolId };
};

it('should return unauthorized', async () => {
const { toolId } = setup();

const response: Response = await testApiClient.get(`${toolId}/metadata`);

expect(response.statusCode).toEqual(HttpStatus.UNAUTHORIZED);
});
});

describe('when externalToolId is given ', () => {
const setup = async () => {
const toolId: string = new ObjectId().toHexString();
const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(undefined, toolId);

const school: SchoolEntity = schoolFactory.buildWithId();
const schoolExternalToolEntitys: SchoolExternalToolEntity[] = schoolExternalToolEntityFactory.buildList(2, {
tool: externalToolEntity,
school,
});

const courseTools: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList(3, {
schoolTool: schoolExternalToolEntitys[0],
contextType: ContextExternalToolType.COURSE,
});

const boardTools: ContextExternalToolEntity[] = contextExternalToolEntityFactory.buildList(2, {
schoolTool: schoolExternalToolEntitys[1],
contextType: ContextExternalToolType.BOARD_ELEMENT,
contextId: new ObjectId().toHexString(),
});

const externalToolMetadata: ExternalToolMetadata = new ExternalToolMetadata({
schoolExternalToolCount: 2,
contextExternalToolCountPerContext: { course: 3, boardElement: 2 },
});

const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({}, [Permission.TOOL_ADMIN]);
await em.persistAndFlush([
adminAccount,
adminUser,
school,
externalToolEntity,
...schoolExternalToolEntitys,
...courseTools,
...boardTools,
]);
em.clear();

const loggedInClient: TestApiClient = await testApiClient.login(adminAccount);

return { loggedInClient, toolId, externalToolEntity, externalToolMetadata };
};

it('should return the metadata of externalTool', async () => {
const { loggedInClient, externalToolEntity } = await setup();

const response: Response = await loggedInClient.get(`${externalToolEntity.id}/metadata`);

expect(response.statusCode).toEqual(HttpStatus.OK);
expect(response.body).toEqual<ExternalToolMetadataResponse>({
schoolExternalToolCount: 2,
contextExternalToolCountPerContext: {
course: 3,
boardElement: 2,
},
});
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ApiProperty } from '@nestjs/swagger';
import { ContextExternalToolType } from '../../../../context-external-tool/entity';

export class ExternalToolMetadataResponse {
@ApiProperty()
schoolExternalToolCount: number;

@ApiProperty({
type: 'object',
properties: Object.fromEntries(
Object.values(ContextExternalToolType).map((key: ContextExternalToolType) => [key, { type: 'number' }])
),
})
contextExternalToolCountPerContext: Record<ContextExternalToolType, number>;

constructor(externalToolMetadataResponse: ExternalToolMetadataResponse) {
this.schoolExternalToolCount = externalToolMetadataResponse.schoolExternalToolCount;
this.contextExternalToolCountPerContext = externalToolMetadataResponse.contextExternalToolCountPerContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './context-external-tool-configuration-template.response';
export * from './context-external-tool-configuration-template-list.response';
export * from './school-external-tool-configuration-template.response';
export * from './school-external-tool-configuration-template-list.response';
export * from './external-tool-metadata.response';
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@ import { LegacyLogger } from '@src/core/logger';
import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication';
import { Response } from 'express';
import { ExternalToolSearchQuery } from '../../common/interface';
import { ExternalTool } from '../domain';
import { ExternalTool, ExternalToolMetadata } from '../domain';
import { ExternalToolLogo } from '../domain/external-tool-logo';
import { ExternalToolRequestMapper, ExternalToolResponseMapper } from '../mapper';

import { ExternalToolRequestMapper, ExternalToolResponseMapper, ExternalToolMetadataMapper } from '../mapper';
import { ExternalToolLogoService } from '../service';
import { ExternalToolCreate, ExternalToolUc, ExternalToolUpdate } from '../uc';
import {
Expand All @@ -30,6 +31,7 @@ import {
ExternalToolSearchParams,
ExternalToolUpdateParams,
SortExternalToolParams,
ExternalToolMetadataResponse,
} from './dto';

@ApiTags('Tool')
Expand Down Expand Up @@ -165,4 +167,26 @@ export class ToolController {
res.setHeader('Cache-Control', 'must-revalidate');
res.send(externalToolLogo.logo);
}

@Get('/:externalToolId/metadata')
@ApiOperation({ summary: 'Gets the metadata of an external tool.' })
@ApiOkResponse({
description: 'Metadata of external tool fetched successfully.',
type: ExternalToolMetadataResponse,
})
@ApiUnauthorizedResponse({ description: 'User is not logged in.' })
async getMetaDataForExternalTool(
@CurrentUser() currentUser: ICurrentUser,
@Param() params: ExternalToolIdParams
): Promise<ExternalToolMetadataResponse> {
const externalToolMetadata: ExternalToolMetadata = await this.externalToolUc.getMetadataForExternalTool(
currentUser.userId,
params.externalToolId
);

const mapped: ExternalToolMetadataResponse =
ExternalToolMetadataMapper.mapToExternalToolMetadataResponse(externalToolMetadata);

return mapped;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class ExternalToolMetadata {
schoolExternalToolCount: number;

contextExternalToolCountPerContext: Record<string, number>;

constructor(externalToolMetadata: ExternalToolMetadata) {
this.schoolExternalToolCount = externalToolMetadata.schoolExternalToolCount;
this.contextExternalToolCountPerContext = externalToolMetadata.contextExternalToolCountPerContext;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/tool/external-tool/domain/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './external-tool.do';
export * from './config';
export * from './external-tool-metadata';
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { OauthProviderServiceModule } from '@infra/oauth-provider';
import { EncryptionModule } from '@infra/encryption';
import { ExternalToolRepo } from '@shared/repo';
import { ToolConfigModule } from '../tool-config.module';
import { ExternalToolMetadataMapper } from './mapper';
import { ToolContextMapper } from '../common/mapper/tool-context.mapper';
import {
ExternalToolConfigurationService,
ExternalToolLogoService,
Expand All @@ -13,6 +15,7 @@ import {
ExternalToolServiceMapper,
ExternalToolValidationService,
ExternalToolVersionIncrementService,
ExternalToolMetadataService,
} from './service';
import { CommonToolModule } from '../common';

Expand All @@ -27,13 +30,17 @@ import { CommonToolModule } from '../common';
ExternalToolConfigurationService,
ExternalToolLogoService,
ExternalToolRepo,
ExternalToolMetadataService,
ExternalToolMetadataMapper,
ToolContextMapper,
],
exports: [
ExternalToolService,
ExternalToolValidationService,
ExternalToolVersionIncrementService,
ExternalToolConfigurationService,
ExternalToolLogoService,
ExternalToolMetadataService,
],
})
export class ExternalToolModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { ExternalToolMetadataResponse } from '../controller/dto';
import { ExternalToolMetadata } from '../domain';

export class ExternalToolMetadataMapper {
static mapToExternalToolMetadataResponse(externalToolMetadata: ExternalToolMetadata): ExternalToolMetadataResponse {
const externalToolMetadataResponse: ExternalToolMetadataResponse = new ExternalToolMetadataResponse({
schoolExternalToolCount: externalToolMetadata.schoolExternalToolCount,
contextExternalToolCountPerContext: externalToolMetadata.contextExternalToolCountPerContext,
});

return externalToolMetadataResponse;
}
}
1 change: 1 addition & 0 deletions apps/server/src/modules/tool/external-tool/mapper/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './external-tool-request.mapper';
export * from './external-tool-response.mapper';
export * from './external-tool-metadata.mapper';
Loading

0 comments on commit caf63ea

Please sign in to comment.