Skip to content

Commit 3c135eb

Browse files
authored
Merge pull request #162 from IQSS/156-create-collection-second-part
Extended create collection use case
2 parents a3e1c0e + eee6302 commit 3c135eb

File tree

14 files changed

+341
-14
lines changed

14 files changed

+341
-14
lines changed

docs/useCases.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ The different use cases currently available in the package are classified below,
1111
- [Collections](#Collections)
1212
- [Collections read use cases](#collections-read-use-cases)
1313
- [Get a Collection](#get-a-collection)
14+
- [Get Collection Facets](#get-collection-facets)
1415
- [Collections write use cases](#collections-write-use-cases)
1516
- [Create a Collection](#create-a-collection)
1617
- [Datasets](#Datasets)
@@ -100,6 +101,33 @@ The `collectionIdOrAlias` is a generic collection identifier, which can be eithe
100101

101102
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.
102103

104+
#### Get Collection Facets
105+
106+
Returns the names of the configured collection facets, given a collection identifier or alias.
107+
108+
##### Example call:
109+
110+
```typescript
111+
import { getCollectionFacets } from '@iqss/dataverse-client-javascript'
112+
113+
const collectionIdOrAlias = 12345
114+
115+
getCollectionFacets
116+
.execute(collectionId)
117+
.then((facets: string[]) => {
118+
/* ... */
119+
})
120+
.catch((error: Error) => {
121+
/* ... */
122+
})
123+
```
124+
125+
_See [use case](../src/collections/domain/useCases/GetCollectionFacets.ts)_ definition.
126+
127+
The `collectionIdOrAlias` is a generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId).
128+
129+
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.
130+
103131
### Collections Write Use Cases
104132

105133
#### Create a Collection

src/collections/domain/dtos/CollectionDTO.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ export interface CollectionDTO {
33
name: string
44
contacts: string[]
55
type: CollectionType
6+
affiliation?: string
7+
description?: string
8+
metadataBlockNames?: string[]
9+
facetIds?: string[]
10+
inputLevels?: CollectionInputLevelDTO[]
11+
}
12+
13+
export interface CollectionInputLevelDTO {
14+
datasetFieldName: string
15+
include: boolean
16+
required: boolean
617
}
718

819
export enum CollectionType {

src/collections/domain/models/Collection.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DvObjectOwnerNode } from '../../../core'
2+
23
export interface Collection {
34
id: number
45
alias: string
@@ -7,6 +8,13 @@ export interface Collection {
78
affiliation?: string
89
description?: string
910
isPartOf: DvObjectOwnerNode
11+
inputLevels?: CollectionInputLevel[]
12+
}
13+
14+
export interface CollectionInputLevel {
15+
datasetFieldName: string
16+
include: boolean
17+
required: boolean
1018
}
1119

1220
export const ROOT_COLLECTION_ALIAS = 'root'

src/collections/domain/repositories/ICollectionsRepository.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export interface ICollectionsRepository {
77
collectionDTO: CollectionDTO,
88
parentCollectionId: number | string
99
): Promise<number>
10+
getCollectionFacets(collectionIdOrAlias: number | string): Promise<string[]>
1011
}
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 { ROOT_COLLECTION_ALIAS } from '../models/Collection'
4+
5+
export class GetCollectionFacets implements UseCase<string[]> {
6+
private collectionsRepository: ICollectionsRepository
7+
8+
constructor(collectionsRepository: ICollectionsRepository) {
9+
this.collectionsRepository = collectionsRepository
10+
}
11+
12+
/**
13+
* Returns the names of the configured collection facets, given a collection identifier or alias.
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<string[]>}
18+
*/
19+
async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ALIAS): Promise<string[]> {
20+
return await this.collectionsRepository.getCollectionFacets(collectionIdOrAlias)
21+
}
22+
}

src/collections/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { CreateCollection } from './domain/useCases/CreateCollection'
22
import { GetCollection } from './domain/useCases/GetCollection'
3+
import { GetCollectionFacets } from './domain/useCases/GetCollectionFacets'
34

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

67
const collectionsRepository = new CollectionsRepository()
78

89
const getCollection = new GetCollection(collectionsRepository)
910
const createCollection = new CreateCollection(collectionsRepository)
11+
const getCollectionFacets = new GetCollectionFacets(collectionsRepository)
1012

11-
export { getCollection, createCollection }
12-
export { Collection } from './domain/models/Collection'
13-
export { CollectionDTO } from './domain/dtos/CollectionDTO'
13+
export { getCollection, createCollection, getCollectionFacets }
14+
export { Collection, CollectionInputLevel } from './domain/models/Collection'
15+
export { CollectionDTO, CollectionInputLevelDTO } from './domain/dtos/CollectionDTO'

src/collections/infra/repositories/CollectionsRepository.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,25 @@ export interface NewCollectionRequestPayload {
99
name: string
1010
dataverseContacts: NewCollectionContactRequestPayload[]
1111
dataverseType: string
12+
metadataBlocks: NewCollectionMetadataBlocksRequestPayload
1213
}
1314

1415
export interface NewCollectionContactRequestPayload {
1516
contactEmail: string
1617
}
1718

19+
export interface NewCollectionMetadataBlocksRequestPayload {
20+
metadataBlockNames: string[]
21+
facetIds: string[]
22+
inputLevels: NewCollectionInputLevelRequestPayload[]
23+
}
24+
25+
export interface NewCollectionInputLevelRequestPayload {
26+
datasetFieldTypeName: string
27+
include: boolean
28+
required: boolean
29+
}
30+
1831
export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
1932
private readonly collectionsResourceName: string = 'dataverses'
2033

@@ -40,11 +53,23 @@ export class CollectionsRepository extends ApiRepository implements ICollections
4053
})
4154
)
4255

56+
const inputLevelsRequestBody: NewCollectionInputLevelRequestPayload[] =
57+
collectionDTO.inputLevels.map((inputLevel) => ({
58+
datasetFieldTypeName: inputLevel.datasetFieldName,
59+
include: inputLevel.include,
60+
required: inputLevel.required
61+
}))
62+
4363
const requestBody: NewCollectionRequestPayload = {
4464
alias: collectionDTO.alias,
4565
name: collectionDTO.name,
4666
dataverseContacts: dataverseContacts,
47-
dataverseType: collectionDTO.type.toString()
67+
dataverseType: collectionDTO.type,
68+
metadataBlocks: {
69+
metadataBlockNames: collectionDTO.metadataBlockNames,
70+
facetIds: collectionDTO.facetIds,
71+
inputLevels: inputLevelsRequestBody
72+
}
4873
}
4974

5075
return this.doPost(`/${this.collectionsResourceName}/${parentCollectionId}`, requestBody)
@@ -53,4 +78,12 @@ export class CollectionsRepository extends ApiRepository implements ICollections
5378
throw error
5479
})
5580
}
81+
82+
public async getCollectionFacets(collectionIdOrAlias: string | number): Promise<string[]> {
83+
return this.doGet(`/${this.collectionsResourceName}/${collectionIdOrAlias}/facets`, true)
84+
.then((response) => response.data.data)
85+
.catch((error) => {
86+
throw error
87+
})
88+
}
5689
}

