Skip to content

Commit fe9a4a2

Browse files
committed
Merge branch 'develop' into edit-metadata-allow-empty-values
2 parents 28495e8 + 96cab38 commit fe9a4a2

File tree

11 files changed

+198
-8
lines changed

11 files changed

+198
-8
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 {
@@ -53,5 +54,9 @@ export interface IDatasetsRepository {
5354
datasetVersionId: string,
5455
deaccessionDTO: DatasetDeaccessionDTO
5556
): Promise<void>
57+
getDatasetDownloadCount(
58+
datasetId: number | string,
59+
includeMDC?: boolean
60+
): Promise<DatasetDownloadCount>
5661
getDatasetVersionsSummaries(datasetId: number | string): Promise<DatasetVersionSummaryInfo[]>
5762
}
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/domain/useCases/validators/SingleMetadataFieldValidator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ export class SingleMetadataFieldValidator extends BaseMetadataFieldValidator {
159159
/**
160160
* This method allows setting empty values for conditionally required child fields.
161161
* A child field is conditionally required if it is required and its parent field is not required.
162-
* The childfield should be required only if any of its sibling fields has a value, otherwise it should be optional.
162+
* The child field should be required only if any of its sibling fields has a value, otherwise it should be optional.
163163
*/
164164

165165
private allowEmptyValueForConditionallyRequiredField(

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 {
@@ -243,6 +244,23 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
243244
})
244245
}
245246

247+
public async getDatasetDownloadCount(
248+
datasetId: number | string,
249+
includeMDC?: boolean
250+
): Promise<DatasetDownloadCount> {
251+
const queryParams = includeMDC !== undefined ? { includeMDC } : {}
252+
253+
return this.doGet(
254+
this.buildApiEndpoint(this.datasetsResourceName, `download/count`, datasetId),
255+
true,
256+
queryParams
257+
)
258+
.then((response) => response.data)
259+
.catch((error) => {
260+
throw error
261+
})
262+
}
263+
246264
public async getDatasetVersionsSummaries(
247265
datasetId: string | number
248266
): Promise<DatasetVersionSummaryInfo[]> {

test/integration/collections/CollectionsRepository.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,8 @@ describe('CollectionsRepository', () => {
610610
expect((actual.items[0] as CollectionPreview).name).toBe(expectedCollectionsName)
611611
expect(actual.facets).toEqual(expectedFacetsFromCollectionOnly)
612612
expect(actual.countPerObjectType.dataverses).toBe(1)
613-
expect(actual.countPerObjectType.datasets).toBe(0)
614-
expect(actual.countPerObjectType.files).toBe(0)
613+
expect(actual.countPerObjectType.datasets).toBe(1)
614+
expect(actual.countPerObjectType.files).toBe(1)
615615

616616
// Test type dataset
617617
const collectionSearchCriteriaForDatasetType = new CollectionSearchCriteria().withItemTypes([
@@ -627,9 +627,9 @@ describe('CollectionsRepository', () => {
627627
expect(actual.totalItemCount).toBe(1)
628628
expect((actual.items[0] as DatasetPreview).title).toBe(expectedDatasetDescription)
629629
expect(actual.facets).toEqual(expectedFacetsFromDatasetOnly)
630-
expect(actual.countPerObjectType.dataverses).toBe(0)
630+
expect(actual.countPerObjectType.dataverses).toBe(1)
631631
expect(actual.countPerObjectType.datasets).toBe(1)
632-
expect(actual.countPerObjectType.files).toBe(0)
632+
expect(actual.countPerObjectType.files).toBe(1)
633633

634634
// Test type file
635635
const collectionSearchCriteriaForFileType = new CollectionSearchCriteria().withItemTypes([
@@ -645,8 +645,8 @@ describe('CollectionsRepository', () => {
645645
expect(actual.totalItemCount).toBe(1)
646646
expect((actual.items[0] as FilePreview).name).toBe(expectedFileName)
647647
expect(actual.facets).toEqual(expectedFacetsFromFileOnly)
648-
expect(actual.countPerObjectType.dataverses).toBe(0)
649-
expect(actual.countPerObjectType.datasets).toBe(0)
648+
expect(actual.countPerObjectType.dataverses).toBe(1)
649+
expect(actual.countPerObjectType.datasets).toBe(1)
650650
expect(actual.countPerObjectType.files).toBe(1)
651651

652652
// Test multiple types
@@ -666,7 +666,7 @@ describe('CollectionsRepository', () => {
666666
expect((actual.items[1] as CollectionPreview).name).toBe(expectedCollectionsName)
667667
expect(actual.facets).toEqual(expectedFacetsFromCollectionAndFile)
668668
expect(actual.countPerObjectType.dataverses).toBe(1)
669-
expect(actual.countPerObjectType.datasets).toBe(0)
669+
expect(actual.countPerObjectType.datasets).toBe(1)
670670
expect(actual.countPerObjectType.files).toBe(1)
671671

672672
// Test Sort by name ascending

test/integration/datasets/DatasetsRepository.test.ts

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

12461246
await deletePublishedDatasetViaApi(testDatasetIds.persistentId)
12471247
})
1248+
})
1249+
1250+
describe('getDatasetDownloadCount', () => {
1251+
test('should return download count for a dataset', async () => {
1252+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1253+
await publishDatasetViaApi(testDatasetIds.numericId)
1254+
await waitForNoLocks(testDatasetIds.numericId, 10)
1255+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.numericId)
1256+
1257+
expect(actual.downloadCount).toBe(0)
1258+
})
1259+
1260+
test('should return download count including MDC data', async () => {
1261+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1262+
await publishDatasetViaApi(testDatasetIds.numericId)
1263+
await waitForNoLocks(testDatasetIds.numericId, 10)
1264+
1265+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.numericId, true)
1266+
1267+
expect(actual.downloadCount).toBe(0)
1268+
})
1269+
1270+
test('should return download count including MDC data with persistent ID', async () => {
1271+
const testDatasetIds = await createDataset.execute(TestConstants.TEST_NEW_DATASET_DTO)
1272+
await publishDatasetViaApi(testDatasetIds.numericId)
1273+
await waitForNoLocks(testDatasetIds.numericId, 10)
1274+
1275+
const actual = await sut.getDatasetDownloadCount(testDatasetIds.persistentId, true)
1276+
1277+
expect(actual.downloadCount).toBe(0)
1278+
})
12481279

12491280
test('should return error when dataset does not exist', async () => {
1281+
await expect(sut.getDatasetDownloadCount(nonExistentTestDatasetId)).rejects.toBeInstanceOf(
1282+
ReadError
1283+
)
1284+
12501285
const expectedError = new ReadError(
12511286
`[404] Dataset with ID ${nonExistentTestDatasetId} not found.`
12521287
)

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

0 commit comments

Comments
 (0)