Skip to content

Commit

Permalink
N21-2203 handle policies-info error responses (#5262)
Browse files Browse the repository at this point in the history
* handle policies-info error responses
* fix everything
---------
Co-authored-by: Marvin Öhlerking <[email protected]>
  • Loading branch information
IgorCapCoder authored Oct 2, 2024
1 parent 6d86aa1 commit 2eba077
Show file tree
Hide file tree
Showing 38 changed files with 403 additions and 90 deletions.
2 changes: 0 additions & 2 deletions apps/server/src/infra/schulconnex-client/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export { SchulconnexRestClientOptions } from './schulconnex-rest-client-options';
export { SchulconnexClientModule } from './schulconnex-client.module';
export { SchulconnexRestClient } from './schulconnex-rest-client';
export * from './response';
export { schulconnexResponseFactory, schulconnexPoliciesInfoResponseFactory } from './testing';
export { SchulconnexClientConfig } from './schulconnex-client-config';
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ export { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-in
export { SchulconnexPoliciesInfoResponse } from './schulconnex-policies-info-response';
export { SchulconnexPoliciesInfoActionType } from './schulconnex-policies-info-action-type';
export { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
export { SchulconnexPoliciesInfoAccessControlResponse } from './schulconnex-policies-info-access-control-response';
export { SchulconnexPoliciesInfoErrorDescriptionResponse } from './schulconnex-policies-info-error-description-response';
export { SchulconnexPoliciesInfoLicenseResponse } from './schulconnex-policies-info-license-response';
export { SchulconnexPoliciesInfoErrorResponse } from './schulconnex-policies-info-error-response';
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Type } from 'class-transformer';
import { IsObject, IsString, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoErrorDescriptionResponse } from './schulconnex-policies-info-error-description-response';

export class SchulconnexPoliciesInfoAccessControlResponse {
@IsString()
'@type'!: string;

@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoErrorDescriptionResponse)
error!: SchulconnexPoliciesInfoErrorDescriptionResponse;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { IsString } from 'class-validator';

export class SchulconnexPoliciesInfoErrorDescriptionResponse {
@IsString()
code!: string;

@IsString()
value!: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Type } from 'class-transformer';
import { IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoAccessControlResponse } from './schulconnex-policies-info-access-control-response';

export class SchulconnexPoliciesInfoErrorResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoAccessControlResponse)
access_control!: SchulconnexPoliciesInfoAccessControlResponse;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Type } from 'class-transformer';
import { IsArray, IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
import { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-info-target-response';

export class SchulconnexPoliciesInfoLicenseResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoTargetResponse)
target!: SchulconnexPoliciesInfoTargetResponse;

@IsArray()
@ValidateNested({ each: true })
@Type(() => SchulconnexPoliciesInfoPermissionResponse)
permission!: SchulconnexPoliciesInfoPermissionResponse[];
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { IsArray } from 'class-validator';
import { IsArray, IsString } from 'class-validator';
import { SchulconnexPoliciesInfoActionType } from './schulconnex-policies-info-action-type';

export class SchulconnexPoliciesInfoPermissionResponse {
@IsArray()
@IsString({ each: true })
action!: SchulconnexPoliciesInfoActionType[];
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Type } from 'class-transformer';
import { IsArray, IsObject, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoPermissionResponse } from './schulconnex-policies-info-permission-response';
import { SchulconnexPoliciesInfoTargetResponse } from './schulconnex-policies-info-target-response';
import { PolymorphicArrayTransform } from '@shared/controller';
import { ClassConstructor } from 'class-transformer';
import { IsArray, ValidateNested } from 'class-validator';
import { SchulconnexPoliciesInfoErrorResponse } from './schulconnex-policies-info-error-response';
import { SchulconnexPoliciesInfoLicenseResponse } from './schulconnex-policies-info-license-response';

export class SchulconnexPoliciesInfoResponse {
@IsObject()
@ValidateNested()
@Type(() => SchulconnexPoliciesInfoTargetResponse)
target!: SchulconnexPoliciesInfoTargetResponse;
const policiesInfoDiscriminator = (
obj: unknown
): ClassConstructor<SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse> =>
typeof obj === 'object' && obj !== null && 'target' in obj && 'permission' in obj
? SchulconnexPoliciesInfoLicenseResponse
: SchulconnexPoliciesInfoErrorResponse;

export class SchulconnexPoliciesInfoResponse {
@IsArray()
@ValidateNested({ each: true })
@Type(() => SchulconnexPoliciesInfoPermissionResponse)
permission!: SchulconnexPoliciesInfoPermissionResponse[];
@PolymorphicArrayTransform(policiesInfoDiscriminator)
data!: (SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse)[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Type } from 'class-transformer';
import { IsObject, IsOptional, ValidateNested } from 'class-validator';
import { SchulconnexGeburtResponse } from './schulconnex-geburt-response';
import { SchulconnexNameResponse } from './schulconnex-name-response';
import 'reflect-metadata';

export class SchulconnexPersonResponse {
@IsObject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ export interface SchulconnexApiInterface {

getPersonenInfo(params: SchulconnexPersonenInfoParams): Promise<SchulconnexResponse[]>;

getPoliciesInfo(accessToken: string, options?: { overrideUrl: string }): Promise<SchulconnexPoliciesInfoResponse[]>;
getPoliciesInfo(accessToken: string, options?: { overrideUrl: string }): Promise<SchulconnexPoliciesInfoResponse>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import { axiosResponseFactory } from '@shared/testing';
import { Logger } from '@src/core/logger';
import { of } from 'rxjs';
import { SchulconnexConfigurationMissingLoggable } from './loggable';
import { SchulconnexPoliciesInfoResponse, SchulconnexResponse } from './response';
import {
SchulconnexPoliciesInfoLicenseResponse,
SchulconnexPoliciesInfoResponse,
SchulconnexResponse,
} from './response';
import { SchulconnexRestClient } from './schulconnex-rest-client';
import { SchulconnexRestClientOptions } from './schulconnex-rest-client-options';
import { schulconnexPoliciesInfoResponseFactory, schulconnexResponseFactory } from './testing';
import { schulconnexPoliciesInfoLicenseResponseFactory, schulconnexResponseFactory } from './testing';

describe(SchulconnexRestClient.name, () => {
let client: SchulconnexRestClient;
Expand Down Expand Up @@ -199,7 +203,8 @@ describe(SchulconnexRestClient.name, () => {
describe('when requesting policies-info', () => {
const setup = () => {
const accessToken = 'accessToken';
const response: SchulconnexPoliciesInfoResponse[] = schulconnexPoliciesInfoResponseFactory.buildList(1);
const response: SchulconnexPoliciesInfoLicenseResponse[] =
schulconnexPoliciesInfoLicenseResponseFactory.buildList(1);

httpService.get.mockReturnValueOnce(of(axiosResponseFactory.build({ data: response })));

Expand All @@ -224,7 +229,7 @@ describe(SchulconnexRestClient.name, () => {
it('should return the response', async () => {
const { accessToken } = setup();

const result: SchulconnexPoliciesInfoResponse[] = await client.getPoliciesInfo(accessToken);
const result: SchulconnexPoliciesInfoResponse = await client.getPoliciesInfo(accessToken);

expect(result).toBeDefined();
});
Expand All @@ -234,7 +239,8 @@ describe(SchulconnexRestClient.name, () => {
const setup = () => {
const accessToken = 'accessToken';
const customUrl = 'https://override.url/policies-info';
const response: SchulconnexPoliciesInfoResponse[] = schulconnexPoliciesInfoResponseFactory.buildList(1);
const response: SchulconnexPoliciesInfoLicenseResponse[] =
schulconnexPoliciesInfoLicenseResponseFactory.buildList(1);

httpService.get.mockReturnValueOnce(of(axiosResponseFactory.build({ data: response })));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ import QueryString from 'qs';
import { lastValueFrom, Observable } from 'rxjs';
import { SchulconnexConfigurationMissingLoggable } from './loggable';
import { SchulconnexPersonenInfoParams } from './request';
import { SchulconnexPoliciesInfoResponse, SchulconnexResponse } from './response';
import {
SchulconnexPoliciesInfoErrorResponse,
SchulconnexPoliciesInfoLicenseResponse,
SchulconnexPoliciesInfoResponse,
SchulconnexResponse,
} from './response';
import { SchulconnexApiInterface } from './schulconnex-api.interface';
import { SchulconnexRestClientOptions } from './schulconnex-rest-client-options';

Expand Down Expand Up @@ -51,15 +56,18 @@ export class SchulconnexRestClient implements SchulconnexApiInterface {
public async getPoliciesInfo(
accessToken: string,
options?: { overrideUrl: string }
): Promise<SchulconnexPoliciesInfoResponse[]> {
): Promise<SchulconnexPoliciesInfoResponse> {
const url: URL = new URL(options?.overrideUrl ?? `${this.SCHULCONNEX_API_BASE_URL}/policies-info`);

const response: Promise<SchulconnexPoliciesInfoResponse[]> = this.getRequest<SchulconnexPoliciesInfoResponse[]>(
url,
accessToken
);
const response: (SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse)[] =
await this.getRequest<(SchulconnexPoliciesInfoLicenseResponse | SchulconnexPoliciesInfoErrorResponse)[]>(
url,
accessToken
);

return response;
const responseObject: SchulconnexPoliciesInfoResponse = { data: response };

return responseObject;
}

private checkOptions(): boolean {
Expand Down
6 changes: 5 additions & 1 deletion apps/server/src/infra/schulconnex-client/testing/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
export { schulconnexResponseFactory } from './schulconnex-response-factory';
export { schulconnexPoliciesInfoResponseFactory } from './schulconnex-policies-info-response-factory';
export {
schulconnexPoliciesInfoLicenseResponseFactory,
schulconnexPoliciesInfoErrorResponseFactory,
schulconnexPoliciesInfoResponseFactory,
} from './schulconnex-policies-info-response.factory';

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Factory } from 'fishery';
import {
SchulconnexPoliciesInfoActionType,
SchulconnexPoliciesInfoErrorResponse,
SchulconnexPoliciesInfoLicenseResponse,
SchulconnexPoliciesInfoResponse,
} from '../response';

export const schulconnexPoliciesInfoLicenseResponseFactory = Factory.define<SchulconnexPoliciesInfoLicenseResponse>(
() => {
return {
target: {
uid: 'bildungscloud',
partOf: '',
},
permission: [
{
action: [SchulconnexPoliciesInfoActionType.EXECUTE],
},
],
};
}
);

export const schulconnexPoliciesInfoErrorResponseFactory = Factory.define<SchulconnexPoliciesInfoErrorResponse>(() => {
return {
access_control: {
'@type': 'bilo error mock',
error: {
code: '500',
value: 'something went wrong',
},
},
};
});

export const schulconnexPoliciesInfoResponseFactory = Factory.define<SchulconnexPoliciesInfoResponse>(() => {
return {
data: [schulconnexPoliciesInfoLicenseResponseFactory.build(), schulconnexPoliciesInfoErrorResponseFactory.build()],
};
});
2 changes: 1 addition & 1 deletion apps/server/src/modules/idp-console/idp-console.module.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConsoleWriterModule } from '@infra/console';
import { RabbitMQWrapperModule } from '@infra/rabbitmq';
import { SchulconnexClientModule } from '@infra/schulconnex-client';
import { SchulconnexClientModule } from '@infra/schulconnex-client/schulconnex-client.module';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { defaultMikroOrmOptions } from '@modules/server';
import { SynchronizationEntity, SynchronizationModule } from '@modules/synchronization';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { SchulconnexResponse, schulconnexResponseFactory, SchulconnexRestClient } from '@infra/schulconnex-client';
import { SchulconnexResponse, SchulconnexRestClient } from '@infra/schulconnex-client';
import { schulconnexResponseFactory } from '@infra/schulconnex-client/testing';
import {
Synchronization,
synchronizationFactory,
Expand Down
1 change: 0 additions & 1 deletion apps/server/src/modules/oauth/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export * from './oauth.module';
export * from './interface';
export * from './service';
1 change: 1 addition & 0 deletions apps/server/src/modules/provisioning/loggable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './school-name-required-loggable-exception';
export * from './group-role-unknown.loggable';
export { SchoolExternalToolCreatedLoggable } from './school-external-tool-created.loggable';
export { FetchingPoliciesInfoFailedLoggable } from './fetching-policies-info-failed.loggable';
export { PoliciesInfoErrorResponseLoggable } from './policies-info-error-response-loggable';
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { SchulconnexPoliciesInfoErrorResponse } from '@infra/schulconnex-client';
import { schulconnexPoliciesInfoErrorResponseFactory } from '@infra/schulconnex-client/testing';
import { PoliciesInfoErrorResponseLoggable } from './policies-info-error-response-loggable';

describe(PoliciesInfoErrorResponseLoggable.name, () => {
describe('getLogMessage', () => {
const setup = () => {
const errorResponse: SchulconnexPoliciesInfoErrorResponse = schulconnexPoliciesInfoErrorResponseFactory.build();

const loggable: PoliciesInfoErrorResponseLoggable = new PoliciesInfoErrorResponseLoggable(errorResponse);

return {
loggable,
errorResponse,
};
};

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

expect(loggable.getLogMessage()).toEqual({
message: 'The /policies-info endpoint returned an error for a media source.',
data: {
type: errorResponse.access_control['@type'],
code: errorResponse.access_control.error.code,
value: errorResponse.access_control.error.value,
},
});
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SchulconnexPoliciesInfoErrorResponse } from '@infra/schulconnex-client';
import { Loggable, LoggableMessage } from '@shared/common/loggable/interfaces';

export class PoliciesInfoErrorResponseLoggable implements Loggable {
constructor(private readonly item: SchulconnexPoliciesInfoErrorResponse) {}

getLogMessage(): LoggableMessage {
return {
message: 'The /policies-info endpoint returned an error for a media source.',
data: {
type: this.item.access_control['@type'],
code: this.item.access_control.error.code,
value: this.item.access_control.error.value,
},
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { SchoolExternalToolModule } from '@modules/tool/school-external-tool';
import { UserModule } from '@modules/user';
import { Module } from '@nestjs/common';
import { LoggerModule } from '@src/core/logger';
import { SchulconnexClientModule } from '@src/infra/schulconnex-client';
import { SchulconnexClientModule } from '@src/infra/schulconnex-client/schulconnex-client.module';
import { ClassModule } from '../class';
import { UserLicenseModule } from '../user-license';
import { ProvisioningService } from './service/provisioning.service';
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export * from './sanis.strategy';
export { SanisProvisioningStrategy } from './sanis.strategy';
export { SchulconnexResponseMapper } from './schulconnex-response-mapper';
Loading

0 comments on commit 2eba077

Please sign in to comment.