@orchesty/connector-pinya 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +190 -0
  3. package/coverage/clover.xml +167 -0
  4. package/coverage/coverage-final.json +6 -0
  5. package/coverage/lcov-report/base.css +224 -0
  6. package/coverage/lcov-report/block-navigation.js +87 -0
  7. package/coverage/lcov-report/favicon.png +0 -0
  8. package/coverage/lcov-report/index.html +161 -0
  9. package/coverage/lcov-report/prettify.css +1 -0
  10. package/coverage/lcov-report/prettify.js +2 -0
  11. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  12. package/coverage/lcov-report/sorter.js +210 -0
  13. package/coverage/lcov-report/src/Batch/PinyaAbsencesBatch.ts.html +346 -0
  14. package/coverage/lcov-report/src/Batch/PinyaEmployeesBatch.ts.html +835 -0
  15. package/coverage/lcov-report/src/Batch/index.html +131 -0
  16. package/coverage/lcov-report/src/Connector/PinyaJobTitlesConnector.ts.html +220 -0
  17. package/coverage/lcov-report/src/Connector/index.html +116 -0
  18. package/coverage/lcov-report/src/PinyaApplication.ts.html +643 -0
  19. package/coverage/lcov-report/src/index.html +116 -0
  20. package/coverage/lcov-report/test/dataProvider.ts.html +280 -0
  21. package/coverage/lcov-report/test/index.html +116 -0
  22. package/coverage/lcov.info +257 -0
  23. package/dist/Batch/PinyaAbsencesBatch.d.ts +33 -0
  24. package/dist/Batch/PinyaAbsencesBatch.js +54 -0
  25. package/dist/Batch/PinyaEmployeesBatch.d.ts +207 -0
  26. package/dist/Batch/PinyaEmployeesBatch.js +44 -0
  27. package/dist/Connector/PinyaJobTitlesConnector.d.ts +15 -0
  28. package/dist/Connector/PinyaJobTitlesConnector.js +21 -0
  29. package/dist/PinyaApplication.d.ts +26 -0
  30. package/dist/PinyaApplication.js +149 -0
  31. package/dist/package.json +22 -0
  32. package/jest.config.js +13 -0
  33. package/package.json +22 -0
  34. package/src/Batch/PinyaAbsencesBatch.ts +87 -0
  35. package/src/Batch/PinyaEmployeesBatch.ts +250 -0
  36. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/empty-input.json +6 -0
  37. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/empty-mock.json +9 -0
  38. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/empty-output.json +7 -0
  39. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/input.json +6 -0
  40. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/mock.json +26 -0
  41. package/src/Batch/__tests__/Data/PinyaAbsencesBatch/output.json +11 -0
  42. package/src/Batch/__tests__/Data/PinyaEmployeesBatch/input.json +6 -0
  43. package/src/Batch/__tests__/Data/PinyaEmployeesBatch/mock.json +203 -0
  44. package/src/Batch/__tests__/Data/PinyaEmployeesBatch/output.json +11 -0
  45. package/src/Batch/__tests__/PinyaAbsencesBatch.ts +21 -0
  46. package/src/Batch/__tests__/PinyaEmployeesBatch.ts +17 -0
  47. package/src/Connector/PinyaJobTitlesConnector.ts +45 -0
  48. package/src/Connector/__tests__/Data/PinyaJobTitlesConnector/input.json +6 -0
  49. package/src/Connector/__tests__/Data/PinyaJobTitlesConnector/mock.json +20 -0
  50. package/src/Connector/__tests__/Data/PinyaJobTitlesConnector/output.json +14 -0
  51. package/src/Connector/__tests__/PinyaJobTitlesConnector.ts +17 -0
  52. package/src/PinyaApplication.ts +186 -0
  53. package/test/dataProvider.ts +65 -0
  54. package/tsconfig.json +8 -0
  55. package/tsconfig.prod.json +13 -0
  56. package/tsconfig.prod.tsbuildinfo +1 -0
