Skip to content

Commit

Permalink
Merge pull request #82 from IQSS/80-files-filters
Browse files Browse the repository at this point in the history
Add filters to getDatasetFiles use case
  • Loading branch information
kcondon authored Sep 11, 2023
2 parents d47a10c + 33671e5 commit ddf1a7b
Show file tree
Hide file tree
Showing 24 changed files with 257 additions and 187 deletions.
2 changes: 0 additions & 2 deletions src/core/infra/repositories/ApiRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { ReadError } from '../../domain/repositories/ReadError';
import { WriteError } from '../../domain/repositories/WriteError';

export abstract class ApiRepository {
DATASET_VERSION_LATEST = ':latest';

public async doGet(apiEndpoint: string, authRequired = false, queryParams: object = {}): Promise<AxiosResponse> {
return await axios
.get(this.buildRequestUrl(apiEndpoint), this.buildRequestConfig(authRequired, queryParams))
Expand Down
4 changes: 4 additions & 0 deletions src/datasets/domain/models/DatasetNotNumberedVersion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum DatasetNotNumberedVersion {
DRAFT = ':draft',
LATEST = ':latest',
}
4 changes: 2 additions & 2 deletions src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Dataset } from '../models/Dataset';

export interface IDatasetsRepository {
getDatasetSummaryFieldNames(): Promise<string[]>;
getDataset(datasetId: number | string, datasetVersionId?: string): Promise<Dataset>;
getDataset(datasetId: number | string, datasetVersionId: string): Promise<Dataset>;
getPrivateUrlDataset(token: string): Promise<Dataset>;
getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise<string>;
getDatasetCitation(datasetId: number, datasetVersionId: string): Promise<string>;
getPrivateUrlDatasetCitation(token: string): Promise<string>;
}
6 changes: 5 additions & 1 deletion src/datasets/domain/useCases/GetDataset.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UseCase } from '../../../core/domain/useCases/UseCase';
import { IDatasetsRepository } from '../repositories/IDatasetsRepository';
import { Dataset } from '../models/Dataset';
import { DatasetNotNumberedVersion } from '../models/DatasetNotNumberedVersion';

export class GetDataset implements UseCase<Dataset> {
private datasetsRepository: IDatasetsRepository;
Expand All @@ -9,7 +10,10 @@ export class GetDataset implements UseCase<Dataset> {
this.datasetsRepository = datasetsRepository;
}

async execute(datasetId: number | string, datasetVersionId?: string): Promise<Dataset> {
async execute(
datasetId: number | string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
): Promise<Dataset> {
return await this.datasetsRepository.getDataset(datasetId, datasetVersionId);
}
}
6 changes: 5 additions & 1 deletion src/datasets/domain/useCases/GetDatasetCitation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { UseCase } from '../../../core/domain/useCases/UseCase';
import { IDatasetsRepository } from '../repositories/IDatasetsRepository';
import { DatasetNotNumberedVersion } from '../models/DatasetNotNumberedVersion';

export class GetDatasetCitation implements UseCase<string> {
private datasetsRepository: IDatasetsRepository;
Expand All @@ -8,7 +9,10 @@ export class GetDatasetCitation implements UseCase<string> {
this.datasetsRepository = datasetsRepository;
}

async execute(datasetId: number, datasetVersionId?: string): Promise<string> {
async execute(
datasetId: number,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
): Promise<string> {
return await this.datasetsRepository.getDatasetCitation(datasetId, datasetVersionId);
}
}
1 change: 1 addition & 0 deletions src/datasets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
getDatasetCitation,
getPrivateUrlDatasetCitation,
};
export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion';
export {
Dataset,
DatasetVersionInfo,
Expand Down
10 changes: 2 additions & 8 deletions src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
});
}

public async getDataset(datasetId: number | string, datasetVersionId?: string): Promise<Dataset> {
if (datasetVersionId === undefined) {
datasetVersionId = this.DATASET_VERSION_LATEST;
}
public async getDataset(datasetId: number | string, datasetVersionId: string): Promise<Dataset> {
let endpoint;
if (typeof datasetId === 'number') {
endpoint = `/datasets/${datasetId}/versions/${datasetVersionId}`;
Expand All @@ -37,10 +34,7 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
});
}

