Skip to content

Commit d235922

Browse files
authored
Merge pull request #274 from IQSS/269-get-dataset-download-count-use-case
Implement Use Case for dataset download count
2 parents 79398c8 + 8be8962 commit d235922

File tree

9 files changed

+190
-0
lines changed

9 files changed

+190
-0
lines changed

docs/useCases.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,38 @@ The `version` parameter should be a string or a [DatasetNotNumberedVersion](../s
834834

835835
You cannot deaccession a dataset more than once. If you call this endpoint twice for the same dataset version, you will get a not found error on the second call, since the dataset you are looking for will no longer be published since it is already deaccessioned.
836836

837+
#### Get Download Count of a Dataset
838+
839+
Total number of downloads requested for a dataset, given a dataset numeric identifier,
840+
841+
##### Example call:
842+
843+
```typescript
844+
import { getDatasetDownloadCount } from '@iqss/dataverse-client-javascript'
845+
846+
/* ... */
847+
848+
const datasetId = 1
849+
const includeMDC = true
850+
851+
getDatasetDownloadCount
852+
.execute(datasetId, includeMDC)
853+
.then((datasetDownloadCount: DatasetDownloadCount) => {
854+
/* ... */
855+
})
856+
857+
/* ... */
858+
```
859+
860+
_See [use case](../src/datasets/domain/useCases/GetDatasetDownloadCount.ts) implementation_.
861+
862+
The `datasetId` parameter is a number for numeric identifiers or string for persistent identifiers.
863+
The `includeMDC` parameter is optional.
864+
865+
- Setting `includeMDC` to True will ignore the `MDCStartDate` setting and return a total count.
866+
- If MDC isn't enabled, the download count will return a total count, without `MDCStartDate`.
867+
- If MDC is enabled but the `includeMDC` is false, the count will be limited to the time before `MDCStartDate`
868+
837869
## Files
838870

839871
### Files read use cases
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface DatasetDownloadCount {
2+
id: number | string
3+
downloadCount: number
4+
MDCStartDate?: string
5+
}

src/datasets/domain/repositories/IDatasetsRepository.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { DatasetDTO } from '../dtos/DatasetDTO'
77
import { DatasetDeaccessionDTO } from '../dtos/DatasetDeaccessionDTO'
88
import { MetadataBlock } from '../../../metadataBlocks'
99
import { DatasetVersionDiff } from '../models/DatasetVersionDiff'
10+
import { DatasetDownloadCount } from '../models/DatasetDownloadCount'
1011
import { DatasetVersionSummaryInfo } from '../models/DatasetVersionSummaryInfo'
1112

1213
export interface IDatasetsRepository {
@@ -52,5 +53,9 @@ export interface IDatasetsRepository {
5253
datasetVersionId: string,
5354
deaccessionDTO: DatasetDeaccessionDTO
5455
): Promise<void>
56+
getDatasetDownloadCount(
57+
datasetId: number | string,
58+
includeMDC?: boolean
59+
): Promise<DatasetDownloadCount>
5560
getDatasetVersionsSummaries(datasetId: number | string): Promise<DatasetVersionSummaryInfo[]>
5661
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { DatasetDownloadCount } from '../models/DatasetDownloadCount'
3+
import { IDatasetsRepository } from '../repositories/IDatasetsRepository'
4+
5+
export class GetDatasetDownloadCount implements UseCase<DatasetDownloadCount> {
6+
private datasetsRepository: IDatasetsRepository
7+
8+
constructor(datasetsRepository: IDatasetsRepository) {
9+
this.datasetsRepository = datasetsRepository
10+
}
11+
12+
/**
13+
* Returns a DatasetDownloadCount instance, with dataset id, count and MDCStartDate(optional).
14+
*
15+
* @param {number | string} [datasetId] - The dataset identifier.
16+
* @param {boolean} [includeMDC(optional)] - Indicates whether to consider include counts from MDC start date or not. The default value is false
17+
* @returns {Promise<DatasetDownloadCount>}
18+
*/
19+
async execute(datasetId: number | string, includeMDC?: boolean): Promise<DatasetDownloadCount> {
20+
return await this.datasetsRepository.getDatasetDownloadCount(datasetId, includeMDC)
21+
}
22+
}

src/datasets/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { PublishDataset } from './domain/useCases/PublishDataset'
1717
import { UpdateDataset } from './domain/useCases/UpdateDataset'
1818
import { GetDatasetVersionDiff } from './domain/useCases/GetDatasetVersionDiff'
1919
import { DeaccessionDataset } from './domain/useCases/DeaccessionDataset'
20+
import { GetDatasetDownloadCount } from './domain/useCases/GetDatasetDownloadCount'
2021
import { GetDatasetVersionsSummaries } from './domain/useCases/GetDatasetVersionsSummaries'
2122

2223
const datasetsRepository = new DatasetsRepository()
@@ -49,6 +50,7 @@ const updateDataset = new UpdateDataset(
4950
datasetResourceValidator
5051
)
5152
const deaccessionDataset = new DeaccessionDataset(datasetsRepository)
53+
const getDatasetDownloadCount = new GetDatasetDownloadCount(datasetsRepository)
5254
const getDatasetVersionsSummaries = new GetDatasetVersionsSummaries(datasetsRepository)
5355

5456
export {
@@ -65,6 +67,7 @@ export {
6567
createDataset,
6668
updateDataset,
6769
deaccessionDataset,
70+
getDatasetDownloadCount,
6871
getDatasetVersionsSummaries
6972
}
7073
export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion'

src/datasets/infra/repositories/DatasetsRepository.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { transformDatasetLocksResponseToDatasetLocks } from './transformers/data
1818
import { transformDatasetPreviewsResponseToDatasetPreviewSubset } from './transformers/datasetPreviewsTransformers'
1919
import { DatasetVersionDiff } from '../../domain/models/DatasetVersionDiff'
2020
import { transformDatasetVersionDiffResponseToDatasetVersionDiff } from './transformers/datasetVersionDiffTransformers'
21+
import { DatasetDownloadCount } from '../../domain/models/DatasetDownloadCount'
2122
import { DatasetVersionSummaryInfo } from '../../domain/models/DatasetVersionSummaryInfo'
2223

2324
export interface GetAllDatasetPreviewsQueryParams {
@@ -237,6 +238,23 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
237238
})
238239
}
239240

241+
public async getDatasetDownloadCount(
242+
datasetId: number | string,
243+
includeMDC?: boolean
244+
): Promise<DatasetDownloadCount> {
245+
const queryParams = includeMDC !== undefined ? { includeMDC } : {}
246+
247+
return this.doGet(
248+
this.buildApiEndpoint(this.datasetsResourceName, `download/count`, datasetId),
249+
true,
250+
queryParams
251+
)
252+
.then((response) => response.data)
253+
.catch((error) => {
254+
throw error
255+
})
256+
}
257+
240258
public async getDatasetVersionsSummaries(
241259
datasetId: string | number
242260
): Promise<DatasetVersionSummaryInfo[]> {

test/integration/datasets/DatasetsRepository.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,8 +1108,43 @@ describe('DatasetsRepository', () => {
11081108

11091109
await deletePublishedDatasetViaApi(testDatasetIds.persistentId)
11101110
})
1111+
})
1112+
1113+
describe('getDatasetDownloadCount', () => {
1114+
test('should return download count for a dataset', async () => {
1115+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1116+
await publishDatasetViaApi(testDatasetIds.numericId)
1117+
await waitForNoLocks(testDatasetIds.numericId, 10)
1118+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.numericId)
1119+
1120+
expect(actual.downloadCount).toBe(0)
1121+
})
1122+
1123+
test('should return download count including MDC data', async () => {
1124+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1125+
await publishDatasetViaApi(testDatasetIds.numericId)
1126+
await waitForNoLocks(testDatasetIds.numericId, 10)
1127+
1128+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.numericId, true)
1129+
1130+
expect(actual.downloadCount).toBe(0)
1131+
})
1132+
1133+
test('should return download count including MDC data with persistent ID', async () => {
1134+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1135+
await publishDatasetViaApi(testDatasetIds.numericId)
1136+
await waitForNoLocks(testDatasetIds.numericId, 10)
1137+
1138+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.persistentId, true)
1139+
1140+
expect(actual.downloadCount).toBe(0)
1141+
})
11111142

