Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getCollection use case and tests #134

Merged
merged 19 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}`
MellyGray marked this conversation as resolved.
Show resolved Hide resolved
}
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
Loading