Skip to content

Commit 34b9d2f

Browse files
committed
Feat: set/unset default template, and its tests
1 parent 9c7e9d8 commit 34b9d2f

13 files changed

Lines changed: 478 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ This changelog follows the principles of [Keep a Changelog](https://keepachangel
1515
- New Use Case: [Create a Template](./docs/useCases.md#create-a-template) under Templates.
1616
- New Use Case: [Get a Template](./docs/useCases.md#get-a-template) under Templates.
1717
- New Use Case: [Delete a Template](./docs/useCases.md#delete-a-template) under Templates.
18+
- Templates: Added `setDefaultTemplate` use case and repository method to support Dataverse endpoint `POST /dataverses/{id}/template/default/{templateId}`.
19+
- Templates: Added `removeDefaultTemplate` use case and repository method to support Dataverse endpoint `DELETE /dataverses/{id}/template/default`.
1820
- New Use Case: [Update Terms of Access](./docs/useCases.md#update-terms-of-access).
1921

2022
### Changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This package is part of the Dataverse Frontend ecosystem and is intended to be u
1010

1111
- **Use case-centric API functions** – Organized around domain-specific actions like `getDataset`, `createCollection`, or `restrictFile`.
1212
- **TypeScript-first** – All use cases include strong typings for inputs and outputs, improving developer experience.
13+
- **Template defaults management** – Set or unset the default template for a collection.
1314

1415
## Installation
1516

docs/useCases.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ The different use cases currently available in the package are classified below,
3232
- [Templates write use cases](#templates-write-use-cases)
3333
- [Create a Template](#create-a-template)
3434
- [Delete a Template](#delete-a-template)
35+
- [Set Default Template](#set-default-template)
36+
- [Remove Default Template](#remove-default-template)
3537
- [Datasets](#Datasets)
3638
- [Datasets read use cases](#datasets-read-use-cases)
3739
- [Get a Dataset](#get-a-dataset)
@@ -669,6 +671,43 @@ await deleteTemplate.execute(templateId)
669671

670672
_See [use case](../src/templates/domain/useCases/DeleteTemplate.ts)_ definition.
671673

674+
#### Set Default Template
675+
676+
Sets the default template for a given Dataverse collection.
677+
678+
You must have edit permissions on the collection in order to use this endpoint.
679+
680+
##### Example call:
681+
682+
```typescript
683+
import { setDefaultTemplate } from '@iqss/dataverse-client-javascript'
684+
685+
const collectionIdOrAlias = ':root'
686+
const templateId = 12345
687+
688+
await setDefaultTemplate.execute(templateId, collectionIdOrAlias)
689+
```
690+
691+
_See [use case](../src/templates/domain/useCases/SetDefaultTemplate.ts)_ definition.
692+
693+
#### Remove Default Template
694+
695+
Removes the default template from a given Dataverse collection.
696+
697+
You must have edit permissions on the collection in order to use this endpoint.
698+
699+
##### Example call:
700+
701+
```typescript
702+
import { removeDefaultTemplate } from '@iqss/dataverse-client-javascript'
703+
704+
const collectionIdOrAlias = ':root'
705+
706+
await removeDefaultTemplate.execute(collectionIdOrAlias)
707+
```
708+
709+
_See [use case](../src/templates/domain/useCases/RemoveDefaultTemplate.ts)_ definition.
710+
672711
## Datasets
673712

674713
### Datasets Read Use Cases

src/templates/domain/repositories/ITemplatesRepository.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ export interface ITemplatesRepository {
66
getTemplate(templateId: number): Promise<Template>
77
getTemplatesByCollectionId(collectionIdOrAlias: number | string): Promise<Template[]>
88
deleteTemplate(templateId: number): Promise<void>
9+
setDefaultTemplate(collectionIdOrAlias: number | string, templateId: number): Promise<void>
10+
unsetDefaultTemplate(collectionIdOrAlias: number | string): Promise<void>
911
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ROOT_COLLECTION_ID } from '../../../collections/domain/models/Collection'
2+
import { UseCase } from '../../../core/domain/useCases/UseCase'
3+
import { ITemplatesRepository } from '../repositories/ITemplatesRepository'
4+
5+
export class RemoveDefaultTemplate implements UseCase<void> {
6+
private templatesRepository: ITemplatesRepository
7+
8+
constructor(templatesRepository: ITemplatesRepository) {
9+
this.templatesRepository = templatesRepository
10+
}
11+
12+
/**
13+
* Removes the default template for the specified collection.
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+
*/
18+
async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ID): Promise<void> {
19+
return await this.templatesRepository.unsetDefaultTemplate(collectionIdOrAlias)
20+
}
21+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ROOT_COLLECTION_ID } from '../../../collections/domain/models/Collection'
2+
import { UseCase } from '../../../core/domain/useCases/UseCase'
3+
import { ITemplatesRepository } from '../repositories/ITemplatesRepository'
4+
5+
export class SetDefaultTemplate implements UseCase<void> {
6+
private templatesRepository: ITemplatesRepository
7+
8+
constructor(templatesRepository: ITemplatesRepository) {
9+
this.templatesRepository = templatesRepository
10+
}
11+
12+
/**
13+
* Sets the default template for the specified collection.
14+
*
15+
* @param {number} templateId - Template id to set as default.
16+
* @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)
17+
* If this parameter is not set, the default value is: ':root'.
18+
*/
19+
async execute(
20+
templateId: number,
21+
collectionIdOrAlias: number | string = ROOT_COLLECTION_ID
22+
): Promise<void> {
23+
return await this.templatesRepository.setDefaultTemplate(collectionIdOrAlias, templateId)
24+
}
25+
}

src/templates/index.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,26 @@ import { CreateTemplate } from './domain/useCases/CreateTemplate'
33
import { DeleteTemplate } from './domain/useCases/DeleteTemplate'
44
import { GetTemplatesByCollectionId } from './domain/useCases/GetTemplatesByCollectionId'
55
import { GetTemplate } from './domain/useCases/GetTemplate'
6+
import { SetDefaultTemplate } from './domain/useCases/SetDefaultTemplate'
7+
import { RemoveDefaultTemplate } from './domain/useCases/RemoveDefaultTemplate'
68

79
const templatesRepository = new TemplatesRepository()
810

911
const createTemplate = new CreateTemplate(templatesRepository)
1012
const deleteTemplate = new DeleteTemplate(templatesRepository)
1113
const getTemplatesByCollectionId = new GetTemplatesByCollectionId(templatesRepository)
1214
const getTemplate = new GetTemplate(templatesRepository)
15+
const setDefaultTemplate = new SetDefaultTemplate(templatesRepository)
16+
const removeDefaultTemplate = new RemoveDefaultTemplate(templatesRepository)
1317

14-
export { createTemplate, deleteTemplate, getTemplatesByCollectionId, getTemplate }
18+
export {
19+
createTemplate,
20+
deleteTemplate,
21+
getTemplatesByCollectionId,
22+
getTemplate,
23+
setDefaultTemplate,
24+
removeDefaultTemplate
25+
}
1526
export {
1627
CreateTemplateDTO,
1728
TemplateFieldDTO,

src/templates/infra/repositories/TemplatesRepository.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,26 @@ export class TemplatesRepository extends ApiRepository implements ITemplatesRepo
5555
throw error
5656
})
5757
}
58+
59+
public async setDefaultTemplate(
60+
collectionIdOrAlias: number | string,
61+
templateId: number
62+
): Promise<void> {
63+
return this.doPost(
64+
`/${this.collectionsResourceName}/${collectionIdOrAlias}/template/default/${templateId}`,
65+
{}
66+
)
67+
.then(() => undefined)
68+
.catch((error) => {
69+
throw error
70+
})
71+
}
72+
73+
public async unsetDefaultTemplate(collectionIdOrAlias: number | string): Promise<void> {
74+
return this.doDelete(`/${this.collectionsResourceName}/${collectionIdOrAlias}/template/default`)
75+
.then(() => undefined)
76+
.catch((error) => {
77+
throw error
78+
})
79+
}
5880
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { ApiConfig } from '../../../src'
2+
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
3+
import { TestConstants } from '../../testHelpers/TestConstants'
4+
import {
5+
createTemplate,
6+
getTemplatesByCollectionId,
7+
setDefaultTemplate,
8+
removeDefaultTemplate
9+
} from '../../../src/templates'
10+
import { CreateTemplateDTO } from '../../../src/templates/domain/dtos/CreateTemplateDTO'
11+
import { MetadataFieldTypeClass } from '../../../src/metadataBlocks/domain/models/MetadataBlock'
12+
import { deleteDatasetTemplateViaApi } from '../../testHelpers/datasets/datasetTemplatesHelper'
13+
14+
describe('RemoveDefaultTemplate.execute', () => {
15+
const collectionIdOrAlias = ':root'
16+
17+
beforeEach(async () => {
18+
ApiConfig.init(
19+
TestConstants.TEST_API_URL,
20+
DataverseApiAuthMechanism.API_KEY,
21+
process.env.TEST_API_KEY
22+
)
23+
})
24+
25+
test('should remove the default template from a collection', async () => {
26+
const templateName = `TestRemoveDefaultTemplate-${Date.now()}`
27+
const templateDto: CreateTemplateDTO = {
28+
name: templateName,
29+
isDefault: false,
30+
fields: [
31+
{
32+
typeName: 'author',
33+
typeClass: MetadataFieldTypeClass.Compound,
34+
multiple: true,
35+
value: [
36+
{
37+
authorName: {
38+
typeName: 'authorName',
39+
typeClass: MetadataFieldTypeClass.Primitive,
40+
value: 'Belicheck, Bill'
41+
},
42+
authorAffiliation: {
43+
typeName: 'authorIdentifierScheme',
44+
typeClass: MetadataFieldTypeClass.Primitive,
45+
value: 'ORCID'
46+
}
47+
}
48+
]
49+
}
50+
],
51+
instructions: [
52+
{
53+
instructionField: 'author',
54+
instructionText: 'The author data'
55+
}
56+
]
57+
}
58+
59+
const templatesBefore = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
60+
const originalDefaultTemplateId =
61+
templatesBefore.find((template) => template.isDefault)?.id ?? null
62+
63+
await createTemplate.execute(templateDto, collectionIdOrAlias)
64+
const templatesAfterCreate = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
65+
const createdTemplate = templatesAfterCreate.find((template) => template.name === templateName)
66+
67+
if (!createdTemplate) {
68+
throw new Error('Created template was not found in collection templates.')
69+
}
70+
71+
await setDefaultTemplate.execute(createdTemplate.id, collectionIdOrAlias)
72+
await removeDefaultTemplate.execute(collectionIdOrAlias)
73+
74+
const templatesAfterRemove = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
75+
const hasDefaultTemplate = templatesAfterRemove.some((template) => template.isDefault)
76+
77+
expect(hasDefaultTemplate).toBe(false)
78+
79+
if (originalDefaultTemplateId && originalDefaultTemplateId !== createdTemplate.id) {
80+
await setDefaultTemplate.execute(originalDefaultTemplateId, collectionIdOrAlias)
81+
}
82+
83+
await deleteDatasetTemplateViaApi(createdTemplate.id)
84+
})
85+
})
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { ApiConfig } from '../../../src'
2+
import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig'
3+
import { TestConstants } from '../../testHelpers/TestConstants'
4+
import {
5+
createTemplate,
6+
getTemplatesByCollectionId,
7+
setDefaultTemplate,
8+
removeDefaultTemplate
9+
} from '../../../src/templates'
10+
import { CreateTemplateDTO } from '../../../src/templates/domain/dtos/CreateTemplateDTO'
11+
import { MetadataFieldTypeClass } from '../../../src/metadataBlocks/domain/models/MetadataBlock'
12+
import { deleteDatasetTemplateViaApi } from '../../testHelpers/datasets/datasetTemplatesHelper'
13+
14+
describe('SetDefaultTemplate.execute', () => {
15+
const collectionIdOrAlias = ':root'
16+
17+
beforeEach(async () => {
18+
ApiConfig.init(
19+
TestConstants.TEST_API_URL,
20+
DataverseApiAuthMechanism.API_KEY,
21+
process.env.TEST_API_KEY
22+
)
23+
})
24+
25+
test('should set the default template for a collection', async () => {
26+
const templateName = `TestDefaultTemplate-${Date.now()}`
27+
const templateDto: CreateTemplateDTO = {
28+
name: templateName,
29+
isDefault: false,
30+
fields: [
31+
{
32+
typeName: 'author',
33+
typeClass: MetadataFieldTypeClass.Compound,
34+
multiple: true,
35+
value: [
36+
{
37+
authorName: {
38+
typeName: 'authorName',
39+
typeClass: MetadataFieldTypeClass.Primitive,
40+
value: 'Belicheck, Bill'
41+
},
42+
authorAffiliation: {
43+
typeName: 'authorIdentifierScheme',
44+
typeClass: MetadataFieldTypeClass.Primitive,
45+
value: 'ORCID'
46+
}
47+
}
48+
]
49+
}
50+
],
51+
instructions: [
52+
{
53+
instructionField: 'author',
54+
instructionText: 'The author data'
55+
}
56+
]
57+
}
58+
59+
const templatesBefore = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
60+
const originalDefaultTemplateId =
61+
templatesBefore.find((template) => template.isDefault)?.id ?? null
62+
63+
await createTemplate.execute(templateDto, collectionIdOrAlias)
64+
const templatesAfterCreate = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
65+
const createdTemplate = templatesAfterCreate.find((template) => template.name === templateName)
66+
67+
if (!createdTemplate) {
68+
throw new Error('Created template was not found in collection templates.')
69+
}
70+
71+
await setDefaultTemplate.execute(createdTemplate.id, collectionIdOrAlias)
72+
73+
const templatesAfterSet = await getTemplatesByCollectionId.execute(collectionIdOrAlias)
74+
const updatedTemplate = templatesAfterSet.find((template) => template.id === createdTemplate.id)
75+
76+
expect(updatedTemplate?.isDefault).toBe(true)
77+
78+
if (originalDefaultTemplateId && originalDefaultTemplateId !== createdTemplate.id) {
79+
await setDefaultTemplate.execute(originalDefaultTemplateId, collectionIdOrAlias)
80+
} else if (!originalDefaultTemplateId) {
81+
await removeDefaultTemplate.execute(collectionIdOrAlias)
82+
}
83+
84+
await deleteDatasetTemplateViaApi(createdTemplate.id)
85+
})
86+
})

0 commit comments

Comments
 (0)