Skip to content

Commit 1681f53

Browse files
authored
Merge pull request #421 from IQSS/409-create-use-case-for-getting-storage-quota-on-dataset
Branch updated and all tests passing locally and in GH.
2 parents 67721ee + af1ab54 commit 1681f53

File tree

12 files changed

+215
-3
lines changed

12 files changed

+215
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
1010

1111
- Datasets: Added `updateDatasetLicense` use case and repository method to support Dataverse endpoint `PUT /datasets/{id}/license`, for updating dataset license or custom terms.
1212
- Datasets: Added `getDatasetStorageDriver` use case and repository method to support Dataverse endpoint `GET /datasets/{identifier}/storageDriver`, for retrieving dataset storage driver configuration with properties: name, type, label, directUpload, directDownload, and uploadOutOfBand.
13-
- Datasets: Added `updateDatasetLicense` use case and repository method to support Dataverse endpoint `PUT /datasets/{id}/license`, for updating dataset license or custom terms
13+
- Datasets: Added `getDatasetUploadLimits` use case and repository method to support Dataverse endpoint `GET /datasets/{id}/uploadlimits`, for retrieving remaining storage upload quotas, if present.
1414
- New Use Case: [Get Collections For Linking Use Case](./docs/useCases.md#get-collections-for-linking).
1515
- New Use Case: [Create a Template](./docs/useCases.md#create-a-template) under Templates.
1616
- New Use Case: [Get a Template](./docs/useCases.md#get-a-template) under Templates.

docs/useCases.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ The different use cases currently available in the package are classified below,
5050
- [Get Dataset Storage Driver](#get-dataset-storage-driver)
5151
- [Get Dataset Available Dataset Types](#get-dataset-available-dataset-types)
5252
- [Get Dataset Available Dataset Type](#get-dataset-available-dataset-type)
53+
- [Get Dataset Upload Limits](#get-dataset-upload-limits)
5354
- [Datasets write use cases](#datasets-write-use-cases)
5455
- [Create a Dataset](#create-a-dataset)
5556
- [Update a Dataset](#update-a-dataset)
@@ -1516,6 +1517,30 @@ deleteDatasetType.execute(datasetTypeId).then(() => {
15161517

15171518
_See [use case](../src/datasets/domain/useCases/DeleteDatasetType.ts) implementation_.
15181519

1520+
#### Get Dataset Upload Limits
1521+
1522+
Returns a [DatasetUploadLimits](../src/datasets/domain/models/DatasetUploadLimits.ts) instance with the remaining dataset storage and/or file upload quotas, if present.
1523+
1524+
##### Example call:
1525+
1526+
```typescript
1527+
import { getDatasetUploadLimits } from '@iqss/dataverse-client-javascript'
1528+
1529+
/* ... */
1530+
1531+
const datasetId = 'doi:10.77777/FK2/AAAAAA'
1532+
1533+
getDatasetUploadLimits.execute(datasetId).then((uploadLimits: DatasetUploadLimits) => {
1534+
/* ... */
1535+
})
1536+
1537+
/* ... */
1538+
```
1539+
1540+
_See [use case](../src/datasets/domain/useCases/GetDatasetUploadLimits.ts) implementation_.
1541+
1542+
If the backend does not define any quota limits for the dataset, the returned object can be empty (`{}`).
1543+
15191544
## Files
15201545

15211546
### Files read use cases

package-lock.json

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"ts-node": "^10.9.2"
5858
},
5959
"dependencies": {
60+
"@iqss/dataverse-client-javascript": "^2.1.0",
6061
"@types/node": "^18.15.11",
6162
"@types/turndown": "^5.0.1",
6263
"axios": "^1.12.2",
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface DatasetUploadLimits {
2+
numberOfFilesRemaining?: number
3+
storageQuotaRemaining?: number
4+
}

src/datasets/domain/repositories/IDatasetsRepository.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { TermsOfAccess } from '../models/Dataset'
1717
import { DatasetLicenseUpdateRequest } from '../dtos/DatasetLicenseUpdateRequest'
1818
import { DatasetTypeDTO } from '../dtos/DatasetTypeDTO'
1919
import { StorageDriver } from '../models/StorageDriver'
20+
import { DatasetUploadLimits } from '../models/DatasetUploadLimits'
2021

2122
export interface IDatasetsRepository {
2223
getDataset(
@@ -102,4 +103,5 @@ export interface IDatasetsRepository {
102103
payload: DatasetLicenseUpdateRequest
103104
): Promise<void>
104105
getDatasetStorageDriver(datasetId: number | string): Promise<StorageDriver>
106+
getDatasetUploadLimits(datasetId: number | string): Promise<DatasetUploadLimits>
105107
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { UseCase } from '../../../core/domain/useCases/UseCase'
2+
import { IDatasetsRepository } from '../repositories/IDatasetsRepository'
3+
import { DatasetUploadLimits } from '../models/DatasetUploadLimits'
4+
5+
export class GetDatasetUploadLimits implements UseCase<DatasetUploadLimits> {
6+
private datasetsRepository: IDatasetsRepository
7+
8+
constructor(datasetsRepository: IDatasetsRepository) {
9+
this.datasetsRepository = datasetsRepository
10+
}
11+
12+
/**
13+
* Returns the remaining dataset storage and/or file upload quotas (if present).
14+
*
15+
* @param {number | string} datasetId - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers).
16+
* @returns {Promise<DatasetUploadLimits>}
17+
*/
18+
async execute(datasetId: number | string): Promise<DatasetUploadLimits> {
19+
return this.datasetsRepository.getDatasetUploadLimits(datasetId)
20+
}
21+
}

src/datasets/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { GetDatasetCitationInOtherFormats } from './domain/useCases/GetDatasetCi
3434
import { UpdateTermsOfAccess } from './domain/useCases/UpdateTermsOfAccess'
3535
import { UpdateDatasetLicense } from './domain/useCases/UpdateDatasetLicense'
3636
import { GetDatasetStorageDriver } from './domain/useCases/GetDatasetStorageDriver'
37+
import { GetDatasetUploadLimits } from './domain/useCases/GetDatasetUploadLimits'
3738

3839
const datasetsRepository = new DatasetsRepository()
3940

@@ -84,6 +85,7 @@ const getDatasetCitationInOtherFormats = new GetDatasetCitationInOtherFormats(da
8485
const updateTermsOfAccess = new UpdateTermsOfAccess(datasetsRepository)
8586
const updateDatasetLicense = new UpdateDatasetLicense(datasetsRepository)
8687
const getDatasetStorageDriver = new GetDatasetStorageDriver(datasetsRepository)
88+
const getDatasetUploadLimits = new GetDatasetUploadLimits(datasetsRepository)
8789

8890
export {
8991
getDataset,
@@ -115,7 +117,8 @@ export {
115117
setAvailableLicensesForDatasetType,
116118
deleteDatasetType,
117119
updateDatasetLicense,
118-
getDatasetStorageDriver
120+
getDatasetStorageDriver,
121+
getDatasetUploadLimits
119122
}
120123
export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion'
121124
export { DatasetUserPermissions } from './domain/models/DatasetUserPermissions'
@@ -155,3 +158,4 @@ export { DatasetLinkedCollection } from './domain/models/DatasetLinkedCollection
155158
export { DatasetType } from './domain/models/DatasetType'
156159
export { DatasetTypeDTO } from './domain/dtos/DatasetTypeDTO'
157160
export { StorageDriver } from './domain/models/StorageDriver'
161+
export { DatasetUploadLimits } from './domain/models/DatasetUploadLimits'

src/datasets/infra/repositories/DatasetsRepository.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { transformTermsOfAccessToUpdatePayload } from './transformers/termsOfAcc
3030
import { DatasetLicenseUpdateRequest } from '../../domain/dtos/DatasetLicenseUpdateRequest'
3131
import { DatasetTypeDTO } from '../../domain/dtos/DatasetTypeDTO'
3232
import { StorageDriver } from '../../domain/models/StorageDriver'
33+
import { DatasetUploadLimits } from '../../domain/models/DatasetUploadLimits'
3334

3435
export interface GetAllDatasetPreviewsQueryParams {
3536
per_page?: number
@@ -511,4 +512,15 @@ export class DatasetsRepository extends ApiRepository implements IDatasetsReposi
511512
throw error
512513
})
513514
}
515+
516+
public async getDatasetUploadLimits(datasetId: number | string): Promise<DatasetUploadLimits> {
517+
return this.doGet(
518+
this.buildApiEndpoint(this.datasetsResourceName, 'uploadlimits', datasetId),
519+
true
520+
)
521+
.then((response) => (response.data?.data?.uploadLimits ?? {}) as DatasetUploadLimits)
522+
.catch((error) => {
523+
throw error
524+
})
525+
}
514526
}

test/integration/datasets/DatasetsRepository.test.ts

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import {
99
waitForDatasetsIndexedInSolr,
1010
deletePublishedDatasetViaApi,
1111
deaccessionDatasetViaApi,
12-
createDatasetLicenseModel
12+
createDatasetLicenseModel,
13+
setDatasetStorageSizeViaApi,
14+
setUseStorageQuotasViaApi
1315
} from '../../testHelpers/datasets/datasetHelper'
1416
import { ReadError } from '../../../src/core/domain/repositories/ReadError'
1517
import {
@@ -2244,4 +2246,49 @@ describe('DatasetsRepository', () => {
22442246
expect(typeof storageDriver.uploadOutOfBand).toBe('boolean')
22452247
})
22462248
})
2249+
2250+
describe('getDatasetUploadLimits', () => {
2251+
const testCollectionAlias = 'UploadLimitsQuotaDataset'
2252+
let testDatasetIds: CreatedDatasetIdentifiers
2253+
const testCollectionStorageQuotaInBytes = 1000
2254+
2255+
beforeAll(async () => {
2256+
await createCollectionViaApi(testCollectionAlias)
2257+
await publishCollectionViaApi(testCollectionAlias)
2258+
testDatasetIds = await createDataset.execute(
2259+
TestConstants.TEST_NEW_DATASET_DTO,
2260+
testCollectionAlias
2261+
)
2262+
await setUseStorageQuotasViaApi(true)
2263+
await publishDatasetViaApi(testDatasetIds.numericId)
2264+
await waitForNoLocks(testDatasetIds.numericId, 10)
2265+
})
2266+
2267+
afterAll(async () => {
2268+
await deletePublishedDatasetViaApi(testDatasetIds.persistentId).catch(() => undefined)
2269+
await deleteCollectionViaApi(testCollectionAlias).catch(() => undefined)
2270+
})
2271+
2272+
test('should return empty for dataset (if DatasetStorageSize is not set)', async () => {
2273+
const uploadLimits = await sut.getDatasetUploadLimits(testDatasetIds.numericId)
2274+
2275+
expect(uploadLimits).toEqual({})
2276+
})
2277+
2278+
test('should return upload limits for dataset (if DatasetStorageSize is set)', async () => {
2279+
await setDatasetStorageSizeViaApi(testDatasetIds.numericId, testCollectionStorageQuotaInBytes)
2280+
const uploadLimits = await sut.getDatasetUploadLimits(testDatasetIds.numericId)
2281+
2282+
expect(uploadLimits).toBeDefined()
2283+
expect(uploadLimits.storageQuotaRemaining).toBeLessThanOrEqual(
2284+
testCollectionStorageQuotaInBytes
2285+
)
2286+
})
2287+
2288+
test('should return error when dataset does not exist', async () => {
2289+
await expect(sut.getDatasetUploadLimits(nonExistentTestDatasetId)).rejects.toBeInstanceOf(
2290+
ReadError
2291+
)
2292+
})
2293+
})
22472294
})

0 commit comments

Comments
 (0)