Skip to content

Commit e0438a2

Browse files
authored
Merge pull request #134 from IQSS/feature/131-create-getcollection-use-case
Add getCollection use case and tests
2 parents f092e4f + d4efec4 commit e0438a2

File tree

26 files changed

+482
-70
lines changed

26 files changed

+482
-70
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ node_modules
88
coverage
99

1010
# ignore npm lock
11-
package-json.lock
11+
package-json.lock
12+
.npmrc

docs/useCases.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ The different use cases currently available in the package are classified below,
88

99
## Table of Contents
1010

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

48+
## Collections
49+
50+
### Collections Read Use Cases
51+
52+
#### Get a Collection
53+
54+
Returns a [Collection](../src/collections/domain/models/Collection.ts) instance, given the search parameters to identify it.
55+
56+
##### Example call:
57+
58+
```typescript
59+
import { getCollection } from '@iqss/dataverse-client-javascript'
60+
61+
/* ... */
62+
// Case 1: Fetch Collection by its numerical ID
63+
const collectionIdOrAlias = 12345
64+
65+
getCollection
66+
.execute(collectionId)
67+
.then((collection: Collection) => {
68+
/* ... */
69+
})
70+
.catch((error: Error) => {
71+
/* ... */
72+
})
73+
74+
/* ... */
75+
76+
// Case 2: Fetch Collection by its alias
77+
const collectionIdOrAlias = 'classicLiterature'
78+
getCollection
79+
.execute(collectionAlias)
80+
.then((collection: Collection) => {
81+
/* ... */
82+
})
83+
.catch((error: Error) => {
84+
/* ... */
85+
})
86+
87+
/* ... */
88+
```
89+
90+
_See [use case](../src/collections/domain/useCases/GetCollection.ts)_ definition.
91+
92+
The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).
93+
94+
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.
95+
4596
## Datasets
4697

4798
### Datasets Read Use Cases
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { DvObjectOwnerNode } from '../../../core'
2+
export interface Collection {
3+
id: number
4+
alias: string
5+
name: string
6+
affiliation?: string
7+
description?: string
8+
isPartOf: DvObjectOwnerNode
9+
}
10+
11+
export const ROOT_COLLECTION_ALIAS = 'root'
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { Collection } from '../models/Collection'
2+
export interface ICollectionsRepository {
3+
getCollection(collectionIdOrAlias: number | string): Promise<Collection>
4+
}
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 { ICollectionsRepository } from '../repositories/ICollectionsRepository'
3+
import { Collection, ROOT_COLLECTION_ALIAS } from '../models/Collection'
4+
5+
export class GetCollection implements UseCase<Collection> {
6+
private collectionsRepository: ICollectionsRepository
7+
8+
constructor(collectionsRepository: ICollectionsRepository) {
9+
this.collectionsRepository = collectionsRepository
10+
}
11+
12+
/**
13+
* Returns a Collection instance, given the search parameters to identify it.
14+
*
15+
* @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)
16+
* If this parameter is not set, the default value is: 'root'
17+
* @returns {Promise<Collection>}
18+
*/
19+
async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS): Promise<Collection> {
20+
return await this.collectionsRepository.getCollection(collectionIdOrAlias)
21+
}
22+
}

src/collections/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { GetCollection } from './domain/useCases/GetCollection'
2+
3+
import { CollectionsRepository } from './infra/repositories/CollectionsRepository'
4+
5+
const collectionsRepository = new CollectionsRepository()
6+
7+
const getCollection = new GetCollection(collectionsRepository)
8+
9+
export { getCollection }
10+
export { Collection } from './domain/models/Collection'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
2+
import { ICollectionsRepository } from '../../domain/repositories/ICollectionsRepository'
3+
import { transformCollectionResponseToCollection } from './transformers/collectionTransformers'
4+
import { Collection, ROOT_COLLECTION_ALIAS } from '../../domain/models/Collection'
5+
export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
6+
private readonly collectionsResourceName: string = 'dataverses'
7+
private readonly collectionsDefaultOperationType: string = 'get'
8+
9+
public async getCollection(
10+
collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS
11+
): Promise<Collection> {
12+
return this.doGet(
13+
this.buildApiEndpoint(
14+
this.collectionsResourceName,
15+
this.collectionsDefaultOperationType,
16+
collectionIdOrAlias
17+
),
18+
true,
19+
{ returnOwners: true }
20+
)
21+
.then((response) => transformCollectionResponseToCollection(response))
22+
.catch((error) => {
23+
throw error
24+
})
25+
}
26+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload'
2+
export interface CollectionPayload {
3+
id: number
4+
alias: string
5+
name: string
6+
affiliation?: string
7+
description?: string
8+
isPartOf: OwnerNodePayload
9+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Collection } from '../../../domain/models/Collection'
2+
import { AxiosResponse } from 'axios'
3+
import { CollectionPayload } from './CollectionPayload'
4+
import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories/transformers/dvObjectOwnerNodeTransformer'
5+
6+
export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => {
7+
const collectionPayload = response.data.data
8+
return transformPayloadToCollection(collectionPayload)
9+
}
10+
11+
const transformPayloadToCollection = (collectionPayload: CollectionPayload): Collection => {
12+
const collectionModel: Collection = {
13+
id: collectionPayload.id,
14+
alias: collectionPayload.alias,
15+
name: collectionPayload.name,
16+
affiliation: collectionPayload.affiliation,
17+
description: collectionPayload.description,
18+
...(collectionPayload.isPartOf && {
19+
isPartOf: transformPayloadToOwnerNode(collectionPayload.isPartOf)
20+
})
21+
}
22+
return collectionModel
23+
}

src/core/infra/repositories/ApiRepository.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ export abstract class ApiRepository {
3939
operation: string,
4040
resourceId: number | string = undefined
4141
) {
42-
let endpoint
43-
if (typeof resourceId === 'number') {
44-
endpoint = `/${resourceName}/${resourceId}/${operation}`
45-
} else if (typeof resourceId === 'string') {
46-
endpoint = `/${resourceName}/:persistentId/${operation}?persistentId=${resourceId}`
47-
} else {
48-
endpoint = `/${resourceName}/${operation}`
42+
if (resourceName === 'dataverses') {
43+
return `/${resourceName}/${resourceId}`
4944
}
50-
return endpoint
45+
46+
return typeof resourceId === 'number'
47+
? `/${resourceName}/${resourceId}/${operation}`
48+
: typeof resourceId === 'string'
49+
? `/${resourceName}/:persistentId/${operation}?persistentId=${resourceId}`
50+
: `/${resourceName}/${operation}`
5151
}
5252

5353
private buildRequestConfig(authRequired: boolean, queryParams: object): AxiosRequestConfig {
@@ -61,7 +61,7 @@ export abstract class ApiRepository {
6161
switch (ApiConfig.dataverseApiAuthMechanism) {
6262
case DataverseApiAuthMechanism.SESSION_COOKIE:
6363
/*
64-
We set { withCredentials: true } to send the JSESSIONID cookie in the requests for API authentication.
64+
We set { withCredentials: true } to send the JSESSIONID cookie in the requests for API authentication.
6565
This is required, along with the session auth feature flag enabled in the backend, to be able to authenticate using the JSESSIONID cookie.
6666
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).
6767
*/

0 commit comments

Comments
 (0)