Skip to content

Commit

Permalink
Merge branch 'main' into CS-3817
Browse files Browse the repository at this point in the history
  • Loading branch information
Jonathan Weyermann committed Feb 4, 2025
2 parents fe8848f + 77b2815 commit 6b9d38a
Show file tree
Hide file tree
Showing 82 changed files with 1,608 additions and 620 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/delivery-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
dotnet-version: '9'
- uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: temurin
cache: maven
- uses: actions/setup-python@v5
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
dotnet-version: '9'
- uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '17'
distribution: temurin
cache: maven
- uses: actions/setup-python@v5
Expand Down
2 changes: 2 additions & 0 deletions apps/configuration-service/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ const initializeApp = async (): Promise<express.Application> => {
ignoreServiceAud: true,
values: [ServiceMetricsValueDefinition],
serviceConfigurations: [
// Register cache service target for configuration.
{
serviceId: adspId`urn:ads:platform:cache-service`,
configuration: {
Expand Down Expand Up @@ -116,6 +117,7 @@ const initializeApp = async (): Promise<express.Application> => {
},
},
},
// Register directory service resource type for configuration.
{
serviceId: adspId`urn:ads:platform:directory-service`,
configuration: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const configurationSchema = {
required: ['namespace', 'name', 'resourceIdPath'],
},
},
required: ['matcher', 'type', 'namePath'],
required: ['matcher', 'type'],
},
},
},
Expand Down
3 changes: 2 additions & 1 deletion apps/directory-service/src/directory/mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ export function mapResource(apiId: AdspId, resource: Resource) {
href: `${apiId}:/resources`,
},
},
_embedded: resource.data && {
_embedded: {
represents: resource.data,
tags: resource.tags?.map((tag) => mapTag(apiId, tag)),
},
}
: null;
Expand Down
15 changes: 10 additions & 5 deletions apps/directory-service/src/directory/model/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class ResourceType {
) {
this.type = type;
this.matcher = new RegExp(matcher);
this.nameGetter = property(namePath);
this.nameGetter = namePath ? property(namePath) : () => undefined;
this.descriptionGetter = descriptionPath ? property(descriptionPath) : () => undefined;
this.eventResourceIdGetter = deleteEvent?.resourceIdPath ? property(deleteEvent?.resourceIdPath) : () => undefined;
this.deleteEventNamespace = deleteEvent?.namespace;
Expand All @@ -35,7 +35,12 @@ export class ResourceType {
return this.matcher.test(urn.resource);
}

public async resolve(token: string, resource: Resource, sync = false): Promise<Resource> {
public async resolve(
token: string,
resource: Resource,
sync = false,
params: Record<string, unknown> = null
): Promise<Resource> {
if (!this.matches(resource?.urn)) {
throw new InvalidOperationError(`Resource type '${this.type}' not matched to resource: ${resource.urn}`);
}
Expand All @@ -53,7 +58,7 @@ export class ResourceType {

const { data } = await axios.get(resourceUrl.href, {
headers: { Authorization: `Bearer ${token}` },
params: { tenantId: resource.tenantId?.toString() },
params: { ...params, tenantId: resource.tenantId?.toString() },
});

this.logger.debug(`Retrieved resource ${resource.urn} and resolving name and description...`, {
Expand All @@ -63,10 +68,10 @@ export class ResourceType {

const name = this.nameGetter(data) || resource.name;
const description = this.descriptionGetter(data) || resource.description;
if (name !== resource.name || description !== resource.description) {
if (name !== resource.name || description !== resource.description || this.type !== resource.type) {
resource = sync
? await this.repository.saveResource({ ...resource, name, description, type: this.type })
: { ...resource, name, description };
: { ...resource, name, description, type: this.type };
}

return { ...resource, data };
Expand Down
122 changes: 122 additions & 0 deletions apps/directory-service/src/directory/router/resource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,51 @@ describe('resource', () => {
);
});

it('can get resources and include tags', async () => {
const req = {
tenant: { id: tenantId },
user: { tenantId, id: 'tester', name: 'Tester', roles: [ServiceRoles.ResourceBrowser] },
query: { includeTags: 'true' },
};
const res = { send: jest.fn() };
const next = jest.fn();

const page = {};
const results = [
{
urn: adspId`urn:ads:platform:file-service:v1:/files/123`,
},
];
repositoryMock.getResources.mockResolvedValueOnce({ results, page });
repositoryMock.getTags.mockResolvedValueOnce({ results: [{ value: 'test-tag', label: 'test value' }] });

const handler = getResources(apiId, repositoryMock);
await handler(req as unknown as Request, res as unknown as Response, next);
expect(repositoryMock.getResources).toHaveBeenCalledWith(
10,
undefined,
expect.objectContaining({ tenantIdEquals: tenantId })
);
expect(repositoryMock.getTags).toHaveBeenCalledWith(
10,
null,
expect.objectContaining({ tenantIdEquals: tenantId, resourceUrnEquals: results[0].urn })
);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({
page,
results: expect.arrayContaining([
expect.objectContaining({
urn: results[0].urn.toString(),
_embedded: expect.objectContaining({
tags: expect.arrayContaining([expect.objectContaining({ value: 'test-tag' })]),
}),
}),
]),
})
);
});

it('can get resources with query params', async () => {
const req = {
tenant: { id: tenantId },
Expand Down Expand Up @@ -940,6 +985,83 @@ describe('resource', () => {
);
});

it('can get specific resources with criteria array', async () => {
const req = {
tenant: { id: tenantId },
user: { tenantId, id: 'tester', name: 'Tester', roles: [ServiceRoles.ResourceBrowser] },
query: {
criteria: JSON.stringify([
'urn:ads:platform:file-service:v1:/files/123',
'urn:ads:platform:file-service:v1:/files/234',
]),
},
};
const res = { send: jest.fn() };
const next = jest.fn();

const page = {};
const results1 = [
{
urn: adspId`urn:ads:platform:file-service:v1:/files/123`,
},
];
const results2 = [
{
urn: adspId`urn:ads:platform:file-service:v1:/files/234`,
},
];
repositoryMock.getResources
.mockResolvedValueOnce({ results: results1, page })
.mockResolvedValueOnce({ results: results2, page });

const handler = getResources(apiId, repositoryMock);
await handler(req as unknown as Request, res as unknown as Response, next);
expect(repositoryMock.getResources).toHaveBeenCalledWith(
1,
null,
expect.objectContaining({ tenantIdEquals: tenantId, urnEquals: expect.any(AdspId) })
);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({
page: expect.objectContaining({ size: 2 }),
results: expect.arrayContaining([
expect.objectContaining({ urn: results1[0].urn.toString() }),
expect.objectContaining({ urn: results2[0].urn.toString() }),
]),
})
);
});

it('can call next with invalid operation for non-urn in criteria array', async () => {
const req = {
tenant: { id: tenantId },
user: { tenantId, id: 'tester', name: 'Tester', roles: [ServiceRoles.ResourceBrowser] },
query: {
criteria: JSON.stringify(['urn:ads:platform:file-service:v1:/files/123', 'invalid value']),
},
};
const res = { send: jest.fn() };
const next = jest.fn();

const page = {};
const results1 = [
{
urn: adspId`urn:ads:platform:file-service:v1:/files/123`,
},
];
repositoryMock.getResources.mockResolvedValueOnce({ results: results1, page });

const handler = getResources(apiId, repositoryMock);
await handler(req as unknown as Request, res as unknown as Response, next);
expect(repositoryMock.getResources).toHaveBeenCalledWith(
1,
null,
expect.objectContaining({ tenantIdEquals: tenantId, urnEquals: expect.any(AdspId) })
);
expect(next).toHaveBeenCalledWith(expect.any(InvalidOperationError));
expect(res.send).not.toHaveBeenCalled();
});

it('can call next with unauthorized', async () => {
const req = {
tenant: { id: tenantId },
Expand Down
41 changes: 35 additions & 6 deletions apps/directory-service/src/directory/router/resource.swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ components:
type: string
_embedded:
type: object
TagSpecification:
type: object
properties:
label:
type: string
value:
type: string
ResourceSpecification:
type: object
properties:
Expand All @@ -53,7 +60,7 @@ components:
type: string
enum: [tag-resource]
tag:
$ref: "#/components/schemas/Tag"
$ref: "#/components/schemas/TagSpecification"
resource:
$ref: "#/components/schemas/ResourceSpecification"
required: [operation, tag, resource]
Expand All @@ -64,7 +71,7 @@ components:
type: string
enum: [untag-resource]
tag:
$ref: "#/components/schemas/Tag"
$ref: "#/components/schemas/TagSpecification"
resource:
$ref: "#/components/schemas/ResourceSpecification"
required: [operation, tag, resource]
Expand Down Expand Up @@ -274,17 +281,39 @@ components:
- Resource
description: Retrieves resources.
parameters:
- name: top
description: Number of results to retrieve.
in: query
required: false
schema:
type: number
- name: after
description: Cursor for page of results to retrieve.
in: query
required: false
schema:
type: string
- name: includeTags
description: Flag indicating if resource tags should be embedded in response.
in: query
required: false
schema:
type: boolean
- name: criteria
description: Criteria for the resources to return.
in: query
required: false
content:
application/json:
schema:
type: object
properties:
typeEquals:
type: string
oneOf:
- type: object
properties:
typeEquals:
type: string
- type: array
items:
type: string
responses:
200:
description: Request completed successfully
Expand Down
Loading

0 comments on commit 6b9d38a

Please sign in to comment.