@@ -0,0 +1,203 @@
1
+ {
2
+ "http": "GET https://api.pinya.hr/employees?PageNumber=0&PageSize=50",
3
+ "code": 200,
4
+ "body":{
5
+ "pageNumber": 1,
6
+ "pageSize": 50,
7
+ "lastPage": 1,
8
+ "totalItemsCount": 1,
9
+ "count": 1,
10
+ "data": [
11
+ {
12
+ "employeeBasic": {
13
+ "employeeGuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
14
+ "employeeNumber": "string",
15
+ "startDate": "string",
16
+ "endDate": "string",
17
+ "terminationWay": "string",
18
+ "terminationReason": "string",
19
+ "firstname": "string",
20
+ "lastname": "string",
21
+ "middlename": "string",
22
+ "degrees": "string",
23
+ "degreesBehind": "string",
24
+ "workEmail": "string",
25
+ "workPhoneNumber": "string",
26
+ "workMobilePhoneNumber": "string",
27
+ "languageSkills": [
28
+ {
29
+ "id": 0,
30
+ "title": "string",
31
+ "code": "string"
32
+ }
33
+ ],
34
+ "computerSkills": [
35
+ {
36
+ "id": 0,
37
+ "title": "string",
38
+ "code": "string"
39
+ }
40
+ ],
41
+ "skills": "string",
42
+ "contractNumber": "string",
43
+ "contractStartDate": "string",
44
+ "endOfProbation": "string",
45
+ "contractEndDate": "string",
46
+ "contractType": {
47
+ "id": 0,
48
+ "title": "string",
49
+ "code": "string"
50
+ },
51
+ "contractValidity": "indefinitePeriod",
52
+ "fte": 0,
53
+ "employmentStatus": {
54
+ "id": 0,
55
+ "title": "string",
56
+ "code": "string"
57
+ },
58
+ "company": {
59
+ "id": 0,
60
+ "title": "string",
61
+ "code": "string"
62
+ },
63
+ "location": {
64
+ "id": 0,
65
+ "title": "string",
66
+ "code": "string"
67
+ },
68
+ "workplace": {
69
+ "id": 0,
70
+ "title": "string",
71
+ "code": "string"
72
+ },
73
+ "division": {
74
+ "id": 0,
75
+ "title": "string",
76
+ "code": "string"
77
+ },
78
+ "department": {
79
+ "id": 0,
80
+ "title": "string",
81
+ "code": "string"
82
+ },
83
+ "jobTitle": {
84
+ "id": 0,
85
+ "title": "string",
86
+ "code": "string"
87
+ },
88
+ "jobLevel": {
89
+ "id": 0,
90
+ "title": "string",
91
+ "code": "string"
92
+ },
93
+ "specialization": {
94
+ "id": 0,
95
+ "title": "string",
96
+ "code": "string"
97
+ },
98
+ "superior": {
99
+ "employeeGuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
100
+ "employeeNumber": "string",
101
+ "employeeName": "string"
102
+ },
103
+ "approver": {
104
+ "employeeGuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
105
+ "employeeNumber": "string",
106
+ "employeeName": "string"
107
+ },
108
+ "dayShift": 0,
109
+ "weekHours": 0,
110
+ "hoursSchedule": "string",
111
+ "vacationClaim": 0,
112
+ "unpaidLeaveClaim": 0,
113
+ "sickDaysClaim": 0,
114
+ "homeOfficeClaim": 0
115
+ },
116
+ "employeePersonal": {
117
+ "birthDate": "string",
118
+ "gender": {
119
+ "id": 0,
120
+ "title": "string",
121
+ "code": "string"
122
+ },
123
+ "maritalStatus": {
124
+ "id": 0,
125
+ "title": "string",
126
+ "code": "string"
127
+ },
128
+ "nationality": "string",
129
+ "healthInsuranceProvider": {
130
+ "id": 0,
131
+ "title": "string",
132
+ "code": "string"
133
+ },
134
+ "healthInsuranceNumber": "string",
135
+ "taxNumber": "string",
136
+ "personalEmail": "string",
137
+ "maidenname": "string",
138
+ "birthCertNumber": "string",
139
+ "birthPlace": "string",
140
+ "numberOfChildren": 0,
141
+ "street": "string",
142
+ "houseNumber": "string",
143
+ "postalCode": "string",
144
+ "city": "string",
145
+ "country": "string",
146
+ "contactStreet": "string",
147
+ "contactHouseNumber": "string",
148
+ "contactPostalCode": "string",
149
+ "contactCity": "string",
150
+ "contactCountry": "string",
151
+ "personalPhoneNumber": "string",
152
+ "emergencyName": "string",
153
+ "emergencyPhone": "string",
154
+ "idCardNumber": "string",
155
+ "idCardValidTo": "string",
156
+ "passportNumber": "string",
157
+ "passportValidTo": "string",
158
+ "drivingLicenceNumber": "string",
159
+ "drivingLicenceValidTo": "string",
160
+ "drivingLicenceGroup": "string",
161
+ "bankName": "string",
162
+ "bankPrefix": "string",
163
+ "bankAccount": "string",
164
+ "bankCode": "string",
165
+ "iban": "string",
166
+ "customFields": {
167
+ "additionalProp1": "string",
168
+ "additionalProp2": "string",
169
+ "additionalProp3": "string"
170
+ }
171
+ },
172
+ "employeeWage": {
173
+ "currency": {
174
+ "id": 0,
175
+ "title": "string",
176
+ "code": "string"
177
+ },
178
+ "basicWage": 0,
179
+ "personalBonus": 0,
180
+ "grossWage": 0,
181
+ "netWage": 0,
182
+ "hourlyWage": 0,
183
+ "agreementExpenses": 0,
184
+ "invoiceExpenses": 0,
185
+ "expenses": 0,
186
+ "transportationBonus": 0,
187
+ "retirementInsurrance": 0,
188
+ "otherBonus": 0,
189
+ "bonus": {
190
+ "id": 0,
191
+ "title": "string",
192
+ "code": "string"
193
+ },
194
+ "customFields": {
195
+ "additionalProp1": "string",
196
+ "additionalProp2": "string",
197
+ "additionalProp3": "string"
198
+ }
199
+ }
200
+ }
201
+ ]
202
+ }
203
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "headers": {
3
+ "user": "TestUser"
4
+ },
5
+ "data": [
6
+ {
7
+ "body": "{\"employeeBasic\":{\"employeeGuid\":\"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\"employeeNumber\":\"string\",\"startDate\":\"string\",\"endDate\":\"string\",\"terminationWay\":\"string\",\"terminationReason\":\"string\",\"firstname\":\"string\",\"lastname\":\"string\",\"middlename\":\"string\",\"degrees\":\"string\",\"degreesBehind\":\"string\",\"workEmail\":\"string\",\"workPhoneNumber\":\"string\",\"workMobilePhoneNumber\":\"string\",\"languageSkills\":[{\"id\":0,\"title\":\"string\",\"code\":\"string\"}],\"computerSkills\":[{\"id\":0,\"title\":\"string\",\"code\":\"string\"}],\"skills\":\"string\",\"contractNumber\":\"string\",\"contractStartDate\":\"string\",\"endOfProbation\":\"string\",\"contractEndDate\":\"string\",\"contractType\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"contractValidity\":\"indefinitePeriod\",\"fte\":0,\"employmentStatus\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"company\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"location\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"workplace\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"division\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"department\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"jobTitle\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"jobLevel\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"specialization\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"superior\":{\"employeeGuid\":\"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\"employeeNumber\":\"string\",\"employeeName\":\"string\"},\"approver\":{\"employeeGuid\":\"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\"employeeNumber\":\"string\",\"employeeName\":\"string\"},\"dayShift\":0,\"weekHours\":0,\"hoursSchedule\":\"string\",\"vacationClaim\":0,\"unpaidLeaveClaim\":0,\"sickDaysClaim\":0,\"homeOfficeClaim\":0},\"employeePersonal\":{\"birthDate\":\"string\",\"gender\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"maritalStatus\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"nationality\":\"string\",\"healthInsuranceProvider\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"healthInsuranceNumber\":\"string\",\"taxNumber\":\"string\",\"personalEmail\":\"string\",\"maidenname\":\"string\",\"birthCertNumber\":\"string\",\"birthPlace\":\"string\",\"numberOfChildren\":0,\"street\":\"string\",\"houseNumber\":\"string\",\"postalCode\":\"string\",\"city\":\"string\",\"country\":\"string\",\"contactStreet\":\"string\",\"contactHouseNumber\":\"string\",\"contactPostalCode\":\"string\",\"contactCity\":\"string\",\"contactCountry\":\"string\",\"personalPhoneNumber\":\"string\",\"emergencyName\":\"string\",\"emergencyPhone\":\"string\",\"idCardNumber\":\"string\",\"idCardValidTo\":\"string\",\"passportNumber\":\"string\",\"passportValidTo\":\"string\",\"drivingLicenceNumber\":\"string\",\"drivingLicenceValidTo\":\"string\",\"drivingLicenceGroup\":\"string\",\"bankName\":\"string\",\"bankPrefix\":\"string\",\"bankAccount\":\"string\",\"bankCode\":\"string\",\"iban\":\"string\",\"customFields\":{\"additionalProp1\":\"string\",\"additionalProp2\":\"string\",\"additionalProp3\":\"string\"}},\"employeeWage\":{\"currency\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"basicWage\":0,\"personalBonus\":0,\"grossWage\":0,\"netWage\":0,\"hourlyWage\":0,\"agreementExpenses\":0,\"invoiceExpenses\":0,\"expenses\":0,\"transportationBonus\":0,\"retirementInsurrance\":0,\"otherBonus\":0,\"bonus\":{\"id\":0,\"title\":\"string\",\"code\":\"string\"},\"customFields\":{\"additionalProp1\":\"string\",\"additionalProp2\":\"string\",\"additionalProp3\":\"string\"}}}",
8
+ "headers": {}
9
+ }
10
+ ]
11
+ }
@@ -0,0 +1,21 @@
1
+ import { container } from '@orchesty/nodejs-connectors/test/TestAbstract';
2
+ import NodeTester from '@orchesty/nodejs-sdk/dist/test/Testers/NodeTester';
3
+ import { initPinyaIntegrationTest } from '../../../test/dataProvider';
4
+ import { PINYA_ABSENCES_BATCH } from '../PinyaAbsencesBatch';
5
+
6
+ let tester: NodeTester;
7
+
8
+ describe('Tests for PinyaAbsencesBatch', () => {
9
+ beforeAll(async () => {
10
+ tester = new NodeTester(container, __filename);
11
+ await initPinyaIntegrationTest();
12
+ });
13
+
14
+ it('process - ok', async () => {
15
+ await tester.testBatch(PINYA_ABSENCES_BATCH);
16
+ });
17
+
18
+ it('process - empty', async () => {
19
+ await tester.testBatch(PINYA_ABSENCES_BATCH, 'empty');
20
+ });
21
+ });
@@ -0,0 +1,17 @@
1
+ import { container } from '@orchesty/nodejs-connectors/test/TestAbstract';
2
+ import NodeTester from '@orchesty/nodejs-sdk/dist/test/Testers/NodeTester';
3
+ import { initPinyaIntegrationTest } from '../../../test/dataProvider';
4
+ import { PINYA_EMPLOYEES_BATCH } from '../PinyaEmployeesBatch';
5
+
6
+ let tester: NodeTester;
7
+
8
+ describe('Tests for PinyaEmployeesBatch', () => {
9
+ beforeAll(async () => {
10
+ tester = new NodeTester(container, __filename);
11
+ await initPinyaIntegrationTest();
12
+ });
13
+
14
+ it('process - ok', async () => {
15
+ await tester.testBatch(PINYA_EMPLOYEES_BATCH);
16
+ });
17
+ });
@@ -0,0 +1,45 @@
1
+ import AConnector from '@orchesty/nodejs-sdk/dist/lib/Connector/AConnector';
2
+ import { HttpMethods } from '@orchesty/nodejs-sdk/dist/lib/Transport/HttpMethods';
3
+ import ProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/ProcessDto';
4
+
5
+ export const PINYA_JOB_TITLES_CONNECTOR = 'pinya-job-titles';
6
+
7
+ export class PinyaJobTitlesConnector extends AConnector {
8
+
9
+ public getName(): string {
10
+ return PINYA_JOB_TITLES_CONNECTOR;
11
+ }
12
+
13
+ public async processAction(dto: ProcessDto): Promise<ProcessDto<PinyaJobTitlesOutput>> {
14
+ const req = await this.getApplication().getRequestDto(
15
+ dto,
16
+ await this.getApplicationInstallFromProcess(dto),
17
+ HttpMethods.GET,
18
+ 'system/job-titles?PageNumber=0&PageSize=150',
19
+ );
20
+ const resp = await this.getSender().send<IResponse>(req, [200]);
21
+ const titles = resp.getJsonBody().data;
22
+
23
+ return dto.setNewJsonData(titles);
24
+ }
25
+
26
+ }
27
+
28
+ interface IResponse {
29
+ pageNumber: number;
30
+ pageSize: number;
31
+ lastPage: number;
32
+ totalItemsCount: number;
33
+ count: number;
34
+ data: PinyaJobTitle[];
35
+ }
36
+
37
+ export interface PinyaJobTitle {
38
+ id: number;
39
+ title: string;
40
+ code: string;
41
+ description: string;
42
+ note: string;
43
+ }
44
+
45
+ export type PinyaJobTitlesOutput = PinyaJobTitle[];
@@ -0,0 +1,6 @@
1
+ {
2
+ "headers": {
3
+ "user": "TestUser"
4
+ },
5
+ "data": {}
6
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "http": "GET https://api.pinya.hr/system/job-titles?PageNumber=0&PageSize=150",
3
+ "code": 200,
4
+ "body": {
5
+ "pageNumber": 0,
6
+ "pageSize": 0,
7
+ "lastPage": 0,
8
+ "totalItemsCount": 1,
9
+ "count": 1,
10
+ "data": [
11
+ {
12
+ "id": 1,
13
+ "title": "string",
14
+ "code": "string",
15
+ "description": "string",
16
+ "note": "string"
17
+ }
18
+ ]
19
+ }
20
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "headers": {
3
+ "user": "TestUser"
4
+ },
5
+ "data": [
6
+ {
7
+ "code": "string",
8
+ "description": "string",
9
+ "id": 1,
10
+ "note": "string",
11
+ "title": "string"
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,17 @@
1
+ import { container } from '@orchesty/nodejs-connectors/test/TestAbstract';
2
+ import NodeTester from '@orchesty/nodejs-sdk/dist/test/Testers/NodeTester';
3
+ import { initPinyaIntegrationTest } from '../../../test/dataProvider';
4
+ import { PINYA_JOB_TITLES_CONNECTOR } from '../PinyaJobTitlesConnector';
5
+
6
+ let tester: NodeTester;
7
+
8
+ describe('Tests for PinyaJobTitlesConnector', () => {
9
+ beforeAll(async () => {
10
+ tester = new NodeTester(container, __filename);
11
+ await initPinyaIntegrationTest();
12
+ });
13
+
14
+ it('process - ok', async () => {
15
+ await tester.testConnector(PINYA_JOB_TITLES_CONNECTOR);
16
+ });
17
+ });
@@ -0,0 +1,186 @@
1
+ import CoreFormsEnum, { getFormName } from '@orchesty/nodejs-sdk/dist/lib/Application/Base/CoreFormsEnum';
2
+ import { ApplicationInstall } from '@orchesty/nodejs-sdk/dist/lib/Application/Database/ApplicationInstall';
3
+ import Field from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Field';
4
+ import FieldType from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FieldType';
5
+ import Form from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Form';
6
+ import FormStack from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FormStack';
7
+ import { ABasicApplication } from '@orchesty/nodejs-sdk/dist/lib/Authorization/Type/Basic/ABasicApplication';
8
+ import CacheService, { ICacheCallback } from '@orchesty/nodejs-sdk/dist/lib/Cache/CacheService';
9
+ import RequestDto from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/RequestDto';
10
+ import ResponseDto from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/ResponseDto';
11
+ import { HttpMethods } from '@orchesty/nodejs-sdk/dist/lib/Transport/HttpMethods';
12
+ import AProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/AProcessDto';
13
+ import { CommonHeaders, JSON_TYPE } from '@orchesty/nodejs-sdk/dist/lib/Utils/Headers';
14
+ import ProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/ProcessDto';
15
+
16
+ export const PINYA_APPLICATION = 'pinya';
17
+
18
+ const BASE_URL = 'https://api.pinya.hr';
19
+ const TENANT_GUID = 'tenantGuid';
20
+ const TENANT_SECRET_KEY = 'secretKey';
21
+ const TENANT_SECRET_KEY_IDENTIFIER = 'secretKeyIdentifier';
22
+
23
+ export const accessCacheKey = 'pinya-accessKey';
24
+ export const accessLockKey = `${accessCacheKey}-lock`;
25
+
26
+ export default class PinyaApplication extends ABasicApplication {
27
+
28
+ public constructor(private readonly cache: CacheService) {
29
+ super();
30
+ }
31
+
32
+ public getName(): string {
33
+ return PINYA_APPLICATION;
34
+ }
35
+
36
+ public getPublicName(): string {
37
+ return 'Pinya';
38
+ }
39
+
40
+ public getDescription(): string {
41
+ return 'HR software, který uvolní ruce vašemu týmu. Minimum administrativy zaručí soustředění na samotnou práci a vyšší výkon firmy';
42
+ }
43
+
44
+ public getFormStack(): FormStack {
45
+ const form = new Form(CoreFormsEnum.AUTHORIZATION_FORM, getFormName(CoreFormsEnum.AUTHORIZATION_FORM))
46
+ .addField(new Field(FieldType.TEXT, TENANT_GUID, 'Tenant guid', undefined, true))
47
+ .addField(new Field(FieldType.TEXT, TENANT_SECRET_KEY, 'Tenant secret key', undefined, true))
48
+ .addField(new Field(FieldType.TEXT, TENANT_SECRET_KEY_IDENTIFIER, 'Tenant secret key identifier', undefined, true));
49
+
50
+ return new FormStack().addForm(form);
51
+ }
52
+
53
+ public isAuthorized(applicationInstall: ApplicationInstall): boolean {
54
+ const authorizationForm = applicationInstall.getSettings()[CoreFormsEnum.AUTHORIZATION_FORM];
55
+
56
+ return authorizationForm?.[TENANT_GUID]
57
+ && authorizationForm?.[TENANT_SECRET_KEY]
58
+ && authorizationForm?.[TENANT_SECRET_KEY_IDENTIFIER];
59
+ }
60
+
61
+ public async getRequestDto(
62
+ dto: AProcessDto,
63
+ applicationInstall: ApplicationInstall,
64
+ method: HttpMethods,
65
+ url?: string,
66
+ data?: unknown,
67
+ ): Promise<RequestDto> {
68
+ const request = new RequestDto(`${BASE_URL}/${url}`, method, dto);
69
+ request.setHeaders({
70
+ [CommonHeaders.CONTENT_TYPE]: JSON_TYPE,
71
+ [CommonHeaders.ACCEPT]: JSON_TYPE,
72
+ [CommonHeaders.AUTHORIZATION]: await this.getAccessToken(applicationInstall),
73
+ });
74
+
75
+ if (data) {
76
+ request.setJsonBody(data);
77
+ }
78
+
79
+ return request;
80
+ }
81
+
82
+ public getLogo(): string {
83
+ return 'PHN2ZyB3aWR0aD0iMjQ2IiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDI0NiAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yNi4xMiA3MC4wM0wyMi42NCA2Mi42N0wyNi4xMiA1NS4zMUgzMy4wOUwzNi41NyA2Mi42N0wzMy4wOSA3MC4wM0gyNi4xMloiIGZpbGw9IiNGMjUzMEQiLz4KPHBhdGggZD0iTTM3LjUxIDYxLjczTDM0LjAyIDU0LjM3TDM3LjUxIDQ3LjAxSDQ0LjQ3TDQ3Ljk1IDU0LjM3TDQ0LjQ3IDYxLjczSDM3LjUxWiIgZmlsbD0iI0YyNTMwRCIvPgo8cGF0aCBkPSJNMzcuNzYgNzguMDdMMzQuMjggNzAuNzFMMzcuNzYgNjMuMzVINDQuNzNMNDguMjEgNzAuNzFMNDQuNzMgNzguMDdIMzcuNzZaIiBmaWxsPSIjMzgzQTNBIi8+CjxwYXRoIGQ9Ik00OC44MyA2OS43NUw0NS4zNSA2Mi4zOEw0OC44MyA1NS4wMkg1NS44TDU5LjI4IDYyLjM4TDU1LjggNjkuNzVINDguODNaIiBmaWxsPSIjRjI1MzBEIi8+CjxwYXRoIGQ9Ik0yNi4wNSA4Ni4wOEwyMi41NyA3OC43MkwyNi4wNSA3MS4zNkgzMy4wMkwzNi41IDc4LjcyTDMzLjAyIDg2LjA4SDI2LjA1WiIgZmlsbD0iI0YyNTMwRCIvPgo8cGF0aCBkPSJNNDkuMjIgODYuMjRMNDUuNzMgNzguODhMNDkuMjIgNzEuNTJINTYuMThMNTkuNjcgNzguODhMNTYuMTggODYuMjRINDkuMjJaIiBmaWxsPSIjRjI1MzBEIi8+CjxwYXRoIGQ9Ik02MC4xMSA2My4yMkw2MC4zNiA3Ny40Mkw1Ni44OCA3MC4wNkw2MC4xMSA2My4yMloiIGZpbGw9IiNGMjUzMEQiLz4KPHBhdGggZD0iTTIyLjA2IDYzLjc3TDIxLjgxIDc3Ljk3TDI1LjMgNzAuNjFMMjIuMDYgNjMuNzdaIiBmaWxsPSIjRjI1MzBEIi8+CjxwYXRoIGQ9Ik01NS43OCA4Ny42NEw1MC40NyA5MC44Mkw0OS4zMiA4Ny4zM0w1NS43OCA4Ny42NFoiIGZpbGw9IiNGMjUzMEQiLz4KPHBhdGggZD0iTTMxLjY2IDkxLjEyTDI1LjYzIDg3LjIzTDMyLjU4IDg3LjgxTDMxLjY2IDkxLjEyWiIgZmlsbD0iI0YyNTMwRCIvPgo8cGF0aCBkPSJNMC41IDkyLjUxQzQuNzc4NSA5MS42NDExIDkuMTM0MTcgOTEuMjA4OSAxMy41IDkxLjIyQzIzLjYzMzMgOTEuMjIgMjguNjk2NyA5Ni4yMiAyOC42OSAxMDYuMjJDMjguNjkgMTE3LjI2NyAyMy41OTY3IDEyMi43OTMgMTMuNDEgMTIyLjhDMTAuODg2MSAxMjIuODAxIDguMzg4NTggMTIyLjI4NyA2LjA3IDEyMS4yOVYxMzQuMjJIMC41VjkyLjUxWk02LjA2IDExNi4zNkM4LjMzNTY0IDExNy40NjkgMTAuODM4OCAxMTguMDMgMTMuMzcgMTE4QzE5LjkzIDExOCAyMy4yMSAxMTQuMDYgMjMuMjEgMTA2LjE4QzIzLjIxIDk5LjMyIDE5Ljk1IDk1Ljg4MzMgMTMuNDMgOTUuODdDMTAuOTU0NCA5NS44Mzg4IDguNDgyNzkgOTYuMDgwMiA2LjA2IDk2LjU5VjExNi4zNloiIGZpbGw9IiMzODNBM0EiIHN0cm9rZT0iIzM4M0EzQSIvPgo8cGF0aCBkPSJNNTUuODYgMTIyLjhWOTEuMjJINTkuNjJMNjAuNjIgOTUuMjJDNjMuNTczIDkyLjY0ODUgNjcuMzU0MyA5MS4yMjgzIDcxLjI3IDkxLjIyQzc5LjE1IDkxLjIyIDgzLjA5IDk1LjE0IDgzLjA5IDEwMi45OFYxMjIuOEg3Ny41MVYxMDIuOTRDNzcuNTEgOTguMjczMyA3NS4xNzY3IDk1Ljk0IDcwLjUxIDk1Ljk0QzY3LjMzIDk1Ljk0IDY0LjMwMzMgOTcuMjczMyA2MS40MyA5OS45NFYxMjIuOTRMNTUuODYgMTIyLjhaIiBmaWxsPSIjMzgzQTNBIiBzdHJva2U9IiMzODNBM0EiLz4KPHBhdGggZD0iTTg4LjMzIDkxLjIySDk0LjMzTDEwMy4zMyAxMTUuODhMMTEyLjU2IDkxLjIySDExOC40NkwxMDUuMzMgMTIzLjczQzEwMy4wMSAxMjkuNDU3IDk5LjUyMzQgMTMzLjAzNyA5NC44NyAxMzQuNDdMOTIuNzMgMTMwLjUzQzk2LjE2MDEgMTI5LjA3OSA5OC45MTIgMTI2LjM4MSAxMDAuNDMgMTIyLjk4TDg4LjMzIDkxLjIyWiIgZmlsbD0iIzM4M0EzQSIgc3Ryb2tlPSIjMzgzQTNBIi8+CjxwYXRoIGQ9Ik0xMjIuMTggMTEzLjI2QzEyMi4xOCAxMDYuOCAxMjYuNDUgMTAzLjU2MyAxMzQuOTkgMTAzLjU1QzEzNy43NDggMTAzLjU1IDE0MC41MDIgMTAzLjc1IDE0My4yMyAxMDQuMTVWMTAxLjE1QzE0My4yMyA5Ny41MyAxNDAuMzkzIDk1LjcxNjcgMTM0LjcyIDk1LjcxQzEzMS4zMzIgOTUuNzMxOSAxMjcuOTY1IDk2LjIzNyAxMjQuNzIgOTcuMjFWOTIuNzJDMTI3Ljk2NSA5MS43NDcgMTMxLjMzMiA5MS4yNDE5IDEzNC43MiA5MS4yMkMxNDQuMSA5MS4yMiAxNDguNzkgOTQuNDg2NyAxNDguNzkgMTAxLjAyVjEyMi44SDE0NS42N0wxNDMuNzQgMTE5LjY3QzE0MC43MDEgMTIxLjcyIDEzNy4xMTYgMTIyLjgxMSAxMzMuNDUgMTIyLjhDMTI1Ljk0MyAxMjIuOCAxMjIuMTg3IDExOS42MiAxMjIuMTggMTEzLjI2Wk0xMzUgMTA4LjA2QzEzMC4xNjcgMTA4LjA2IDEyNy43NTEgMTA5Ljc2MyAxMjcuNzUgMTEzLjE3QzEyNy43NDkgMTE2LjU3NyAxMjkuNjUzIDExOC4yOCAxMzMuNDYgMTE4LjI4QzEzNi45NzEgMTE4LjM4OSAxNDAuNDE3IDExNy4zMTEgMTQzLjI0IDExNS4yMlYxMDguNjZDMTQwLjUxMiAxMDguMjYgMTM3Ljc1OCAxMDguMDYgMTM1IDEwOC4wNloiIGZpbGw9IiMzODNBM0EiIHN0cm9rZT0iIzM4M0EzQSIvPgo8cGF0aCBkPSJNMzcuOTggNzkuNzNMMzMuMTMgMTE0Ljg4TDQxLjMgMTIzLjI4TDQ5LjAzIDExNC44OEw0NC45NSA3OS43M0gzNy45OFoiIGZpbGw9IiMzODNBM0EiLz4KPHBhdGggZD0iTTYxLjMzIDIzLjU4QzYxLjMzIDIzLjU4IDQxLjE4IDIwLjc4IDQxLjMzIDQ0LjY2QzQxLjMzIDQ0LjY2IDYxLjE5IDI5LjYyIDYxLjMzIDIzLjU4WiIgZmlsbD0iIzg1OUEyMyIvPgo8cGF0aCBkPSJNNDEuNjYgNDQuNDFDNDEuNjYgNDQuNDEgNDEuNTggMzMuOCA1MS40IDMwLjQxQzYxLjIyIDI3LjAyIDYxLjMzIDIzLjYxIDYxLjMzIDIzLjYxQzYxLjMzIDIzLjYxIDY0LjA3IDQ0LjY5IDQxLjY2IDQ0LjQxWiIgZmlsbD0iI0I4RDEyNCIvPgo8cGF0aCBkPSJNMjEuNyAzMi41N0MyMS43IDMyLjU3IDM1LjU2IDI3LjQ2IDM5LjEyIDQ0LjQ1QzM5LjEyIDQ0LjQ1IDIyLjcyIDM2Ljg0IDIxLjcgMzIuNTdaIiBmaWxsPSIjODU5QTIzIi8+CjxwYXRoIGQ9Ik0zOC44NyA0NC4zMkMzOC44NyA0NC4zMiAzNy4yOSAzNi43NyAyOS43OSAzNS44N0MyMi4yOSAzNC45NyAyMS43IDMyLjU3IDIxLjcgMzIuNTdDMjEuNyAzMi41NyAyMyA0OCAzOC44NyA0NC4zMloiIGZpbGw9IiNCOEQxMjQiLz4KPHBhdGggZD0iTTM2IDE2QzM2IDE2IDQ5LjA2IDIyLjk0IDM4Ljc5IDM2Ljk0QzM4LjgxIDM2Ljk0IDMzLjUzIDE5LjYxIDM2IDE2WiIgZmlsbD0iIzg1OUEyMyIvPgo8cGF0aCBkPSJNMzguNzQgMzYuNjNDMzguNzQgMzYuNjMgNDMuMyAzMC40MSAzOSAyNC4yMkMzNC43IDE4LjAzIDM2IDE2IDM2IDE2QzM2IDE2IDI1LjQyIDI3LjI3IDM4Ljc0IDM2LjYzWiIgZmlsbD0iI0I4RDEyNCIvPgo8cGF0aCBkPSJNNDUuNiA0Ny4xM0w1NC45IDUzLjg3TDQ5LjE4IDUzLjc5TDQ1LjYgNDcuMTNaIiBmaWxsPSIjRjI1MzBEIi8+CjxwYXRoIGQ9Ik0zNi4wNiA0Ny4yOEwyNi43NSA1NC4wMkwzMi40OCA1My45NEwzNi4wNiA0Ny4yOFoiIGZpbGw9IiNGMjUzMEQiLz4KPHBhdGggZD0iTTE3MC42MyA5MS44OUgxNjYuODZWMTI0LjM1SDE3MC42M1Y5MS44OVoiIGZpbGw9IiMzODNBM0EiLz4KPHBhdGggZD0iTTIxNC40MiAxMjRIMjA4Ljk0VjEwOS44MUgxOTQuNzhWMTI0SDE4OS4zM1Y5MkgxOTQuODNWMTA1LjMzSDIwOC45OVY5Mi4wMUgyMTQuNDdMMjE0LjQyIDEyNFoiIGZpbGw9IiNGMzkzMjIiLz4KPHBhdGggZD0iTTIzMy40NiAxMTEuN0gyMjcuMzNWMTI0SDIyMS44M1Y5MkgyMzIuOTdDMjM2LjYxNyA5MiAyMzkuNDM3IDkyLjgyNjcgMjQxLjQzIDk0LjQ4QzI0My40MjMgOTYuMTMzMyAyNDQuNDIzIDk4LjUzMzMgMjQ0LjQzIDEwMS42OEMyNDQuNDk4IDEwMy41ODYgMjQzLjk2MyAxMDUuNDY2IDI0Mi45IDEwNy4wNUMyNDEuODE2IDEwOC41NDQgMjQwLjMzMiAxMDkuNzAxIDIzOC42MiAxMTAuMzlMMjQ1LjczIDEyMy43MVYxMjMuOTlIMjM5Ljg0TDIzMy40NiAxMTEuN1pNMjI3LjMzIDEwNy4yMkgyMzIuOTlDMjM0LjU2NCAxMDcuMzA4IDIzNi4xMTMgMTA2LjgwMSAyMzcuMzMgMTA1LjhDMjM3Ljg1NCAxMDUuMzA3IDIzOC4yNjUgMTA0LjcwNyAyMzguNTM2IDEwNC4wNDFDMjM4LjgwNyAxMDMuMzc1IDIzOC45MzEgMTAyLjY1OCAyMzguOSAxMDEuOTRDMjM4Ljk0NiAxMDEuMjA5IDIzOC44NDEgMTAwLjQ3NyAyMzguNTkyIDk5Ljc4ODlDMjM4LjM0MiA5OS4xMDA3IDIzNy45NTQgOTguNDcxMyAyMzcuNDUgOTcuOTRDMjM2LjI0OCA5Ni45MjMgMjM0LjcwMiA5Ni40MDQyIDIzMy4xMyA5Ni40OUgyMjcuMzNWMTA3LjIyWiIgZmlsbD0iI0YzOTMyMiIvPgo8L3N2Zz4=';
84
+ }
85
+
86
+ private async accessToken(applicationInstall: ApplicationInstall): Promise<IAccess> {
87
+ const {
88
+ tenantGuid,
89
+ secretKey,
90
+ secretKeyIdentifier,
91
+ } = applicationInstall.getSettings()[CoreFormsEnum.AUTHORIZATION_FORM];
92
+
93
+ const requestDto = new RequestDto(
94
+ `${BASE_URL}/authentication`,
95
+ HttpMethods.POST,
96
+ new ProcessDto(),
97
+ JSON.stringify({
98
+ tenantGuid,
99
+ tenantSecretKey: secretKey,
100
+ tenantSecretKeyIdentifier: secretKeyIdentifier,
101
+ }),
102
+ {
103
+ [CommonHeaders.CONTENT_TYPE]: JSON_TYPE,
104
+ [CommonHeaders.ACCEPT]: JSON_TYPE,
105
+ },
106
+ );
107
+
108
+ return this.cache.entryWithLock(
109
+ accessCacheKey,
110
+ accessLockKey,
111
+ requestDto,
112
+ this.accessCallBack.bind(this),
113
+ );
114
+ }
115
+
116
+ private async getAccessToken(applicationInstall: ApplicationInstall): Promise<string> {
117
+ const date = new Date();
118
+ date.setMinutes(date.getMinutes() - 1);
119
+ const storedAccessToken = await this.accessToken(applicationInstall);
120
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
121
+ let accessToken = storedAccessToken.accessToken;
122
+
123
+ if (storedAccessToken.expiration < date.getTime()) {
124
+ // eslint-disable-next-line @typescript-eslint/prefer-destructuring
125
+ accessToken = (await this.accessTokenByRefreshToken(
126
+ storedAccessToken.accessToken,
127
+ storedAccessToken.refreshToken,
128
+ )).accessToken;
129
+ }
130
+ return `Bearer ${accessToken}`;
131
+ }
132
+
133
+ private async accessTokenByRefreshToken(
134
+ accessToken: string,
135
+ refreshToken: string,
136
+ ): Promise<IAccess> {
137
+ const requestDto = new RequestDto(
138
+ `${BASE_URL}/authentication/refresh`,
139
+ HttpMethods.POST,
140
+ new ProcessDto(),
141
+ JSON.stringify({
142
+ accessToken,
143
+ refreshToken,
144
+ }),
145
+ );
146
+
147
+ await this.cache.deleteCache(accessCacheKey);
148
+
149
+ return this.cache.entryWithLock<IAccess>(
150
+ accessCacheKey,
151
+ accessLockKey,
152
+ requestDto,
153
+ this.accessCallBack.bind(this),
154
+ );
155
+ }
156
+
157
+ // eslint-disable-next-line @hanaboso/arrow
158
+ private readonly accessCallBack = async (res: ResponseDto): Promise<ICacheCallback<IAccess>> => {
159
+ const body = await res.getJsonBody() as { data: IAccess };
160
+ const token: IAccess = {
161
+ accessToken: body.data.accessToken,
162
+ refreshToken: body.data.refreshToken,
163
+ expiration: this.getExpirationWithOffset(new Date().getTime() + 5 * 60_000),
164
+ };
165
+
166
+ // Tested to last no more than 5 minutes
167
+ const expire = 5 * 60;
168
+ return {
169
+ dataToStore: token,
170
+ expire,
171
+ };
172
+ };
173
+
174
+ private getExpirationWithOffset(expiration: number, secondsOffset = 10): number {
175
+ const date = new Date(expiration);
176
+ date.setSeconds(date.getSeconds() - secondsOffset);
177
+ return date.getTime();
178
+ }
179
+
180
+ }
181
+
182
+ interface IAccess {
183
+ accessToken: string;
184
+ refreshToken: string;
185
+ expiration: number;
186
+ }
@@ -0,0 +1,65 @@
1
+ import { appInstall, DEFAULT_ACCESS_TOKEN, DEFAULT_USER } from '@orchesty/nodejs-connectors/test/DataProvider';
2
+ import { container } from '@orchesty/nodejs-connectors/test/TestAbstract';
3
+ import CoreFormsEnum from '@orchesty/nodejs-sdk/dist/lib/Application/Base/CoreFormsEnum';
4
+ import { ACCESS_TOKEN } from '@orchesty/nodejs-sdk/dist/lib/Authorization/Provider/OAuth2/OAuth2Provider';
5
+ import CacheService from '@orchesty/nodejs-sdk/dist/lib/Cache/CacheService';
6
+ import { getEnv } from '@orchesty/nodejs-sdk/dist/lib/Config/Config';
7
+ import DatabaseClient from '@orchesty/nodejs-sdk/dist/lib/Storage/Database/Client';
8
+ import Redis from '@orchesty/nodejs-sdk/dist/lib/Storage/Redis/Redis';
9
+ import CurlSender from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/CurlSender';
10
+ import PinyaAbsencesBatch from '../src/Batch/PinyaAbsencesBatch';
11
+ import PinyaEmployeesBatch from '../src/Batch/PinyaEmployeesBatch';
12
+ import { PinyaJobTitlesConnector } from '../src/Connector/PinyaJobTitlesConnector';
13
+ import PinyaApplication, { PINYA_APPLICATION } from '../src/PinyaApplication';
14
+
15
+ export async function initPinyaIntegrationTest(): Promise<void> {
16
+ const sender = container.get(CurlSender);
17
+ const db = container.get(DatabaseClient);
18
+ appInstall(PINYA_APPLICATION, DEFAULT_USER, {
19
+ [CoreFormsEnum.AUTHORIZATION_FORM]: {
20
+ [ACCESS_TOKEN]: DEFAULT_ACCESS_TOKEN,
21
+ },
22
+ });
23
+
24
+ if (!container.has(Redis)) {
25
+ container.set(new Redis(getEnv('REDIS_DSN')));
26
+ }
27
+ const redisService = container.get(Redis);
28
+
29
+ if (!container.has(CacheService)) {
30
+ container.set(new CacheService(redisService, sender));
31
+ }
32
+ const cacheService = container.get(CacheService);
33
+
34
+ const app = new PinyaApplication(cacheService);
35
+ container.setApplication(app);
36
+
37
+ container.setNode(
38
+ new PinyaEmployeesBatch()
39
+ .setSender(sender)
40
+ .setDb(db)
41
+ .setApplication(app),
42
+ );
43
+ container.setNode(
44
+ new PinyaAbsencesBatch()
45
+ .setSender(sender)
46
+ .setDb(db)
47
+ .setApplication(app),
48
+ );
49
+ container.setNode(
50
+ new PinyaJobTitlesConnector()
51
+ .setSender(sender)
52
+ .setDb(db)
53
+ .setApplication(app),
54
+ );
55
+
56
+ await redisService.set(
57
+ 'pinya-accessKey',
58
+ JSON.stringify({
59
+ accessToken: 'accessToken',
60
+ refreshToken: 'refreshToken',
61
+ expiration: new Date().getTime() + 1_000,
62
+ }),
63
+ 100,
64
+ );
65
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "composite": true
6
+ },
7
+ "include": ["src", "test", "../../test"]
8
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ },
6
+ "include": [
7
+ "src",
8
+ ],
9
+ "exclude": [
10
+ "node_modules",
11
+ "src/**/__tests__",
12
+ ]
13
+ }