Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/auth-manager/openapi3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@
- connection
parameters:
- $ref: '#/components/parameters/environmentQueryParam'
- in: query
name: latest

Check failure on line 286 in packages/auth-manager/openapi3.yaml

View workflow job for this annotation

GitHub Actions / Run OpenAPI lint Check (auth-manager)

boolean-parameter-prefixes

Boolean parameter `latest` should have `should`, `is` or `has` prefix.
schema:
type: boolean
- in: query
name: isEnabled
schema:
Expand Down Expand Up @@ -534,6 +538,11 @@
name: isTemplate
schema:
type: boolean
- in: query
name: latest

Check failure on line 542 in packages/auth-manager/openapi3.yaml

View workflow job for this annotation

GitHub Actions / Run OpenAPI lint Check (auth-manager)

boolean-parameter-prefixes

Boolean parameter `latest` should have `should`, `is` or `has` prefix.
schema:
type: boolean
default: false
responses:
'200':
description: OK
Expand Down
39 changes: 38 additions & 1 deletion packages/auth-manager/src/asset/DAL/assetRepository.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,53 @@
import { Asset } from '@map-colonies/auth-core';
import { FactoryFunction } from 'tsyringe';
import { DataSource, Repository } from 'typeorm';
import { ArrayContains, DataSource, Repository } from 'typeorm';
import { Criteria } from '../models/assetManager';

export type AssetRepository = Repository<Asset> & {
getMaxVersionWithLock: (name: string) => Promise<number | null>;
getMaxVersion: (name: string) => Promise<number | null>;
findAllBy: (criteria: Criteria) => Promise<Asset[]>;
};

export const assetRepositoryFactory: FactoryFunction<AssetRepository> = (container) => {
const dataSource = container.resolve(DataSource);

return dataSource.getRepository(Asset).extend({
// New method to find the latest assets based on dynamic criteria
async findAllBy(criteria): Promise<Asset[]> {
// Start building the main query, filtering by the initial criteria
const qb = this.createQueryBuilder();

if (criteria.isTemplate !== undefined) {
qb.andWhere('isTemplate = :isTemplate', {
isTemplate: criteria.isTemplate,
});
}

if (criteria.type !== undefined) {
qb.andWhere('type = :type', {
type: criteria.type,
});
}

if (criteria.environment !== undefined) {
qb.andWhere({
environment: ArrayContains(criteria.environment),
});
}

if (criteria.latest ?? false) {
qb.distinctOn(['name']).orderBy({
name: 'ASC',
version: 'DESC',
});
}

const result = await qb.getMany();

return result;
},

async getMaxVersionWithLock(name: string): Promise<number | null> {
const result = await this.createQueryBuilder()
.select('version')
Expand Down
8 changes: 4 additions & 4 deletions packages/auth-manager/src/asset/models/assetManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type Logger } from '@map-colonies/js-logger';
import { IAsset } from '@map-colonies/auth-core';
import { inject, injectable } from 'tsyringe';
import { ArrayContains } from 'typeorm';
import type { SetRequired } from 'type-fest';
import { operations } from '@openapi';
import { SERVICES } from '@common/constants';
Expand All @@ -10,6 +9,7 @@ import { AssetVersionMismatchError, AssetNotFoundError } from './errors';

export type ResponseAsset = SetRequired<IAsset, 'createdAt'>;
export type RequestAsset = Omit<IAsset, 'createdAt'>;
export type Criteria = NonNullable<operations['getAssets']['parameters']['query']>;

@injectable()
export class AssetManager {
Expand All @@ -18,11 +18,11 @@ export class AssetManager {
@inject(SERVICES.ASSET_REPOSITORY) private readonly assetRepository: AssetRepository
) {}

public async getAssets(searchParams: NonNullable<operations['getAssets']['parameters']['query']>): Promise<ResponseAsset[]> {
public async getAssets(searchParams: Criteria): Promise<ResponseAsset[]> {
this.logger.info({ msg: 'fetching assets', searchParams });
const { environment, isTemplate, type } = searchParams;
const criteria = searchParams;

return this.assetRepository.findBy({ environment: environment ? ArrayContains(environment) : undefined, isTemplate, type });
return this.assetRepository.findAllBy(criteria);
}

public async getNamedAssets(name: string): Promise<ResponseAsset[]> {
Expand Down
1 change: 1 addition & 0 deletions packages/auth-manager/src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ let configInstance: ConfigType | undefined;
async function initConfig(): Promise<void> {
configInstance = await config({
schema: infraOpalaManagerV1,
offlineMode: true,
});
}

Expand Down
2 changes: 2 additions & 0 deletions packages/auth-manager/src/openapi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,7 @@ export interface operations {
parameters: {
query?: {
environment?: components['parameters']['environmentQueryParam'];
latest?: boolean;
isEnabled?: boolean;
isNoBrowser?: boolean;
isNoOrigin?: boolean;
Expand Down Expand Up @@ -1011,6 +1012,7 @@ export interface operations {
environment?: components['parameters']['environmentQueryParam'];
type?: components['schemas']['assetType'];
isTemplate?: boolean;
latest?: boolean;
};
header?: never;
path?: never;
Expand Down
18 changes: 17 additions & 1 deletion packages/auth-manager/tests/integration/asset/asset.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ describe('client', function () {
expect(res).toSatisfyApiSpec();
expect(res.body).toSatisfyAll((a: IAsset) => a.type === AssetType.DATA);
});

it('should return 200 status code and all the latest assets', async function () {
const asset = getFakeAsset();
const assets: IAsset[] = [asset, { ...asset, version: 2 }];

const connection = depContainer.resolve(DataSource);
await connection.getRepository(Asset).save(assets);

const res = await requestSender.getAssets({ queryParams: { latest: true } });

expect(res).toHaveProperty('status', httpStatusCodes.OK);
expect(res).toSatisfyApiSpec();
const returnedAssetsWithName = (res.body as unknown as IAsset[]).filter((a: IAsset) => a.name === asset.name);
expect(returnedAssetsWithName).toHaveLength(1);
expect(returnedAssetsWithName[0]?.version).toBe(2);
});
});

describe('POST /asset', function () {
Expand Down Expand Up @@ -263,7 +279,7 @@ describe('client', function () {
describe('GET /asset', function () {
it('should return 500 status code if db throws an error', async function () {
const repo = depContainer.resolve<AssetRepository>(SERVICES.ASSET_REPOSITORY);
jest.spyOn(repo, 'findBy').mockRejectedValue(new Error());
jest.spyOn(repo, 'findAllBy').mockRejectedValue(new Error());

const res = await requestSender.getAssets();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('AssetManager', () => {
let assetManager: AssetManager;
const mockedRepository = {
findBy: jest.fn(),
findAllBy: jest.fn(),
findOne: jest.fn(),
transaction: jest.fn(),
};
Expand All @@ -19,15 +20,15 @@ describe('AssetManager', () => {
describe('#getAssets', () => {
it('should return the array of assets', async function () {
const asset = getFakeAsset();
mockedRepository.findBy.mockResolvedValue([asset]);
mockedRepository.findAllBy.mockResolvedValue([asset]);

const assetPromise = assetManager.getAssets({});

await expect(assetPromise).resolves.toStrictEqual([asset]);
});

it('should throw an error if one is thrown by the repository', async function () {
mockedRepository.findBy.mockRejectedValue(new Error());
mockedRepository.findAllBy.mockRejectedValue(new Error());

const assetPromise = assetManager.getAssets({});

Expand Down
Loading