@equinor/fusion-framework-module-services 5.1.1 → 5.1.2
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.
- package/CHANGELOG.md +11 -0
- package/dist/esm/context/index.js.map +1 -1
- package/dist/esm/context/query/generate-endpoint.js +58 -12
- package/dist/esm/context/query/generate-endpoint.js.map +1 -1
- package/dist/esm/context/related/generate-endpoint.js +56 -12
- package/dist/esm/context/related/generate-endpoint.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/context/get/client.d.ts +2 -1
- package/dist/types/context/get/generate-endpoint.d.ts +2 -1
- package/dist/types/context/get/generate-parameters.d.ts +2 -1
- package/dist/types/context/index.d.ts +1 -0
- package/dist/types/context/query/client.d.ts +2 -1
- package/dist/types/context/query/generate-endpoint.d.ts +11 -1
- package/dist/types/context/query/generate-parameters.d.ts +2 -1
- package/dist/types/context/related/client.d.ts +2 -1
- package/dist/types/context/related/generate-endpoint.d.ts +11 -1
- package/dist/types/context/related/generate-parameters.d.ts +2 -1
- package/dist/types/notification/notification/delete/client.d.ts +2 -1
- package/dist/types/notification/notification/delete/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/notification/delete/generate-parameters.d.ts +2 -1
- package/dist/types/notification/notification/get/client.d.ts +2 -1
- package/dist/types/notification/notification/get/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/notification/get/generate-parameters.d.ts +2 -1
- package/dist/types/notification/notification/getAll/client.d.ts +2 -1
- package/dist/types/notification/notification/getAll/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/notification/getAll/generate-parameters.d.ts +2 -1
- package/dist/types/notification/notification/patch/client.d.ts +2 -1
- package/dist/types/notification/notification/patch/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/notification/patch/generate-parameters.d.ts +2 -1
- package/dist/types/notification/notification/post/client.d.ts +2 -1
- package/dist/types/notification/notification/post/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/notification/post/generate-parameters.d.ts +2 -1
- package/dist/types/notification/settings/get/client.d.ts +2 -1
- package/dist/types/notification/settings/get/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/settings/get/generate-parameters.d.ts +2 -1
- package/dist/types/notification/settings/put/client.d.ts +2 -1
- package/dist/types/notification/settings/put/generate-endpoint.d.ts +2 -1
- package/dist/types/notification/settings/put/generate-parameters.d.ts +2 -1
- package/dist/types/people/person-details/client.d.ts +1 -1
- package/dist/types/people/person-details/generate-parameters.d.ts +1 -1
- package/dist/types/people/person-photo/client.d.ts +1 -1
- package/dist/types/people/person-photo/generate-parameters.d.ts +1 -1
- package/dist/types/people/query/client.d.ts +1 -1
- package/dist/types/people/query/generate-parameters.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +11 -7
- package/src/context/index.ts +2 -0
- package/src/context/query/generate-endpoint.ts +76 -27
- package/src/context/related/generate-endpoint.ts +74 -26
- package/src/version.ts +1 -1
- package/tests/context.test.ts +109 -0
- package/tests/mocks/get-context-item.ts +60 -0
- package/tests/mocks/index.ts +1 -0
- package/tests/setup.ts +73 -0
- package/tsconfig.json +4 -3
- package/vitest.config.ts +11 -0
|
@@ -10,38 +10,86 @@ import type {
|
|
|
10
10
|
RelatedContextOdataParameters,
|
|
11
11
|
} from './types';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Builds an OData filter object based on the provided filter object.
|
|
15
|
+
*
|
|
16
|
+
* @param filterObj - The filter object containing key-value pairs to be transformed into an OData filter.
|
|
17
|
+
* @returns A new object representing the OData filter.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const filter = buildOdataFilter({ type: ['exampleType'], otherKey: 'value' });
|
|
22
|
+
* // filter will be { $filter: { in: ['exampleType'] }, otherKey: 'value' }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
function buildOdataFilter(filterObj: RelatedContextOdataFilter): Record<string, unknown> {
|
|
26
|
+
return Object.keys(filterObj).reduce(
|
|
27
|
+
(acc, key) => {
|
|
28
28
|
switch (key) {
|
|
29
|
-
case '
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
case 'type':
|
|
30
|
+
if (filterObj[key]?.length) {
|
|
31
|
+
acc[key] = { in: filterObj[key] };
|
|
32
|
+
}
|
|
33
|
+
break;
|
|
34
34
|
default:
|
|
35
|
-
|
|
35
|
+
acc[key] = filterObj[key as keyof typeof filterObj];
|
|
36
|
+
break;
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
}
|
|
38
|
+
return acc;
|
|
39
|
+
},
|
|
40
|
+
{} as Record<string, unknown>,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Builds an OData object from the given parameters.
|
|
46
|
+
*
|
|
47
|
+
* This function takes an object of parameters and processes each key-value pair.
|
|
48
|
+
* It filters out any entries with falsy values and then reduces the remaining entries
|
|
49
|
+
* into a new object. For the 'filter' key, it calls a specific function to build the
|
|
50
|
+
* OData filter. For all other keys, it directly assigns the value from the parameters.
|
|
51
|
+
*
|
|
52
|
+
* @param parameters - The parameters to build the OData object from.
|
|
53
|
+
* @returns An object representing the OData parameters.
|
|
54
|
+
*/
|
|
55
|
+
function buildOdataObject(parameters: RelatedContextOdataParameters): Record<string, unknown> {
|
|
56
|
+
return Object.entries(parameters)
|
|
57
|
+
.filter(([_, value]) => !!value)
|
|
58
|
+
.reduce(
|
|
59
|
+
(acc, [key, value]) => {
|
|
60
|
+
switch (key) {
|
|
61
|
+
case 'filter':
|
|
62
|
+
acc[key] = buildOdataFilter(value as RelatedContextOdataFilter);
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
acc[key] = parameters[key as keyof typeof parameters];
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
return acc;
|
|
69
|
+
},
|
|
70
|
+
{} as Record<string, unknown>,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates search parameters for a query.
|
|
76
|
+
*
|
|
77
|
+
* @param args - The arguments for creating search parameters. It can be either a string or an object of type `RelatedContextOdataParameters`.
|
|
78
|
+
* @returns The search parameters as a string.
|
|
79
|
+
*/
|
|
80
|
+
function createSearchParameters(args: string | RelatedContextOdataParameters): string {
|
|
42
81
|
return typeof args === 'string' ? args : buildOdataQuery(buildOdataObject(args));
|
|
43
|
-
}
|
|
82
|
+
}
|
|
44
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Generates an endpoint URL based on the provided API version and arguments.
|
|
86
|
+
*
|
|
87
|
+
* @template TVersion - The type of the API version, defaults to the keys of `ApiVersion`.
|
|
88
|
+
* @param version - The API version to use for generating the endpoint.
|
|
89
|
+
* @param args - The arguments required to generate the endpoint, including `id` and `query`.
|
|
90
|
+
* @returns The generated endpoint URL as a string.
|
|
91
|
+
* @throws {UnsupportedApiVersion} If the provided API version is not supported.
|
|
92
|
+
*/
|
|
45
93
|
export const generateEndpoint = <TVersion extends string = keyof typeof ApiVersion>(
|
|
46
94
|
version: TVersion,
|
|
47
95
|
args: RelatedContextArgs<TVersion>,
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '5.1.
|
|
2
|
+
export const version = '5.1.2';
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach, type MockInstance } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { BASE_URL } from './setup';
|
|
4
|
+
|
|
5
|
+
import { ContextApiClient, ApiVersion } from '../src/context';
|
|
6
|
+
|
|
7
|
+
import { HttpClient } from '@equinor/fusion-framework-module-http/client';
|
|
8
|
+
import { UnsupportedApiVersion } from '../src/errors';
|
|
9
|
+
|
|
10
|
+
import { mockContextItem } from './mocks';
|
|
11
|
+
|
|
12
|
+
describe('Context', () => {
|
|
13
|
+
let httpClient: HttpClient;
|
|
14
|
+
let httpClientWatcher: MockInstance;
|
|
15
|
+
let contextClient: ContextApiClient;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
httpClient = new HttpClient(BASE_URL, {
|
|
19
|
+
/* options */
|
|
20
|
+
});
|
|
21
|
+
httpClientWatcher = vi.spyOn(httpClient, 'json');
|
|
22
|
+
contextClient = new ContextApiClient(httpClient, 'json');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
httpClientWatcher.mockRestore();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('get', () => {
|
|
30
|
+
it('should generate parameters for valid API version and arguments for V1', async () => {
|
|
31
|
+
const contextId = '123e4567-e89b-12d3-a456-426614174000';
|
|
32
|
+
const expected = mockContextItem(contextId);
|
|
33
|
+
const result = await contextClient.get('v1', { id: contextId });
|
|
34
|
+
|
|
35
|
+
expect(result).toMatchObject(expected);
|
|
36
|
+
|
|
37
|
+
expect(httpClientWatcher).toHaveBeenCalledWith(
|
|
38
|
+
`/contexts/${contextId}/?api-version=${ApiVersion.v1}`,
|
|
39
|
+
undefined,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should throw UnsupportedApiVersion for unsupported API version', async () => {
|
|
44
|
+
// Attempt to get context with an unsupported API version 'v2'
|
|
45
|
+
// This should throw an UnsupportedApiVersion error because 'v2' is not a valid API version
|
|
46
|
+
try {
|
|
47
|
+
await contextClient.get('v2', { id: '123e4567-e89b-12d3-a456-426614174000' });
|
|
48
|
+
expect(true).toBe(false); // This line should not be reached
|
|
49
|
+
} catch (error) {
|
|
50
|
+
// Verify that the error is of type UnsupportedApiVersion
|
|
51
|
+
expect(error).toBeInstanceOf(UnsupportedApiVersion);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('query', () => {
|
|
57
|
+
it('should generate parameters for valid API version and arguments for V1', async () => {
|
|
58
|
+
const result = await contextClient.query('v1', { query: { filter: { type: ['master'] } } });
|
|
59
|
+
|
|
60
|
+
expect(result).toHaveLength(10);
|
|
61
|
+
|
|
62
|
+
const searchParams = new URLSearchParams();
|
|
63
|
+
searchParams.append('$filter', "type in ('master')");
|
|
64
|
+
searchParams.append('api-version', ApiVersion.v1);
|
|
65
|
+
|
|
66
|
+
expect(httpClientWatcher).toHaveBeenCalledWith(`/contexts/?${searchParams}`, undefined);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should throw UnsupportedApiVersion for unsupported API version', async () => {
|
|
70
|
+
// Attempt to get context with an unsupported API version 'v2'
|
|
71
|
+
// This should throw an UnsupportedApiVersion error because 'v2' is not a valid API version
|
|
72
|
+
try {
|
|
73
|
+
await contextClient.query('v2', { query: { filter: { type: ['master'] } } });
|
|
74
|
+
expect(true).toBe(false); // This line should not be reached
|
|
75
|
+
} catch (error) {
|
|
76
|
+
// Verify that the error is of type UnsupportedApiVersion
|
|
77
|
+
expect(error).toBeInstanceOf(UnsupportedApiVersion);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('related', () => {
|
|
83
|
+
it('should generate parameters for valid API version and arguments for V1', async () => {
|
|
84
|
+
const contextId = '123e4567-e89b-12d3-a456-426614174000';
|
|
85
|
+
|
|
86
|
+
await contextClient.related('v1', { id: contextId, query: { filter: { type: ['master'] } } });
|
|
87
|
+
|
|
88
|
+
const searchParams = new URLSearchParams();
|
|
89
|
+
searchParams.append('$filter', "type in ('master')");
|
|
90
|
+
searchParams.append('api-version', ApiVersion.v1);
|
|
91
|
+
|
|
92
|
+
expect(httpClientWatcher).toHaveBeenCalledWith(
|
|
93
|
+
`/contexts/${contextId}/relations?${searchParams}`,
|
|
94
|
+
undefined,
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
it('should throw UnsupportedApiVersion for unsupported API version', async () => {
|
|
98
|
+
// Attempt to get context with an unsupported API version 'v2'
|
|
99
|
+
// This should throw an UnsupportedApiVersion error because 'v2' is not a valid API version
|
|
100
|
+
try {
|
|
101
|
+
await contextClient.related('v2', { id: '123e4567-e89b-12d3-a456-426614174000' });
|
|
102
|
+
expect(true).toBe(false); // This line should not be reached
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// Verify that the error is of type UnsupportedApiVersion
|
|
105
|
+
expect(error).toBeInstanceOf(UnsupportedApiVersion);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Faker, da, en } from '@faker-js/faker';
|
|
2
|
+
import { type ApiContextEntity, ApiVersion } from '../../src/context';
|
|
3
|
+
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Converts a string to a seed for the Faker.js library.
|
|
8
|
+
*
|
|
9
|
+
* @param uuid - The UUID string to convert.
|
|
10
|
+
* @returns A numeric seed derived from the UUID.
|
|
11
|
+
*/
|
|
12
|
+
function stringToSeed(uuid: string): number {
|
|
13
|
+
const hash = createHash('sha256').update(uuid).digest('hex');
|
|
14
|
+
return Number.parseInt(hash.slice(0, 8), 16);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Since generation of dates might have a slight difference in milliseconds
|
|
19
|
+
* we normalize the date to the nearest minute, so we can compare them.
|
|
20
|
+
*
|
|
21
|
+
* @note
|
|
22
|
+
* This might fail at exactly midnight, but the odds for that are slim. Rerun the test if it fails.
|
|
23
|
+
*
|
|
24
|
+
* @param date date to normalize
|
|
25
|
+
* @returns string
|
|
26
|
+
*/
|
|
27
|
+
function normalizeDateMock(date: Date): string {
|
|
28
|
+
date.setMinutes(0);
|
|
29
|
+
date.setSeconds(0);
|
|
30
|
+
date.setMilliseconds(0);
|
|
31
|
+
return date.toISOString();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const mockContextItem = <V extends ApiVersion = ApiVersion.v1>(
|
|
35
|
+
id: string,
|
|
36
|
+
version: V = ApiVersion.v1 as V,
|
|
37
|
+
): ApiContextEntity<V> => {
|
|
38
|
+
const seed = stringToSeed(id);
|
|
39
|
+
const faker = new Faker({ seed, locale: en });
|
|
40
|
+
switch (version) {
|
|
41
|
+
case ApiVersion.v1:
|
|
42
|
+
return {
|
|
43
|
+
id: String(id),
|
|
44
|
+
externalId: faker.string.uuid(),
|
|
45
|
+
created: normalizeDateMock(faker.date.recent()),
|
|
46
|
+
source: faker.string.uuid(),
|
|
47
|
+
type: {
|
|
48
|
+
id: faker.string.uuid(),
|
|
49
|
+
isChildType: false,
|
|
50
|
+
parentTypeIds: null,
|
|
51
|
+
},
|
|
52
|
+
value: null,
|
|
53
|
+
title: faker.lorem.sentence(),
|
|
54
|
+
isActive: true,
|
|
55
|
+
isDeleted: false,
|
|
56
|
+
updated: normalizeDateMock(faker.date.past()),
|
|
57
|
+
} satisfies ApiContextEntity<ApiVersion.v1> as ApiContextEntity<V>;
|
|
58
|
+
}
|
|
59
|
+
throw new Error('Not implemented');
|
|
60
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { mockContextItem } from './get-context-item';
|
package/tests/setup.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeAll } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { setupServer } from 'msw/node';
|
|
4
|
+
import { http, HttpResponse } from 'msw';
|
|
5
|
+
|
|
6
|
+
import { mockContextItem } from './mocks';
|
|
7
|
+
|
|
8
|
+
import type { ApiVersion } from '../src/context';
|
|
9
|
+
|
|
10
|
+
export const BASE_URL = 'https://localhost';
|
|
11
|
+
|
|
12
|
+
// Define mock handlers for fetch requests
|
|
13
|
+
const handlers = [
|
|
14
|
+
// Mock GET /contexts
|
|
15
|
+
http.get(new URL('contexts', BASE_URL).toString(), (req) => {
|
|
16
|
+
const url = new URL(req.request.url);
|
|
17
|
+
const apiVersion = url.searchParams.get('api-version');
|
|
18
|
+
if (!apiVersion) {
|
|
19
|
+
return HttpResponse.error();
|
|
20
|
+
}
|
|
21
|
+
return HttpResponse.json(
|
|
22
|
+
new Array(10)
|
|
23
|
+
.fill(null)
|
|
24
|
+
.map((_, index) => mockContextItem(`context-item-${index}`, apiVersion as ApiVersion.v1)),
|
|
25
|
+
);
|
|
26
|
+
}),
|
|
27
|
+
|
|
28
|
+
// Mock GET /contexts/:id
|
|
29
|
+
http.get(new URL('contexts/:id', BASE_URL).toString(), (req) => {
|
|
30
|
+
const { id } = req.params;
|
|
31
|
+
if (!id || typeof id !== 'string') {
|
|
32
|
+
return HttpResponse.error();
|
|
33
|
+
}
|
|
34
|
+
const url = new URL(req.request.url);
|
|
35
|
+
const apiVersion = url.searchParams.get('api-version');
|
|
36
|
+
if (!apiVersion) {
|
|
37
|
+
return HttpResponse.error();
|
|
38
|
+
}
|
|
39
|
+
return HttpResponse.json(mockContextItem(id, apiVersion as ApiVersion.v1));
|
|
40
|
+
}),
|
|
41
|
+
|
|
42
|
+
// Mock GET /contexts/:id/relations
|
|
43
|
+
http.get(new URL('contexts/:id/relations', BASE_URL).toString(), (req) => {
|
|
44
|
+
const { id } = req.params;
|
|
45
|
+
if (!id || typeof id !== 'string') {
|
|
46
|
+
return HttpResponse.error();
|
|
47
|
+
}
|
|
48
|
+
const url = new URL(req.request.url);
|
|
49
|
+
const apiVersion = url.searchParams.get('api-version');
|
|
50
|
+
if (!apiVersion) {
|
|
51
|
+
return HttpResponse.error();
|
|
52
|
+
}
|
|
53
|
+
return HttpResponse.json(
|
|
54
|
+
new Array(3)
|
|
55
|
+
.fill(null)
|
|
56
|
+
.map((_, index) =>
|
|
57
|
+
mockContextItem(`context--related-item-${index}`, apiVersion as ApiVersion.v1),
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
}),
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
// Initialize the server
|
|
64
|
+
export const server = setupServer(...handlers);
|
|
65
|
+
|
|
66
|
+
// Start server before all tests
|
|
67
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'warn' }));
|
|
68
|
+
|
|
69
|
+
// Reset handlers after each test
|
|
70
|
+
afterEach(() => server.resetHandlers());
|
|
71
|
+
|
|
72
|
+
// Stop server after all tests
|
|
73
|
+
afterAll(() => server.close());
|
package/tsconfig.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
"paths": {
|
|
9
9
|
"@equinor/fusion-framework-module-services": [
|
|
10
|
-
"
|
|
10
|
+
"./"
|
|
11
11
|
],
|
|
12
12
|
"@equinor/fusion-framework-module-services/*": [
|
|
13
13
|
"./*"
|
|
@@ -29,10 +29,11 @@
|
|
|
29
29
|
}
|
|
30
30
|
],
|
|
31
31
|
"include": [
|
|
32
|
-
"src/**/*",
|
|
32
|
+
"src/**/*", "tests/**/*",
|
|
33
33
|
],
|
|
34
34
|
"exclude": [
|
|
35
35
|
"node_modules",
|
|
36
|
-
"lib"
|
|
36
|
+
"lib",
|
|
37
|
+
"tests"
|
|
37
38
|
]
|
|
38
39
|
}
|
package/vitest.config.ts
ADDED