public async getDatasetCitation(datasetId: number, datasetVersionId?: string): Promise<string> {
if (datasetVersionId === undefined) {
datasetVersionId = this.DATASET_VERSION_LATEST;
}
public async getDatasetCitation(datasetId: number, datasetVersionId: string): Promise<string> {
return this.doGet(`/datasets/${datasetId}/versions/${datasetVersionId}/citation`, true)
.then((response) => response.data.data.message)
.catch((error) => {
Expand Down
1 change: 1 addition & 0 deletions src/files/domain/models/File.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface File {
version: number;
description?: string;
restricted: boolean;
latestRestricted: boolean;
directoryLabel?: string;
datasetVersionId?: number;
categories?: string[];
Expand Down
45 changes: 45 additions & 0 deletions src/files/domain/models/FileCriteria.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export class FileCriteria {
constructor(
public readonly orderCriteria: FileOrderCriteria = FileOrderCriteria.NAME_AZ,
public readonly contentType?: string,
public readonly accessStatus?: FileAccessStatus,
public readonly categoryName?: string,
public readonly searchText?: string,
) {}

withOrderCriteria(orderCriteria: FileOrderCriteria): FileCriteria {
return new FileCriteria(orderCriteria, this.contentType, this.accessStatus, this.categoryName);
}

withContentType(contentType: string | undefined): FileCriteria {
return new FileCriteria(this.orderCriteria, contentType, this.accessStatus, this.categoryName);
}

withAccessStatus(accessStatus: FileAccessStatus | undefined): FileCriteria {
return new FileCriteria(this.orderCriteria, this.contentType, accessStatus, this.categoryName);
}

withCategoryName(categoryName: string | undefined): FileCriteria {
return new FileCriteria(this.orderCriteria, this.contentType, this.accessStatus, categoryName);
}

withSearchText(searchText: string | undefined): FileCriteria {
return new FileCriteria(this.orderCriteria, this.contentType, this.accessStatus, this.categoryName, searchText);
}
}

export enum FileOrderCriteria {
NAME_AZ = 'NameAZ',
NAME_ZA = 'NameZA',
NEWEST = 'Newest',
OLDEST = 'Oldest',
SIZE = 'Size',
TYPE = 'Type',
}

export enum FileAccessStatus {
PUBLIC = 'Public',
RESTRICTED = 'Restricted',
EMBARGOED = 'EmbargoedThenRestricted',
EMBARGOED_RESTRICTED = 'EmbargoedThenPublic',
}
8 changes: 0 additions & 8 deletions src/files/domain/models/FileOrderCriteria.ts

This file was deleted.

6 changes: 3 additions & 3 deletions src/files/domain/repositories/IFilesRepository.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { FileOrderCriteria } from '../models/FileOrderCriteria';
import { File } from '../models/File';
import { FileDataTable } from '../models/FileDataTable';
import { FileUserPermissions } from '../models/FileUserPermissions';
import { FileCriteria } from '../models/FileCriteria';

export interface IFilesRepository {
getDatasetFiles(
datasetId: number | string,
datasetVersionId?: string,
datasetVersionId: string,
limit?: number,
offset?: number,
orderCriteria?: FileOrderCriteria,
fileCriteria?: FileCriteria,
): Promise<File[]>;

getFileDownloadCount(fileId: number | string): Promise<number>;
Expand Down
9 changes: 5 additions & 4 deletions src/files/domain/useCases/GetDatasetFiles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { UseCase } from '../../../core/domain/useCases/UseCase';
import { FileOrderCriteria } from '../models/FileOrderCriteria';
import { IFilesRepository } from '../repositories/IFilesRepository';
import { File } from '../models/File';
import { FileCriteria } from '../models/FileCriteria';
import { DatasetNotNumberedVersion } from '../../../datasets';

export class GetDatasetFiles implements UseCase<File[]> {
private filesRepository: IFilesRepository;
Expand All @@ -12,11 +13,11 @@ export class GetDatasetFiles implements UseCase<File[]> {

async execute(
datasetId: number | string,
datasetVersionId?: string,
datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST,
limit?: number,
offset?: number,
orderCriteria?: FileOrderCriteria,
fileCriteria?: FileCriteria,
): Promise<File[]> {
return await this.filesRepository.getDatasetFiles(datasetId, datasetVersionId, limit, offset, orderCriteria);
return await this.filesRepository.getDatasetFiles(datasetId, datasetVersionId, limit, offset, fileCriteria);
}
}
3 changes: 2 additions & 1 deletion src/files/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const getFileDataTables = new GetFileDataTables(filesRepository);
export { getDatasetFiles, getFileDownloadCount, getFileUserPermissions, getFileDataTables };

export { File, FileEmbargo, FileChecksum } from './domain/models/File';
export { FileOrderCriteria } from './domain/models/FileOrderCriteria';
export { FileUserPermissions } from './domain/models/FileUserPermissions';
export { FileCriteria, FileOrderCriteria, FileAccessStatus } from './domain/models/FileCriteria';
export {
FileDataTable,
FileDataVariable,
Expand Down
35 changes: 27 additions & 8 deletions src/files/infra/repositories/FilesRepository.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository';
import { IFilesRepository } from '../../domain/repositories/IFilesRepository';
import { FileOrderCriteria } from '../../domain/models/FileOrderCriteria';
import { File } from '../../domain/models/File';
import { transformFilesResponseToFiles } from './transformers/fileTransformers';
import { FileDataTable } from '../../domain/models/FileDataTable';
import { transformDataTablesResponseToDataTables } from './transformers/fileDataTableTransformers';
import { FileUserPermissions } from '../../domain/models/FileUserPermissions';
import { transformFileUserPermissionsResponseToFileUserPermissions } from './transformers/fileUserPermissionsTransformers';
import { FileCriteria } from '../../domain/models/FileCriteria';

export interface GetFilesQueryParams {
limit?: number;
offset?: number;
orderCriteria?: string;
contentType?: string;
accessStatus?: string;
categoryName?: string;
searchText?: string;
}

export class FilesRepository extends ApiRepository implements IFilesRepository {
public async getDatasetFiles(
datasetId: number | string,
datasetVersionId?: string,
datasetVersionId: string,
limit?: number,
offset?: number,
orderCriteria?: FileOrderCriteria,
fileCriteria?: FileCriteria,
): Promise<File[]> {
if (datasetVersionId === undefined) {
datasetVersionId = this.DATASET_VERSION_LATEST;
}
let endpoint;
if (typeof datasetId === 'number') {
endpoint = `/datasets/${datasetId}/versions/${datasetVersionId}/files`;
Expand All @@ -38,8 +39,8 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
if (offset !== undefined) {
queryParams.offset = offset;
}
if (orderCriteria !== undefined) {
queryParams.orderCriteria = orderCriteria.toString();
if (fileCriteria !== undefined) {
this.applyFileCriteriaToQueryParams(queryParams, fileCriteria);
}
return this.doGet(endpoint, true, queryParams)
.then((response) => transformFilesResponseToFiles(response))
Expand Down Expand Up @@ -89,4 +90,22 @@ export class FilesRepository extends ApiRepository implements IFilesRepository {
throw error;
});
}

private applyFileCriteriaToQueryParams(queryParams: GetFilesQueryParams, fileCriteria: FileCriteria) {
if (fileCriteria.accessStatus !== undefined) {
queryParams.accessStatus = fileCriteria.accessStatus.toString();
}
if (fileCriteria.categoryName !== undefined) {
queryParams.categoryName = fileCriteria.categoryName;
}
if (fileCriteria.contentType !== undefined) {
queryParams.contentType = fileCriteria.contentType;
}
if (fileCriteria.searchText !== undefined) {
queryParams.searchText = fileCriteria.searchText;
}
if (fileCriteria.orderCriteria !== undefined) {
queryParams.orderCriteria = fileCriteria.orderCriteria.toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const transformFilePayloadToFile = (filePayload: any): File => {
version: filePayload.version,
...(filePayload.dataFile.description && { description: filePayload.dataFile.description }),
restricted: filePayload.restricted,
latestRestricted: filePayload.dataFile.restricted,
...(filePayload.dataFile.directoryLabel && { directoryLabel: filePayload.dataFile.directoryLabel }),
...(filePayload.dataFile.datasetVersionId && { datasetVersionId: filePayload.dataFile.datasetVersionId }),
...(filePayload.dataFile.categories && { categories: filePayload.dataFile.categories }),
Expand Down
30 changes: 18 additions & 12 deletions test/integration/datasets/DatasetsRepository.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import { ApiConfig, DataverseApiAuthMechanism } from '../../../src/core/infra/re
import { TestConstants } from '../../testHelpers/TestConstants';
import { createDatasetViaApi, createPrivateUrlViaApi } from '../../testHelpers/datasets/datasetHelper';
import { ReadError } from '../../../src/core/domain/repositories/ReadError';
import { DatasetNotNumberedVersion } from '../../../src/datasets';

describe('DatasetsRepository', () => {
const sut: DatasetsRepository = new DatasetsRepository();
const nonExistentTestDatasetId = 100;

const latestVersionId = DatasetNotNumberedVersion.LATEST;

beforeAll(async () => {
ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, process.env.TEST_API_KEY);
await createDatasetViaApi()
Expand All @@ -28,19 +31,19 @@ describe('DatasetsRepository', () => {

describe('getDataset', () => {
describe('by numeric id', () => {
test('should return dataset when it exists filtering by id', async () => {
const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID);
test('should return dataset when it exists filtering by id and version id', async () => {
const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, latestVersionId);
expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID);
});

test('should return dataset when it exists filtering by id and version id', async () => {
const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, ':draft');
const actual = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, latestVersionId);
expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID);
});

test('should return error when dataset does not exist', async () => {
let error: ReadError = undefined;
await sut.getDataset(nonExistentTestDatasetId).catch((e) => (error = e));
await sut.getDataset(nonExistentTestDatasetId, latestVersionId).catch((e) => (error = e));

assert.match(
error.message,
Expand All @@ -49,23 +52,23 @@ describe('DatasetsRepository', () => {
});
});
describe('by persistent id', () => {
test('should return dataset when it exists filtering by persistent id', async () => {
const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID);
const actual = await sut.getDataset(createdDataset.persistentId);
test('should return dataset when it exists filtering by persistent id and version id', async () => {
const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, latestVersionId);
const actual = await sut.getDataset(createdDataset.persistentId, latestVersionId);
expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID);
});

test('should return dataset when it exists filtering by persistent id and version id', async () => {
const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID);
const actual = await sut.getDataset(createdDataset.persistentId, ':draft');
const createdDataset = await sut.getDataset(TestConstants.TEST_CREATED_DATASET_ID, latestVersionId);
const actual = await sut.getDataset(createdDataset.persistentId, latestVersionId);
expect(actual.id).toBe(TestConstants.TEST_CREATED_DATASET_ID);
});

test('should return error when dataset does not exist', async () => {
let error: ReadError = undefined;

const testWrongPersistentId = 'wrongPersistentId';
await sut.getDataset(testWrongPersistentId).catch((e) => (error = e));
await sut.getDataset(testWrongPersistentId, latestVersionId).catch((e) => (error = e));

assert.match(
error.message,
Expand All @@ -77,14 +80,17 @@ describe('DatasetsRepository', () => {

describe('getDatasetCitation', () => {
test('should return citation when dataset exists', async () => {
const actualDatasetCitation = await sut.getDatasetCitation(TestConstants.TEST_CREATED_DATASET_ID);
const actualDatasetCitation = await sut.getDatasetCitation(
TestConstants.TEST_CREATED_DATASET_ID,
latestVersionId,
);
expect(typeof actualDatasetCitation).toBe('string');
});

test('should return error when dataset does not exist', async () => {
let error: ReadError = undefined;

await sut.getDatasetCitation(nonExistentTestDatasetId).catch((e) => (error = e));
await sut.getDatasetCitation(nonExistentTestDatasetId, latestVersionId).catch((e) => (error = e));

assert.match(
error.message,
Expand Down
2 changes: 1 addition & 1 deletion test/integration/environment/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ POSTGRES_VERSION=13
DATAVERSE_DB_USER=dataverse
SOLR_VERSION=8.11.1
DATAVERSE_IMAGE_REGISTRY=ghcr.io
DATAVERSE_IMAGE_TAG=9692-files-api-extension-display-data
DATAVERSE_IMAGE_TAG=9714-files-api-extension-filters
DATAVERSE_BOOTSTRAP_TIMEOUT=5m
Loading

0 comments on commit ddf1a7b

Please sign in to comment.