Skip to content

Commit fee53f8

Browse files
committed
Merge branch 'develop' of https://github.com/IQSS/dataverse-client-javascript into feature/133-add-publish-dataset-use-case
2 parents 022c950 + e0438a2 commit fee53f8

File tree

26 files changed

+479
-67
lines changed

26 files changed

+479
-67
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)
@@ -43,6 +46,54 @@ The different use cases currently available in the package are classified below,
4346
- [Get Maximum Embargo Duration In Months](#get-maximum-embargo-duration-in-months)
4447
- [Get ZIP Download Limit](#get-zip-download-limit)
4548

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

4899
### 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)