11121143
test('should return error when dataset does not exist', async () => {
1144+
await expect(sut.getDatasetDownloadCount(nonExistentTestDatasetId)).rejects.toBeInstanceOf(
1145+
ReadError
1146+
)
1147+
11131148
const expectedError = new ReadError(
11141149
`[404] Dataset with ID ${nonExistentTestDatasetId} not found.`
11151150
)

test/unit/datasets/DatasetsRepository.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '../../testHelpers/datasets/datasetHelper'
3131
import { WriteError } from '../../../src'
3232
import { VersionUpdateType } from '../../../src/datasets/domain/models/Dataset'
33+
import { DatasetDownloadCount } from '../../../src/datasets/domain/models/DatasetDownloadCount'
3334
import { createDatasetVersionSummaryModel } from '../../testHelpers/datasets/datasetVersionsSummariesHelper'
3435

3536
describe('DatasetsRepository', () => {
@@ -1016,6 +1017,38 @@ describe('DatasetsRepository', () => {
10161017
})
10171018
})
10181019

1020+
describe('getDatasetDownloadCount', () => {
1021+
const testDatasetDownloadCount: DatasetDownloadCount = {
1022+
id: testDatasetModel.id,
1023+
downloadCount: 1,
1024+
MDCStartDate: '2021-01-01'
1025+
}
1026+
test('should return download count when response is successful', async () => {
1027+
jest.spyOn(axios, 'get').mockResolvedValue({ data: testDatasetDownloadCount })
1028+
1029+
const actual = await sut.getDatasetDownloadCount(testDatasetModel.id)
1030+
1031+
expect(axios.get).toHaveBeenCalledWith(
1032+
`${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/download/count`,
1033+
TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY
1034+
)
1035+
expect(actual).toStrictEqual(testDatasetDownloadCount)
1036+
})
1037+
1038+
test('should return error on repository read error', async () => {
1039+
jest.spyOn(axios, 'get').mockRejectedValue(TestConstants.TEST_ERROR_RESPONSE)
1040+
1041+
let error = undefined as unknown as ReadError
1042+
await sut.getDatasetDownloadCount(testDatasetModel.id).catch((e) => (error = e))
1043+
1044+
expect(axios.get).toHaveBeenCalledWith(
1045+
`${TestConstants.TEST_API_URL}/datasets/${testDatasetModel.id}/download/count`,
1046+
TestConstants.TEST_EXPECTED_AUTHENTICATED_REQUEST_CONFIG_API_KEY
1047+
)
1048+
expect(error).toBeInstanceOf(Error)
1049+
})
1050+
})
1051+
10191052
describe('getDatasetVersionSummaries', () => {
10201053
const testDatasetVersionSummaries = createDatasetVersionSummaryModel()
10211054

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { GetDatasetDownloadCount } from '../../../src/datasets/domain/useCases/GetDatasetDownloadCount'
2+
import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository'
3+
import { ReadError } from '../../../src/core/domain/repositories/ReadError'
4+
import { DatasetDownloadCount } from '../../../src/datasets/domain/models/DatasetDownloadCount'
5+
6+
describe('execute', () => {
7+
const testDatasetId = 1
8+
const testDatasetDownloadCount: DatasetDownloadCount = {
9+
id: testDatasetId,
10+
downloadCount: 1,
11+
MDCStartDate: '2021-01-01'
12+
}
13+
14+
test('should return count on repository success filtering by id', async () => {
15+
const filesRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository
16+
filesRepositoryStub.getDatasetDownloadCount = jest
17+
.fn()
18+
.mockResolvedValue(testDatasetDownloadCount)
19+
const sut = new GetDatasetDownloadCount(filesRepositoryStub)
20+
21+
const actual = await sut.execute(testDatasetId)
22+
23+
expect(actual).toBe(testDatasetDownloadCount)
24+
expect(filesRepositoryStub.getDatasetDownloadCount).toHaveBeenCalledWith(
25+
testDatasetId,
26+
undefined
27+
)
28+
})
29+
30+
test('should return error result on repository error', async () => {
31+
const filesRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository
32+
filesRepositoryStub.getDatasetDownloadCount = jest.fn().mockRejectedValue(new ReadError())
33+
const sut = new GetDatasetDownloadCount(filesRepositoryStub)
34+
35+
await expect(sut.execute(testDatasetId)).rejects.toThrow(ReadError)
36+
})
37+
})

0 commit comments

Comments
 (0)