Skip to content

Commit

Permalink
add vidis client
Browse files Browse the repository at this point in the history
  • Loading branch information
sdinkov committed Dec 20, 2024
1 parent b88a030 commit 30db6c8
Show file tree
Hide file tree
Showing 29 changed files with 2,976 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { HttpStatus } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { HttpModule } from '@nestjs/axios';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { Logger } from '@src/core/logger';
import { DefaultEncryptionService, EncryptionService, SymetricKeyEncryptionService } from '@infra/encryption';
import { mediaSourceFactory } from '@modules/media-source/testing';
import { MediaSourceDataFormat, MediaSourceService } from '@modules/media-source';
import { MediaSchoolLicenseService } from '@modules/school-license';
import { SchoolService } from '@modules/school';
import MockAdapter from 'axios-mock-adapter';
import axios from 'axios';
import { vidisResponseFactory } from '../testing';
import { VidisSyncService } from './vidis-sync.service';

describe(`${VidisSyncService.name} Integration`, () => {
let module: TestingModule;
let axiosMock: MockAdapter;
let service: VidisSyncService;
let encryptionService: DeepMocked<SymetricKeyEncryptionService>;

beforeAll(async () => {
module = await Test.createTestingModule({
imports: [HttpModule],
providers: [
VidisSyncService,
{ provide: MediaSchoolLicenseService, useValue: createMock<MediaSchoolLicenseService>() },
{ provide: SchoolService, useValue: createMock<SchoolService>() },
{ provide: MediaSourceService, useValue: createMock<MediaSourceService>() },
{ provide: Logger, useValue: createMock<Logger>() },
{ provide: DefaultEncryptionService, useValue: createMock<EncryptionService>() },
],
}).compile();

service = module.get(VidisSyncService);
encryptionService = module.get(DefaultEncryptionService);
axiosMock = new MockAdapter(axios);
});

afterAll(async () => {
await module.close();
});

afterEach(() => {
jest.clearAllMocks();
});

describe('getSchoolActivationsFromVidis', () => {
describe('when the vidis media source is passed', () => {
const setup = () => {
const mediaSource = mediaSourceFactory.withBasicAuthConfig().build({ format: MediaSourceDataFormat.VIDIS });
const vidisResponse = vidisResponseFactory.build();

encryptionService.decrypt.mockReturnValueOnce('username');
encryptionService.decrypt.mockReturnValueOnce('password');

axiosMock.onGet().replyOnce(HttpStatus.OK, vidisResponse);

return { mediaSource, vidisResponse };
};

it('should return the school activation items from vidis', async () => {
const { mediaSource, vidisResponse } = setup();

const items = await service.getSchoolActivationsFromVidis(mediaSource);

expect(items).toEqual(vidisResponse.items);
});
});
});
});
46 changes: 46 additions & 0 deletions apps/server/src/infra/vidis-client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# VIDIS API CLIENT

> A short introduction how this module can be used and the api client is generated.
## How to use the api client

The clients for the different Tsp endpoints should be created through TspClientFactory.
Through the create methods of the factory the basic configuration will be set. Currently the
factory sets the base url and generates the JWT used for the requests. You can use the client
like this:

```typescript
export class MyNewService {
// inject the factory into the constructor
constructor(private readonly tspClientFactory: TspClientFactory) {}

public async doSomeStuff(): Promise<void> {
// this will create a fully initialized client
const exportClient = tspClientFactory.createExportClient();

// calling the api
const versionResponse = await exportClient.version();


// do other stuff...
}
}
```

## How the code generation works

> IMPORTANT: Currently we are using the `openapi.json` and not the spec from
> https://test2.schulportal-thueringen.de/tip-ms/api/swagger.json, because we have to patch the security schemas
> manually into to the specification so the generator can generate them correctly. The provided
> specification does not contain all necessary definitions. Only the `Export` endpoints are
> decorated with the security definitions.
We are using the openapi-generator-cli to generate apis, models and supporting files in the
`generated` directory. **DO NOT** modify anything in the `generated` folder, because it will
be deleted on the next client generation.

