Skip to content

Commit

Permalink
Merge pull request #134 from IQSS/feature/131-create-getcollection-us…
Browse files Browse the repository at this point in the history
…e-case

Add getCollection use case and tests
  • Loading branch information
GPortas authored Mar 28, 2024
2 parents f092e4f + d4efec4 commit e0438a2
Show file tree
Hide file tree
Showing 26 changed files with 482 additions and 70 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ node_modules
coverage

# ignore npm lock
package-json.lock
package-json.lock
.npmrc
51 changes: 51 additions & 0 deletions docs/useCases.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ The different use cases currently available in the package are classified below,

## Table of Contents

- [Collections](#Collections)
- [Collections read use cases](#collections-read-use-cases)
- [Get a Collection](#get-a-collection)
- [Datasets](#Datasets)
- [Datasets read use cases](#datasets-read-use-cases)
- [Get a Dataset](#get-a-dataset)
Expand Down Expand Up @@ -42,6 +45,54 @@ The different use cases currently available in the package are classified below,
- [Get Maximum Embargo Duration In Months](#get-maximum-embargo-duration-in-months)
- [Get ZIP Download Limit](#get-zip-download-limit)

## Collections

### Collections Read Use Cases

#### Get a Collection

Returns a [Collection](../src/collections/domain/models/Collection.ts) instance, given the search parameters to identify it.

##### Example call:

```typescript
import { getCollection } from '@iqss/dataverse-client-javascript'

/* ... */
// Case 1: Fetch Collection by its numerical ID
const collectionIdOrAlias = 12345

getCollection
.execute(collectionId)
.then((collection: Collection) => {
/* ... */
})
.catch((error: Error) => {
/* ... */
})

/* ... */

// Case 2: Fetch Collection by its alias
const collectionIdOrAlias = 'classicLiterature'
getCollection
.execute(collectionAlias)
.then((collection: Collection) => {
/* ... */
})
.catch((error: Error) => {
/* ... */
})

/* ... */
```

_See [use case](../src/collections/domain/useCases/GetCollection.ts)_ definition.

The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).

If no collection identifier is specified, the default collection identifier; `root` will be used. If you want to search for a different collection, you must add the collection identifier as a parameter in the use case call.

## Datasets

### Datasets Read Use Cases
Expand Down
11 changes: 11 additions & 0 deletions src/collections/domain/models/Collection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { DvObjectOwnerNode } from '../../../core'
export interface Collection {
id: number
alias: string
name: string
affiliation?: string
description?: string
isPartOf: DvObjectOwnerNode
}

export const ROOT_COLLECTION_ALIAS = 'root'
4 changes: 4 additions & 0 deletions src/collections/domain/repositories/ICollectionsRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Collection } from '../models/Collection'
export interface ICollectionsRepository {
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
}
22 changes: 22 additions & 0 deletions src/collections/domain/useCases/GetCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { UseCase } from '../../../core/domain/useCases/UseCase'
import { ICollectionsRepository } from '../repositories/ICollectionsRepository'
import { Collection, ROOT_COLLECTION_ALIAS } from '../models/Collection'

export class GetCollection implements UseCase<Collection> {
private collectionsRepository: ICollectionsRepository

constructor(collectionsRepository: ICollectionsRepository) {
this.collectionsRepository = collectionsRepository
}

/**
* Returns a Collection instance, given the search parameters to identify it.
*
* @param {number | string} [collectionIdOrAlias = 'root'] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId)
* If this parameter is not set, the default value is: 'root'
* @returns {Promise<Collection>}
*/
async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS): Promise<Collection> {
return await this.collectionsRepository.getCollection(collectionIdOrAlias)
}
}
10 changes: 10 additions & 0 deletions src/collections/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { GetCollection } from './domain/useCases/GetCollection'

import { CollectionsRepository } from './infra/repositories/CollectionsRepository'

const collectionsRepository = new CollectionsRepository()

const getCollection = new GetCollection(collectionsRepository)

export { getCollection }
export { Collection } from './domain/models/Collection'
26 changes: 26 additions & 0 deletions src/collections/infra/repositories/CollectionsRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
import { ICollectionsRepository } from '../../domain/repositories/ICollectionsRepository'
import { transformCollectionResponseToCollection } from './transformers/collectionTransformers'
import { Collection, ROOT_COLLECTION_ALIAS } from '../../domain/models/Collection'
export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
private readonly collectionsResourceName: string = 'dataverses'
private readonly collectionsDefaultOperationType: string = 'get'

public async getCollection(
collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS
): Promise<Collection> {
return this.doGet(
this.buildApiEndpoint(
this.collectionsResourceName,
this.collectionsDefaultOperationType,
collectionIdOrAlias
),
true,
{ returnOwners: true }
)
.then((response) => transformCollectionResponseToCollection(response))
.catch((error) => {
throw error
})
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload'
export interface CollectionPayload {
id: number
alias: string
name: string
affiliation?: string
description?: string
isPartOf: OwnerNodePayload
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Collection } from '../../../domain/models/Collection'
import { AxiosResponse } from 'axios'
import { CollectionPayload } from './CollectionPayload'
import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories/transformers/dvObjectOwnerNodeTransformer'

export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => {
const collectionPayload = response.data.data
return transformPayloadToCollection(collectionPayload)
}

const transformPayloadToCollection = (collectionPayload: CollectionPayload): Collection => {
const collectionModel: Collection = {
id: collectionPayload.id,
alias: collectionPayload.alias,
name: collectionPayload.name,
affiliation: collectionPayload.affiliation,
description: collectionPayload.description,
...(collectionPayload.isPartOf && {
isPartOf: transformPayloadToOwnerNode(collectionPayload.isPartOf)
})
}
return collectionModel
}
18 changes: 9 additions & 9 deletions src/core/infra/repositories/ApiRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ export abstract class ApiRepository {
operation: string,
resourceId: number | string = undefined
) {
let endpoint
if (typeof resourceId === 'number') {
endpoint = `/${resourceName}/${resourceId}/${operation}`
} else if (typeof resourceId === 'string') {
endpoint = `/${resourceName}/:persistentId/${operation}?persistentId=${resourceId}`
} else {
endpoint = `/${resourceName}/${operation}`
if (resourceName === 'dataverses') {
return `/${resourceName}/${resourceId}`
}
return endpoint

return typeof resourceId === 'number'
? `/${resourceName}/${resourceId}/${operation}`
: typeof resourceId === 'string'
? `/${resourceName}/:persistentId/${operation}?persistentId=${resourceId}`
: `/${resourceName}/${operation}`
}

private buildRequestConfig(authRequired: boolean, queryParams: object): AxiosRequestConfig {
Expand All @@ -61,7 +61,7 @@ export abstract class ApiRepository {
switch (ApiConfig.dataverseApiAuthMechanism) {
case DataverseApiAuthMechanism.SESSION_COOKIE:
/*
We set { withCredentials: true } to send the JSESSIONID cookie in the requests for API authentication.
We set { withCredentials: true } to send the JSESSIONID cookie in the requests for API authentication.
This is required, along with the session auth feature flag enabled in the backend, to be able to authenticate using the JSESSIONID cookie.
Auth mechanisms like this are configurable to set the one that fits the particular use case of js-dataverse. (For the SPA MVP, it is the session cookie API auth).
*/
Expand Down
14 changes: 7 additions & 7 deletions src/datasets/domain/repositories/IDatasetsRepository.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
import { Dataset } from '../models/Dataset'
import { DatasetUserPermissions } from '../models/DatasetUserPermissions'
import { DatasetLock } from '../models/DatasetLock'
import { DatasetPreviewSubset } from '../models/DatasetPreviewSubset'
import { DatasetUserPermissions } from '../models/DatasetUserPermissions'
import { CreatedDatasetIdentifiers } from '../models/CreatedDatasetIdentifiers'
import { NewDatasetDTO } from '../dtos/NewDatasetDTO'
import { MetadataBlock } from '../../../metadataBlocks'
import { CreatedDatasetIdentifiers } from '../models/CreatedDatasetIdentifiers'

export interface IDatasetsRepository {
getDatasetSummaryFieldNames(): Promise<string[]>
getDataset(
datasetId: number | string,
datasetVersionId: string,
includeDeaccessioned: boolean
): Promise<Dataset>
getPrivateUrlDataset(token: string): Promise<Dataset>
getDatasetLocks(datasetId: number | string): Promise<DatasetLock[]>
getDatasetCitation(
datasetId: number,
datasetVersionId: string,
includeDeaccessioned: boolean
): Promise<string>
getPrivateUrlDatasetCitation(token: string): Promise<string>
getDatasetUserPermissions(datasetId: number | string): Promise<DatasetUserPermissions>
getDatasetLocks(datasetId: number | string): Promise<DatasetLock[]>
getPrivateUrlDataset(token: string): Promise<Dataset>
getAllDatasetPreviews(
limit?: number,
offset?: number,
collectionId?: string
): Promise<DatasetPreviewSubset>
getDatasetSummaryFieldNames(): Promise<string[]>
getPrivateUrlDatasetCitation(token: string): Promise<string>
getDatasetUserPermissions(datasetId: number | string): Promise<DatasetUserPermissions>
createDataset(
newDataset: NewDatasetDTO,
datasetMetadataBlocks: MetadataBlock[],
Expand Down
3 changes: 2 additions & 1 deletion src/datasets/domain/useCases/CreateDataset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { NewResourceValidator } from '../../../core/domain/useCases/validators/N
import { IMetadataBlocksRepository } from '../../../metadataBlocks/domain/repositories/IMetadataBlocksRepository'
import { MetadataBlock } from '../../../metadataBlocks'
import { CreatedDatasetIdentifiers } from '../models/CreatedDatasetIdentifiers'
import { ROOT_COLLECTION_ALIAS } from '../../../collections/domain/models/Collection'

export class CreateDataset implements UseCase<CreatedDatasetIdentifiers> {
private datasetsRepository: IDatasetsRepository
Expand Down Expand Up @@ -33,7 +34,7 @@ export class CreateDataset implements UseCase<CreatedDatasetIdentifiers> {
*/
async execute(
newDataset: NewDatasetDTO,
collectionId = 'root'
collectionId = ROOT_COLLECTION_ALIAS
): Promise<CreatedDatasetIdentifiers> {
const metadataBlocks = await this.getNewDatasetMetadataBlocks(newDataset)

Expand Down
46 changes: 23 additions & 23 deletions src/datasets/index.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { DatasetsRepository } from './infra/repositories/DatasetsRepository'
import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'
import { MetadataBlocksRepository } from '../metadataBlocks/infra/repositories/MetadataBlocksRepository'
import { GetDataset } from './domain/useCases/GetDataset'
import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'
import { GetDatasetCitation } from './domain/useCases/GetDatasetCitation'
import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDatasetCitation'
import { GetDatasetUserPermissions } from './domain/useCases/GetDatasetUserPermissions'
import { CreateDataset } from './domain/useCases/CreateDataset'
import { GetDatasetLocks } from './domain/useCases/GetDatasetLocks'
import { GetDatasetCitation } from './domain/useCases/GetDatasetCitation'
import { GetPrivateUrlDataset } from './domain/useCases/GetPrivateUrlDataset'
import { GetAllDatasetPreviews } from './domain/useCases/GetAllDatasetPreviews'
import { NewDatasetResourceValidator } from './domain/useCases/validators/NewDatasetResourceValidator'
import { MetadataBlocksRepository } from '../metadataBlocks/infra/repositories/MetadataBlocksRepository'
import { CreateDataset } from './domain/useCases/CreateDataset'
import { MetadataFieldValidator } from './domain/useCases/validators/MetadataFieldValidator'
import { GetDatasetUserPermissions } from './domain/useCases/GetDatasetUserPermissions'
import { NewDatasetResourceValidator } from './domain/useCases/validators/NewDatasetResourceValidator'
import { GetDatasetSummaryFieldNames } from './domain/useCases/GetDatasetSummaryFieldNames'
import { GetPrivateUrlDatasetCitation } from './domain/useCases/GetPrivateUrlDatasetCitation'
import { SingleMetadataFieldValidator } from './domain/useCases/validators/SingleMetadataFieldValidator'
import { MultipleMetadataFieldValidator } from './domain/useCases/validators/MultipleMetadataFieldValidator'

const datasetsRepository = new DatasetsRepository()

const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository)
const getDataset = new GetDataset(datasetsRepository)
const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository)
const getDatasetCitation = new GetDatasetCitation(datasetsRepository)
const getPrivateUrlDatasetCitation = new GetPrivateUrlDatasetCitation(datasetsRepository)
const getDatasetUserPermissions = new GetDatasetUserPermissions(datasetsRepository)
const getDatasetLocks = new GetDatasetLocks(datasetsRepository)
const getDatasetCitation = new GetDatasetCitation(datasetsRepository)
const getPrivateUrlDataset = new GetPrivateUrlDataset(datasetsRepository)
const getAllDatasetPreviews = new GetAllDatasetPreviews(datasetsRepository)
const getDatasetUserPermissions = new GetDatasetUserPermissions(datasetsRepository)
const getDatasetSummaryFieldNames = new GetDatasetSummaryFieldNames(datasetsRepository)
const getPrivateUrlDatasetCitation = new GetPrivateUrlDatasetCitation(datasetsRepository)
const singleMetadataFieldValidator = new SingleMetadataFieldValidator()
const metadataFieldValidator = new MetadataFieldValidator(
new SingleMetadataFieldValidator(),
Expand All @@ -36,37 +36,37 @@ const createDataset = new CreateDataset(
)

export {
getDatasetSummaryFieldNames,
getDataset,
getPrivateUrlDataset,
getDatasetCitation,
getPrivateUrlDatasetCitation,
getDatasetUserPermissions,
getDatasetLocks,
getDatasetCitation,
getPrivateUrlDataset,
getAllDatasetPreviews,
getDatasetUserPermissions,
getDatasetSummaryFieldNames,
getPrivateUrlDatasetCitation,
createDataset
}
export { DatasetNotNumberedVersion } from './domain/models/DatasetNotNumberedVersion'
export { DatasetUserPermissions } from './domain/models/DatasetUserPermissions'
export { DatasetLock, DatasetLockType } from './domain/models/DatasetLock'
export {
Dataset,
DatasetLicense,
DatasetVersionInfo,
DatasetVersionState,
DatasetLicense,
DatasetMetadataBlocks,
DatasetMetadataBlock,
DatasetMetadataBlocks,
DatasetMetadataFields,
DatasetMetadataFieldValue,
DatasetMetadataSubField
DatasetMetadataSubField,
DatasetMetadataFieldValue
} from './domain/models/Dataset'
export { DatasetPreview } from './domain/models/DatasetPreview'
export { DatasetPreviewSubset } from './domain/models/DatasetPreviewSubset'
export {
NewDatasetDTO as NewDataset,
NewDatasetMetadataBlockValuesDTO as NewDatasetMetadataBlockValues,
NewDatasetMetadataFieldsDTO as NewDatasetMetadataFields,
NewDatasetMetadataFieldValueDTO as NewDatasetMetadataFieldValue,
NewDatasetMetadataBlockValuesDTO as NewDatasetMetadataBlockValues,
NewDatasetMetadataChildFieldValueDTO as NewDatasetMetadataChildFieldValue
} from './domain/dtos/NewDatasetDTO'
export { CreatedDatasetIdentifiers } from './domain/models/CreatedDatasetIdentifiers'
12 changes: 6 additions & 6 deletions src/datasets/infra/repositories/DatasetsRepository.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
import { IDatasetsRepository } from '../../domain/repositories/IDatasetsRepository'
import { Dataset } from '../../domain/models/Dataset'
import { transformVersionResponseToDataset } from './transformers/datasetTransformers'
import { DatasetUserPermissions } from '../../domain/models/DatasetUserPermissions'
import { transformDatasetUserPermissionsResponseToDatasetUserPermissions } from './transformers/datasetUserPermissionsTransformers'
import { DatasetLock } from '../../domain/models/DatasetLock'
import { transformDatasetLocksResponseToDatasetLocks } from './transformers/datasetLocksTransformers'
import { transformDatasetPreviewsResponseToDatasetPreviewSubset } from './transformers/datasetPreviewsTransformers'
import { DatasetUserPermissions } from '../../domain/models/DatasetUserPermissions'
import { CreatedDatasetIdentifiers } from '../../domain/models/CreatedDatasetIdentifiers'
import { DatasetPreviewSubset } from '../../domain/models/DatasetPreviewSubset'
import { NewDatasetDTO } from '../../domain/dtos/NewDatasetDTO'
import { MetadataBlock } from '../../../metadataBlocks'
import { transformVersionResponseToDataset } from './transformers/datasetTransformers'
import { transformNewDatasetModelToRequestPayload } from './transformers/newDatasetTransformers'
import { CreatedDatasetIdentifiers } from '../../domain/models/CreatedDatasetIdentifiers'
import { transformDatasetLocksResponseToDatasetLocks } from './transformers/datasetLocksTransformers'
import { transformDatasetPreviewsResponseToDatasetPreviewSubset } from './transformers/datasetPreviewsTransformers'
import { transformDatasetUserPermissionsResponseToDatasetUserPermissions } from './transformers/datasetUserPermissionsTransformers'

export interface GetAllDatasetPreviewsQueryParams {
per_page?: number
Expand Down
Loading

0 comments on commit e0438a2

Please sign in to comment.