src/collections/infra/repositories/transformers/CollectionPayload.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload'
2+
23
export interface CollectionPayload {
34
id: number
45
alias: string
@@ -7,4 +8,11 @@ export interface CollectionPayload {
78
isReleased: string
89
description?: string
910
isPartOf: OwnerNodePayload
11+
inputLevels?: CollectionInputLevelPayload[]
12+
}
13+
14+
export interface CollectionInputLevelPayload {
15+
datasetFieldTypeName: string
16+
required: boolean
17+
include: boolean
1018
}

src/collections/infra/repositories/transformers/collectionTransformers.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { Collection } from '../../../domain/models/Collection'
1+
import { Collection, CollectionInputLevel } from '../../../domain/models/Collection'
22
import { AxiosResponse } from 'axios'
3-
import { CollectionPayload } from './CollectionPayload'
3+
import { CollectionInputLevelPayload, CollectionPayload } from './CollectionPayload'
44
import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories/transformers/dvObjectOwnerNodeTransformer'
55
import { transformHtmlToMarkdown } from '../../../../datasets/infra/repositories/transformers/datasetTransformers'
66

@@ -21,7 +21,20 @@ const transformPayloadToCollection = (collectionPayload: CollectionPayload): Col
2121
}),
2222
...(collectionPayload.isPartOf && {
2323
isPartOf: transformPayloadToOwnerNode(collectionPayload.isPartOf)
24+
}),
25+
...(collectionPayload.inputLevels && {
26+
inputLevels: transformInputLevelsPayloadToInputLevels(collectionPayload.inputLevels)
2427
})
2528
}
2629
return collectionModel
2730
}
31+
32+
const transformInputLevelsPayloadToInputLevels = (
33+
inputLevelsPayload: CollectionInputLevelPayload[]
34+
): CollectionInputLevel[] => {
35+
return inputLevelsPayload.map((inputLevel) => ({
36+
datasetFieldName: inputLevel.datasetFieldTypeName,
37+
include: inputLevel.include,
38+
required: inputLevel.required
39+
}))
40+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { ApiConfig, ReadError, getCollectionFacets } from '../../../src'
2+
import { TestConstants } from '../../testHelpers/TestConstants'
3+
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
4+
import { ROOT_COLLECTION_ALIAS } from '../../../src/collections/domain/models/Collection'
5+
6+
describe('execute', () => {
7+
beforeEach(async () => {
8+
ApiConfig.init(
9+
TestConstants.TEST_API_URL,
10+
DataverseApiAuthMechanism.API_KEY,
11+
process.env.TEST_API_KEY
12+
)
13+
})
14+
15+
test('should return facets when a valid collection alias is provided', async () => {
16+
let actual: string[] = []
17+
try {
18+
actual = await getCollectionFacets.execute(ROOT_COLLECTION_ALIAS)
19+
} catch (error) {
20+
throw new Error('Facets should be retrieved')
21+
} finally {
22+
expect(actual).toContain('authorName')
23+
expect(actual).toContain('subject')
24+
expect(actual).toContain('keywordValue')
25+
expect(actual).toContain('dateOfDeposit')
26+
}
27+
})
28+
29+
test('should throw an error when collection does not exist', async () => {
30+
expect.assertions(2)
31+
let readError: ReadError
32+
try {
33+
await getCollectionFacets.execute(TestConstants.TEST_DUMMY_COLLECTION_ID)
34+
throw new Error('Use case should throw an error')
35+
} catch (error) {
36+
readError = error
37+
} finally {
38+
expect(readError).toBeInstanceOf(ReadError)
39+
expect(readError.message).toEqual(
40+
`There was an error when reading the resource. Reason was: [404] Can't find dataverse with identifier='${TestConstants.TEST_DUMMY_COLLECTION_ID}'`
41+
)
42+
}
43+
})
44+
})

0 commit comments

Comments
 (0)