The client generation is done with the npm command `npm run generate-client:tsp-api`. This
will delete the old and create new files. We are using the `tsp-api` generator configuration
from the `openapitools.json` found in the repository root. You can add new endpoints by
extending the `FILTER` list in the `openapiNormalizer` section with new `operationId` entries.
New models must be added to the list of `models` in the `globalProperty` section.
4 changes: 4 additions & 0 deletions apps/server/src/infra/vidis-client/generated/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
wwwroot/*.js
node_modules
typings
dist
1 change: 1 addition & 0 deletions apps/server/src/infra/vidis-client/generated/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# empty npmignore to ensure all required files (e.g., in the dist folder) are published by npm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAPI Generator Ignore
# Generated by openapi-generator https://github.com/openapitools/openapi-generator

# Use this file to prevent files from being overwritten by the generator.
# The patterns follow closely to .gitignore or .dockerignore.

# As an example, the C# client generator defines ApiClient.cs.
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
#ApiClient.cs

# You can match any string of characters against a directory, file or extension with a single asterisk (*):
#foo/*/qux
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux

# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
#foo/**/qux
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux

# You can also negate patterns with an exclamation (!).
# For example, you can ignore all files in a docs folder with the file extension .md:
#docs/*.md
# Then explicitly reverse the ignore rule for a single file:
#!docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.gitignore
.npmignore
api.ts
api/default-api.ts
api/education-provider-api.ts
api/idmbetreiber-api.ts
base.ts
common.ts
configuration.ts
git_push.sh
index.ts
models/activation-dto.ts
models/facet-value.ts
models/facet.ts
models/index.ts
models/offer-category-dto.ts
models/offer-dto.ts
models/page-activation-dto.ts
models/page-offer-dto.ts
models/page-school-dto.ts
models/school-dto.ts
20 changes: 20 additions & 0 deletions apps/server/src/infra/vidis-client/generated/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* tslint:disable */
/* eslint-disable */
/**
* Vidis REST
* Vidis REST API
*
* The version of the OpenAPI document: v1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/



export * from './api/default-api';
export * from './api/education-provider-api';
export * from './api/idmbetreiber-api';

142 changes: 142 additions & 0 deletions apps/server/src/infra/vidis-client/generated/api/default-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* tslint:disable */
/* eslint-disable */
/**
* Vidis REST
* Vidis REST API
*
* The version of the OpenAPI document: v1.0
*
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


import type { Configuration } from '../configuration';
import type { AxiosPromise, AxiosInstance, RawAxiosRequestConfig } from 'axios';
import globalAxios from 'axios';
// Some imports not used depending on template conditions
// @ts-ignore
import { DUMMY_BASE_URL, assertParamExists, setApiKeyToObject, setBasicAuthToObject, setBearerAuthToObject, setOAuthToObject, setSearchParams, serializeDataIfNeeded, toPathString, createRequestFunction } from '../common';
// @ts-ignore
import { BASE_PATH, COLLECTION_FORMATS, type RequestArgs, BaseAPI, RequiredError, operationServerMap } from '../base';
/**
* DefaultApi - axios parameter creator
* @export
*/
export const DefaultApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
*
* @param {string} type
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getOpenAPI: async (type: string, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'type' is not null or undefined
assertParamExists('getOpenAPI', 'type', type)
const localVarPath = `/v1.0/openapi.{type}`
.replace(`{${"type"}}`, encodeURIComponent(String(type)));
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}

const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;



setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};

return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
}
};

/**
* DefaultApi - functional programming interface
* @export
*/
export const DefaultApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration)
return {
/**
*
* @param {string} type
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getOpenAPI(type: string, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getOpenAPI(type, options);
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
const localVarOperationServerBasePath = operationServerMap['DefaultApi.getOpenAPI']?.[localVarOperationServerIndex]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
},
}
};

/**
* DefaultApi - factory interface
* @export
*/
export const DefaultApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = DefaultApiFp(configuration)
return {
/**
*
* @param {string} type
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getOpenAPI(type: string, options?: any): AxiosPromise<void> {
return localVarFp.getOpenAPI(type, options).then((request) => request(axios, basePath));
},
};
};

/**
* DefaultApi - interface
* @export
* @interface DefaultApi
*/
export interface DefaultApiInterface {
/**
*
* @param {string} type
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApiInterface
*/
getOpenAPI(type: string, options?: RawAxiosRequestConfig): AxiosPromise<void>;

}

/**
* DefaultApi - object-oriented interface
* @export
* @class DefaultApi
* @extends {BaseAPI}
*/
export class DefaultApi extends BaseAPI implements DefaultApiInterface {
/**
*
* @param {string} type
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof DefaultApi
*/
public getOpenAPI(type: string, options?: RawAxiosRequestConfig) {
return DefaultApiFp(this.configuration).getOpenAPI(type, options).then((request) => request(this.axios, this.basePath));
}
}

Loading

0 comments on commit 30db6c8

Please sign in to comment.