├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── copy_labels.yml │ ├── deploy_alpha.yml │ ├── deploy_pr.yml │ └── lint.yml ├── .gitignore ├── .npmignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── installation.md ├── localDevelopment.md └── useCases.md ├── jest.config.functional.ts ├── jest.config.integration.ts ├── jest.config.ts ├── jest.config.unit.ts ├── package-lock.json ├── package.json ├── src ├── auth │ ├── domain │ │ ├── repositories │ │ │ └── IAuthRepository.ts │ │ └── useCases │ │ │ └── Logout.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ └── AuthRepository.ts ├── collections │ ├── domain │ │ ├── dtos │ │ │ ├── CollectionDTO.ts │ │ │ └── CollectionFeaturedItemsDTO.ts │ │ ├── models │ │ │ ├── Collection.ts │ │ │ ├── CollectionContact.ts │ │ │ ├── CollectionFacet.ts │ │ │ ├── CollectionFeaturedItem.ts │ │ │ ├── CollectionItemSubset.ts │ │ │ ├── CollectionItemType.ts │ │ │ ├── CollectionPreview.ts │ │ │ ├── CollectionSearchCriteria.ts │ │ │ ├── CollectionType.ts │ │ │ ├── CollectionUserPermissions.ts │ │ │ └── MyDataCollectionItemSubset.ts │ │ ├── repositories │ │ │ └── ICollectionsRepository.ts │ │ └── useCases │ │ │ ├── CreateCollection.ts │ │ │ ├── DeleteCollection.ts │ │ │ ├── DeleteCollectionFeaturedItems.ts │ │ │ ├── GetCollection.ts │ │ │ ├── GetCollectionFacets.ts │ │ │ ├── GetCollectionFeaturedItems.ts │ │ │ ├── GetCollectionItems.ts │ │ │ ├── GetCollectionUserPermissions.ts │ │ │ ├── GetMyDataCollectionItems.ts │ │ │ ├── PublishCollection.ts │ │ │ ├── UpdateCollection.ts │ │ │ └── UpdateCollectionFeaturedItems.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ ├── CollectionsRepository.ts │ │ └── transformers │ │ ├── CollectionFacetPayload.ts │ │ ├── CollectionFeaturedItemPayload.ts │ │ ├── CollectionItemsCountPerObjectTypePayload.ts │ │ ├── CollectionItemsFacetsPayload.ts │ │ ├── CollectionPayload.ts │ │ ├── CollectionPreviewPayload.ts │ │ ├── CollectionUserPermissionsPayload.ts │ │ ├── MyDataCollectionPreviewPayload.ts │ │ ├── MyDataCountPerObjectTypePayload.ts │ │ ├── MyDataPublicationStatusCountsPayload.ts │ │ ├── collectionFeaturedItemsTransformer.ts │ │ ├── collectionPreviewsTransformers.ts │ │ ├── collectionTransformers.ts │ │ └── collectionUserPermissionsTransformers.ts ├── contactInfo │ ├── domain │ │ ├── dtos │ │ │ └── ContactDTO.ts │ │ ├── models │ │ │ └── Contact.ts │ │ ├── repositories │ │ │ └── IContactRepository.ts │ │ └── useCases │ │ │ └── SubmitContactInfo.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ └── ContactRepository.ts ├── core │ ├── domain │ │ ├── models │ │ │ ├── DvObjectOwnerNode.ts │ │ │ └── PublicationStatus.ts │ │ ├── repositories │ │ │ ├── ReadError.ts │ │ │ ├── RepositoryError.ts │ │ │ └── WriteError.ts │ │ └── useCases │ │ │ ├── UseCase.ts │ │ │ └── validators │ │ │ ├── ResourceValidator.ts │ │ │ └── errors │ │ │ └── ResourceValidationError.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ ├── ApiConfig.ts │ │ ├── ApiConstants.ts │ │ ├── ApiRepository.ts │ │ ├── apiConfigBuilders.ts │ │ └── transformers │ │ ├── OwnerNodePayload.ts │ │ └── dvObjectOwnerNodeTransformer.ts ├── datasets │ ├── domain │ │ ├── dtos │ │ │ ├── DatasetDTO.ts │ │ │ └── DatasetDeaccessionDTO.ts │ │ ├── models │ │ │ ├── CreatedDatasetIdentifiers.ts │ │ │ ├── Dataset.ts │ │ │ ├── DatasetDownloadCount.ts │ │ │ ├── DatasetLock.ts │ │ │ ├── DatasetNotNumberedVersion.ts │ │ │ ├── DatasetPreview.ts │ │ │ ├── DatasetPreviewSubset.ts │ │ │ ├── DatasetUserPermissions.ts │ │ │ ├── DatasetVersionDiff.ts │ │ │ └── DatasetVersionSummaryInfo.ts │ │ ├── repositories │ │ │ └── IDatasetsRepository.ts │ │ └── useCases │ │ │ ├── CreateDataset.ts │ │ │ ├── DatasetWriteUseCase.ts │ │ │ ├── DeaccessionDataset.ts │ │ │ ├── DeleteDatasetDraft.ts │ │ │ ├── GetAllDatasetPreviews.ts │ │ │ ├── GetDataset.ts │ │ │ ├── GetDatasetCitation.ts │ │ │ ├── GetDatasetDownloadCount.ts │ │ │ ├── GetDatasetLocks.ts │ │ │ ├── GetDatasetSummaryFieldNames.ts │ │ │ ├── GetDatasetUserPermissions.ts │ │ │ ├── GetDatasetVersionDiff.ts │ │ │ ├── GetDatasetVersionsSummaries.ts │ │ │ ├── GetPrivateUrlDataset.ts │ │ │ ├── GetPrivateUrlDatasetCitation.ts │ │ │ ├── PublishDataset.ts │ │ │ ├── UpdateDataset.ts │ │ │ └── validators │ │ │ ├── BaseMetadataFieldValidator.ts │ │ │ ├── DatasetResourceValidator.ts │ │ │ ├── MetadataFieldValidator.ts │ │ │ ├── MultipleMetadataFieldValidator.ts │ │ │ ├── SingleMetadataFieldValidator.ts │ │ │ └── errors │ │ │ ├── ControlledVocabularyFieldError.ts │ │ │ ├── DateFormatFieldError.ts │ │ │ ├── EmptyFieldError.ts │ │ │ └── FieldValidationError.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ ├── DatasetsRepository.ts │ │ └── transformers │ │ ├── DatasetPayload.ts │ │ ├── DatasetPreviewPayload.ts │ │ ├── DatasetVersionDiffPayload.ts │ │ ├── MyDataDatasetPreviewPayload.ts │ │ ├── datasetLocksTransformers.ts │ │ ├── datasetPreviewsTransformers.ts │ │ ├── datasetTransformers.ts │ │ ├── datasetUserPermissionsTransformers.ts │ │ └── datasetVersionDiffTransformers.ts ├── files │ ├── domain │ │ ├── clients │ │ │ ├── DirectUploadClientError.ts │ │ │ └── IDirectUploadClient.ts │ │ ├── dtos │ │ │ ├── RestrictFileDTO.ts │ │ │ ├── UpdateFileMetadataDTO.ts │ │ │ └── UploadedFileDTO.ts │ │ ├── models │ │ │ ├── FileCounts.ts │ │ │ ├── FileCriteria.ts │ │ │ ├── FileDataTable.ts │ │ │ ├── FileDownloadSizeMode.ts │ │ │ ├── FileModel.ts │ │ │ ├── FilePreview.ts │ │ │ ├── FileUploadDestination.ts │ │ │ ├── FileUserPermissions.ts │ │ │ ├── FileVersionSummaryInfo.ts │ │ │ └── FilesSubset.ts │ │ ├── repositories │ │ │ └── IFilesRepository.ts │ │ └── useCases │ │ │ ├── AddUploadedFilesToDataset.ts │ │ │ ├── DeleteFile.ts │ │ │ ├── GetDatasetFileCounts.ts │ │ │ ├── GetDatasetFiles.ts │ │ │ ├── GetDatasetFilesTotalDownloadSize.ts │ │ │ ├── GetFile.ts │ │ │ ├── GetFileAndDataset.ts │ │ │ ├── GetFileCitation.ts │ │ │ ├── GetFileDataTables.ts │ │ │ ├── GetFileDownloadCount.ts │ │ │ ├── GetFileUserPermissions.ts │ │ │ ├── GetFileVersionSummaries.ts │ │ │ ├── IsFileDeleted.ts │ │ │ ├── ReplaceFile.ts │ │ │ ├── RestrictFile.ts │ │ │ ├── UpdateFileCategories.ts │ │ │ ├── UpdateFileMetadata.ts │ │ │ ├── UpdateFileTabularTags.ts │ │ │ └── UploadFile.ts │ ├── index.ts │ └── infra │ │ ├── clients │ │ ├── DirectUploadClient.ts │ │ └── errors │ │ │ ├── AddUploadedFileToDatasetError.ts │ │ │ ├── FilePartUploadError.ts │ │ │ ├── FileUploadCancelError.ts │ │ │ ├── FileUploadError.ts │ │ │ ├── MultipartAbortError.ts │ │ │ ├── MultipartCompletionError.ts │ │ │ └── UrlGenerationError.ts │ │ └── repositories │ │ ├── FilesRepository.ts │ │ └── transformers │ │ ├── FilePayload.ts │ │ ├── FilePreviewPayload.ts │ │ ├── MyDataFilePreviewPayload.ts │ │ ├── fileCountsTransformers.ts │ │ ├── fileDataTableTransformers.ts │ │ ├── filePreviewTransformers.ts │ │ ├── fileTransformers.ts │ │ ├── fileUploadDestinationsTransformers.ts │ │ ├── fileUserPermissionsTransformers.ts │ │ └── fileVersionSummaryInfoTransformers.ts ├── index.ts ├── info │ ├── domain │ │ ├── models │ │ │ └── DataverseVersion.ts │ │ ├── repositories │ │ │ └── IDataverseInfoRepository.ts │ │ └── useCases │ │ │ ├── GetApplicationTermsOfUse.ts │ │ │ ├── GetDataverseVersion.ts │ │ │ ├── GetMaxEmbargoDurationInMonths.ts │ │ │ └── GetZipDownloadLimit.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ └── DataverseInfoRepository.ts ├── metadataBlocks │ ├── domain │ │ ├── models │ │ │ └── MetadataBlock.ts │ │ ├── repositories │ │ │ ├── IMetadataBlocksRepository.ts │ │ │ └── IMetadataFieldInfosRepository.ts │ │ └── useCases │ │ │ ├── GetAllFacetableMetadataFields.ts │ │ │ ├── GetAllMetadataBlocks.ts │ │ │ ├── GetCollectionMetadataBlocks.ts │ │ │ └── GetMetadataBlockByName.ts │ ├── index.ts │ └── infra │ │ └── repositories │ │ ├── MetadataBlocksRepository.ts │ │ ├── MetadataFieldInfosRepository.ts │ │ └── transformers │ │ ├── MetadataBlockPayload.ts │ │ ├── MetadataFieldInfoPayload.ts │ │ └── metadataBlockTransformers.ts └── users │ ├── domain │ ├── dtos │ │ └── UserDTO.ts │ ├── models │ │ ├── ApiTokenInfo.ts │ │ └── AuthenticatedUser.ts │ ├── repositories │ │ └── IUsersRepository.ts │ └── useCases │ │ ├── DeleteCurrentApiToken.ts │ │ ├── GetCurrentApiToken.ts │ │ ├── GetCurrentAuthenticatedUser.ts │ │ ├── RecreateCurrentApiToken.ts │ │ └── RegisterUser.ts │ ├── index.ts │ └── infra │ └── repositories │ ├── UsersRepository.ts │ └── transformers │ └── apiTokenInfoTransformers.ts ├── test ├── environment │ ├── .env │ ├── .gitignore │ ├── conf │ │ └── localstack │ │ │ └── buckets.sh │ ├── docker-compose.yml │ └── setup.ts ├── functional │ ├── collections │ │ ├── CreateCollection.test.ts │ │ ├── DeleteCollection.test.ts │ │ ├── DeleteCollectionFeaturedItems.test.ts │ │ ├── GetCollectionFacets.test.ts │ │ ├── GetCollectionFeaturedItems.test.ts │ │ ├── GetCollectionItems.test.ts │ │ ├── GetCollectionUserPermissions.test.ts │ │ ├── GetMyDataCollectionItems.test.ts │ │ ├── PublishCollection.test.ts │ │ ├── UpdateCollection.test.ts │ │ └── UpdateCollectionFeaturedItems.test.ts │ ├── contact │ │ └── SubmitContactInfo.test.ts │ ├── datasets │ │ ├── CreateDataset.test.ts │ │ ├── DeaccessionDataset.test.ts │ │ ├── DeleteDatasetDraft.test.ts │ │ ├── GetDataset.test.ts │ │ ├── PublishDataset.test.ts │ │ └── UpdateDataset.test.ts │ ├── files │ │ ├── DeleteFile.test.ts │ │ ├── RestrictFile.test.ts │ │ ├── UpdateFileCategories.test.ts │ │ ├── UpdateFileMetadata.test.ts │ │ └── UpdateFileTabularTags.test.ts │ ├── metadataBlocks │ │ ├── GetAllFacetableMetadataFields.test.ts │ │ ├── GetAllMetadataBlocks.test.ts │ │ └── GetCollectionMetadataBlocks.test.ts │ └── users │ │ ├── DeleteCurrentApiToken.test.ts │ │ ├── GetCurrentApiToken.test.ts │ │ └── RecreateCurrentApiToken.test.ts ├── integration │ ├── auth │ │ └── AuthRepository.test.ts │ ├── collections │ │ └── CollectionsRepository.test.ts │ ├── contact │ │ └── ContactRepository.test.ts │ ├── datasets │ │ └── DatasetsRepository.test.ts │ ├── files │ │ ├── DirectUpload.test.ts │ │ └── FilesRepository.test.ts │ ├── info │ │ └── DataverseInfoRepository.test.ts │ ├── metadataBlocks │ │ ├── MetadataBlocksRepository.test.ts │ │ └── MetadataFieldsInfoRepository.test.ts │ └── users │ │ └── UsersRepository.test.ts ├── testHelpers │ ├── TestConstants.ts │ ├── collections │ │ ├── collectionFeaturedItemsHelper.ts │ │ ├── collectionHelper.ts │ │ ├── collectionItemsFacetsHelper.ts │ │ ├── collectionPreviewHelper.ts │ │ └── collectionUserPermissionsHelper.ts │ ├── datasets │ │ ├── datasetHelper.ts │ │ ├── datasetLockHelper.ts │ │ ├── datasetPreviewHelper.ts │ │ ├── datasetUserPermissionsHelper.ts │ │ ├── datasetVersionDiffHelper.ts │ │ └── datasetVersionsSummariesHelper.ts │ ├── files │ │ ├── fileCountsHelper.ts │ │ ├── fileDataTablesHelper.ts │ │ ├── filePreviewHelper.ts │ │ ├── fileUploadDestinationHelper.ts │ │ ├── fileUserPermissionsHelper.ts │ │ ├── filesHelper.ts │ │ ├── filesTotalDownloadSizeHelper.ts │ │ ├── test-file-1.txt │ │ ├── test-file-2.txt │ │ ├── test-file-3.txt │ │ └── test-file-4.tab │ ├── info │ │ └── infoHelper.ts │ ├── metadataBlocks │ │ └── metadataBlockHelper.ts │ └── users │ │ ├── apiTokenHelper.ts │ │ ├── authenticatedUserHelper.ts │ │ └── builtinUserApiHelper.ts └── unit │ ├── .DS_Store │ ├── auth │ ├── AuthRepository.test.ts │ ├── BearerTokenMechanism.test.ts │ └── Logout.test.ts │ ├── collections │ ├── CollectionsRepository.test.ts │ ├── CreateCollection.test.ts │ ├── DeleteCollection.test.ts │ ├── DeleteCollectionFeaturedItems.test.ts │ ├── GetCollection.test.ts │ ├── GetCollectionFacets.test.ts │ ├── GetCollectionFeaturedItems.test.ts │ ├── GetCollectionItems.test.ts │ ├── GetCollectionUserPermissions.test.ts │ ├── GetMyDataCollectionItems.test.ts │ ├── PublishCollection.test.ts │ ├── UpdateCollection.test.ts │ └── UpdateCollectionFeaturedItems.test.ts │ ├── contact │ └── SubmitContactInfo.test.ts │ ├── datasets │ ├── CreateDataset.test.ts │ ├── DatasetResourceValidator.test.ts │ ├── DatasetsRepository.test.ts │ ├── DeaccessionDataset.test.ts │ ├── DeleteDatasetDraft.test.ts │ ├── GetAllDatasetPreviews.test.ts │ ├── GetDataset.test.ts │ ├── GetDatasetCitation.test.ts │ ├── GetDatasetDownloadCount.test.ts │ ├── GetDatasetLocks.test.ts │ ├── GetDatasetSummaryFieldNames.test.ts │ ├── GetDatasetUserPermissions.test.ts │ ├── GetDatasetVersionDiff.test.ts │ ├── GetDatasetVersionsSummaries.test.ts │ ├── GetPrivateUrlDataset.test.ts │ ├── GetPrivateUrlDatasetCitation.test.ts │ ├── PublishDataset.test.ts │ ├── UpdateDataset.test.ts │ └── datasetTransformers.test.ts │ ├── files │ ├── AddUploadedFileToDataset.test.ts │ ├── DeleteFile.test.ts │ ├── DirectUploadClient.test.ts │ ├── FilesRepository.test.ts │ ├── GetDatasetFileCounts.test.ts │ ├── GetDatasetFiles.test.ts │ ├── GetDatasetFilesTotalDownloadSize.test.ts │ ├── GetFile.test.ts │ ├── GetFileAndDataset.test.ts │ ├── GetFileCitation.test.ts │ ├── GetFileDataTables.test.ts │ ├── GetFileDownloadCount.test.ts │ ├── GetFileUserPermissions.test.ts │ ├── GetFileVersionSummaries.test.ts │ ├── IsFileDeleted.test.ts │ ├── ReplaceFile.test.ts │ ├── RestrictFile.test.ts │ ├── UpdateFileCategories.test.ts │ ├── UpdateFileMetadata.test.ts │ ├── UpdateFileTabularTags.test.ts │ └── UploadFile.test.ts │ ├── info │ ├── DataverseInfoRepository.test.ts │ ├── GetApplicationTermsOfUse.test.ts │ ├── GetDataverseVersion.test.ts │ ├── GetMaxEmbargoDurationInMonths.test.ts │ └── GetZipDownloadLimit.test.ts │ ├── metadataBlocks │ ├── GetAllFacetableMetadataFields.test.ts │ ├── GetAllMetadataBlocks.test.ts │ ├── GetCollectionMetadataBlocks.test.ts │ ├── GetMetadataBlockByName.test.ts │ ├── MetadataBlocksRepository.test.ts │ └── MetadataFieldsInfoRepository.test.ts │ └── users │ ├── DeleteCurrentApiToken.test.ts │ ├── GetCurrentApiToken.test.ts │ ├── GetCurrentAuthenticatedUser.test.ts │ ├── RecreateCurrentApiToken.test.ts │ ├── RegisterUser.test.ts │ └── UsersRepository.test.ts ├── tsconfig.json └── tsconfig.tests.json /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | docs -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | ## What steps does it take to reproduce the issue? 2 | 3 | ## When does this issue occur? 4 | 5 | ## What happens? 6 | 7 | ## What did you expect to happen? 8 | 9 | ## Which version of js-dataverse are you using? 10 | 11 | ## Any related open or closed issues to this bug report? 12 | 13 | ## Screenshots: 14 | 15 | No matter the issue, screenshots are always welcome. 16 | 17 | To add a screenshot, please use one of the following formats and/or methods described here: 18 | 19 | - https://help.github.com/en/articles/file-attachments-on-issues-and-pull-requests 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | ## Overview of the feature request 2 | 3 | ## What inspired the request? 4 | 5 | ## What existing behavior do you want changed? 6 | 7 | ## Any brand new behavior do you want to add to js-dataverse? 8 | 9 | ## Any open or closed issues related to this feature request? 10 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## What this PR does / why we need it: 2 | 3 | ## Which issue(s) this PR closes: 4 | 5 | - Closes # 6 | 7 | ## Related Dataverse PRs: 8 | 9 | - Depends on # 10 | 11 | ## Special notes for your reviewer: 12 | 13 | ## Suggestions on how to test this: 14 | 15 | ## Is there a release notes update needed for this change?: 16 | 17 | ## Additional documentation: 18 | -------------------------------------------------------------------------------- /.github/workflows/copy_labels.yml: -------------------------------------------------------------------------------- 1 | name: Copy labels from issue to pull request 2 | 3 | on: 4 | pull_request: 5 | types: [opened] 6 | 7 | jobs: 8 | copy-labels: 9 | runs-on: ubuntu-latest 10 | name: Copy labels from linked issues 11 | steps: 12 | - name: copy-labels 13 | uses: michalvankodev/copy-issue-labels@v1.3.0 14 | with: 15 | repo-token: ${{ secrets.GITHUB_TOKEN }} 16 | labels-to-exclude: | 17 | Size: 3 18 | Size: 10 19 | Size: 20 20 | Size: 33 21 | Size: 80 22 | Original size: 3 23 | Original size: 10 24 | Original size: 20 25 | Original size: 33 26 | Original size: 80 27 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: lint 2 | 3 | on: push 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: actions/setup-node@v3 11 | with: 12 | node-version: 19 13 | 14 | - name: Install npm dependencies 15 | run: npm ci 16 | 17 | - name: Type check 18 | run: npm run typecheck 19 | 20 | - name: ESLint 21 | run: npm run lint:eslint 22 | 23 | - name: Prettier 24 | run: npm run lint:prettier 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | 4 | # intellij 5 | .idea 6 | 7 | # Visual Studio 8 | /.vscode 9 | 10 | # unit tests 11 | coverage 12 | 13 | # ignore npm lock 14 | package-json.lock 15 | .npmrc -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | lib/ 2 | coverage -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "tabWidth": 2, 4 | "printWidth": 100, 5 | "singleQuote": true, 6 | "trailingComma": "none", 7 | "bracketSameLine": true 8 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to js-dataverse 2 | 3 | First of all thank you very much for your interest in contributing to this project! 4 | 5 | ## Getting started 6 | 7 | 1. Fork the repository and clone your fork locally 8 | 2. Follow the [Local Development](./docs/localDevelopment.md) guide for setting up your local development environment 9 | 3. Create a branch and apply the desired changes on it 10 | 4. Create a pull request from your fork branch targeting the develop branch of the root repository 11 | 12 | ## Checklist before creating PR 13 | 14 | - Project builds 15 | - Lint and format checks pass 16 | - Unit and integration tests pass 17 | - Unit and integration tests for new functionality/fix are added 18 | - Documentation is updated (Any new use case added or modified should be documented in the [Use Cases](./docs/useCases.md) section) 19 | 20 | ## Code of Conduct 21 | 22 | We abide by the upstream Code of Conduct at https://github.com/IQSS/dataverse/blob/develop/CODE_OF_CONDUCT.md and in addition ask the following. 23 | 24 | ### Git 25 | 26 | - Branch names are self descriptive 27 | - Commit messages are short and concise 28 | - Branch is put up to date before creating PR 29 | 30 | ### Our responsibilities 31 | 32 | - To keep the code clean 33 | - To provide constructive feedback to other developers 34 | - To maintain readable code at all times 35 | 36 | ## Getting help 37 | 38 | Please, do not hesitate to contact us through: 39 | 40 | - Zulip: https://dataverse.zulipchat.com/#narrow/stream/410361-ui-dev 41 | - Google Group: https://groups.google.com/g/dataverse-dev 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 dell-research-harvard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-dataverse 2 | 3 | [![npm](https://img.shields.io/npm/v/js-dataverse.svg)](https://www.npmjs.com/package/js-dataverse) 4 | 5 | A JavaScript/TypeScript API wrapper for [Dataverse](http://guides.dataverse.org/en/latest/api/). 6 | 7 | - [Installation](./docs/installation.md) 8 | - [Use Cases](./docs/useCases.md) 9 | - [Local Development](./docs/localDevelopment.md) 10 | - [Contributing](./CONTRIBUTING.md) 11 | - [License](./LICENSE) 12 | -------------------------------------------------------------------------------- /jest.config.functional.ts: -------------------------------------------------------------------------------- 1 | import config from './jest.config' 2 | 3 | config.modulePathIgnorePatterns = ['/test/unit', '/test/integration'] 4 | console.log('RUNNING FUNCTIONAL TESTS') 5 | 6 | export default config 7 | -------------------------------------------------------------------------------- /jest.config.integration.ts: -------------------------------------------------------------------------------- 1 | import config from './jest.config' 2 | 3 | config.modulePathIgnorePatterns = ['/test/unit', '/test/functional'] 4 | console.log('RUNNING INTEGRATION TESTS') 5 | 6 | export default config 7 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'jest' 2 | 3 | const config: Config = { 4 | roots: ['/test'], 5 | transform: { 6 | '^.+\\.ts$': 'ts-jest' 7 | }, 8 | testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.ts$', 9 | moduleFileExtensions: ['ts', 'js', 'json', 'node'], 10 | coveragePathIgnorePatterns: ['node_modules', 'testHelpers'], 11 | globalSetup: '/test/environment/setup.ts', 12 | testTimeout: 35000, 13 | coverageThreshold: { 14 | global: { 15 | branches: 90, 16 | functions: 95, 17 | lines: 95, 18 | statements: 95 19 | } 20 | } 21 | } 22 | 23 | export default config 24 | -------------------------------------------------------------------------------- /jest.config.unit.ts: -------------------------------------------------------------------------------- 1 | import config from './jest.config' 2 | 3 | config.modulePathIgnorePatterns = ['/test/integration', '/test/functional'] 4 | delete config.globalSetup 5 | delete config.testTimeout 6 | console.log('RUNNING UNIT TESTS') 7 | 8 | export default config 9 | -------------------------------------------------------------------------------- /src/auth/domain/repositories/IAuthRepository.ts: -------------------------------------------------------------------------------- 1 | export interface IAuthRepository { 2 | logout(): Promise 3 | } 4 | -------------------------------------------------------------------------------- /src/auth/domain/useCases/Logout.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IAuthRepository } from '../repositories/IAuthRepository' 3 | 4 | export class Logout implements UseCase { 5 | private authRepository: IAuthRepository 6 | 7 | constructor(logoutRepository: IAuthRepository) { 8 | this.authRepository = logoutRepository 9 | } 10 | 11 | async execute(): Promise { 12 | await this.authRepository.logout() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/auth/index.ts: -------------------------------------------------------------------------------- 1 | import { AuthRepository } from './infra/repositories/AuthRepository' 2 | import { Logout } from './domain/useCases/Logout' 3 | 4 | const logout = new Logout(new AuthRepository()) 5 | 6 | export { logout } 7 | -------------------------------------------------------------------------------- /src/auth/infra/repositories/AuthRepository.ts: -------------------------------------------------------------------------------- 1 | import { ApiRepository } from '../../../core/infra/repositories/ApiRepository' 2 | import { IAuthRepository } from '../../domain/repositories/IAuthRepository' 3 | 4 | export class AuthRepository extends ApiRepository implements IAuthRepository { 5 | public async logout(): Promise { 6 | return this.doPost('/logout', '') 7 | .then(() => undefined) 8 | .catch((error) => { 9 | throw error 10 | }) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/collections/domain/dtos/CollectionDTO.ts: -------------------------------------------------------------------------------- 1 | import { CollectionType } from '../models/CollectionType' 2 | 3 | export interface CollectionDTO { 4 | alias: string 5 | name: string 6 | contacts: string[] 7 | type: CollectionType 8 | affiliation?: string 9 | description?: string 10 | metadataBlockNames?: string[] 11 | facetIds?: string[] 12 | inputLevels?: CollectionInputLevelDTO[] 13 | inheritMetadataBlocksFromParent: boolean 14 | inheritFacetsFromParent: boolean 15 | } 16 | 17 | export interface CollectionInputLevelDTO { 18 | datasetFieldName: string 19 | include: boolean 20 | required: boolean 21 | } 22 | -------------------------------------------------------------------------------- /src/collections/domain/dtos/CollectionFeaturedItemsDTO.ts: -------------------------------------------------------------------------------- 1 | export type CollectionFeaturedItemsDTO = CollectionFeaturedItemDTO[] 2 | 3 | export interface CollectionFeaturedItemDTO { 4 | id?: number 5 | content: string 6 | displayOrder: number 7 | file?: File 8 | keepFile: boolean 9 | } 10 | -------------------------------------------------------------------------------- /src/collections/domain/models/Collection.ts: -------------------------------------------------------------------------------- 1 | import { DvObjectOwnerNode } from '../../../core' 2 | import { CollectionContact } from './CollectionContact' 3 | import { CollectionType } from './CollectionType' 4 | 5 | export interface Collection { 6 | id: number 7 | alias: string 8 | name: string 9 | isReleased: boolean 10 | affiliation?: string 11 | description?: string 12 | isPartOf: DvObjectOwnerNode 13 | inputLevels?: CollectionInputLevel[] 14 | type: CollectionType 15 | contacts?: CollectionContact[] 16 | isMetadataBlockRoot: boolean 17 | isFacetRoot: boolean 18 | childCount: number 19 | } 20 | 21 | export interface CollectionInputLevel { 22 | datasetFieldName: string 23 | include: boolean 24 | required: boolean 25 | } 26 | 27 | export const ROOT_COLLECTION_ID = ':root' 28 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionContact.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionContact { 2 | email: string 3 | displayOrder: number 4 | } 5 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionFacet.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionFacet { 2 | id: number 3 | name: string 4 | displayName: string 5 | } 6 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionFeaturedItem.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionFeaturedItem { 2 | id: number 3 | content: string 4 | imageFileName?: string 5 | imageFileUrl?: string 6 | displayOrder: number 7 | } 8 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionItemSubset.ts: -------------------------------------------------------------------------------- 1 | import { DatasetPreview } from '../../../datasets' 2 | import { FilePreview } from '../../../files' 3 | import { CollectionPreview } from './CollectionPreview' 4 | 5 | export interface CollectionItemSubset { 6 | items: (CollectionPreview | DatasetPreview | FilePreview)[] 7 | facets: CollectionItemsFacet[] 8 | totalItemCount: number 9 | countPerObjectType?: CountPerObjectType 10 | } 11 | 12 | export interface CollectionItemsFacet { 13 | name: string 14 | friendlyName: string 15 | labels: CollectionItemsFacetLabel[] 16 | } 17 | 18 | export interface CollectionItemsFacetLabel { 19 | name: string 20 | count: number 21 | } 22 | 23 | export interface CountPerObjectType { 24 | collections: number 25 | datasets: number 26 | files: number 27 | } 28 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionItemType.ts: -------------------------------------------------------------------------------- 1 | export enum CollectionItemType { 2 | FILE = 'file', 3 | DATASET = 'dataset', 4 | COLLECTION = 'collection' 5 | } 6 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionPreview.ts: -------------------------------------------------------------------------------- 1 | import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' 2 | import { CollectionItemType } from './CollectionItemType' 3 | 4 | export interface CollectionPreview { 5 | type: CollectionItemType.COLLECTION 6 | name: string 7 | parentName: string 8 | alias: string 9 | parentAlias: string 10 | description: string 11 | affiliation: string 12 | publicationStatuses: PublicationStatus[] 13 | releaseOrCreateDate: Date 14 | imageUrl?: string 15 | userRoles?: string[] 16 | } 17 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionType.ts: -------------------------------------------------------------------------------- 1 | export enum CollectionType { 2 | RESEARCHERS = 'RESEARCHERS', 3 | RESEARCH_PROJECTS = 'RESEARCH_PROJECTS', 4 | JOURNALS = 'JOURNALS', 5 | ORGANIZATIONS_INSTITUTIONS = 'ORGANIZATIONS_INSTITUTIONS', 6 | TEACHING_COURSES = 'TEACHING_COURSES', 7 | UNCATEGORIZED = 'UNCATEGORIZED', 8 | LABORATORY = 'LABORATORY', 9 | RESEARCH_GROUP = 'RESEARCH_GROUP', 10 | DEPARTMENT = 'DEPARTMENT' 11 | } 12 | -------------------------------------------------------------------------------- /src/collections/domain/models/CollectionUserPermissions.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionUserPermissions { 2 | canAddCollection: boolean 3 | canAddDataset: boolean 4 | canViewUnpublishedCollection: boolean 5 | canEditCollection: boolean 6 | canManageCollectionPermissions: boolean 7 | canPublishCollection: boolean 8 | canDeleteCollection: boolean 9 | } 10 | -------------------------------------------------------------------------------- /src/collections/domain/models/MyDataCollectionItemSubset.ts: -------------------------------------------------------------------------------- 1 | import { CollectionPreview } from './CollectionPreview' 2 | import { DatasetPreview } from '../../../datasets' 3 | import { FilePreview } from '../../../files' 4 | import { CountPerObjectType } from './CollectionItemSubset' 5 | import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' 6 | 7 | export interface MyDataCollectionItemSubset { 8 | items: (CollectionPreview | DatasetPreview | FilePreview)[] 9 | publicationStatusCounts: PublicationStatusCount[] 10 | totalItemCount: number 11 | countPerObjectType: CountPerObjectType 12 | } 13 | 14 | export interface PublicationStatusCount { 15 | publicationStatus: PublicationStatus 16 | count: number 17 | } 18 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/CreateCollection.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { CollectionDTO } from '../dtos/CollectionDTO' 3 | import { ROOT_COLLECTION_ID } from '../models/Collection' 4 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 5 | 6 | export class CreateCollection implements UseCase { 7 | private collectionsRepository: ICollectionsRepository 8 | 9 | constructor(collectionsRepository: ICollectionsRepository) { 10 | this.collectionsRepository = collectionsRepository 11 | } 12 | 13 | /** 14 | * Creates a new collection, given a CollectionDTO object and an optional collection identifier, which defaults to :root. 15 | * 16 | * @param {CollectionDTO} [newCollection] - CollectionDTO object including the new collection data. 17 | * @param {string} [parentCollectionId] - Specifies the parent collection identifier (optional, defaults to :root). 18 | * @returns {Promise} - The created collection identifier. 19 | * @throws {WriteError} - If there are errors while writing data. 20 | */ 21 | async execute( 22 | newCollection: CollectionDTO, 23 | parentCollectionId: number | string = ROOT_COLLECTION_ID 24 | ): Promise { 25 | return await this.collectionsRepository.createCollection(newCollection, parentCollectionId) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/DeleteCollection.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 3 | 4 | export class DeleteCollection implements UseCase { 5 | private collectionsRepository: ICollectionsRepository 6 | 7 | constructor(collectionsRepository: ICollectionsRepository) { 8 | this.collectionsRepository = collectionsRepository 9 | } 10 | 11 | /** 12 | * Deletes the Dataverse collection whose database ID or alias is given: 13 | * 14 | * @param {number | string} [collectionIdOrAlias] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) 15 | * @returns {Promise} -This method does not return anything upon successful completion. 16 | */ 17 | async execute(collectionIdOrAlias: number | string): Promise { 18 | return await this.collectionsRepository.deleteCollection(collectionIdOrAlias) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/DeleteCollectionFeaturedItems.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ROOT_COLLECTION_ID } from '../models/Collection' 3 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 4 | 5 | export class DeleteCollectionFeaturedItems implements UseCase { 6 | private collectionsRepository: ICollectionsRepository 7 | 8 | constructor(collectionsRepository: ICollectionsRepository) { 9 | this.collectionsRepository = collectionsRepository 10 | } 11 | 12 | /** 13 | * Deletes all featured items from a collection, given a collection identifier. 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} - This method does not return anything upon successful completion. 18 | * @throws {WriteError} - If there are errors while writing data. 19 | */ 20 | async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ID): Promise { 21 | return await this.collectionsRepository.deleteCollectionFeaturedItems(collectionIdOrAlias) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/GetCollection.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 3 | import { Collection, ROOT_COLLECTION_ID } from '../models/Collection' 4 | 5 | export class GetCollection implements UseCase { 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} 18 | */ 19 | async execute(collectionIdOrAlias: number | string = ROOT_COLLECTION_ID): Promise { 20 | return await this.collectionsRepository.getCollection(collectionIdOrAlias) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/GetCollectionFacets.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 3 | import { ROOT_COLLECTION_ID } from '../models/Collection' 4 | import { CollectionFacet } from '../models/CollectionFacet' 5 | 6 | export class GetCollectionFacets implements UseCase { 7 | private collectionsRepository: ICollectionsRepository 8 | 9 | constructor(collectionsRepository: ICollectionsRepository) { 10 | this.collectionsRepository = collectionsRepository 11 | } 12 | 13 | /** 14 | * Returns a CollectionFacet array containing the facets of the requested collection, given the collection identifier or alias. 15 | * 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 | * @returns {Promise} 19 | */ 20 | async execute( 21 | collectionIdOrAlias: number | string = ROOT_COLLECTION_ID 22 | ): Promise { 23 | return await this.collectionsRepository.getCollectionFacets(collectionIdOrAlias) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/GetCollectionFeaturedItems.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 3 | import { ROOT_COLLECTION_ID } from '../models/Collection' 4 | import { CollectionFeaturedItem } from '../models/CollectionFeaturedItem' 5 | 6 | export class GetCollectionFeaturedItems implements UseCase { 7 | private collectionsRepository: ICollectionsRepository 8 | 9 | constructor(collectionsRepository: ICollectionsRepository) { 10 | this.collectionsRepository = collectionsRepository 11 | } 12 | 13 | /** 14 | * Returns a CollectionFeaturedItem array containing the featured items of the requested collection, given the collection identifier or alias. 15 | * 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 | * @returns {Promise} 19 | */ 20 | async execute( 21 | collectionIdOrAlias: number | string = ROOT_COLLECTION_ID 22 | ): Promise { 23 | return await this.collectionsRepository.getCollectionFeaturedItems(collectionIdOrAlias) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/GetCollectionUserPermissions.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { CollectionUserPermissions } from '../models/CollectionUserPermissions' 3 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 4 | import { ROOT_COLLECTION_ID } from '../models/Collection' 5 | 6 | export class GetCollectionUserPermissions implements UseCase { 7 | private collectionsRepository: ICollectionsRepository 8 | 9 | constructor(collectionsRepository: ICollectionsRepository) { 10 | this.collectionsRepository = collectionsRepository 11 | } 12 | 13 | /** 14 | * Returns an instance of CollectionUserPermissions that includes the permissions that the calling user has on a particular Collection. 15 | * 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 | * @returns {Promise} 19 | */ 20 | async execute( 21 | collectionIdOrAlias: number | string = ROOT_COLLECTION_ID 22 | ): Promise { 23 | return await this.collectionsRepository.getCollectionUserPermissions(collectionIdOrAlias) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/PublishCollection.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' // Assuming Axios for HTTP requests 3 | 4 | export class PublishCollection implements UseCase { 5 | private collectionsRepository: ICollectionsRepository 6 | 7 | constructor(collectionsRepository: ICollectionsRepository) { 8 | this.collectionsRepository = collectionsRepository 9 | } 10 | 11 | /** 12 | * Publishes a collection, given its identifier. 13 | * 14 | * @param {number | string} [collectionIdOrAlias] - The collection identifier, which can be a string (for collection alias), or a number (for numeric identifiers). 15 | * @returns {Promise} - This method does not return anything upon successful completion. 16 | */ 17 | async execute(collectionIdOrAlias: number | string): Promise { 18 | return await this.collectionsRepository.publishCollection(collectionIdOrAlias) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/collections/domain/useCases/UpdateCollection.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { CollectionDTO } from '../dtos/CollectionDTO' 3 | import { ICollectionsRepository } from '../repositories/ICollectionsRepository' 4 | 5 | export class UpdateCollection implements UseCase { 6 | private collectionsRepository: ICollectionsRepository 7 | 8 | constructor(collectionsRepository: ICollectionsRepository) { 9 | this.collectionsRepository = collectionsRepository 10 | } 11 | 12 | /** 13 | * Updates an existing collection, given a collection identifier and a CollectionDTO including the updated collection data. 14 | * 15 | * @param {number | string} [collectionIdOrAlias] - A generic collection identifier, which can be either a string (for queries by CollectionAlias), or a number (for queries by CollectionId) 16 | * @param {CollectionDTO} [newCollection] - CollectionDTO object including the updated collection data. 17 | * @returns {Promise} -This method does not return anything upon successful completion. 18 | * @throws {WriteError} - If there are errors while writing data. 19 | */ 20 | async execute( 21 | collectionIdOrAlias: number | string, 22 | updatedCollection: CollectionDTO 23 | ): Promise { 24 | return await this.collectionsRepository.updateCollection(collectionIdOrAlias, updatedCollection) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionFacetPayload.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionFacetPayload { 2 | id: string 3 | name: string 4 | displayName: string 5 | } 6 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionFeaturedItemPayload.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionFeaturedItemPayload { 2 | id: number 3 | content: string 4 | imageFileName: string | null 5 | imageFileUrl: string | null 6 | displayOrder: number 7 | } 8 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionItemsCountPerObjectTypePayload.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionItemsCountPerObjectTypePayload { 2 | Dataverses: number 3 | Datasets: number 4 | Files: number 5 | } 6 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionItemsFacetsPayload.ts: -------------------------------------------------------------------------------- 1 | export type CollectionItemsFacetPayload = [Record] 2 | 3 | export interface CollectionItemsFacetPayloadValue { 4 | friendly: string 5 | labels: Record[] 6 | } 7 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionPayload.ts: -------------------------------------------------------------------------------- 1 | import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload' 2 | 3 | export interface CollectionPayload { 4 | id: number 5 | alias: string 6 | name: string 7 | affiliation?: string 8 | isReleased: boolean 9 | description?: string 10 | isPartOf: OwnerNodePayload 11 | inputLevels?: CollectionInputLevelPayload[] 12 | dataverseContacts?: CollectionContactPayload[] 13 | dataverseType: string 14 | isMetadataBlockRoot: boolean 15 | isFacetRoot: boolean 16 | childCount: number 17 | } 18 | 19 | export interface CollectionInputLevelPayload { 20 | datasetFieldTypeName: string 21 | required: boolean 22 | include: boolean 23 | } 24 | 25 | export interface CollectionContactPayload { 26 | contactEmail: string 27 | displayOrder: number 28 | } 29 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionPreviewPayload.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionPreviewPayload { 2 | name: string 3 | parentDataverseName: string 4 | identifier: string 5 | parentDataverseIdentifier: string 6 | url: string 7 | image_url: string 8 | description: string 9 | type?: string 10 | publicationStatuses: string[] 11 | affiliation: string 12 | published_at: string 13 | } 14 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/CollectionUserPermissionsPayload.ts: -------------------------------------------------------------------------------- 1 | export interface CollectionUserPermissionsPayload { 2 | canAddDataverse: boolean 3 | canAddDataset: boolean 4 | canViewUnpublishedDataverse: boolean 5 | canEditDataverse: boolean 6 | canManageDataversePermissions: boolean 7 | canPublishDataverse: boolean 8 | canDeleteDataverse: boolean 9 | } 10 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/MyDataCollectionPreviewPayload.ts: -------------------------------------------------------------------------------- 1 | export interface MyDataCollectionPreviewPayload { 2 | name: string 3 | parentDataverseName: string 4 | identifier: string 5 | parentDataverseIdentifier: string 6 | url: string 7 | image_url: string 8 | description: string 9 | type?: string 10 | publicationStatuses: string[] 11 | affiliation: string 12 | published_at: string 13 | user_roles: string[] 14 | } 15 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/MyDataCountPerObjectTypePayload.ts: -------------------------------------------------------------------------------- 1 | export interface MyDataCountPerObjectTypePayload { 2 | dataverses_count: number 3 | datasets_count: number 4 | files_count: number 5 | } 6 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/MyDataPublicationStatusCountsPayload.ts: -------------------------------------------------------------------------------- 1 | export interface MyDataPublicationStatusCountsPayload { 2 | deaccessioned_count: number 3 | draft_count: number 4 | in_review_count: number 5 | published_count: number 6 | unpublished_count: number 7 | } 8 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/collectionFeaturedItemsTransformer.ts: -------------------------------------------------------------------------------- 1 | import { CollectionFeaturedItem } from '../../../domain/models/CollectionFeaturedItem' 2 | import { CollectionFeaturedItemPayload } from './CollectionFeaturedItemPayload' 3 | 4 | export const transformCollectionFeaturedItemsPayloadToCollectionFeaturedItems = ( 5 | collectionFeaturedItemsPayload: CollectionFeaturedItemPayload[] 6 | ): CollectionFeaturedItem[] => { 7 | return collectionFeaturedItemsPayload 8 | .map((collectionFeaturedItemPayload) => ({ 9 | id: collectionFeaturedItemPayload.id, 10 | content: collectionFeaturedItemPayload.content, 11 | imageFileUrl: collectionFeaturedItemPayload.imageFileUrl || undefined, 12 | imageFileName: collectionFeaturedItemPayload.imageFileName || undefined, 13 | displayOrder: collectionFeaturedItemPayload.displayOrder 14 | })) 15 | .sort((a, b) => a.displayOrder - b.displayOrder) 16 | } 17 | -------------------------------------------------------------------------------- /src/collections/infra/repositories/transformers/collectionUserPermissionsTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { CollectionUserPermissions } from '../../../domain/models/CollectionUserPermissions' 3 | 4 | export const transformCollectionUserPermissionsResponseToCollectionUserPermissions = ( 5 | response: AxiosResponse 6 | ): CollectionUserPermissions => { 7 | const collectionUserPermissionsPayload = response.data.data 8 | return { 9 | canAddCollection: collectionUserPermissionsPayload.canAddDataverse, 10 | canAddDataset: collectionUserPermissionsPayload.canAddDataset, 11 | canViewUnpublishedCollection: collectionUserPermissionsPayload.canViewUnpublishedDataverse, 12 | canEditCollection: collectionUserPermissionsPayload.canEditDataverse, 13 | canManageCollectionPermissions: collectionUserPermissionsPayload.canManageDataversePermissions, 14 | canPublishCollection: collectionUserPermissionsPayload.canPublishDataverse, 15 | canDeleteCollection: collectionUserPermissionsPayload.canDeleteDataverse 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/contactInfo/domain/dtos/ContactDTO.ts: -------------------------------------------------------------------------------- 1 | export interface ContactDTO { 2 | targetId?: number 3 | identifier?: string 4 | subject: string 5 | body: string 6 | fromEmail: string 7 | } 8 | -------------------------------------------------------------------------------- /src/contactInfo/domain/models/Contact.ts: -------------------------------------------------------------------------------- 1 | export interface Contact { 2 | fromEmail: string 3 | body: string 4 | subject: string 5 | } 6 | -------------------------------------------------------------------------------- /src/contactInfo/domain/repositories/IContactRepository.ts: -------------------------------------------------------------------------------- 1 | import { Contact } from '../models/Contact' 2 | import { ContactDTO } from '../dtos/ContactDTO' 3 | 4 | export interface IContactRepository { 5 | submitContactInfo(contactDTO: ContactDTO): Promise 6 | } 7 | -------------------------------------------------------------------------------- /src/contactInfo/domain/useCases/SubmitContactInfo.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ContactDTO } from '../dtos/ContactDTO' 3 | import { Contact } from '../models/Contact' 4 | import { IContactRepository } from '../repositories/IContactRepository' 5 | 6 | export class SubmitContactInfo implements UseCase { 7 | private contactRepository: IContactRepository 8 | 9 | constructor(contactRepository: IContactRepository) { 10 | this.contactRepository = contactRepository 11 | } 12 | 13 | /** 14 | * Submits contact information and returns a Contact model containing the submitted data. 15 | * 16 | * @param {ContactDTO} contactDTO - The contact information to be submitted. 17 | * @returns {Promise} A promise resolving to a list of contact. 18 | */ 19 | 20 | async execute(contactDTO: ContactDTO): Promise { 21 | return await this.contactRepository.submitContactInfo(contactDTO) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/contactInfo/index.ts: -------------------------------------------------------------------------------- 1 | import { SubmitContactInfo } from './domain/useCases/SubmitContactInfo' 2 | import { ContactRepository } from './infra/repositories/ContactRepository' 3 | 4 | const contactRepository = new ContactRepository() 5 | const submitContactInfo = new SubmitContactInfo(contactRepository) 6 | 7 | export { submitContactInfo } 8 | export { Contact } from './domain/models/Contact' 9 | export { ContactDTO } from './domain/dtos/ContactDTO' 10 | -------------------------------------------------------------------------------- /src/contactInfo/infra/repositories/ContactRepository.ts: -------------------------------------------------------------------------------- 1 | import { ApiRepository } from '../../../core/infra/repositories/ApiRepository' 2 | import { Contact } from '../../domain/models/Contact' 3 | import { IContactRepository } from '../../domain/repositories/IContactRepository' 4 | import { ContactDTO } from '../../domain/dtos/ContactDTO' 5 | 6 | export class ContactRepository extends ApiRepository implements IContactRepository { 7 | public async submitContactInfo(contactDTO: ContactDTO): Promise { 8 | return this.doPost(`/sendfeedback`, contactDTO) 9 | .then((response) => { 10 | const responseData = response.data 11 | const contact: Contact[] = responseData.data.map((item: Contact) => ({ 12 | fromEmail: item.fromEmail, 13 | subject: item.subject, 14 | body: item.body 15 | })) 16 | 17 | return contact 18 | }) 19 | .catch((error) => { 20 | throw error 21 | }) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/domain/models/DvObjectOwnerNode.ts: -------------------------------------------------------------------------------- 1 | export interface DvObjectOwnerNode { 2 | type: DvObjectType 3 | displayName: string 4 | identifier: string 5 | persistentIdentifier?: string 6 | version?: string 7 | isReleased?: boolean 8 | isPartOf?: DvObjectOwnerNode 9 | } 10 | 11 | export enum DvObjectType { 12 | DATAVERSE = 'DATAVERSE', 13 | DATASET = 'DATASET', 14 | FILE = 'FILE' 15 | } 16 | -------------------------------------------------------------------------------- /src/core/domain/models/PublicationStatus.ts: -------------------------------------------------------------------------------- 1 | export enum PublicationStatus { 2 | Published = 'Published', 3 | Unpublished = 'Unpublished', 4 | Draft = 'Draft', 5 | Deaccessioned = 'Deaccessioned', 6 | InReview = 'In Review' 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domain/repositories/ReadError.ts: -------------------------------------------------------------------------------- 1 | import { RepositoryError } from './RepositoryError' 2 | 3 | export class ReadError extends RepositoryError { 4 | constructor(reason?: string) { 5 | super('There was an error when reading the resource.', reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domain/repositories/RepositoryError.ts: -------------------------------------------------------------------------------- 1 | export abstract class RepositoryError extends Error { 2 | constructor(message: string, reason?: string) { 3 | if (reason) { 4 | message += ` Reason was: ${reason}` 5 | } 6 | super(message) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/core/domain/repositories/WriteError.ts: -------------------------------------------------------------------------------- 1 | import { RepositoryError } from './RepositoryError' 2 | 3 | export class WriteError extends RepositoryError { 4 | constructor(reason?: string) { 5 | super('There was an error when writing the resource.', reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/core/domain/useCases/UseCase.ts: -------------------------------------------------------------------------------- 1 | export interface UseCase { 2 | execute(...args: unknown[]): Promise 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domain/useCases/validators/ResourceValidator.ts: -------------------------------------------------------------------------------- 1 | export interface ResourceValidator { 2 | validate(...args: unknown[]): void 3 | } 4 | -------------------------------------------------------------------------------- /src/core/domain/useCases/validators/errors/ResourceValidationError.ts: -------------------------------------------------------------------------------- 1 | export class ResourceValidationError extends Error { 2 | constructor(message: string) { 3 | super(message) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | export { ReadError } from './domain/repositories/ReadError' 2 | export { WriteError } from './domain/repositories/WriteError' 3 | export { ApiConfig } from './infra/repositories/ApiConfig' 4 | export { DvObjectOwnerNode, DvObjectType } from './domain/models/DvObjectOwnerNode' 5 | export { PublicationStatus } from './domain/models/PublicationStatus' 6 | -------------------------------------------------------------------------------- /src/core/infra/repositories/ApiConfig.ts: -------------------------------------------------------------------------------- 1 | export class ApiConfig { 2 | static dataverseApiUrl: string 3 | static dataverseApiAuthMechanism: DataverseApiAuthMechanism 4 | static dataverseApiKey?: string 5 | static bearerTokenLocalStorageKey?: string 6 | 7 | static init( 8 | dataverseApiUrl: string, 9 | dataverseApiAuthMechanism: DataverseApiAuthMechanism, 10 | dataverseApiKey?: string, 11 | bearerTokenLocalStorageKey?: string 12 | ) { 13 | this.dataverseApiUrl = dataverseApiUrl 14 | this.dataverseApiAuthMechanism = dataverseApiAuthMechanism 15 | this.dataverseApiKey = dataverseApiKey 16 | this.bearerTokenLocalStorageKey = bearerTokenLocalStorageKey 17 | } 18 | } 19 | 20 | export enum DataverseApiAuthMechanism { 21 | API_KEY = 'api-key', 22 | SESSION_COOKIE = 'session-cookie', // Temporal and only for dev purposes 23 | BEARER_TOKEN = 'bearer-token' 24 | } 25 | -------------------------------------------------------------------------------- /src/core/infra/repositories/ApiConstants.ts: -------------------------------------------------------------------------------- 1 | export class ApiConstants { 2 | static readonly CONTENT_TYPE_APPLICATION_JSON = 'application/json' 3 | static readonly CONTENT_TYPE_MULTIPART_FORM_DATA = 'multipart/form-data' 4 | } 5 | -------------------------------------------------------------------------------- /src/core/infra/repositories/transformers/OwnerNodePayload.ts: -------------------------------------------------------------------------------- 1 | export interface OwnerNodePayload { 2 | type: string 3 | displayName: string 4 | identifier: string 5 | persistentIdentifier?: string 6 | version?: string 7 | isReleased?: boolean 8 | isPartOf?: OwnerNodePayload 9 | } 10 | -------------------------------------------------------------------------------- /src/core/infra/repositories/transformers/dvObjectOwnerNodeTransformer.ts: -------------------------------------------------------------------------------- 1 | import { OwnerNodePayload } from './OwnerNodePayload' 2 | import { DvObjectOwnerNode, DvObjectType } from '../../../domain/models/DvObjectOwnerNode' 3 | 4 | export const transformPayloadToOwnerNode = ( 5 | ownerNodePayload: OwnerNodePayload 6 | ): DvObjectOwnerNode => { 7 | return { 8 | type: ownerNodePayload.type as DvObjectType, 9 | displayName: ownerNodePayload.displayName, 10 | identifier: ownerNodePayload.identifier, 11 | ...(ownerNodePayload.persistentIdentifier && { 12 | persistentIdentifier: ownerNodePayload.persistentIdentifier 13 | }), 14 | ...(ownerNodePayload.version && { version: ownerNodePayload.version }), 15 | ...(ownerNodePayload.isReleased !== undefined && { isReleased: ownerNodePayload.isReleased }), 16 | ...(ownerNodePayload.isPartOf && { 17 | isPartOf: transformPayloadToOwnerNode(ownerNodePayload.isPartOf) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/datasets/domain/dtos/DatasetDTO.ts: -------------------------------------------------------------------------------- 1 | import { DatasetLicense } from '../models/Dataset' 2 | 3 | export interface DatasetDTO { 4 | license?: DatasetLicense 5 | metadataBlockValues: DatasetMetadataBlockValuesDTO[] 6 | } 7 | 8 | export interface DatasetMetadataBlockValuesDTO { 9 | name: string 10 | fields: DatasetMetadataFieldsDTO 11 | } 12 | 13 | export type DatasetMetadataFieldsDTO = Record 14 | 15 | export type DatasetMetadataFieldValueDTO = 16 | | string 17 | | string[] 18 | | DatasetMetadataChildFieldValueDTO 19 | | DatasetMetadataChildFieldValueDTO[] 20 | 21 | export type DatasetMetadataChildFieldValueDTO = Record 22 | -------------------------------------------------------------------------------- /src/datasets/domain/dtos/DatasetDeaccessionDTO.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetDeaccessionDTO { 2 | deaccessionReason: string 3 | deaccessionForwardURL?: string 4 | } 5 | -------------------------------------------------------------------------------- /src/datasets/domain/models/CreatedDatasetIdentifiers.ts: -------------------------------------------------------------------------------- 1 | export interface CreatedDatasetIdentifiers { 2 | persistentId: string 3 | numericId: number 4 | } 5 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetDownloadCount.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetDownloadCount { 2 | id: number | string 3 | downloadCount: number 4 | MDCStartDate?: string 5 | } 6 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetLock.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetLock { 2 | lockType: DatasetLockType 3 | date?: string 4 | userId: string 5 | datasetPersistentId: string 6 | message?: string 7 | } 8 | 9 | export enum DatasetLockType { 10 | INGEST = 'Ingest', 11 | WORKFLOW = 'Workflow', 12 | IN_REVIEW = 'InReview', 13 | DCM_UPLOAD = 'DcmUpload', 14 | GLOBUS_UPLOAD = 'GlobusUpload', 15 | FINALIZE_PUBLICATION = 'finalizePublication', 16 | EDIT_IN_PROGRESS = 'EditInProgress', 17 | FILE_VALIDATION_FAILED = 'FileValidationFailed' 18 | } 19 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetNotNumberedVersion.ts: -------------------------------------------------------------------------------- 1 | export enum DatasetNotNumberedVersion { 2 | DRAFT = ':draft', 3 | LATEST = ':latest', 4 | LATEST_PUBLISHED = ':latest-published' 5 | } 6 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetPreview.ts: -------------------------------------------------------------------------------- 1 | import { CollectionItemType } from '../../../collections' 2 | import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' 3 | import { DatasetVersionInfo } from './Dataset' 4 | 5 | export interface DatasetPreview { 6 | type: CollectionItemType.DATASET 7 | persistentId: string 8 | title: string 9 | versionId: number 10 | versionInfo: DatasetVersionInfo 11 | citation: string 12 | description: string 13 | publicationStatuses: PublicationStatus[] 14 | parentCollectionName: string 15 | parentCollectionAlias: string 16 | imageUrl?: string 17 | userRoles?: string[] 18 | } 19 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetPreviewSubset.ts: -------------------------------------------------------------------------------- 1 | import { DatasetPreview } from './DatasetPreview' 2 | 3 | export interface DatasetPreviewSubset { 4 | datasetPreviews: DatasetPreview[] 5 | totalDatasetCount: number 6 | } 7 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetUserPermissions.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetUserPermissions { 2 | canViewUnpublishedDataset: boolean 3 | canEditDataset: boolean 4 | canPublishDataset: boolean 5 | canManageDatasetPermissions: boolean 6 | canDeleteDatasetDraft: boolean 7 | } 8 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetVersionDiff.ts: -------------------------------------------------------------------------------- 1 | import { DatasetVersionState } from './Dataset' 2 | 3 | export interface DatasetVersionDiff { 4 | oldVersion: VersionSummary 5 | newVersion: VersionSummary 6 | metadataChanges?: MetadataBlockDiff[] 7 | filesAdded?: FileSummary[] 8 | filesRemoved?: FileSummary[] 9 | fileChanges?: FileDiff[] 10 | filesReplaced?: FileReplacement[] 11 | termsOfAccess?: { 12 | changed: FieldDiff[] 13 | } 14 | } 15 | 16 | export interface FileSummary { 17 | fileName: string 18 | MD5: string 19 | type: string 20 | fileId: number 21 | filePath: string 22 | description: string 23 | isRestricted: boolean 24 | tags: string[] 25 | categories: string[] 26 | } 27 | 28 | export interface VersionSummary { 29 | versionNumber: string 30 | lastUpdatedDate: string 31 | versionState: DatasetVersionState 32 | } 33 | export interface MetadataBlockDiff { 34 | blockName: string 35 | changed: FieldDiff[] 36 | } 37 | 38 | export interface FileDiff { 39 | fileName: string 40 | md5: string 41 | fileId: number 42 | changed: FieldDiff[] 43 | } 44 | 45 | export interface FileReplacement { 46 | oldFile: FileSummary 47 | newFile: FileSummary 48 | } 49 | export interface FieldDiff { 50 | fieldName: string 51 | oldValue: string 52 | newValue: string 53 | } 54 | -------------------------------------------------------------------------------- /src/datasets/domain/models/DatasetVersionSummaryInfo.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetVersionSummaryInfo { 2 | id: number 3 | versionNumber: string 4 | summary?: DatasetVersionSummary | DatasetVersionSummaryStringValues 5 | contributors: string 6 | publishedOn?: string 7 | } 8 | 9 | export type DatasetVersionSummary = { 10 | [key: string]: 11 | | SummaryUpdates 12 | | SummaryUpdatesWithFields 13 | | FilesSummaryUpdates 14 | | boolean 15 | | Deaccessioned 16 | } 17 | 18 | interface Deaccessioned { 19 | reason: string 20 | url: string 21 | } 22 | 23 | interface SummaryUpdates { 24 | added: number 25 | deleted: number 26 | changed: number 27 | } 28 | 29 | interface SummaryUpdatesWithFields { 30 | [key: string]: SummaryUpdates 31 | } 32 | 33 | interface FilesSummaryUpdates { 34 | added: number 35 | removed: number 36 | replaced: number 37 | changedFileMetaData: number 38 | changedVariableMetadata: number 39 | } 40 | 41 | export enum DatasetVersionSummaryStringValues { 42 | firstPublished = 'firstPublished', 43 | firstDraft = 'firstDraft', 44 | versionDeaccessioned = 'versionDeaccessioned', 45 | previousVersionDeaccessioned = 'previousVersionDeaccessioned' 46 | } 47 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/DeaccessionDataset.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { DatasetDeaccessionDTO } from '../dtos/DatasetDeaccessionDTO' 4 | import { DatasetNotNumberedVersion } from '../models/DatasetNotNumberedVersion' 5 | 6 | export class DeaccessionDataset implements UseCase { 7 | private datasetsRepository: IDatasetsRepository 8 | 9 | constructor(datasetsRepository: IDatasetsRepository) { 10 | this.datasetsRepository = datasetsRepository 11 | } 12 | 13 | /** 14 | * Deaccession a dataset, given a dataset id, a dataset version id, and a DatasetDeaccessionDTO object. 15 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @param {string | DatasetNotNumberedVersion} [datasetVersionId] - The dataset version identifier, which can be a version-specific numeric string (for example, 1.0) or a DatasetNotNumberedVersion enum value. 17 | * @returns A promise that resolves when the dataset is deaccessioned 18 | * @throws An error if the dataset could not be deaccessioned 19 | */ 20 | async execute( 21 | datasetId: number | string, 22 | datasetVersionId: string | DatasetNotNumberedVersion, 23 | DatasetDeaccessionDTO: DatasetDeaccessionDTO 24 | ): Promise { 25 | return await this.datasetsRepository.deaccessionDataset( 26 | datasetId, 27 | datasetVersionId, 28 | DatasetDeaccessionDTO 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/DeleteDatasetDraft.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | 4 | export class DeleteDatasetDraft implements UseCase { 5 | private datasetsRepository: IDatasetsRepository 6 | 7 | constructor(datasetsRepository: IDatasetsRepository) { 8 | this.datasetsRepository = datasetsRepository 9 | } 10 | 11 | /** 12 | * Delete a Draft Dataset 13 | * 14 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 15 | */ 16 | async execute(datasetId: number | string): Promise { 17 | return this.datasetsRepository.deleteDatasetDraft(datasetId) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetAllDatasetPreviews.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { DatasetPreviewSubset } from '../models/DatasetPreviewSubset' 4 | 5 | export class GetAllDatasetPreviews implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns an instance of DatasetPreviewSubset that contains reduced information for each dataset that the calling user can access in the installation. 14 | * 15 | * @param {number} [limit] - Limit for pagination (optional). 16 | * @param {number} [offset] - Offset for pagination (optional). 17 | * @param {string} [collectionId] - Collection id (optional). 18 | * @returns {Promise} 19 | */ 20 | async execute( 21 | limit?: number, 22 | offset?: number, 23 | collectionId?: string 24 | ): Promise { 25 | return await this.datasetsRepository.getAllDatasetPreviews(limit, offset, collectionId) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetCitation.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { DatasetNotNumberedVersion } from '../models/DatasetNotNumberedVersion' 4 | 5 | export class GetDatasetCitation implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns the Dataset citation text. 14 | * 15 | * @param {number} [datasetId] - The dataset identifier. 16 | * @param {string | DatasetNotNumberedVersion} [datasetVersionId=DatasetNotNumberedVersion.LATEST] - The dataset version identifier, which can be a version-specific numeric string (for example, 1.0) or a DatasetNotNumberedVersion enum value. If this parameter is not set, the default value is: DatasetNotNumberedVersion.LATEST 17 | * @param {boolean} [includeDeaccessioned=false] - Indicates whether to consider deaccessioned versions in the dataset search or not. The default value is false 18 | * @returns {Promise} 19 | */ 20 | async execute( 21 | datasetId: number, 22 | datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST, 23 | includeDeaccessioned = false 24 | ): Promise { 25 | return await this.datasetsRepository.getDatasetCitation( 26 | datasetId, 27 | datasetVersionId, 28 | includeDeaccessioned 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetDownloadCount.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { DatasetDownloadCount } from '../models/DatasetDownloadCount' 3 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 4 | 5 | export class GetDatasetDownloadCount implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns a DatasetDownloadCount instance, with dataset id, count and MDCStartDate(optional). 14 | * 15 | * @param {number | string} [datasetId] - The dataset identifier. 16 | * @param {boolean} [includeMDC(optional)] - Indicates whether to consider include counts from MDC start date or not. The default value is false 17 | * @returns {Promise} 18 | */ 19 | async execute(datasetId: number | string, includeMDC?: boolean): Promise { 20 | return await this.datasetsRepository.getDatasetDownloadCount(datasetId, includeMDC) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetLocks.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { DatasetLock } from '../models/DatasetLock' 4 | 5 | export class GetDatasetLocks implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns all locks present in a Dataset. 14 | * 15 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @returns {Promise} 17 | */ 18 | async execute(datasetId: number | string): Promise { 19 | return await this.datasetsRepository.getDatasetLocks(datasetId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetSummaryFieldNames.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | 4 | export class GetDatasetSummaryFieldNames implements UseCase { 5 | private datasetsRepository: IDatasetsRepository 6 | 7 | constructor(datasetsRepository: IDatasetsRepository) { 8 | this.datasetsRepository = datasetsRepository 9 | } 10 | 11 | /** 12 | * Returns the names of the dataset summary fields configured in the installation. 13 | * 14 | * @returns {Promise} 15 | */ 16 | async execute(): Promise { 17 | return await this.datasetsRepository.getDatasetSummaryFieldNames() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetUserPermissions.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { DatasetUserPermissions } from '../models/DatasetUserPermissions' 4 | 5 | export class GetDatasetUserPermissions implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns an instance of DatasetUserPermissions that includes the permissions that the calling user has on a particular Dataset. 14 | * 15 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @returns {Promise} 17 | */ 18 | async execute(datasetId: number | string): Promise { 19 | return await this.datasetsRepository.getDatasetUserPermissions(datasetId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetDatasetVersionsSummaries.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { DatasetVersionSummaryInfo } from '../models/DatasetVersionSummaryInfo' 3 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 4 | 5 | export class GetDatasetVersionsSummaries implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns a list of versions for a given dataset including a summary of differences between consecutive versions where available. 14 | * Draft versions will only be available to users who have permission to view unpublished drafts. 15 | * 16 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 17 | * @returns {Promise} - An array of DatasetVersionSummaryInfo. 18 | */ 19 | async execute(datasetId: number | string): Promise { 20 | return await this.datasetsRepository.getDatasetVersionsSummaries(datasetId) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetPrivateUrlDataset.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | import { Dataset } from '../models/Dataset' 4 | 5 | export class GetPrivateUrlDataset implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Returns a Dataset instance, given an associated Private URL Token. 14 | * 15 | * @param {string} [token] - A Private URL token. 16 | * @param {boolean} [keepRawFields=false] - Indicates whether or not the use case should keep the metadata fields as they are and avoid the transformation to markdown. The default value is false. 17 | * @returns {Promise} 18 | */ 19 | async execute(token: string, keepRawFields = false): Promise { 20 | return await this.datasetsRepository.getPrivateUrlDataset(token, keepRawFields) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/GetPrivateUrlDatasetCitation.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' 3 | 4 | export class GetPrivateUrlDatasetCitation implements UseCase { 5 | private datasetsRepository: IDatasetsRepository 6 | 7 | constructor(datasetsRepository: IDatasetsRepository) { 8 | this.datasetsRepository = datasetsRepository 9 | } 10 | 11 | /** 12 | * Returns the Dataset citation text, given an associated Private URL Token. 13 | * 14 | * @param {string} [token] - A Private URL token. 15 | * @returns {Promise} 16 | */ 17 | async execute(token: string): Promise { 18 | return await this.datasetsRepository.getPrivateUrlDatasetCitation(token) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/PublishDataset.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { VersionUpdateType } from '../models/Dataset' 3 | import { IDatasetsRepository } from '../repositories/IDatasetsRepository' // Assuming Axios for HTTP requests 4 | 5 | export class PublishDataset implements UseCase { 6 | private datasetsRepository: IDatasetsRepository 7 | 8 | constructor(datasetsRepository: IDatasetsRepository) { 9 | this.datasetsRepository = datasetsRepository 10 | } 11 | 12 | /** 13 | * Publishes a dataset, given its identifier and the type of version update type. 14 | * 15 | * @param {number | string} [datasetId] - The dataset identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @param {VersionUpdateType} versionUpdateType - Specifies the type of version update, 'major', 'minor' or 'updatecurrent' 17 | * @returns {Promise} - This method does not return anything upon successful completion. 18 | */ 19 | async execute(datasetId: number | string, versionUpdateType: VersionUpdateType): Promise { 20 | return await this.datasetsRepository.publishDataset(datasetId, versionUpdateType) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/BaseMetadataFieldValidator.ts: -------------------------------------------------------------------------------- 1 | import { DatasetMetadataFieldValueDTO } from '../../dtos/DatasetDTO' 2 | import { FieldValidationError } from './errors/FieldValidationError' 3 | import { MetadataFieldInfo } from '../../../../metadataBlocks' 4 | 5 | export interface DatasetMetadataFieldAndValueInfo { 6 | metadataFieldInfo: MetadataFieldInfo 7 | metadataFieldKey: string 8 | metadataFieldValue: DatasetMetadataFieldValueDTO 9 | metadataBlockName: string 10 | metadataParentFieldKey?: string 11 | metadataFieldPosition?: number 12 | allowEmptyForConditionallyRequiredField?: boolean 13 | } 14 | 15 | export abstract class BaseMetadataFieldValidator { 16 | abstract validate(datasetMetadataFieldAndValueInfo: DatasetMetadataFieldAndValueInfo): void 17 | 18 | protected createGeneralValidationError( 19 | datasetMetadataFieldAndValueInfo: DatasetMetadataFieldAndValueInfo, 20 | reason: string 21 | ): FieldValidationError { 22 | return new FieldValidationError( 23 | datasetMetadataFieldAndValueInfo.metadataFieldKey, 24 | datasetMetadataFieldAndValueInfo.metadataBlockName, 25 | datasetMetadataFieldAndValueInfo.metadataParentFieldKey, 26 | datasetMetadataFieldAndValueInfo.metadataFieldPosition, 27 | reason 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/DatasetResourceValidator.ts: -------------------------------------------------------------------------------- 1 | import { DatasetDTO, DatasetMetadataBlockValuesDTO } from '../../dtos/DatasetDTO' 2 | import { ResourceValidator } from '../../../../core/domain/useCases/validators/ResourceValidator' 3 | import { MetadataBlock } from '../../../../metadataBlocks' 4 | import { BaseMetadataFieldValidator } from './BaseMetadataFieldValidator' 5 | 6 | export class DatasetResourceValidator implements ResourceValidator { 7 | constructor(private metadataFieldValidator: BaseMetadataFieldValidator) {} 8 | 9 | public validate(resource: DatasetDTO, metadataBlocks: MetadataBlock[]) { 10 | for (const metadataBlockValues of resource.metadataBlockValues) { 11 | this.validateMetadataBlock(metadataBlockValues, metadataBlocks) 12 | } 13 | } 14 | 15 | private validateMetadataBlock( 16 | metadataBlockValues: DatasetMetadataBlockValuesDTO, 17 | metadataBlocks: MetadataBlock[] 18 | ) { 19 | const metadataBlockName = metadataBlockValues.name 20 | const metadataBlock = metadataBlocks.find( 21 | (metadataBlock) => metadataBlock.name === metadataBlockName 22 | ) as MetadataBlock 23 | 24 | for (const metadataFieldKey of Object.keys(metadataBlock.metadataFields)) { 25 | this.metadataFieldValidator.validate({ 26 | metadataFieldInfo: metadataBlock.metadataFields[metadataFieldKey], 27 | metadataFieldKey: metadataFieldKey, 28 | metadataFieldValue: metadataBlockValues.fields[metadataFieldKey], 29 | metadataBlockName: metadataBlockName 30 | }) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/errors/ControlledVocabularyFieldError.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationError } from './FieldValidationError' 2 | 3 | export class ControlledVocabularyFieldError extends FieldValidationError { 4 | constructor( 5 | metadataFieldName: string, 6 | citationBlockName: string, 7 | parentMetadataFieldName?: string, 8 | fieldPosition?: number 9 | ) { 10 | super( 11 | metadataFieldName, 12 | citationBlockName, 13 | parentMetadataFieldName, 14 | fieldPosition, 15 | 'The field does not have a valid controlled vocabulary value.' 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/errors/DateFormatFieldError.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationError } from './FieldValidationError' 2 | 3 | export class DateFormatFieldError extends FieldValidationError { 4 | constructor( 5 | metadataFieldName: string, 6 | citationBlockName: string, 7 | validDateFormat: string, 8 | parentMetadataFieldName?: string, 9 | fieldPosition?: number 10 | ) { 11 | super( 12 | metadataFieldName, 13 | citationBlockName, 14 | parentMetadataFieldName, 15 | fieldPosition, 16 | `The field requires a valid date format (${validDateFormat}).` 17 | ) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/errors/EmptyFieldError.ts: -------------------------------------------------------------------------------- 1 | import { FieldValidationError } from './FieldValidationError' 2 | 3 | export class EmptyFieldError extends FieldValidationError { 4 | constructor( 5 | metadataFieldName: string, 6 | citationBlockName: string, 7 | parentMetadataFieldName?: string, 8 | fieldPosition?: number 9 | ) { 10 | super( 11 | metadataFieldName, 12 | citationBlockName, 13 | parentMetadataFieldName, 14 | fieldPosition, 15 | 'The field should not be empty.' 16 | ) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/datasets/domain/useCases/validators/errors/FieldValidationError.ts: -------------------------------------------------------------------------------- 1 | import { ResourceValidationError } from '../../../../../core/domain/useCases/validators/errors/ResourceValidationError' 2 | 3 | export class FieldValidationError extends ResourceValidationError { 4 | citationBlockName: string 5 | metadataFieldName: string 6 | parentMetadataFieldName?: string 7 | fieldPosition?: number 8 | 9 | constructor( 10 | metadataFieldName: string, 11 | citationBlockName: string, 12 | parentMetadataFieldName?: string, 13 | fieldPosition?: number, 14 | reason?: string 15 | ) { 16 | let message = `There was an error when validating the field ${metadataFieldName} from metadata block ${citationBlockName}` 17 | if (parentMetadataFieldName) { 18 | message += ` with parent field ${parentMetadataFieldName}` 19 | } 20 | if (fieldPosition != undefined) { 21 | message += ` in position ${fieldPosition}` 22 | } 23 | if (reason) { 24 | message += `. Reason was: ${reason}` 25 | } 26 | super(message) 27 | this.citationBlockName = citationBlockName 28 | this.metadataFieldName = metadataFieldName 29 | this.parentMetadataFieldName = parentMetadataFieldName 30 | this.fieldPosition = fieldPosition 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/DatasetPreviewPayload.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetPreviewPayload { 2 | global_id: string 3 | name: string 4 | versionId: number 5 | majorVersion: number 6 | minorVersion: number 7 | versionState: string 8 | createdAt: string 9 | updatedAt: string 10 | published_at?: string 11 | citation: string 12 | citationHtml: string 13 | description: string 14 | type?: string 15 | publicationStatuses: string[] 16 | identifier_of_dataverse: string 17 | name_of_dataverse: string 18 | image_url?: string 19 | } 20 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/DatasetVersionDiffPayload.ts: -------------------------------------------------------------------------------- 1 | export interface DatasetVersionDiffPayload { 2 | oldVersion: VersionSummaryPayload 3 | newVersion: VersionSummaryPayload 4 | metadataChanges: MetadataBlockDiffPayload[] 5 | filesAdded: FileSummaryPayload[] 6 | filesRemoved: FileSummaryPayload[] 7 | fileChanges: FileDiffPayload[] 8 | filesReplaced: FileReplacementPayload[] 9 | TermsOfAccess: { changed: FieldDiffPayload[] } 10 | } 11 | 12 | export interface FileSummaryPayload { 13 | fileName: string 14 | MD5: string 15 | type: string 16 | fileId: number 17 | filePath: string 18 | description: string 19 | isRestricted: boolean 20 | tags: string[] 21 | categories: string[] 22 | } 23 | 24 | export interface VersionSummaryPayload { 25 | versionNumber: string 26 | lastUpdatedDate: string 27 | } 28 | export interface MetadataBlockDiffPayload { 29 | blockName: string 30 | changed: FieldDiffPayload[] 31 | } 32 | 33 | export interface FileDiffPayload { 34 | fileName: string 35 | md5: string 36 | fileId: number 37 | changed: FieldDiffPayload[] 38 | } 39 | export interface FieldDiffPayload { 40 | fieldName: string 41 | oldValue: string 42 | newValue: string 43 | } 44 | 45 | export interface FileReplacementPayload { 46 | oldFile: FileSummaryPayload 47 | newFile: FileSummaryPayload 48 | } 49 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/MyDataDatasetPreviewPayload.ts: -------------------------------------------------------------------------------- 1 | export interface MyDataDatasetPreviewPayload { 2 | name: string 3 | type: string 4 | global_id: string 5 | description: string 6 | publisher: string 7 | citationHtml: string 8 | identifier_of_dataverse: string 9 | name_of_dataverse: string 10 | publicationStatuses: string[] 11 | majorVersion: number 12 | minorVersion: number 13 | versionId: number 14 | versionState: string 15 | createdAt: string 16 | updatedAt: string 17 | publication_statuses: string[] 18 | user_roles: string[] 19 | image_url?: string 20 | published_at?: string 21 | } 22 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/datasetLocksTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { DatasetLock, DatasetLockType } from '../../../domain/models/DatasetLock' 3 | 4 | export interface DatasetLockPayload { 5 | lockType: string 6 | date?: string 7 | user: string 8 | dataset: string 9 | message?: string 10 | } 11 | 12 | export const transformDatasetLocksResponseToDatasetLocks = ( 13 | response: AxiosResponse 14 | ): DatasetLock[] => { 15 | const datasetLocks: DatasetLock[] = [] 16 | const datasetLocksPayload = response.data.data 17 | datasetLocksPayload.forEach(function (datasetLockPayload: DatasetLockPayload) { 18 | datasetLocks.push(transformDatasetLockPayloadToDatasetLock(datasetLockPayload)) 19 | }) 20 | return datasetLocks 21 | } 22 | 23 | const transformDatasetLockPayloadToDatasetLock = ( 24 | datasetLockPayload: DatasetLockPayload 25 | ): DatasetLock => { 26 | return { 27 | lockType: datasetLockPayload.lockType as DatasetLockType, 28 | ...(datasetLockPayload.date && { date: datasetLockPayload.date }), 29 | userId: datasetLockPayload.user, 30 | datasetPersistentId: datasetLockPayload.dataset, 31 | ...(datasetLockPayload.message && { message: datasetLockPayload.message }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/datasetUserPermissionsTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { DatasetUserPermissions } from '../../../domain/models/DatasetUserPermissions' 3 | 4 | export const transformDatasetUserPermissionsResponseToDatasetUserPermissions = ( 5 | response: AxiosResponse 6 | ): DatasetUserPermissions => { 7 | const datasetUserPermissionsPayload = response.data.data 8 | return { 9 | canViewUnpublishedDataset: datasetUserPermissionsPayload.canViewUnpublishedDataset, 10 | canEditDataset: datasetUserPermissionsPayload.canEditDataset, 11 | canPublishDataset: datasetUserPermissionsPayload.canPublishDataset, 12 | canManageDatasetPermissions: datasetUserPermissionsPayload.canManageDatasetPermissions, 13 | canDeleteDatasetDraft: datasetUserPermissionsPayload.canDeleteDatasetDraft 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/datasets/infra/repositories/transformers/datasetVersionDiffTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { DatasetVersionDiff } from '../../../domain/models/DatasetVersionDiff' 3 | 4 | export const transformDatasetVersionDiffResponseToDatasetVersionDiff = ( 5 | response: AxiosResponse 6 | ): DatasetVersionDiff => { 7 | const datasetVersionDiffPayload = response.data.data 8 | const retValue = { 9 | oldVersion: datasetVersionDiffPayload.oldVersion, 10 | newVersion: datasetVersionDiffPayload.newVersion, 11 | metadataChanges: datasetVersionDiffPayload.metadataChanges, 12 | filesAdded: datasetVersionDiffPayload.filesAdded, 13 | filesRemoved: datasetVersionDiffPayload.filesRemoved, 14 | fileChanges: datasetVersionDiffPayload.fileChanges, 15 | filesReplaced: datasetVersionDiffPayload.filesReplaced, 16 | termsOfAccess: datasetVersionDiffPayload.TermsOfAccess 17 | } 18 | return retValue 19 | } 20 | -------------------------------------------------------------------------------- /src/files/domain/clients/DirectUploadClientError.ts: -------------------------------------------------------------------------------- 1 | export class DirectUploadClientError extends Error { 2 | fileName: string 3 | datasetId: number | string 4 | 5 | constructor(fileName: string, datasetId: number | string, reason: string) { 6 | super(reason) 7 | this.datasetId = datasetId 8 | this.fileName = fileName 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/files/domain/clients/IDirectUploadClient.ts: -------------------------------------------------------------------------------- 1 | import { FileUploadDestination } from '../models/FileUploadDestination' 2 | 3 | export interface IDirectUploadClient { 4 | uploadFile( 5 | datasetId: number | string, 6 | file: File, 7 | progress: (now: number) => void, 8 | abortController: AbortController, 9 | destination?: FileUploadDestination 10 | ): Promise 11 | } 12 | -------------------------------------------------------------------------------- /src/files/domain/dtos/RestrictFileDTO.ts: -------------------------------------------------------------------------------- 1 | export interface RestrictFileDTO { 2 | restrict: boolean 3 | enableAccessRequest?: boolean 4 | termsOfAccess?: string 5 | } 6 | -------------------------------------------------------------------------------- /src/files/domain/dtos/UpdateFileMetadataDTO.ts: -------------------------------------------------------------------------------- 1 | export interface UpdateFileMetadataDTO { 2 | label?: string 3 | directoryLabel?: string 4 | description?: string 5 | prevFreeform?: string 6 | categories?: string[] 7 | dataFileTags?: string[] 8 | restrict?: boolean 9 | } 10 | -------------------------------------------------------------------------------- /src/files/domain/dtos/UploadedFileDTO.ts: -------------------------------------------------------------------------------- 1 | export interface UploadedFileDTO { 2 | fileName: string 3 | description?: string 4 | directoryLabel?: string 5 | categories?: string[] 6 | restrict?: boolean 7 | storageId: string 8 | checksumValue: string 9 | checksumType: string 10 | mimeType: string 11 | forceReplace?: boolean // Only used in the ReplaceFile use case, whether to allow the mimetype to change 12 | } 13 | -------------------------------------------------------------------------------- /src/files/domain/models/FileCounts.ts: -------------------------------------------------------------------------------- 1 | import { FileAccessStatus } from './FileCriteria' 2 | 3 | export interface FileCounts { 4 | total: number 5 | perContentType: FileContentTypeCount[] 6 | perAccessStatus: FileAccessStatusCount[] 7 | perCategoryName: FileCategoryNameCount[] 8 | } 9 | 10 | export interface FileContentTypeCount { 11 | contentType: string 12 | count: number 13 | } 14 | 15 | export interface FileAccessStatusCount { 16 | accessStatus: FileAccessStatus 17 | count: number 18 | } 19 | 20 | export interface FileCategoryNameCount { 21 | categoryName: string 22 | count: number 23 | } 24 | -------------------------------------------------------------------------------- /src/files/domain/models/FileDownloadSizeMode.ts: -------------------------------------------------------------------------------- 1 | export enum FileDownloadSizeMode { 2 | ALL = 'All', 3 | ORIGINAL = 'Original', 4 | ARCHIVAL = 'Archival' 5 | } 6 | -------------------------------------------------------------------------------- /src/files/domain/models/FileModel.ts: -------------------------------------------------------------------------------- 1 | import { DvObjectOwnerNode } from '../../../core/domain/models/DvObjectOwnerNode' 2 | 3 | export interface FileModel { 4 | id: number 5 | persistentId: string 6 | name: string 7 | pidURL?: string 8 | sizeBytes: number 9 | version: number 10 | description?: string 11 | restricted: boolean 12 | latestRestricted: boolean 13 | directoryLabel?: string 14 | datasetVersionId?: number 15 | categories?: string[] 16 | contentType: string 17 | friendlyType: string 18 | embargo?: FileEmbargo 19 | storageIdentifier?: string 20 | originalFormat?: string 21 | originalFormatLabel?: string 22 | originalSize?: number 23 | originalName?: string 24 | UNF?: string 25 | rootDataFileId?: number 26 | previousDataFileId?: number 27 | md5?: string 28 | checksum?: FileChecksum 29 | metadataId?: number 30 | tabularTags?: string[] 31 | creationDate?: string 32 | publicationDate?: string 33 | deleted: boolean 34 | tabularData: boolean 35 | fileAccessRequest?: boolean 36 | isPartOf?: DvObjectOwnerNode 37 | } 38 | 39 | export interface FileEmbargo { 40 | dateAvailable: Date 41 | reason?: string 42 | } 43 | 44 | export interface FileChecksum { 45 | type: string 46 | value: string 47 | } 48 | -------------------------------------------------------------------------------- /src/files/domain/models/FilePreview.ts: -------------------------------------------------------------------------------- 1 | import { CollectionItemType } from '../../../collections' 2 | import { PublicationStatus } from '../../../core/domain/models/PublicationStatus' 3 | 4 | export interface FilePreview { 5 | type: CollectionItemType.FILE 6 | name: string 7 | url: string 8 | imageUrl?: string 9 | fileId: number 10 | filePersistentId?: string 11 | description: string 12 | fileType: string 13 | fileContentType: string 14 | sizeInBytes: number 15 | md5?: string 16 | checksum?: FilePreviewChecksum 17 | unf: string 18 | datasetName: string 19 | datasetId: number 20 | datasetPersistentId: string 21 | datasetCitation: string 22 | publicationStatuses: PublicationStatus[] 23 | releaseOrCreateDate: Date 24 | restricted: boolean 25 | canDownloadFile: boolean 26 | categories?: string[] 27 | tabularTags?: string[] 28 | variables?: number 29 | observations?: number 30 | userRoles?: string[] 31 | } 32 | 33 | export interface FilePreviewChecksum { 34 | type: string 35 | value: string 36 | } 37 | -------------------------------------------------------------------------------- /src/files/domain/models/FileUploadDestination.ts: -------------------------------------------------------------------------------- 1 | export interface FileUploadDestination { 2 | urls: string[] 3 | storageId: string 4 | partSize: number 5 | abortEndpoint?: string 6 | completeEndpoint?: string 7 | } 8 | -------------------------------------------------------------------------------- /src/files/domain/models/FileUserPermissions.ts: -------------------------------------------------------------------------------- 1 | export interface FileUserPermissions { 2 | canDownloadFile: boolean 3 | canManageFilePermissions: boolean 4 | canEditOwnerDataset: boolean 5 | } 6 | -------------------------------------------------------------------------------- /src/files/domain/models/FileVersionSummaryInfo.ts: -------------------------------------------------------------------------------- 1 | import { DatasetVersionState } from '../../../datasets/domain/models/Dataset' 2 | 3 | export interface FileVersionSummaryInfo { 4 | datasetVersion: string 5 | contributors?: string 6 | publishedDate?: string 7 | fileDifferenceSummary?: FileDifferenceSummary 8 | versionState?: DatasetVersionState 9 | datafileId: number 10 | persistentId?: string 11 | versionNote?: string 12 | } 13 | 14 | export type FileDifferenceSummary = { 15 | file?: FileChangeType 16 | fileAccess?: 'Restricted' | 'Unrestricted' 17 | fileMetadata?: FileMetadataChange[] 18 | deaccessionedReason?: string 19 | fileTags?: { [key in FileChangeType]?: number } 20 | } 21 | 22 | export type FileChangeType = 'Added' | 'Deleted' | 'Replaced' | 'Changed' 23 | 24 | export interface FileMetadataChange { 25 | name: string 26 | action: FileChangeType 27 | } 28 | -------------------------------------------------------------------------------- /src/files/domain/models/FilesSubset.ts: -------------------------------------------------------------------------------- 1 | import { FileModel } from './FileModel' 2 | 3 | export interface FilesSubset { 4 | files: FileModel[] 5 | totalFilesCount: number 6 | } 7 | -------------------------------------------------------------------------------- /src/files/domain/useCases/DeleteFile.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../repositories/IFilesRepository' 2 | import { UseCase } from '../../../core/domain/useCases/UseCase' 3 | 4 | export class DeleteFile implements UseCase { 5 | constructor(private readonly filesRepository: IFilesRepository) {} 6 | 7 | /** 8 | * Deletes a file. 9 | * More detailed information about the file deletion behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#deleting-files 10 | * 11 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 12 | * @returns {Promise} -This method does not return anything upon successful completion. 13 | */ 14 | async execute(fileId: number | string): Promise { 15 | return await this.filesRepository.deleteFile(fileId) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFile.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../repositories/IFilesRepository' 2 | import { FileModel } from '../models/FileModel' 3 | import { DatasetNotNumberedVersion } from '../../../datasets' 4 | import { UseCase } from '../../../core/domain/useCases/UseCase' 5 | 6 | export class GetFile implements UseCase { 7 | constructor(private readonly filesRepository: IFilesRepository) {} 8 | 9 | /** 10 | * Returns a FileModel instance, given the search parameters to identify it. 11 | * 12 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 13 | * @param {string | DatasetNotNumberedVersion} [datasetVersionId=DatasetNotNumberedVersion.LATEST] - The dataset version identifier, which can be a version-specific numeric string (for example, 1.0) or a DatasetNotNumberedVersion enum value. If this parameter is not set, the default value is: DatasetNotNumberedVersion.LATEST 14 | * @returns {Promise} 15 | */ 16 | async execute( 17 | fileId: number | string, 18 | datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST 19 | ): Promise { 20 | return (await this.filesRepository.getFile(fileId, datasetVersionId, false)) as FileModel 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileAndDataset.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../repositories/IFilesRepository' 2 | import { FileModel } from '../models/FileModel' 3 | import { DatasetNotNumberedVersion, Dataset } from '../../../datasets' 4 | import { UseCase } from '../../../core/domain/useCases/UseCase' 5 | 6 | export class GetFileAndDataset implements UseCase<[FileModel, Dataset]> { 7 | constructor(private readonly filesRepository: IFilesRepository) {} 8 | 9 | /** 10 | * Returns a tuple including the FileModel instance and the associated Dataset, given the search parameters to identify the File. 11 | * 12 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 13 | * @param {string | DatasetNotNumberedVersion} [datasetVersionId=DatasetNotNumberedVersion.LATEST] - The dataset version identifier, which can be a version-specific numeric string (for example, 1.0) or a DatasetNotNumberedVersion enum value. If this parameter is not set, the default value is: DatasetNotNumberedVersion.LATEST 14 | * @returns {Promise<[FileModel, Dataset]>} 15 | */ 16 | async execute( 17 | fileId: number | string, 18 | datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST 19 | ): Promise<[FileModel, Dataset]> { 20 | return (await this.filesRepository.getFile(fileId, datasetVersionId, true)) as [ 21 | FileModel, 22 | Dataset 23 | ] 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileCitation.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | import { DatasetNotNumberedVersion } from '../../../datasets' 4 | 5 | export class GetFileCitation implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * Returns the File citation text. 14 | * 15 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @param {string | DatasetNotNumberedVersion} [datasetVersionId=DatasetNotNumberedVersion.LATEST] - The dataset version identifier, which can be a version-specific numeric string (for example, 1.0) or a DatasetNotNumberedVersion enum value. If this parameter is not set, the default value is: DatasetNotNumberedVersion.LATEST 17 | * @param {boolean} [includeDeaccessioned=false] - Indicates whether to consider deaccessioned versions in the dataset search or not. The default value is false 18 | * @returns {Promise} 19 | */ 20 | async execute( 21 | fileId: number, 22 | datasetVersionId: string | DatasetNotNumberedVersion = DatasetNotNumberedVersion.LATEST, 23 | includeDeaccessioned = false 24 | ): Promise { 25 | return await this.filesRepository.getFileCitation( 26 | fileId, 27 | datasetVersionId, 28 | includeDeaccessioned 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileDataTables.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | import { FileDataTable } from '../models/FileDataTable' 4 | 5 | export class GetFileDataTables implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * This use case is oriented toward tabular files and provides an array of FileDataTable objects for an existing tabular file. 14 | * 15 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @returns {Promise} 17 | */ 18 | async execute(fileId: number | string): Promise { 19 | return await this.filesRepository.getFileDataTables(fileId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileDownloadCount.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | 4 | export class GetFileDownloadCount implements UseCase { 5 | private filesRepository: IFilesRepository 6 | 7 | constructor(filesRepository: IFilesRepository) { 8 | this.filesRepository = filesRepository 9 | } 10 | 11 | /** 12 | * Provides the download count for a particular File. 13 | * 14 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 15 | * @returns {Promise} 16 | */ 17 | async execute(fileId: number | string): Promise { 18 | return await this.filesRepository.getFileDownloadCount(fileId) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileUserPermissions.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | import { FileUserPermissions } from '../models/FileUserPermissions' 4 | 5 | export class GetFileUserPermissions implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * Returns a FileUserPermissions object, which includes the permissions that the calling user has on a particular File. 14 | * 15 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @returns {Promise} 17 | */ 18 | async execute(fileId: number | string): Promise { 19 | return await this.filesRepository.getFileUserPermissions(fileId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/files/domain/useCases/GetFileVersionSummaries.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { FileVersionSummaryInfo } from '../models/FileVersionSummaryInfo' 3 | import { IFilesRepository } from '../repositories/IFilesRepository' 4 | 5 | export class GetFileVersionSummaries implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * Returns a list of versions for a given file including a summary of differences between consecutive versions 14 | * 15 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @returns {Promise} - An array of FileVersionSummaryInfo. 17 | */ 18 | async execute(fileId: number | string): Promise { 19 | return await this.filesRepository.getFileVersionSummaries(fileId) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/files/domain/useCases/IsFileDeleted.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../repositories/IFilesRepository' 2 | import { UseCase } from '../../../core/domain/useCases/UseCase' 3 | 4 | export class IsFileDeleted implements UseCase { 5 | constructor(private readonly filesRepository: IFilesRepository) {} 6 | 7 | /** 8 | * Returns a boolean, indicating whether the file has been deleted or not. 9 | * 10 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 11 | * @returns {Promise} - A boolean indicating whether the file has been deleted or not. 12 | */ 13 | async execute(fileId: number | string): Promise { 14 | return await this.filesRepository.isFileDeleted(fileId) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/files/domain/useCases/ReplaceFile.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { UploadedFileDTO } from '../dtos/UploadedFileDTO' 3 | import { IFilesRepository } from '../repositories/IFilesRepository' 4 | 5 | export class ReplaceFile implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * Replaces an existing file. 14 | * 15 | * This method completes the flow initiated by the UploadFile use case, which involves replacing an existing file with a new one just uploaded. 16 | * (https://guides.dataverse.org/en/latest/developers/s3-direct-upload-api.html#replacing-an-existing-file-in-the-dataset) 17 | * 18 | * Note: This use case can be used independently of the UploadFile use case, e.g., supporting scenarios in which the files already exist in S3 or have been uploaded via some out-of-band method. 19 | * 20 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 21 | * @param {UploadedFileDTO} [uploadedFileDTO] - File DTO associated with the uploaded file. 22 | * @returns {Promise} A promise that resolves when the file has been successfully replaced and returns the new file identifier. 23 | * @throws {WriteError} - If there are errors while writing data. 24 | */ 25 | async execute(fileId: number | string, uploadedFileDTO: UploadedFileDTO): Promise { 26 | return await this.filesRepository.replaceFile(fileId, uploadedFileDTO) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/files/domain/useCases/RestrictFile.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../repositories/IFilesRepository' 2 | import { UseCase } from '../../../core/domain/useCases/UseCase' 3 | import { RestrictFileDTO } from '../dtos/RestrictFileDTO' 4 | 5 | export class RestrictFile implements UseCase { 6 | constructor(private readonly filesRepository: IFilesRepository) {} 7 | 8 | /** 9 | * Restrict or unrestrict an existing file. 10 | * More detailed information about the file restriction behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#restrict-files 11 | * 12 | * @param {number | string} [fileId] - The File identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 13 | * @param {RestrictFileDTO} [restrictFileDTO] - The DTO containing the file restriction information. 14 | * @returns {Promise} -This method does not return anything upon successful completion. 15 | */ 16 | 17 | async execute(fileId: number | string, restrictFileDTO: RestrictFileDTO): Promise { 18 | return await this.filesRepository.restrictFile(fileId, restrictFileDTO) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/files/domain/useCases/UpdateFileCategories.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | 4 | export class UpdateFileCategories implements UseCase { 5 | private filesRepository: IFilesRepository 6 | 7 | constructor(filesRepository: IFilesRepository) { 8 | this.filesRepository = filesRepository 9 | } 10 | 11 | /** 12 | * Updates the categories for a particular File. 13 | * More detailed information about updating a file's categories behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#updating-file-metadata 14 | * 15 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @param {string[]} [categories] - The categories to be added to the file. 17 | * @param {boolean} [replace](optional) - If true, replaces the existing categories with the new ones. If false, adds the new categories to the existing ones. 18 | * @returns {Promise} 19 | */ 20 | async execute(fileId: number | string, categories: string[], replace?: boolean): Promise { 21 | await this.filesRepository.updateFileCategories(fileId, categories, replace) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/files/domain/useCases/UpdateFileMetadata.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | import { UpdateFileMetadataDTO } from '../dtos/UpdateFileMetadataDTO' 4 | 5 | export class UpdateFileMetadata implements UseCase { 6 | private filesRepository: IFilesRepository 7 | 8 | constructor(filesRepository: IFilesRepository) { 9 | this.filesRepository = filesRepository 10 | } 11 | 12 | /** 13 | * Updates the metadata for a particular File. 14 | * More detailed information about updating a file's metadata behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#updating-file-metadata 15 | * 16 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 17 | * @param {UpdateFileMetadataDTO} [updateFileMetadataDTO] - The DTO containing the metadata updates. 18 | * @returns {Promise} 19 | */ 20 | async execute( 21 | fileId: number | string, 22 | updateFileMetadataDTO: UpdateFileMetadataDTO 23 | ): Promise { 24 | await this.filesRepository.updateFileMetadata(fileId, updateFileMetadataDTO) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/files/domain/useCases/UpdateFileTabularTags.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IFilesRepository } from '../repositories/IFilesRepository' 3 | 4 | export class UpdateFileTabularTags implements UseCase { 5 | private filesRepository: IFilesRepository 6 | 7 | constructor(filesRepository: IFilesRepository) { 8 | this.filesRepository = filesRepository 9 | } 10 | 11 | /** 12 | * Updates the tabular tabular Tags for a particular File. 13 | * More detailed information about updating a file's tabularTags behavior can be found in https://guides.dataverse.org/en/latest/api/native-api.html#updating-file-metadata 14 | * 15 | * @param {number | string} [fileId] - The file identifier, which can be a string (for persistent identifiers), or a number (for numeric identifiers). 16 | * @param {string[]} [tabularTags] - The tabular tags to be added to the file. 17 | * @param {boolean} [replace](optional) - If true, replaces the existing tabularTags with the new ones. If false, adds the new tabularTags to the existing ones. 18 | * @returns {Promise} 19 | */ 20 | async execute(fileId: number | string, tabularTags: string[], replace?: boolean): Promise { 21 | await this.filesRepository.updateFileTabularTags(fileId, tabularTags, replace) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/AddUploadedFileToDatasetError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class AddUploadedFileToDatasetError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string, reason: string) { 5 | super(fileName, datasetId, reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/FilePartUploadError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class FilePartUploadError extends DirectUploadClientError { 4 | partNumber: number 5 | 6 | constructor(fileName: string, datasetId: number | string, reason: string, partNumber: number) { 7 | super(fileName, datasetId, reason) 8 | this.partNumber = partNumber 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/FileUploadCancelError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class FileUploadCancelError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string) { 5 | super(fileName, datasetId, 'Upload aborted') 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/FileUploadError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class FileUploadError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string, reason: string) { 5 | super(fileName, datasetId, reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/MultipartAbortError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class MultipartAbortError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string, reason: string) { 5 | super(fileName, datasetId, reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/MultipartCompletionError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class MultipartCompletionError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string, reason: string) { 5 | super(fileName, datasetId, reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/clients/errors/UrlGenerationError.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../domain/clients/DirectUploadClientError' 2 | 3 | export class UrlGenerationError extends DirectUploadClientError { 4 | constructor(fileName: string, datasetId: number | string, reason: string) { 5 | super(fileName, datasetId, reason) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/files/infra/repositories/transformers/FilePayload.ts: -------------------------------------------------------------------------------- 1 | import { OwnerNodePayload } from '../../../../core/infra/repositories/transformers/OwnerNodePayload' 2 | 3 | export interface FilePayload { 4 | dataFile: { 5 | id: number 6 | persistentId: string 7 | filename: string 8 | pidURL?: string 9 | filesize: number 10 | version: number 11 | description?: string 12 | restricted: boolean 13 | datasetVersionId?: number 14 | categories?: string[] 15 | contentType: string 16 | friendlyType: string 17 | embargo?: EmbargoPayload 18 | storageIdentifier?: string 19 | originalFormat?: string 20 | originalFormatLabel?: string 21 | originalSize?: number 22 | originalName?: string 23 | UNF?: string 24 | rootDataFileId?: number 25 | previousDataFileId?: number 26 | md5?: string 27 | checksum?: ChecksumPayload 28 | fileMetadataId?: number 29 | tabularTags?: string[] 30 | creationDate?: string 31 | publicationDate?: string 32 | deleted: boolean 33 | tabularData: boolean 34 | fileAccessRequest?: boolean 35 | isPartOf?: OwnerNodePayload 36 | } 37 | version: number 38 | restricted: boolean 39 | directoryLabel?: string 40 | label: string 41 | datasetVersionId: number 42 | } 43 | 44 | export interface EmbargoPayload { 45 | dateAvailable: string 46 | reason?: string 47 | } 48 | 49 | export interface ChecksumPayload { 50 | type: string 51 | value: string 52 | } 53 | -------------------------------------------------------------------------------- /src/files/infra/repositories/transformers/FilePreviewPayload.ts: -------------------------------------------------------------------------------- 1 | export interface FilePreviewPayload { 2 | name: string 3 | url: string 4 | image_url?: string 5 | file_id: string 6 | file_persistent_id?: string 7 | description: string 8 | published_at?: string 9 | file_type: string 10 | file_content_type: string 11 | size_in_bytes: number 12 | md5?: string 13 | checksum?: FilePreviewChecksumPayload 14 | type?: string 15 | unf: string 16 | dataset_name: string 17 | dataset_id: string 18 | dataset_persistent_id: string 19 | dataset_citation: string 20 | publicationStatuses: string[] 21 | releaseOrCreateDate: string 22 | restricted: boolean 23 | canDownloadFile: boolean 24 | categories?: string[] 25 | tabularTags?: string[] 26 | variables?: number 27 | observations?: number 28 | } 29 | 30 | export interface FilePreviewChecksumPayload { 31 | type: string 32 | value: string 33 | } 34 | -------------------------------------------------------------------------------- /src/files/infra/repositories/transformers/MyDataFilePreviewPayload.ts: -------------------------------------------------------------------------------- 1 | import { FilePreviewChecksumPayload } from '../../../../../src/files/infra/repositories/transformers/FilePreviewPayload' 2 | 3 | export interface MyDataFilePreviewPayload { 4 | name: string 5 | type: string 6 | url: string 7 | file_id: string 8 | file_type: string 9 | file_content_type: string 10 | size_in_bytes: number 11 | md5: string 12 | checksum: FilePreviewChecksumPayload 13 | unf: string 14 | dataset_name: string 15 | dataset_id: string 16 | dataset_persistent_id: string 17 | dataset_citation: string 18 | restricted: boolean 19 | canDownloadFile: boolean 20 | matches: string[] 21 | score: number 22 | entity_id: number 23 | publicationStatuses: string[] 24 | releaseOrCreateDate: string 25 | is_draft_state: boolean 26 | is_in_review_state: boolean 27 | is_unpublished_state: boolean 28 | is_published: boolean 29 | is_deaccesioned: boolean 30 | is_valid: boolean 31 | date_to_display_on_card: string 32 | parentIdentifier: string 33 | parentName: string 34 | user_roles: string[] 35 | image_url?: string 36 | variables?: number 37 | observations?: number 38 | file_persistent_id?: string 39 | description?: string 40 | } 41 | -------------------------------------------------------------------------------- /src/files/infra/repositories/transformers/fileUserPermissionsTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { FileUserPermissions } from '../../../domain/models/FileUserPermissions' 3 | 4 | export const transformFileUserPermissionsResponseToFileUserPermissions = ( 5 | response: AxiosResponse 6 | ): FileUserPermissions => { 7 | const fileUserPermissionsPayload = response.data.data 8 | return { 9 | canDownloadFile: fileUserPermissionsPayload.canDownloadFile, 10 | canManageFilePermissions: fileUserPermissionsPayload.canManageFilePermissions, 11 | canEditOwnerDataset: fileUserPermissionsPayload.canEditOwnerDataset 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './core' 2 | export * from './info' 3 | export * from './users' 4 | export * from './auth' 5 | export * from './datasets' 6 | export * from './collections' 7 | export * from './metadataBlocks' 8 | export * from './files' 9 | export * from './contactInfo' 10 | -------------------------------------------------------------------------------- /src/info/domain/models/DataverseVersion.ts: -------------------------------------------------------------------------------- 1 | export interface DataverseVersion { 2 | number: string 3 | build: string 4 | } 5 | -------------------------------------------------------------------------------- /src/info/domain/repositories/IDataverseInfoRepository.ts: -------------------------------------------------------------------------------- 1 | import { DataverseVersion } from '../models/DataverseVersion' 2 | 3 | export interface IDataverseInfoRepository { 4 | getDataverseVersion(): Promise 5 | getZipDownloadLimit(): Promise 6 | getMaxEmbargoDurationInMonths(): Promise 7 | getApplicationTermsOfUse(lang?: string): Promise 8 | } 9 | -------------------------------------------------------------------------------- /src/info/domain/useCases/GetApplicationTermsOfUse.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDataverseInfoRepository } from '../repositories/IDataverseInfoRepository' 3 | 4 | export class GetApplicationTermsOfUse implements UseCase { 5 | private dataverseInfoRepository: IDataverseInfoRepository 6 | 7 | constructor(dataverseInfoRepository: IDataverseInfoRepository) { 8 | this.dataverseInfoRepository = dataverseInfoRepository 9 | } 10 | 11 | /** 12 | * Returns a string containing the application terms of use. 13 | * 14 | * @param {string} [lang] - Optional language parameter to specify the language of the terms of use. 15 | * @returns {Promise} 16 | */ 17 | async execute(lang?: string): Promise { 18 | return await this.dataverseInfoRepository.getApplicationTermsOfUse(lang) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/info/domain/useCases/GetDataverseVersion.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDataverseInfoRepository } from '../repositories/IDataverseInfoRepository' 3 | import { DataverseVersion } from '../models/DataverseVersion' 4 | 5 | export class GetDataverseVersion implements UseCase { 6 | private dataverseInfoRepository: IDataverseInfoRepository 7 | 8 | constructor(dataverseInfoRepository: IDataverseInfoRepository) { 9 | this.dataverseInfoRepository = dataverseInfoRepository 10 | } 11 | 12 | /** 13 | * Returns a DataverseVersion object, which contains version information for the Dataverse backend installation. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.dataverseInfoRepository.getDataverseVersion() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/info/domain/useCases/GetMaxEmbargoDurationInMonths.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDataverseInfoRepository } from '../repositories/IDataverseInfoRepository' 3 | 4 | export class GetMaxEmbargoDurationInMonths implements UseCase { 5 | private dataverseInfoRepository: IDataverseInfoRepository 6 | 7 | constructor(dataverseInfoRepository: IDataverseInfoRepository) { 8 | this.dataverseInfoRepository = dataverseInfoRepository 9 | } 10 | 11 | /** 12 | * Returns a number indicating the configured maximum embargo duration in months. 13 | * 14 | * @returns {Promise} 15 | */ 16 | async execute(): Promise { 17 | return await this.dataverseInfoRepository.getMaxEmbargoDurationInMonths() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/info/domain/useCases/GetZipDownloadLimit.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IDataverseInfoRepository } from '../repositories/IDataverseInfoRepository' 3 | 4 | export class GetZipDownloadLimit implements UseCase { 5 | private dataverseInfoRepository: IDataverseInfoRepository 6 | 7 | constructor(dataverseInfoRepository: IDataverseInfoRepository) { 8 | this.dataverseInfoRepository = dataverseInfoRepository 9 | } 10 | 11 | /** 12 | * Returns a number indicating the configured ZIP download limit in bytes. 13 | * 14 | * @returns {Promise} 15 | */ 16 | async execute(): Promise { 17 | return await this.dataverseInfoRepository.getZipDownloadLimit() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/info/index.ts: -------------------------------------------------------------------------------- 1 | import { DataverseInfoRepository } from './infra/repositories/DataverseInfoRepository' 2 | import { GetDataverseVersion } from './domain/useCases/GetDataverseVersion' 3 | import { GetZipDownloadLimit } from './domain/useCases/GetZipDownloadLimit' 4 | import { GetMaxEmbargoDurationInMonths } from './domain/useCases/GetMaxEmbargoDurationInMonths' 5 | import { GetApplicationTermsOfUse } from './domain/useCases/GetApplicationTermsOfUse' 6 | 7 | const dataverseInfoRepository = new DataverseInfoRepository() 8 | 9 | const getDataverseVersion = new GetDataverseVersion(dataverseInfoRepository) 10 | const getZipDownloadLimit = new GetZipDownloadLimit(dataverseInfoRepository) 11 | const getMaxEmbargoDurationInMonths = new GetMaxEmbargoDurationInMonths(dataverseInfoRepository) 12 | const getApplicationTermsOfUse = new GetApplicationTermsOfUse(dataverseInfoRepository) 13 | 14 | export { 15 | getDataverseVersion, 16 | getZipDownloadLimit, 17 | getMaxEmbargoDurationInMonths, 18 | getApplicationTermsOfUse 19 | } 20 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/repositories/IMetadataBlocksRepository.ts: -------------------------------------------------------------------------------- 1 | import { MetadataBlock } from '../models/MetadataBlock' 2 | 3 | export interface IMetadataBlocksRepository { 4 | getMetadataBlockByName(metadataBlockName: string): Promise 5 | 6 | getCollectionMetadataBlocks( 7 | collectionIdOrAlias: number | string, 8 | onlyDisplayedOnCreate: boolean 9 | ): Promise 10 | 11 | getAllMetadataBlocks(): Promise 12 | } 13 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/repositories/IMetadataFieldInfosRepository.ts: -------------------------------------------------------------------------------- 1 | import { MetadataFieldInfo } from '../..' 2 | 3 | export interface IMetadataFieldInfosRepository { 4 | getAllFacetableMetadataFields(): Promise 5 | } 6 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/useCases/GetAllFacetableMetadataFields.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { MetadataFieldInfo } from '../..' 3 | import { IMetadataFieldInfosRepository } from '../repositories/IMetadataFieldInfosRepository' 4 | 5 | export class GetAllFacetableMetadataFields implements UseCase { 6 | private metadataFieldInfosRepository: IMetadataFieldInfosRepository 7 | 8 | constructor(metadataFieldInfosRepository: IMetadataFieldInfosRepository) { 9 | this.metadataFieldInfosRepository = metadataFieldInfosRepository 10 | } 11 | 12 | /** 13 | * Returns a MetadataFieldInfo array containing all facetable metadata fields defined in the installation. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.metadataFieldInfosRepository.getAllFacetableMetadataFields() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/useCases/GetAllMetadataBlocks.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { MetadataBlock } from '../..' 3 | import { IMetadataBlocksRepository } from '../repositories/IMetadataBlocksRepository' 4 | 5 | export class GetAllMetadataBlocks implements UseCase { 6 | private metadataBlocksRepository: IMetadataBlocksRepository 7 | 8 | constructor(metadataBlocksRepository: IMetadataBlocksRepository) { 9 | this.metadataBlocksRepository = metadataBlocksRepository 10 | } 11 | 12 | /** 13 | * Returns a MetadataBlock array containing the metadata blocks defined in the installation. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.metadataBlocksRepository.getAllMetadataBlocks() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/useCases/GetCollectionMetadataBlocks.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { MetadataBlock } from '../..' 3 | import { ROOT_COLLECTION_ID } from '../../../collections/domain/models/Collection' 4 | import { IMetadataBlocksRepository } from '../repositories/IMetadataBlocksRepository' 5 | 6 | export class GetCollectionMetadataBlocks implements UseCase { 7 | private metadataBlocksRepository: IMetadataBlocksRepository 8 | 9 | constructor(metadataBlocksRepository: IMetadataBlocksRepository) { 10 | this.metadataBlocksRepository = metadataBlocksRepository 11 | } 12 | 13 | /** 14 | * Returns a MetadataBlock array containing the metadata blocks from the requested collection. 15 | * 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 | * @param {boolean} [onlyDisplayedOnCreate=false] - Indicates whether or not to return only the metadata blocks that are displayed on dataset creation. The default value is false. 19 | * @returns {Promise} 20 | */ 21 | async execute( 22 | collectionIdOrAlias: number | string = ROOT_COLLECTION_ID, 23 | onlyDisplayedOnCreate = false 24 | ): Promise { 25 | return await this.metadataBlocksRepository.getCollectionMetadataBlocks( 26 | collectionIdOrAlias, 27 | onlyDisplayedOnCreate 28 | ) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/metadataBlocks/domain/useCases/GetMetadataBlockByName.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IMetadataBlocksRepository } from '../repositories/IMetadataBlocksRepository' 3 | import { MetadataBlock } from '../models/MetadataBlock' 4 | 5 | export class GetMetadataBlockByName implements UseCase { 6 | private metadataBlocksRepository: IMetadataBlocksRepository 7 | 8 | constructor(metadataBlocksRepository: IMetadataBlocksRepository) { 9 | this.metadataBlocksRepository = metadataBlocksRepository 10 | } 11 | 12 | /** 13 | * Returns a MetadataBlock instance, given its name. 14 | * 15 | * @param {string} [metadataBlockName] - The requested metadata block name. 16 | * @returns {Promise} 17 | */ 18 | async execute(metadataBlockName: string): Promise { 19 | return await this.metadataBlocksRepository.getMetadataBlockByName(metadataBlockName) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/metadataBlocks/index.ts: -------------------------------------------------------------------------------- 1 | import { GetMetadataBlockByName } from './domain/useCases/GetMetadataBlockByName' 2 | import { MetadataBlocksRepository } from './infra/repositories/MetadataBlocksRepository' 3 | import { GetCollectionMetadataBlocks } from './domain/useCases/GetCollectionMetadataBlocks' 4 | import { GetAllFacetableMetadataFields } from './domain/useCases/GetAllFacetableMetadataFields' 5 | import { MetadataFieldInfosRepository } from './infra/repositories/MetadataFieldInfosRepository' 6 | import { GetAllMetadataBlocks } from './domain/useCases/GetAllMetadataBlocks' 7 | 8 | const metadataBlocksRepository = new MetadataBlocksRepository() 9 | const metadataFieldInfosRepository = new MetadataFieldInfosRepository() 10 | 11 | const getMetadataBlockByName = new GetMetadataBlockByName(metadataBlocksRepository) 12 | const getCollectionMetadataBlocks = new GetCollectionMetadataBlocks(metadataBlocksRepository) 13 | const getAllFacetableMetadataFields = new GetAllFacetableMetadataFields( 14 | metadataFieldInfosRepository 15 | ) 16 | const getAllMetadataBlocks = new GetAllMetadataBlocks(metadataBlocksRepository) 17 | 18 | export { 19 | getMetadataBlockByName, 20 | getCollectionMetadataBlocks, 21 | getAllFacetableMetadataFields, 22 | getAllMetadataBlocks 23 | } 24 | 25 | export { 26 | MetadataBlock, 27 | MetadataFieldInfo, 28 | MetadataFieldType, 29 | MetadataFieldTypeClass 30 | } from './domain/models/MetadataBlock' 31 | -------------------------------------------------------------------------------- /src/metadataBlocks/infra/repositories/MetadataFieldInfosRepository.ts: -------------------------------------------------------------------------------- 1 | import { ApiRepository } from '../../../core/infra/repositories/ApiRepository' 2 | import { MetadataFieldInfo } from '../..' 3 | import { IMetadataFieldInfosRepository } from '../../domain/repositories/IMetadataFieldInfosRepository' 4 | import { transformMetadataFieldInfosResponseToMetadataFieldInfos } from './transformers/metadataBlockTransformers' 5 | 6 | export class MetadataFieldInfosRepository 7 | extends ApiRepository 8 | implements IMetadataFieldInfosRepository 9 | { 10 | private readonly datasetFieldsResourceName: string = 'datasetfields' 11 | 12 | public async getAllFacetableMetadataFields(): Promise { 13 | return this.doGet(`/${this.datasetFieldsResourceName}/facetables`, false) 14 | .then((response) => transformMetadataFieldInfosResponseToMetadataFieldInfos(response)) 15 | .catch((error) => { 16 | throw error 17 | }) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/metadataBlocks/infra/repositories/transformers/MetadataBlockPayload.ts: -------------------------------------------------------------------------------- 1 | export interface MetadataBlockPayload { 2 | id: number 3 | name: string 4 | displayName: string 5 | displayOnCreate: boolean 6 | fields: Record 7 | } 8 | -------------------------------------------------------------------------------- /src/metadataBlocks/infra/repositories/transformers/MetadataFieldInfoPayload.ts: -------------------------------------------------------------------------------- 1 | export interface MetadataFieldInfoPayload { 2 | name: string 3 | displayName: string 4 | displayOnCreate: boolean 5 | title: string 6 | type: string 7 | typeClass: string 8 | watermark: string 9 | description: string 10 | multiple: boolean 11 | isControlledVocabulary: boolean 12 | displayFormat: string 13 | displayOrder: number 14 | isRequired: boolean 15 | controlledVocabularyValues?: string[] 16 | childMetadataFields?: Record 17 | } 18 | -------------------------------------------------------------------------------- /src/users/domain/dtos/UserDTO.ts: -------------------------------------------------------------------------------- 1 | export interface UserDTO { 2 | username?: string 3 | firstName?: string 4 | lastName?: string 5 | emailAddress?: string 6 | position?: string 7 | affiliation?: string 8 | termsAccepted: boolean 9 | } 10 | -------------------------------------------------------------------------------- /src/users/domain/models/ApiTokenInfo.ts: -------------------------------------------------------------------------------- 1 | export interface ApiTokenInfo { 2 | apiToken: string 3 | expirationDate: Date 4 | } 5 | -------------------------------------------------------------------------------- /src/users/domain/models/AuthenticatedUser.ts: -------------------------------------------------------------------------------- 1 | export interface AuthenticatedUser { 2 | id: number 3 | persistentUserId: string 4 | identifier: string 5 | displayName: string 6 | firstName: string 7 | lastName: string 8 | email: string 9 | superuser: boolean 10 | deactivated: boolean 11 | createdTime: string 12 | authenticationProviderId: string 13 | lastLoginTime?: string 14 | lastApiUseTime?: string 15 | deactivatedTime?: string 16 | affiliation?: string 17 | position?: string 18 | emailLastConfirmed?: string 19 | } 20 | -------------------------------------------------------------------------------- /src/users/domain/repositories/IUsersRepository.ts: -------------------------------------------------------------------------------- 1 | import { ApiTokenInfo } from '../models/ApiTokenInfo' 2 | import { AuthenticatedUser } from '../models/AuthenticatedUser' 3 | import { UserDTO } from '../dtos/UserDTO' 4 | 5 | export interface IUsersRepository { 6 | getCurrentAuthenticatedUser(): Promise 7 | recreateCurrentApiToken(): Promise 8 | getCurrentApiToken(): Promise 9 | deleteCurrentApiToken(): Promise 10 | registerUser(userDTO: UserDTO): Promise 11 | } 12 | -------------------------------------------------------------------------------- /src/users/domain/useCases/DeleteCurrentApiToken.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IUsersRepository } from '../repositories/IUsersRepository' 3 | 4 | export class DeleteCurrentApiToken implements UseCase { 5 | private usersRepository: IUsersRepository 6 | 7 | constructor(usersRepository: IUsersRepository) { 8 | this.usersRepository = usersRepository 9 | } 10 | 11 | /** 12 | * Deletes the API token of the current authenticated user. 13 | * 14 | * @returns {Promise} 15 | */ 16 | async execute(): Promise { 17 | return await this.usersRepository.deleteCurrentApiToken() 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/users/domain/useCases/GetCurrentApiToken.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ApiTokenInfo } from '../models/ApiTokenInfo' 3 | import { IUsersRepository } from '../repositories/IUsersRepository' 4 | 5 | export class GetCurrentApiToken implements UseCase { 6 | private usersRepository: IUsersRepository 7 | 8 | constructor(usersRepository: IUsersRepository) { 9 | this.usersRepository = usersRepository 10 | } 11 | 12 | /** 13 | * Returns the current ApiTokenInfo corresponding to the current authenticated user. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.usersRepository.getCurrentApiToken() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/users/domain/useCases/GetCurrentAuthenticatedUser.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { IUsersRepository } from '../repositories/IUsersRepository' 3 | import { AuthenticatedUser } from '../models/AuthenticatedUser' 4 | 5 | export class GetCurrentAuthenticatedUser implements UseCase { 6 | private usersRepository: IUsersRepository 7 | 8 | constructor(usersRepository: IUsersRepository) { 9 | this.usersRepository = usersRepository 10 | } 11 | 12 | /** 13 | * Returns the current AuthenticatedUser corresponding to the authentication mechanism provided through ApiConfig. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.usersRepository.getCurrentAuthenticatedUser() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/users/domain/useCases/RecreateCurrentApiToken.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { ApiTokenInfo } from '../models/ApiTokenInfo' 3 | import { IUsersRepository } from '../repositories/IUsersRepository' 4 | 5 | export class RecreateCurrentApiToken implements UseCase { 6 | private usersRepository: IUsersRepository 7 | 8 | constructor(usersRepository: IUsersRepository) { 9 | this.usersRepository = usersRepository 10 | } 11 | 12 | /** 13 | * Reacreates the API token of the current authenticated user and returns the new ApiTokenInfo. 14 | * 15 | * @returns {Promise} 16 | */ 17 | async execute(): Promise { 18 | return await this.usersRepository.recreateCurrentApiToken() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/users/domain/useCases/RegisterUser.ts: -------------------------------------------------------------------------------- 1 | import { UseCase } from '../../../core/domain/useCases/UseCase' 2 | import { UserDTO } from '../dtos/UserDTO' 3 | import { IUsersRepository } from '../repositories/IUsersRepository' 4 | 5 | export class RegisterUser implements UseCase { 6 | private usersRepository: IUsersRepository 7 | 8 | constructor(usersRepository: IUsersRepository) { 9 | this.usersRepository = usersRepository 10 | } 11 | 12 | /** 13 | * Registers a new user, given a UserDTO object. Only available through DataverseApiAuthMechanism.BEARER_TOKEN auth mechanism. 14 | * 15 | * @param {UserDTO} [userDTO] - UserDTO object including the new user data. 16 | * @returns {Promise} - This method does not return anything upon successful completion. 17 | * @throws {WriteError} - If there are errors while writing data. 18 | */ 19 | async execute(userDTO: UserDTO): Promise { 20 | return await this.usersRepository.registerUser(userDTO) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/users/index.ts: -------------------------------------------------------------------------------- 1 | import { UsersRepository } from './infra/repositories/UsersRepository' 2 | import { GetCurrentAuthenticatedUser } from './domain/useCases/GetCurrentAuthenticatedUser' 3 | import { RecreateCurrentApiToken } from './domain/useCases/RecreateCurrentApiToken' 4 | import { GetCurrentApiToken } from './domain/useCases/GetCurrentApiToken' 5 | import { DeleteCurrentApiToken } from './domain/useCases/DeleteCurrentApiToken' 6 | import { RegisterUser } from './domain/useCases/RegisterUser' 7 | 8 | const usersRepository = new UsersRepository() 9 | 10 | const getCurrentAuthenticatedUser = new GetCurrentAuthenticatedUser(usersRepository) 11 | const recreateCurrentApiToken = new RecreateCurrentApiToken(usersRepository) 12 | const getCurrentApiToken = new GetCurrentApiToken(usersRepository) 13 | const deleteCurrentApiToken = new DeleteCurrentApiToken(usersRepository) 14 | const registerUser = new RegisterUser(usersRepository) 15 | 16 | export { 17 | getCurrentAuthenticatedUser, 18 | recreateCurrentApiToken, 19 | getCurrentApiToken, 20 | deleteCurrentApiToken, 21 | registerUser 22 | } 23 | export { AuthenticatedUser } from './domain/models/AuthenticatedUser' 24 | export { ApiTokenInfo } from './domain/models/ApiTokenInfo' 25 | export { UserDTO } from './domain/dtos/UserDTO' 26 | -------------------------------------------------------------------------------- /src/users/infra/repositories/transformers/apiTokenInfoTransformers.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios' 2 | import { ApiTokenInfo } from '../../../domain/models/ApiTokenInfo' 3 | 4 | export const transformGetApiTokenResponseToApiTokenInfo = ( 5 | response: AxiosResponse 6 | ): ApiTokenInfo => { 7 | const apiTokenInfoPayload = response.data.data 8 | const messageParts = apiTokenInfoPayload.message.split(' ') 9 | const expirationDateFormattedTimestamp = `${messageParts[4]}T${messageParts[5]}` 10 | return { 11 | apiToken: messageParts[1], 12 | expirationDate: new Date(expirationDateFormattedTimestamp) 13 | } 14 | } 15 | 16 | export const transformRecreateApiTokenResponseToApiTokenInfo = ( 17 | response: AxiosResponse 18 | ): ApiTokenInfo => { 19 | const apiTokenInfoPayload = response.data.data 20 | const messageParts = apiTokenInfoPayload.message.split(' ') 21 | const expirationDateFormattedTimestamp = `${messageParts[9]}T${messageParts[10]}` 22 | return { 23 | apiToken: messageParts[5], 24 | expirationDate: new Date(expirationDateFormattedTimestamp) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/environment/.env: -------------------------------------------------------------------------------- 1 | POSTGRES_VERSION=13 2 | DATAVERSE_DB_USER=dataverse 3 | SOLR_VERSION=9.8.0 4 | DATAVERSE_IMAGE_REGISTRY=docker.io 5 | DATAVERSE_IMAGE_TAG=unstable 6 | DATAVERSE_BOOTSTRAP_TIMEOUT=5m 7 | -------------------------------------------------------------------------------- /test/environment/.gitignore: -------------------------------------------------------------------------------- 1 | docker-dev-volumes 2 | -------------------------------------------------------------------------------- /test/environment/conf/localstack/buckets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # https://stackoverflow.com/questions/53619901/auto-create-s3-buckets-on-localstack 3 | awslocal s3 mb s3://mybucket 4 | -------------------------------------------------------------------------------- /test/functional/metadataBlocks/GetAllFacetableMetadataFields.test.ts: -------------------------------------------------------------------------------- 1 | import { ApiConfig, MetadataFieldInfo, getAllFacetableMetadataFields } from '../../../src' 2 | import { TestConstants } from '../../testHelpers/TestConstants' 3 | import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' 4 | 5 | describe('execute', () => { 6 | beforeAll(async () => { 7 | ApiConfig.init( 8 | TestConstants.TEST_API_URL, 9 | DataverseApiAuthMechanism.API_KEY, 10 | process.env.TEST_API_KEY 11 | ) 12 | }) 13 | 14 | test('should return all facetable metadata fields', async () => { 15 | let metadataFieldInfos: MetadataFieldInfo[] | null = null 16 | try { 17 | metadataFieldInfos = await getAllFacetableMetadataFields.execute() 18 | } catch (error) { 19 | throw new Error('Should not raise an error') 20 | } finally { 21 | expect(metadataFieldInfos?.length).toBe(64) 22 | expect(metadataFieldInfos?.[0].name).toBe('authorName') 23 | expect(metadataFieldInfos?.[0].displayName).toBe('Author Name') 24 | } 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/functional/metadataBlocks/GetAllMetadataBlocks.test.ts: -------------------------------------------------------------------------------- 1 | import { ApiConfig, getAllMetadataBlocks, MetadataBlock } from '../../../src' 2 | import { TestConstants } from '../../testHelpers/TestConstants' 3 | import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' 4 | 5 | describe('execute', () => { 6 | beforeEach(async () => { 7 | ApiConfig.init( 8 | TestConstants.TEST_API_URL, 9 | DataverseApiAuthMechanism.API_KEY, 10 | process.env.TEST_API_KEY 11 | ) 12 | }) 13 | 14 | test('should successfully return metadatablocks', async () => { 15 | let metadataBlocks: MetadataBlock[] | null = null 16 | try { 17 | metadataBlocks = await getAllMetadataBlocks.execute() 18 | } catch (error) { 19 | throw new Error('Should not raise an error') 20 | } finally { 21 | expect(metadataBlocks).not.toBeNull() 22 | expect(metadataBlocks?.length).toBe(7) 23 | expect(metadataBlocks?.[0].metadataFields.title.name).toBe('title') 24 | } 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/functional/users/DeleteCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { ApiConfig, WriteError, deleteCurrentApiToken } from '../../../src' 2 | import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' 3 | import { TestConstants } from '../../testHelpers/TestConstants' 4 | import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' 5 | 6 | describe('execute', () => { 7 | beforeAll(async () => { 8 | ApiConfig.init( 9 | TestConstants.TEST_API_URL, 10 | DataverseApiAuthMechanism.API_KEY, 11 | process.env.TEST_API_KEY 12 | ) 13 | }) 14 | 15 | afterAll(async () => { 16 | ApiConfig.init( 17 | TestConstants.TEST_API_URL, 18 | DataverseApiAuthMechanism.API_KEY, 19 | process.env.TEST_API_KEY 20 | ) 21 | }) 22 | 23 | test('should successfully recreate the API token', async () => { 24 | const testApiToken = await createApiTokenViaApi('deleteCurrentApiTokenFTUser') 25 | ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, testApiToken) 26 | await deleteCurrentApiToken.execute() 27 | await expect(deleteCurrentApiToken.execute()).rejects.toBeInstanceOf(WriteError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/functional/users/GetCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { ApiConfig, getCurrentApiToken } from '../../../src' 2 | import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' 3 | import { TestConstants } from '../../testHelpers/TestConstants' 4 | 5 | describe('execute', () => { 6 | beforeAll(async () => { 7 | ApiConfig.init( 8 | TestConstants.TEST_API_URL, 9 | DataverseApiAuthMechanism.API_KEY, 10 | process.env.TEST_API_KEY 11 | ) 12 | }) 13 | 14 | test('should return the current API token', async () => { 15 | const actualTokenInfo = await getCurrentApiToken.execute() 16 | expect(actualTokenInfo.apiToken).toBe(process.env.TEST_API_KEY) 17 | expect(typeof actualTokenInfo.expirationDate).toBe('object') 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/functional/users/RecreateCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { ApiConfig, recreateCurrentApiToken } from '../../../src' 2 | import { DataverseApiAuthMechanism } from '../../../src/core/infra/repositories/ApiConfig' 3 | import { TestConstants } from '../../testHelpers/TestConstants' 4 | import { createApiTokenViaApi } from '../../testHelpers/users/apiTokenHelper' 5 | 6 | describe('execute', () => { 7 | beforeAll(async () => { 8 | ApiConfig.init( 9 | TestConstants.TEST_API_URL, 10 | DataverseApiAuthMechanism.API_KEY, 11 | process.env.TEST_API_KEY 12 | ) 13 | }) 14 | 15 | afterAll(async () => { 16 | ApiConfig.init( 17 | TestConstants.TEST_API_URL, 18 | DataverseApiAuthMechanism.API_KEY, 19 | process.env.TEST_API_KEY 20 | ) 21 | }) 22 | 23 | test('should successfully recreate the API token', async () => { 24 | const testApiToken = await createApiTokenViaApi('recreateCurrentApiTokenFTUser') 25 | ApiConfig.init(TestConstants.TEST_API_URL, DataverseApiAuthMechanism.API_KEY, testApiToken) 26 | const actualRecreatedApiTokenInfo = await recreateCurrentApiToken.execute() 27 | expect(actualRecreatedApiTokenInfo.apiToken).not.toBeUndefined() 28 | expect(actualRecreatedApiTokenInfo.apiToken).not.toBe(testApiToken) 29 | expect(typeof actualRecreatedApiTokenInfo.expirationDate).toBe('object') 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/integration/auth/AuthRepository.test.ts: -------------------------------------------------------------------------------- 1 | import { AuthRepository } from '../../../src/auth/infra/repositories/AuthRepository' 2 | import { WriteError } from '../../../src/core/domain/repositories/WriteError' 3 | import { 4 | ApiConfig, 5 | DataverseApiAuthMechanism 6 | } from '../../../src/core/infra/repositories/ApiConfig' 7 | import { TestConstants } from '../../testHelpers/TestConstants' 8 | 9 | describe('logout', () => { 10 | const sut: AuthRepository = new AuthRepository() 11 | 12 | ApiConfig.init( 13 | TestConstants.TEST_API_URL, 14 | DataverseApiAuthMechanism.API_KEY, 15 | process.env.TEST_API_KEY 16 | ) 17 | 18 | test('should return error due to disabled feature flag', async () => { 19 | const expectedError = new WriteError( 20 | '[500] This endpoint is only available when session authentication feature flag is enabled' 21 | ) 22 | 23 | await expect(sut.logout()).rejects.toThrow(expectedError) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/integration/metadataBlocks/MetadataFieldsInfoRepository.test.ts: -------------------------------------------------------------------------------- 1 | import { MetadataFieldInfosRepository } from '../../../src/metadataBlocks/infra/repositories/MetadataFieldInfosRepository' 2 | import { 3 | ApiConfig, 4 | DataverseApiAuthMechanism 5 | } from '../../../src/core/infra/repositories/ApiConfig' 6 | import { TestConstants } from '../../testHelpers/TestConstants' 7 | 8 | describe('getAllFacetableMetadataFields', () => { 9 | const sut: MetadataFieldInfosRepository = new MetadataFieldInfosRepository() 10 | 11 | beforeAll(async () => { 12 | ApiConfig.init( 13 | TestConstants.TEST_API_URL, 14 | DataverseApiAuthMechanism.API_KEY, 15 | process.env.TEST_API_KEY 16 | ) 17 | }) 18 | 19 | test('should return all facetable metadata fields', async () => { 20 | const actual = await sut.getAllFacetableMetadataFields() 21 | 22 | expect(actual.length).toBe(64) 23 | expect(actual[0].name).toBe('authorName') 24 | expect(actual[0].displayName).toBe('Author Name') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/testHelpers/collections/collectionItemsFacetsHelper.ts: -------------------------------------------------------------------------------- 1 | import { CollectionItemsFacet } from '../../../src/collections/domain/models/CollectionItemSubset' 2 | import { CollectionItemsFacetPayload } from '../../../src/collections/infra/repositories/transformers/CollectionItemsFacetsPayload' 3 | 4 | export const createCollectionItemsFacetsModel = (): CollectionItemsFacet[] => { 5 | return [ 6 | { 7 | name: 'facet1', 8 | friendlyName: 'Facet 1', 9 | labels: [ 10 | { name: 'Label 1', count: 5 }, 11 | { name: 'Label 2', count: 4 } 12 | ] 13 | }, 14 | { 15 | name: 'facet2', 16 | friendlyName: 'Facet 2', 17 | labels: [ 18 | { name: 'Label 3', count: 8 }, 19 | { name: 'Label 4', count: 9 } 20 | ] 21 | } 22 | ] 23 | } 24 | 25 | export const createCollectionItemsFacetsPayload = (): CollectionItemsFacetPayload => { 26 | return [ 27 | { 28 | facet1: { 29 | friendly: 'Facet 1', 30 | labels: [{ 'Label 1': 5 }, { 'Label 2': 4 }] 31 | }, 32 | facet2: { 33 | friendly: 'Facet 2', 34 | labels: [{ 'Label 3': 8 }, { 'Label 4': 9 }] 35 | } 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /test/testHelpers/collections/collectionPreviewHelper.ts: -------------------------------------------------------------------------------- 1 | import { CollectionItemType, CollectionPreview } from '../../../src' 2 | import { CollectionPreviewPayload } from '../../../src/collections/infra/repositories/transformers/CollectionPreviewPayload' 3 | import { PublicationStatus } from '../../../src/core/domain/models/PublicationStatus' 4 | 5 | export const createCollectionPreviewModel = (): CollectionPreview => { 6 | const collectionPreviewModel: CollectionPreview = { 7 | type: CollectionItemType.COLLECTION, 8 | name: 'test collection', 9 | parentName: 'parent collection', 10 | alias: 'testcollection', 11 | parentAlias: 'parentcollection', 12 | description: 'test description', 13 | affiliation: 'test affiliation', 14 | publicationStatuses: [PublicationStatus.Published], 15 | releaseOrCreateDate: new Date('2023-05-15T08:21:01Z'), 16 | imageUrl: 'http://dataverse.com' 17 | } 18 | return collectionPreviewModel 19 | } 20 | 21 | export const createCollectionPreviewPayload = (): CollectionPreviewPayload => { 22 | return { 23 | name: 'test collection', 24 | parentDataverseName: 'parent collection', 25 | identifier: 'testcollection', 26 | parentDataverseIdentifier: 'parentcollection', 27 | description: 'test description', 28 | affiliation: 'test affiliation', 29 | publicationStatuses: ['Published'], 30 | published_at: '2023-05-15T08:21:01Z', 31 | image_url: 'http://dataverse.com', 32 | type: 'dataverse', 33 | url: 'http://dataverse.com' 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test/testHelpers/collections/collectionUserPermissionsHelper.ts: -------------------------------------------------------------------------------- 1 | import { CollectionUserPermissions } from '../../../src/collections/domain/models/CollectionUserPermissions' 2 | import { CollectionUserPermissionsPayload } from '../../../src/collections/infra/repositories/transformers/CollectionUserPermissionsPayload' 3 | 4 | export const createCollectionUserPermissionsModel = (): CollectionUserPermissions => { 5 | return { 6 | canAddCollection: true, 7 | canAddDataset: true, 8 | canViewUnpublishedCollection: true, 9 | canEditCollection: true, 10 | canManageCollectionPermissions: true, 11 | canPublishCollection: true, 12 | canDeleteCollection: true 13 | } 14 | } 15 | 16 | export const createCollectionUserPermissionsPayload = (): CollectionUserPermissionsPayload => { 17 | return { 18 | canAddDataverse: true, 19 | canAddDataset: true, 20 | canViewUnpublishedDataverse: true, 21 | canEditDataverse: true, 22 | canManageDataversePermissions: true, 23 | canPublishDataverse: true, 24 | canDeleteDataverse: true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/testHelpers/datasets/datasetLockHelper.ts: -------------------------------------------------------------------------------- 1 | import { DatasetLock, DatasetLockType } from '../../../src/datasets/domain/models/DatasetLock' 2 | import { DatasetLockPayload } from '../../../src/datasets/infra/repositories/transformers/datasetLocksTransformers' 3 | 4 | export const createDatasetLockModel = (): DatasetLock => { 5 | return { 6 | lockType: DatasetLockType.EDIT_IN_PROGRESS, 7 | date: '2023-05-15T08:21:03Z', 8 | userId: '1', 9 | datasetPersistentId: 'doi:10.5072/FK2/QYOVTJ', 10 | message: 'Test.' 11 | } 12 | } 13 | 14 | export const createDatasetLockPayload = (): DatasetLockPayload => { 15 | return { 16 | lockType: DatasetLockType.EDIT_IN_PROGRESS.toString(), 17 | date: '2023-05-15T08:21:03Z', 18 | user: '1', 19 | dataset: 'doi:10.5072/FK2/QYOVTJ', 20 | message: 'Test.' 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test/testHelpers/datasets/datasetUserPermissionsHelper.ts: -------------------------------------------------------------------------------- 1 | import { DatasetUserPermissions } from '../../../src/datasets/domain/models/DatasetUserPermissions' 2 | 3 | export const createDatasetUserPermissionsModel = (): DatasetUserPermissions => { 4 | return { 5 | canViewUnpublishedDataset: true, 6 | canEditDataset: true, 7 | canPublishDataset: true, 8 | canManageDatasetPermissions: true, 9 | canDeleteDatasetDraft: true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/testHelpers/datasets/datasetVersionsSummariesHelper.ts: -------------------------------------------------------------------------------- 1 | import { DatasetVersionSummaryInfo } from '../../../src/datasets/domain/models/DatasetVersionSummaryInfo' 2 | 3 | export const createDatasetVersionSummaryModel = ( 4 | props?: Partial 5 | ): DatasetVersionSummaryInfo => ({ 6 | id: 1, 7 | contributors: 'John Doe', 8 | versionNumber: 'DRAFT', 9 | publishedOn: '2021-01-01', 10 | summary: { 11 | 'Citation Metadata': { 12 | Title: { 13 | added: 0, 14 | deleted: 0, 15 | changed: 1 16 | } 17 | }, 18 | files: { 19 | added: 0, 20 | removed: 0, 21 | replaced: 0, 22 | changedFileMetaData: 0, 23 | changedVariableMetadata: 0 24 | }, 25 | termsAccessChanged: false 26 | }, 27 | ...props 28 | }) 29 | -------------------------------------------------------------------------------- /test/testHelpers/files/fileCountsHelper.ts: -------------------------------------------------------------------------------- 1 | import { FileCounts } from '../../../src/files/domain/models/FileCounts' 2 | import { FileAccessStatus } from '../../../src/files/domain/models/FileCriteria' 3 | 4 | export const createFileCountsModel = (): FileCounts => { 5 | return { 6 | total: 4, 7 | perContentType: [ 8 | { 9 | contentType: 'text/plain', 10 | count: 4 11 | } 12 | ], 13 | perAccessStatus: [ 14 | { 15 | accessStatus: FileAccessStatus.PUBLIC, 16 | count: 3 17 | }, 18 | { 19 | accessStatus: FileAccessStatus.RESTRICTED, 20 | count: 1 21 | } 22 | ], 23 | perCategoryName: [ 24 | { 25 | categoryName: 'testCategory', 26 | count: 2 27 | } 28 | ] 29 | } 30 | } 31 | 32 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 33 | export const createFileCountsPayload = (): any => { 34 | return { 35 | total: 4, 36 | perContentType: { 37 | 'text/plain': 4 38 | }, 39 | perAccessStatus: { 40 | Public: 3, 41 | Restricted: 1 42 | }, 43 | perCategoryName: { 44 | testCategory: 2 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/testHelpers/files/fileUserPermissionsHelper.ts: -------------------------------------------------------------------------------- 1 | import { FileUserPermissions } from '../../../src/files/domain/models/FileUserPermissions' 2 | 3 | export const createFileUserPermissionsModel = (): FileUserPermissions => { 4 | return { 5 | canDownloadFile: true, 6 | canManageFilePermissions: true, 7 | canEditOwnerDataset: true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/testHelpers/files/filesTotalDownloadSizeHelper.ts: -------------------------------------------------------------------------------- 1 | export const createFilesTotalDownloadSizePayload = (): { storageSize: number; message: string } => { 2 | return { 3 | storageSize: 173, 4 | message: 5 | 'Total size of the files available for download in this version of the dataset: 173 bytes' 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /test/testHelpers/files/test-file-1.txt: -------------------------------------------------------------------------------- 1 | test file 1 2 | -------------------------------------------------------------------------------- /test/testHelpers/files/test-file-2.txt: -------------------------------------------------------------------------------- 1 | test file 2 2 | -------------------------------------------------------------------------------- /test/testHelpers/files/test-file-3.txt: -------------------------------------------------------------------------------- 1 | test file 3 2 | -------------------------------------------------------------------------------- /test/testHelpers/files/test-file-4.tab: -------------------------------------------------------------------------------- 1 | position name age 2 | 1 "Belle" 36 3 | 2 "Lola" 37 4 | 3 "Jayden" 45 5 | 4 "Margaret" 37 6 | 5 "Russell" 40 7 | 6 "Bertie" 60 8 | 7 "Maud" 34 9 | 8 "Mabel" 31 10 | 9 "Trevor" 51 11 | 10 "Duane" 26 12 | -------------------------------------------------------------------------------- /test/testHelpers/info/infoHelper.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios' 2 | import { TestConstants } from '../TestConstants' 3 | 4 | export const setMaxEmbargoDurationInMonthsViaApi = async ( 5 | maxEmbargoDurationInMonths: number 6 | ): Promise => { 7 | return await axios.put( 8 | `${TestConstants.TEST_API_URL}/admin/settings/:MaxEmbargoDurationInMonths`, 9 | maxEmbargoDurationInMonths.toString(), 10 | { 11 | headers: { 'Content-Type': 'text/plain' } 12 | } 13 | ) 14 | } 15 | 16 | export const setApplicationTermsOfUseViaApi = async ( 17 | applicationTermsOfUse: string 18 | ): Promise => { 19 | return await axios.put( 20 | `${TestConstants.TEST_API_URL}/admin/settings/:ApplicationTermsOfUse`, 21 | applicationTermsOfUse, 22 | { 23 | headers: { 'Content-Type': 'text/plain' } 24 | } 25 | ) 26 | } 27 | 28 | export const deleteApplicationTermsOfUseViaApi = async (): Promise => { 29 | return await axios.delete(`${TestConstants.TEST_API_URL}/admin/settings/:ApplicationTermsOfUse`) 30 | } 31 | -------------------------------------------------------------------------------- /test/testHelpers/users/apiTokenHelper.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { TestConstants } from '../TestConstants' 3 | 4 | export const createApiTokenViaApi = async ( 5 | userName: string, 6 | createSuperUser = false 7 | ): Promise => { 8 | try { 9 | await axios.post( 10 | `${TestConstants.TEST_API_URL}/builtin-users?key=burrito&password=${userName}`, 11 | JSON.stringify({ 12 | userName: userName, 13 | firstName: 'John', 14 | lastName: 'Doe', 15 | email: `${userName}@test.com` 16 | }), 17 | { 18 | headers: { 19 | 'Content-Type': 'application/json' 20 | } 21 | } 22 | ) 23 | const token = await axios 24 | .get(`${TestConstants.TEST_API_URL}/builtin-users/${userName}/api-token?password=${userName}`) 25 | .then((response) => response.data.data.message) 26 | if (createSuperUser) { 27 | await axios.put(`${TestConstants.TEST_API_URL}/admin/superuser/${userName}`, 'true') 28 | } 29 | return token 30 | } catch (error) { 31 | throw new Error(`Error while creating API token`) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/testHelpers/users/authenticatedUserHelper.ts: -------------------------------------------------------------------------------- 1 | import { AuthenticatedUser } from '../../../src/users/domain/models/AuthenticatedUser' 2 | 3 | export const createAuthenticatedUser = (): AuthenticatedUser => { 4 | return { 5 | id: 1, 6 | persistentUserId: 'Test', 7 | identifier: '@Test', 8 | displayName: 'Test User', 9 | firstName: 'Testname', 10 | lastName: 'Testlastname', 11 | email: 'testuser@dataverse.org', 12 | superuser: false, 13 | deactivated: false, 14 | createdTime: '2023-04-14T11:52:28Z', 15 | authenticationProviderId: 'builtin', 16 | lastLoginTime: '2023-04-14T11:52:28Z', 17 | lastApiUseTime: '2023-04-14T15:53:32Z' 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/testHelpers/users/builtinUserApiHelper.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { TestConstants } from '../TestConstants' 3 | 4 | export const createBuiltInUser = async (userName: string): Promise => { 5 | try { 6 | await axios.post( 7 | `${TestConstants.TEST_API_URL}/builtin-users?key=burrito&password=${userName}`, 8 | JSON.stringify({ 9 | userName: userName, 10 | firstName: 'John', 11 | lastName: 'Doe', 12 | email: `${userName}@test.com` 13 | }), 14 | { 15 | headers: { 16 | 'Content-Type': 'application/json' 17 | } 18 | } 19 | ) 20 | return axios 21 | .get(`${TestConstants.TEST_API_URL}/builtin-users/${userName}/api-token?password=${userName}`) 22 | .then((response) => response.data.data.message) 23 | } catch (error) { 24 | console.log(error) 25 | throw new Error(`Error while creating API token`) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/unit/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IQSS/dataverse-client-javascript/05f7d3f25c8c7b758c7188cc7d18cf34de6dd427/test/unit/.DS_Store -------------------------------------------------------------------------------- /test/unit/auth/Logout.test.ts: -------------------------------------------------------------------------------- 1 | import { Logout } from '../../../src/auth/domain/useCases/Logout' 2 | import { IAuthRepository } from '../../../src/auth/domain/repositories/IAuthRepository' 3 | import { WriteError } from '../../../src/core/domain/repositories/WriteError' 4 | 5 | describe('execute', () => { 6 | test('should not return error on repository success', async () => { 7 | const authRepositoryStub: IAuthRepository = {} as IAuthRepository 8 | authRepositoryStub.logout = jest.fn() 9 | const sut = new Logout(authRepositoryStub) 10 | await sut.execute() 11 | expect(authRepositoryStub.logout).toHaveBeenCalled() 12 | }) 13 | 14 | test('should return error result on repository error', async () => { 15 | const testWriteError = new WriteError() 16 | const authRepositoryStub: IAuthRepository = {} as IAuthRepository 17 | authRepositoryStub.logout = jest.fn().mockRejectedValue(testWriteError) 18 | const sut = new Logout(authRepositoryStub) 19 | 20 | await expect(sut.execute()).rejects.toThrow(testWriteError) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/unit/collections/CreateCollection.test.ts: -------------------------------------------------------------------------------- 1 | import { CreateCollection } from '../../../src/collections/domain/useCases/CreateCollection' 2 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 3 | import { WriteError } from '../../../src' 4 | import { createCollectionDTO } from '../../testHelpers/collections/collectionHelper' 5 | 6 | describe('execute', () => { 7 | const testCollectionDTO = createCollectionDTO() 8 | const testCollectionId = 1 9 | 10 | test('should return undefined on repository success', async () => { 11 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 12 | collectionRepositoryStub.createCollection = jest.fn().mockResolvedValue(testCollectionId) 13 | const testCreateCollection = new CreateCollection(collectionRepositoryStub) 14 | 15 | const actual = await testCreateCollection.execute(testCollectionDTO) 16 | 17 | expect(actual).toEqual(testCollectionId) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 22 | collectionRepositoryStub.createCollection = jest.fn().mockRejectedValue(new WriteError()) 23 | const testCreateCollection = new CreateCollection(collectionRepositoryStub) 24 | 25 | await expect(testCreateCollection.execute(testCollectionDTO)).rejects.toThrow(WriteError) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/unit/collections/DeleteCollection.test.ts: -------------------------------------------------------------------------------- 1 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 2 | import { WriteError } from '../../../src' 3 | import { DeleteCollection } from '../../../src/collections/domain/useCases/DeleteCollection' 4 | 5 | describe('execute', () => { 6 | test('should return undefined on repository success', async () => { 7 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 8 | collectionRepositoryStub.deleteCollection = jest.fn().mockResolvedValue(undefined) 9 | const testDeleteCollection = new DeleteCollection(collectionRepositoryStub) 10 | 11 | const actual = await testDeleteCollection.execute(1) 12 | 13 | expect(actual).toEqual(undefined) 14 | }) 15 | 16 | test('should return error result on repository error', async () => { 17 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 18 | collectionRepositoryStub.deleteCollection = jest.fn().mockRejectedValue(new WriteError()) 19 | const testDeleteCollection = new DeleteCollection(collectionRepositoryStub) 20 | 21 | await expect(testDeleteCollection.execute(1)).rejects.toThrow(WriteError) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/unit/collections/DeleteCollectionFeaturedItems.test.ts: -------------------------------------------------------------------------------- 1 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 2 | import { WriteError } from '../../../src' 3 | import { DeleteCollectionFeaturedItems } from '../../../src/collections/domain/useCases/DeleteCollectionFeaturedItems' 4 | 5 | describe('execute', () => { 6 | test('should return undefined on repository success', async () => { 7 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 8 | collectionRepositoryStub.deleteCollectionFeaturedItems = jest.fn().mockResolvedValue(undefined) 9 | const testDeleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems( 10 | collectionRepositoryStub 11 | ) 12 | 13 | const actual = await testDeleteCollectionFeaturedItems.execute(1) 14 | 15 | expect(actual).toEqual(undefined) 16 | }) 17 | 18 | test('should return error result on repository error', async () => { 19 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 20 | collectionRepositoryStub.deleteCollectionFeaturedItems = jest 21 | .fn() 22 | .mockRejectedValue(new WriteError()) 23 | const testDeleteCollectionFeaturedItems = new DeleteCollectionFeaturedItems( 24 | collectionRepositoryStub 25 | ) 26 | 27 | await expect(testDeleteCollectionFeaturedItems.execute(1)).rejects.toThrow(WriteError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/collections/GetCollection.test.ts: -------------------------------------------------------------------------------- 1 | import { GetCollection } from '../../../src/collections/domain/useCases/GetCollection' 2 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 3 | import { ReadError } from '../../../src' 4 | import { createCollectionModel } from '../../testHelpers/collections/collectionHelper' 5 | 6 | describe('execute', () => { 7 | test('should return collection on repository success', async () => { 8 | const testCollection = createCollectionModel() 9 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 10 | collectionRepositoryStub.getCollection = jest.fn().mockResolvedValue(testCollection) 11 | const testGetCollection = new GetCollection(collectionRepositoryStub) 12 | 13 | const actual = await testGetCollection.execute(1) 14 | 15 | expect(actual).toEqual(testCollection) 16 | }) 17 | 18 | test('should return error result on repository error', async () => { 19 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 20 | collectionRepositoryStub.getCollection = jest.fn().mockRejectedValue(new ReadError()) 21 | const testGetCollection = new GetCollection(collectionRepositoryStub) 22 | 23 | await expect(testGetCollection.execute(1)).rejects.toThrow(ReadError) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/unit/collections/GetCollectionFacets.test.ts: -------------------------------------------------------------------------------- 1 | import { GetCollectionFacets } from '../../../src/collections/domain/useCases/GetCollectionFacets' 2 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 3 | import { ReadError } from '../../../src' 4 | import { createCollectionFacetModel } from '../../testHelpers/collections/collectionHelper' 5 | 6 | describe('execute', () => { 7 | test('should return collection facets on repository success', async () => { 8 | const testFacet = createCollectionFacetModel() 9 | const testFacets = [testFacet] 10 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 11 | collectionRepositoryStub.getCollectionFacets = jest.fn().mockResolvedValue(testFacets) 12 | const testGetCollectionFacets = new GetCollectionFacets(collectionRepositoryStub) 13 | 14 | const actual = await testGetCollectionFacets.execute(1) 15 | 16 | expect(actual[0]).toEqual(testFacet) 17 | }) 18 | 19 | test('should return error result on repository error', async () => { 20 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 21 | collectionRepositoryStub.getCollectionFacets = jest.fn().mockRejectedValue(new ReadError()) 22 | const testGetCollectionFacets = new GetCollectionFacets(collectionRepositoryStub) 23 | 24 | await expect(testGetCollectionFacets.execute(1)).rejects.toThrow(ReadError) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/unit/collections/GetCollectionFeaturedItems.test.ts: -------------------------------------------------------------------------------- 1 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 2 | import { ReadError } from '../../../src' 3 | import { createCollectionFeaturedItemsModel } from '../../testHelpers/collections/collectionFeaturedItemsHelper' 4 | import { GetCollectionFeaturedItems } from '../../../src/collections/domain/useCases/GetCollectionFeaturedItems' 5 | 6 | describe('execute', () => { 7 | test('should return collection featured items on repository success', async () => { 8 | const testFeaturedItems = createCollectionFeaturedItemsModel() 9 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 10 | collectionRepositoryStub.getCollectionFeaturedItems = jest 11 | .fn() 12 | .mockResolvedValue(testFeaturedItems) 13 | const testGetCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionRepositoryStub) 14 | 15 | const actual = await testGetCollectionFeaturedItems.execute(1) 16 | 17 | expect(actual).toEqual(testFeaturedItems) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const collectionRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 22 | collectionRepositoryStub.getCollectionFeaturedItems = jest 23 | .fn() 24 | .mockRejectedValue(new ReadError()) 25 | const testGetCollectionFeaturedItems = new GetCollectionFeaturedItems(collectionRepositoryStub) 26 | 27 | await expect(testGetCollectionFeaturedItems.execute(1)).rejects.toThrow(ReadError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/collections/PublishCollection.test.ts: -------------------------------------------------------------------------------- 1 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 2 | import { PublishCollection } from '../../../src/collections/domain/useCases/PublishCollection' 3 | import { WriteError } from '../../../src' 4 | 5 | describe('execute', () => { 6 | test('should return undefined on repository success', async () => { 7 | const collectionsRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 8 | collectionsRepositoryStub.publishCollection = jest.fn().mockResolvedValue(undefined) 9 | const sut = new PublishCollection(collectionsRepositoryStub) 10 | 11 | const actual = await sut.execute(1) 12 | 13 | expect(actual).toEqual(undefined) 14 | }) 15 | 16 | test('should return error result on repository error', async () => { 17 | const collectionsRepositoryStub: ICollectionsRepository = {} as ICollectionsRepository 18 | collectionsRepositoryStub.publishCollection = jest.fn().mockRejectedValue(new WriteError()) 19 | const sut = new PublishCollection(collectionsRepositoryStub) 20 | 21 | await expect(sut.execute(1)).rejects.toThrow(WriteError) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/unit/collections/UpdateCollection.test.ts: -------------------------------------------------------------------------------- 1 | import { UpdateCollection } from '../../../src/collections/domain/useCases/UpdateCollection' 2 | import { createCollectionDTO } from '../../testHelpers/collections/collectionHelper' 3 | import { WriteError } from '../../../src' 4 | import { ICollectionsRepository } from '../../../src/collections/domain/repositories/ICollectionsRepository' 5 | 6 | describe('execute', () => { 7 | const testCollection = createCollectionDTO() 8 | 9 | test('should return undefined when repository call is successful', async () => { 10 | const collectionsRepositoryStub = {} 11 | collectionsRepositoryStub.updateCollection = jest.fn().mockResolvedValue(undefined) 12 | 13 | const sut = new UpdateCollection(collectionsRepositoryStub) 14 | 15 | const actual = await sut.execute(1, testCollection) 16 | 17 | expect(actual).toEqual(undefined) 18 | }) 19 | 20 | test('should throw WriteError when the repository raises an error', async () => { 21 | const collectionsRepositoryStub = {} 22 | const testWriteError = new WriteError('Test error') 23 | collectionsRepositoryStub.updateCollection = jest.fn().mockRejectedValue(testWriteError) 24 | 25 | const sut = new UpdateCollection(collectionsRepositoryStub) 26 | await expect(sut.execute(1, testCollection)).rejects.toThrow(testWriteError) 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /test/unit/datasets/DeaccessionDataset.test.ts: -------------------------------------------------------------------------------- 1 | import { DeaccessionDataset } from '../../../src/datasets/domain/useCases/DeaccessionDataset' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { WriteError, DatasetDeaccessionDTO } from '../../../src' 4 | 5 | const deaccessionDatasetDTO: DatasetDeaccessionDTO = { 6 | deaccessionReason: 'Deaccessioning the dataset for testing purposes' 7 | } 8 | 9 | describe('execute', () => { 10 | test('should return undefined on repository success', async () => { 11 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 12 | datasetsRepositoryStub.deaccessionDataset = jest.fn().mockResolvedValue(undefined) 13 | const sut = new DeaccessionDataset(datasetsRepositoryStub) 14 | 15 | const actual = await sut.execute(1, '1.0', deaccessionDatasetDTO) 16 | 17 | expect(actual).toEqual(undefined) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 22 | datasetsRepositoryStub.deaccessionDataset = jest.fn().mockRejectedValue(new WriteError()) 23 | const sut = new DeaccessionDataset(datasetsRepositoryStub) 24 | 25 | await expect(sut.execute(111, '1.0', deaccessionDatasetDTO)).rejects.toThrow(WriteError) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/unit/datasets/DeleteDatasetDraft.test.ts: -------------------------------------------------------------------------------- 1 | import { DeleteDatasetDraft } from '../../../src/datasets/domain/useCases/DeleteDatasetDraft' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { WriteError } from '../../../src' 4 | 5 | describe('execute', () => { 6 | test('should return undefined on delete success', async () => { 7 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 8 | datasetsRepositoryStub.deleteDatasetDraft = jest.fn().mockResolvedValue(undefined) 9 | const sut = new DeleteDatasetDraft(datasetsRepositoryStub) 10 | 11 | const actual = await sut.execute(1) 12 | expect(actual).toEqual(undefined) 13 | }) 14 | 15 | test('should return error result on delete error', async () => { 16 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 17 | datasetsRepositoryStub.deleteDatasetDraft = jest.fn().mockRejectedValue(new WriteError()) 18 | const sut = new DeleteDatasetDraft(datasetsRepositoryStub) 19 | 20 | const nonExistentDatasetId = 111 21 | await expect(sut.execute(nonExistentDatasetId)).rejects.toThrow(WriteError) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDataset.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDataset } from '../../../src/datasets/domain/useCases/GetDataset' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper' 4 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 5 | 6 | describe('execute', () => { 7 | test('should return dataset on repository success', async () => { 8 | const testDataset = createDatasetModel() 9 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 10 | datasetsRepositoryStub.getDataset = jest.fn().mockResolvedValue(testDataset) 11 | const sut = new GetDataset(datasetsRepositoryStub) 12 | 13 | const actual = await sut.execute(1) 14 | 15 | expect(actual).toEqual(testDataset) 16 | }) 17 | 18 | test('should return error result on repository error', async () => { 19 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 20 | datasetsRepositoryStub.getDataset = jest.fn().mockRejectedValue(new ReadError()) 21 | const sut = new GetDataset(datasetsRepositoryStub) 22 | 23 | await expect(sut.execute(1)).rejects.toThrow(ReadError) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetCitation.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDatasetCitation } from '../../../src/datasets/domain/useCases/GetDatasetCitation' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | const testId = 1 7 | 8 | test('should return successful result with citation on repository success', async () => { 9 | const testCitation = 'test citation' 10 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 11 | datasetsRepositoryStub.getDatasetCitation = jest.fn().mockResolvedValue(testCitation) 12 | const sut = new GetDatasetCitation(datasetsRepositoryStub) 13 | 14 | const actual = await sut.execute(testId) 15 | 16 | expect(actual).toEqual(testCitation) 17 | }) 18 | 19 | test('should return error result on repository error', async () => { 20 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 21 | datasetsRepositoryStub.getDatasetCitation = jest.fn().mockRejectedValue(new ReadError()) 22 | const sut = new GetDatasetCitation(datasetsRepositoryStub) 23 | 24 | await expect(sut.execute(testId)).rejects.toThrow(ReadError) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetLocks.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDatasetLocks } from '../../../src/datasets/domain/useCases/GetDatasetLocks' 2 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 3 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 4 | import { createDatasetLockModel } from '../../testHelpers/datasets/datasetLockHelper' 5 | 6 | describe('execute', () => { 7 | const testDatasetId = 1 8 | 9 | test('should return dataset locks on repository success', async () => { 10 | const testDatasetLocks = [createDatasetLockModel()] 11 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 12 | datasetsRepositoryStub.getDatasetLocks = jest.fn().mockResolvedValue(testDatasetLocks) 13 | const sut = new GetDatasetLocks(datasetsRepositoryStub) 14 | 15 | const actual = await sut.execute(testDatasetId) 16 | 17 | expect(actual).toEqual(testDatasetLocks) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 22 | datasetsRepositoryStub.getDatasetLocks = jest.fn().mockRejectedValue(new ReadError()) 23 | const sut = new GetDatasetLocks(datasetsRepositoryStub) 24 | 25 | await expect(sut.execute(testDatasetId)).rejects.toThrow(ReadError) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetSummaryFieldNames.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDatasetSummaryFieldNames } from '../../../src/datasets/domain/useCases/GetDatasetSummaryFieldNames' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | test('should return successful result with field names on repository success', async () => { 7 | const testFieldNames = ['test1', 'test2'] 8 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 9 | datasetsRepositoryStub.getDatasetSummaryFieldNames = jest.fn().mockResolvedValue(testFieldNames) 10 | const sut = new GetDatasetSummaryFieldNames(datasetsRepositoryStub) 11 | 12 | const actual = await sut.execute() 13 | 14 | expect(actual).toEqual(testFieldNames) 15 | }) 16 | 17 | test('should return error result on repository error', async () => { 18 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 19 | datasetsRepositoryStub.getDatasetSummaryFieldNames = jest 20 | .fn() 21 | .mockRejectedValue(new ReadError()) 22 | const sut = new GetDatasetSummaryFieldNames(datasetsRepositoryStub) 23 | 24 | await expect(sut.execute()).rejects.toThrow(ReadError) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetUserPermissions.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDatasetUserPermissions } from '../../../src/datasets/domain/useCases/GetDatasetUserPermissions' 2 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 3 | import { createDatasetUserPermissionsModel } from '../../testHelpers/datasets/datasetUserPermissionsHelper' 4 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 5 | 6 | const testDatasetId = 1 7 | describe('execute', () => { 8 | test('should return dataset user permissions on repository success', async () => { 9 | const testDatasetUserPermissions = createDatasetUserPermissionsModel() 10 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 11 | datasetsRepositoryStub.getDatasetUserPermissions = jest 12 | .fn() 13 | .mockResolvedValue(testDatasetUserPermissions) 14 | const sut = new GetDatasetUserPermissions(datasetsRepositoryStub) 15 | 16 | const actual = await sut.execute(testDatasetId) 17 | 18 | expect(actual).toEqual(testDatasetUserPermissions) 19 | expect(datasetsRepositoryStub.getDatasetUserPermissions).toHaveBeenCalledWith(testDatasetId) 20 | }) 21 | 22 | test('should return error result on repository error', async () => { 23 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 24 | datasetsRepositoryStub.getDatasetUserPermissions = jest.fn().mockRejectedValue(new ReadError()) 25 | const sut = new GetDatasetUserPermissions(datasetsRepositoryStub) 26 | 27 | await expect(sut.execute(testDatasetId)).rejects.toThrow(ReadError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetVersionDiff.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { createDatasetVersionDiff } from '../../testHelpers/datasets/datasetVersionDiffHelper' 4 | import { GetDatasetVersionDiff } from '../../../src/datasets/domain/useCases/GetDatasetVersionDiff' 5 | 6 | describe('execute', () => { 7 | const testDatasetId = 1 8 | 9 | test('should return dataset version diff on repository success', async () => { 10 | const testDatasetVersionDiff = [createDatasetVersionDiff()] 11 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 12 | datasetsRepositoryStub.getDatasetVersionDiff = jest 13 | .fn() 14 | .mockResolvedValue(testDatasetVersionDiff) 15 | const sut = new GetDatasetVersionDiff(datasetsRepositoryStub) 16 | 17 | const actual = await sut.execute(testDatasetId, '1.0', '2.0') 18 | 19 | expect(actual).toEqual(testDatasetVersionDiff) 20 | }) 21 | 22 | test('should return error result on repository error', async () => { 23 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 24 | datasetsRepositoryStub.getDatasetVersionDiff = jest.fn().mockRejectedValue(new ReadError()) 25 | const sut = new GetDatasetVersionDiff(datasetsRepositoryStub) 26 | 27 | await expect(sut.execute(testDatasetId, '1.0', '2.0')).rejects.toThrow(ReadError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/datasets/GetDatasetVersionsSummaries.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { createDatasetVersionSummaryModel } from '../../testHelpers/datasets/datasetVersionsSummariesHelper' 4 | import { GetDatasetVersionsSummaries } from '../../../src/datasets/domain/useCases/GetDatasetVersionsSummaries' 5 | 6 | const testDatasetId = 1 7 | 8 | describe('execute', () => { 9 | test('should return dataset versions summaries on repository success', async () => { 10 | const testDatasetVersionsSummaries = [createDatasetVersionSummaryModel()] 11 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 12 | datasetsRepositoryStub.getDatasetVersionsSummaries = jest 13 | .fn() 14 | .mockResolvedValue(testDatasetVersionsSummaries) 15 | const sut = new GetDatasetVersionsSummaries(datasetsRepositoryStub) 16 | 17 | const actual = await sut.execute(testDatasetId) 18 | 19 | expect(actual).toEqual(testDatasetVersionsSummaries) 20 | expect(datasetsRepositoryStub.getDatasetVersionsSummaries).toHaveBeenCalledWith(testDatasetId) 21 | }) 22 | 23 | test('should return error result on repository error', async () => { 24 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 25 | datasetsRepositoryStub.getDatasetVersionsSummaries = jest 26 | .fn() 27 | .mockRejectedValue(new ReadError()) 28 | const sut = new GetDatasetVersionsSummaries(datasetsRepositoryStub) 29 | 30 | await expect(sut.execute(testDatasetId)).rejects.toThrow(ReadError) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/unit/datasets/GetPrivateUrlDataset.test.ts: -------------------------------------------------------------------------------- 1 | import { GetPrivateUrlDataset } from '../../../src/datasets/domain/useCases/GetPrivateUrlDataset' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { createDatasetModel } from '../../testHelpers/datasets/datasetHelper' 4 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 5 | 6 | describe('execute', () => { 7 | const testPrivateUrlToken = 'token' 8 | 9 | test('should return dataset on repository success', async () => { 10 | const testDataset = createDatasetModel() 11 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 12 | datasetsRepositoryStub.getPrivateUrlDataset = jest.fn().mockResolvedValue(testDataset) 13 | const sut = new GetPrivateUrlDataset(datasetsRepositoryStub) 14 | 15 | const actual = await sut.execute(testPrivateUrlToken) 16 | 17 | expect(actual).toEqual(testDataset) 18 | expect(datasetsRepositoryStub.getPrivateUrlDataset).toHaveBeenCalledWith( 19 | testPrivateUrlToken, 20 | false 21 | ) 22 | }) 23 | 24 | test('should return error result on repository error', async () => { 25 | const testReadError = new ReadError() 26 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 27 | datasetsRepositoryStub.getPrivateUrlDataset = jest.fn().mockRejectedValue(testReadError) 28 | const sut = new GetPrivateUrlDataset(datasetsRepositoryStub) 29 | 30 | await expect(sut.execute(testPrivateUrlToken)).rejects.toThrow(testReadError) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/unit/datasets/GetPrivateUrlDatasetCitation.test.ts: -------------------------------------------------------------------------------- 1 | import { GetPrivateUrlDatasetCitation } from '../../../src/datasets/domain/useCases/GetPrivateUrlDatasetCitation' 2 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | const testPrivateUrlToken = 'token' 7 | test('should return successful result with citation on repository success', async () => { 8 | const testCitation = 'test citation' 9 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 10 | datasetsRepositoryStub.getPrivateUrlDatasetCitation = jest.fn().mockResolvedValue(testCitation) 11 | const sut = new GetPrivateUrlDatasetCitation(datasetsRepositoryStub) 12 | 13 | const actual = await sut.execute(testPrivateUrlToken) 14 | 15 | expect(actual).toEqual(testCitation) 16 | expect(datasetsRepositoryStub.getPrivateUrlDatasetCitation).toHaveBeenCalledWith( 17 | testPrivateUrlToken 18 | ) 19 | }) 20 | 21 | test('should return error result on repository error', async () => { 22 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 23 | datasetsRepositoryStub.getPrivateUrlDatasetCitation = jest 24 | .fn() 25 | .mockRejectedValue(new ReadError()) 26 | const sut = new GetPrivateUrlDatasetCitation(datasetsRepositoryStub) 27 | 28 | await expect(sut.execute(testPrivateUrlToken)).rejects.toThrow(ReadError) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/datasets/PublishDataset.test.ts: -------------------------------------------------------------------------------- 1 | import { IDatasetsRepository } from '../../../src/datasets/domain/repositories/IDatasetsRepository' 2 | import { PublishDataset } from '../../../src/datasets/domain/useCases/PublishDataset' 3 | import { VersionUpdateType } from '../../../src/datasets/domain/models/Dataset' 4 | import { WriteError } from '../../../src' 5 | 6 | describe('execute', () => { 7 | test('should return undefined on repository success', async () => { 8 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 9 | datasetsRepositoryStub.publishDataset = jest.fn().mockResolvedValue(undefined) 10 | const sut = new PublishDataset(datasetsRepositoryStub) 11 | 12 | const actual = await sut.execute(1, VersionUpdateType.MAJOR) 13 | 14 | expect(actual).toEqual(undefined) 15 | }) 16 | 17 | test('should return error result on repository error', async () => { 18 | const datasetsRepositoryStub: IDatasetsRepository = {} as IDatasetsRepository 19 | datasetsRepositoryStub.publishDataset = jest.fn().mockRejectedValue(new WriteError()) 20 | const sut = new PublishDataset(datasetsRepositoryStub) 21 | 22 | await expect(sut.execute(1, VersionUpdateType.MAJOR)).rejects.toThrow(WriteError) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /test/unit/files/AddUploadedFileToDataset.test.ts: -------------------------------------------------------------------------------- 1 | import { DirectUploadClientError } from '../../../src/files/domain/clients/DirectUploadClientError' 2 | import { AddUploadedFilesToDataset } from '../../../src/files/domain/useCases/AddUploadedFilesToDataset' 3 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 4 | 5 | describe('execute', () => { 6 | const testUploadedFileDTOs = [ 7 | { 8 | fileName: 'testfile', 9 | storageId: 'testStorageId', 10 | checksumValue: 'testChecksumValue', 11 | checksumType: 'md5', 12 | mimeType: 'test/type' 13 | } 14 | ] 15 | 16 | test('should return undefined on client success', async () => { 17 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 18 | filesRepositoryStub.addUploadedFilesToDataset = jest.fn().mockResolvedValue(undefined) 19 | 20 | const sut = new AddUploadedFilesToDataset(filesRepositoryStub) 21 | 22 | const actual = await sut.execute(1, testUploadedFileDTOs) 23 | 24 | expect(actual).toEqual(undefined) 25 | }) 26 | 27 | test('should return error on client error', async () => { 28 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 29 | filesRepositoryStub.addUploadedFilesToDataset = jest 30 | .fn() 31 | .mockRejectedValue(new DirectUploadClientError('test', 'test', 'test')) 32 | 33 | const sut = new AddUploadedFilesToDataset(filesRepositoryStub) 34 | 35 | await expect(sut.execute(1, testUploadedFileDTOs)).rejects.toThrow(DirectUploadClientError) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /test/unit/files/DeleteFile.test.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 2 | import { WriteError } from '../../../src' 3 | import { DeleteFile } from '../../../src/files/domain/useCases/DeleteFile' 4 | 5 | describe('execute', () => { 6 | test('should return undefined when repository call is successful', async () => { 7 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 8 | filesRepositoryStub.deleteFile = jest.fn().mockResolvedValue(undefined) 9 | 10 | const sut = new DeleteFile(filesRepositoryStub) 11 | 12 | const actual = await sut.execute(1) 13 | 14 | expect(actual).toEqual(undefined) 15 | }) 16 | 17 | test('should return error result on repository error', async () => { 18 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 19 | filesRepositoryStub.deleteFile = jest.fn().mockRejectedValue(new WriteError()) 20 | 21 | const sut = new DeleteFile(filesRepositoryStub) 22 | 23 | await expect(sut.execute(1)).rejects.toThrow(WriteError) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /test/unit/files/GetDatasetFileCounts.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDatasetFileCounts } from '../../../src/files/domain/useCases/GetDatasetFileCounts' 2 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | import { DatasetNotNumberedVersion } from '../../../src/datasets' 5 | import { FileCounts } from '../../../src/files/domain/models/FileCounts' 6 | import { createFileCountsModel } from '../../testHelpers/files/fileCountsHelper' 7 | 8 | describe('execute', () => { 9 | test('should return file counts on repository success', async () => { 10 | const testFileCounts: FileCounts = createFileCountsModel() 11 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 12 | filesRepositoryStub.getDatasetFileCounts = jest.fn().mockResolvedValue(testFileCounts) 13 | 14 | const sut = new GetDatasetFileCounts(filesRepositoryStub) 15 | 16 | const actual = await sut.execute(1) 17 | 18 | expect(actual).toEqual(testFileCounts) 19 | expect(filesRepositoryStub.getDatasetFileCounts).toHaveBeenCalledWith( 20 | 1, 21 | DatasetNotNumberedVersion.LATEST, 22 | false, 23 | undefined 24 | ) 25 | }) 26 | 27 | test('should return error result on repository error', async () => { 28 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 29 | filesRepositoryStub.getDatasetFileCounts = jest.fn().mockRejectedValue(new ReadError()) 30 | 31 | const sut = new GetDatasetFileCounts(filesRepositoryStub) 32 | 33 | await expect(sut.execute(1)).rejects.toThrow(ReadError) 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/unit/files/GetFileCitation.test.ts: -------------------------------------------------------------------------------- 1 | import { DatasetNotNumberedVersion, ReadError } from '../../../src' 2 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 3 | import { GetFileCitation } from '../../../src/files/domain/useCases/GetFileCitation' 4 | 5 | describe('execute', () => { 6 | const testId = 1 7 | 8 | test('should return successful result with file citation on repository success', async () => { 9 | const testCitation = 'test citation' 10 | const filesRepositoryStub = {} 11 | filesRepositoryStub.getFileCitation = jest.fn().mockResolvedValue(testCitation) 12 | 13 | const sut = new GetFileCitation(filesRepositoryStub) 14 | 15 | const actual = await sut.execute(testId) 16 | 17 | expect(actual).toEqual(testCitation) 18 | expect(filesRepositoryStub.getFileCitation).toHaveBeenCalledWith( 19 | testId, 20 | DatasetNotNumberedVersion.LATEST, 21 | false 22 | ) 23 | }) 24 | 25 | test('should return error result on repository error', async () => { 26 | const filesRepositoryStub = {} 27 | filesRepositoryStub.getFileCitation = jest.fn().mockRejectedValue(new ReadError()) 28 | 29 | const sut = new GetFileCitation(filesRepositoryStub) 30 | 31 | await expect(sut.execute(testId)).rejects.toThrow(ReadError) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/unit/files/GetFileDataTables.test.ts: -------------------------------------------------------------------------------- 1 | import { GetFileDataTables } from '../../../src/files/domain/useCases/GetFileDataTables' 2 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | import { FileDataTable } from '../../../src/files/domain/models/FileDataTable' 5 | import { createFileDataTableModel } from '../../testHelpers/files/fileDataTablesHelper' 6 | 7 | describe('execute', () => { 8 | const testFileId = 1 9 | 10 | test('should return file data tables on repository success', async () => { 11 | const testDataTables: FileDataTable[] = [createFileDataTableModel()] 12 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 13 | filesRepositoryStub.getFileDataTables = jest.fn().mockResolvedValue(testDataTables) 14 | const sut = new GetFileDataTables(filesRepositoryStub) 15 | 16 | const actual = await sut.execute(testFileId) 17 | 18 | expect(actual).toBe(testDataTables) 19 | expect(filesRepositoryStub.getFileDataTables).toHaveBeenCalledWith(testFileId) 20 | }) 21 | 22 | test('should return error result on repository error', async () => { 23 | const testReadError = new ReadError() 24 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 25 | filesRepositoryStub.getFileDataTables = jest.fn().mockRejectedValue(testReadError) 26 | const sut = new GetFileDataTables(filesRepositoryStub) 27 | 28 | let actualError: ReadError | undefined = undefined 29 | await sut.execute(testFileId).catch((e: ReadError) => (actualError = e)) 30 | 31 | expect(actualError).toBe(testReadError) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/unit/files/GetFileUserPermissions.test.ts: -------------------------------------------------------------------------------- 1 | import { GetFileUserPermissions } from '../../../src/files/domain/useCases/GetFileUserPermissions' 2 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | import { createFileUserPermissionsModel } from '../../testHelpers/files/fileUserPermissionsHelper' 5 | 6 | describe('execute', () => { 7 | const testFileId = 1 8 | 9 | test('should return file user permissions on repository success', async () => { 10 | const testFileUserPermissions = createFileUserPermissionsModel() 11 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 12 | filesRepositoryStub.getFileUserPermissions = jest 13 | .fn() 14 | .mockResolvedValue(testFileUserPermissions) 15 | 16 | const sut = new GetFileUserPermissions(filesRepositoryStub) 17 | 18 | const actual = await sut.execute(testFileId) 19 | 20 | expect(actual).toEqual(testFileUserPermissions) 21 | expect(filesRepositoryStub.getFileUserPermissions).toHaveBeenCalledWith(testFileId) 22 | }) 23 | 24 | test('should return error result on repository error', async () => { 25 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 26 | filesRepositoryStub.getFileUserPermissions = jest.fn().mockRejectedValue(new ReadError()) 27 | const sut = new GetFileUserPermissions(filesRepositoryStub) 28 | 29 | await expect(sut.execute(testFileId)).rejects.toThrow(ReadError) 30 | }) 31 | }) 32 | -------------------------------------------------------------------------------- /test/unit/files/IsFileDeleted.test.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 2 | import { IsFileDeleted } from '../../../src/files/domain/useCases/IsFileDeleted' 3 | import { ReadError } from '../../../src' 4 | 5 | describe('execute', () => { 6 | test('should return true when file has been deleted', async () => { 7 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 8 | filesRepositoryStub.isFileDeleted = jest.fn().mockResolvedValue(true) 9 | const sut = new IsFileDeleted(filesRepositoryStub) 10 | 11 | const result = await sut.execute(1) 12 | 13 | expect(result).toBe(true) 14 | }) 15 | 16 | test('should return false when file has not been deleted', async () => { 17 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 18 | filesRepositoryStub.isFileDeleted = jest.fn().mockResolvedValue(false) 19 | const sut = new IsFileDeleted(filesRepositoryStub) 20 | 21 | const result = await sut.execute(1) 22 | 23 | expect(result).toBe(false) 24 | }) 25 | 26 | test('should return error result on repository error', async () => { 27 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 28 | filesRepositoryStub.isFileDeleted = jest.fn().mockRejectedValue(new ReadError()) 29 | const sut = new IsFileDeleted(filesRepositoryStub) 30 | 31 | await expect(sut.execute(1)).rejects.toThrow(ReadError) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/unit/files/ReplaceFile.test.ts: -------------------------------------------------------------------------------- 1 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 2 | import { UploadedFileDTO } from '../../../src/files' 3 | import { ReplaceFile } from '../../../src/files/domain/useCases/ReplaceFile' 4 | import { WriteError } from '../../../src/core' 5 | 6 | describe('execute', () => { 7 | const testUploadedFileDTO: UploadedFileDTO = { 8 | fileName: 'testfile', 9 | storageId: 'testStorageId', 10 | checksumValue: 'testChecksumValue', 11 | checksumType: 'md5', 12 | mimeType: 'test/type' 13 | } 14 | 15 | test('should return file id on client success', async () => { 16 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 17 | filesRepositoryStub.replaceFile = jest.fn().mockResolvedValue(1) 18 | 19 | const sut = new ReplaceFile(filesRepositoryStub) 20 | 21 | const actual = await sut.execute(1, testUploadedFileDTO) 22 | 23 | expect(actual).toEqual(1) 24 | }) 25 | 26 | test('should return error on client error', async () => { 27 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 28 | filesRepositoryStub.replaceFile = jest.fn().mockRejectedValue(new WriteError('Some error')) 29 | 30 | const sut = new ReplaceFile(filesRepositoryStub) 31 | 32 | await expect(sut.execute(1, testUploadedFileDTO)).rejects.toThrow(WriteError) 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /test/unit/files/RestrictFile.test.ts: -------------------------------------------------------------------------------- 1 | import { WriteError } from '../../../src' 2 | import { RestrictFileDTO } from '../../../src/files/domain/dtos/RestrictFileDTO' 3 | import { IFilesRepository } from '../../../src/files/domain/repositories/IFilesRepository' 4 | import { RestrictFile } from '../../../src/files/domain/useCases/RestrictFile' 5 | 6 | describe('execute', () => { 7 | const restrictFileDTO: RestrictFileDTO = { 8 | restrict: true, 9 | enableAccessRequest: true, 10 | termsOfAccess: 'This file is restricted for testing purposes' 11 | } 12 | 13 | test('should return undefined when repository call is successful', async () => { 14 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 15 | filesRepositoryStub.restrictFile = jest.fn().mockResolvedValue(undefined) 16 | 17 | const sut = new RestrictFile(filesRepositoryStub) 18 | 19 | const actual = await sut.execute(1, restrictFileDTO) 20 | 21 | expect(actual).toEqual(undefined) 22 | }) 23 | 24 | test('should return error result on repository error', async () => { 25 | const filesRepositoryStub: IFilesRepository = {} as IFilesRepository 26 | filesRepositoryStub.restrictFile = jest.fn().mockRejectedValue(new WriteError()) 27 | 28 | const sut = new RestrictFile(filesRepositoryStub) 29 | 30 | await expect(sut.execute(1, restrictFileDTO)).rejects.toThrow(WriteError) 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /test/unit/info/GetApplicationTermsOfUse.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 2 | import { IDataverseInfoRepository } from '../../../src/info/domain/repositories/IDataverseInfoRepository' 3 | import { GetApplicationTermsOfUse } from '../../../src/info/domain/useCases/GetApplicationTermsOfUse' 4 | 5 | describe('execute', () => { 6 | test('should return successful result with terms of use on repository success', async () => { 7 | const testTermsOfUse = 'Be excellent to each other.' 8 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 9 | dataverseInfoRepositoryStub.getApplicationTermsOfUse = jest 10 | .fn() 11 | .mockResolvedValue(testTermsOfUse) 12 | const sut = new GetApplicationTermsOfUse(dataverseInfoRepositoryStub) 13 | 14 | const actual = await sut.execute() 15 | 16 | expect(actual).toBe(testTermsOfUse) 17 | }) 18 | 19 | test('should return error result on repository error', async () => { 20 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 21 | const testReadError = new ReadError() 22 | dataverseInfoRepositoryStub.getApplicationTermsOfUse = jest 23 | .fn() 24 | .mockRejectedValue(testReadError) 25 | const sut = new GetApplicationTermsOfUse(dataverseInfoRepositoryStub) 26 | 27 | await expect(sut.execute()).rejects.toThrow(ReadError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/info/GetDataverseVersion.test.ts: -------------------------------------------------------------------------------- 1 | import { GetDataverseVersion } from '../../../src/info/domain/useCases/GetDataverseVersion' 2 | import { IDataverseInfoRepository } from '../../../src/info/domain/repositories/IDataverseInfoRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | test('should return successful result with version on repository success', async () => { 7 | const testDataverseVersion = '5.13' 8 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 9 | dataverseInfoRepositoryStub.getDataverseVersion = jest 10 | .fn() 11 | .mockResolvedValue(testDataverseVersion) 12 | const sut = new GetDataverseVersion(dataverseInfoRepositoryStub) 13 | 14 | const actual = await sut.execute() 15 | 16 | expect(actual).toBe(testDataverseVersion) 17 | }) 18 | 19 | test('should return error result on repository error', async () => { 20 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 21 | const testReadError = new ReadError() 22 | dataverseInfoRepositoryStub.getDataverseVersion = jest.fn().mockRejectedValue(testReadError) 23 | const sut = new GetDataverseVersion(dataverseInfoRepositoryStub) 24 | 25 | let actualError: ReadError | undefined = undefined 26 | await sut.execute().catch((e) => (actualError = e)) 27 | 28 | expect(actualError).toBe(testReadError) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/info/GetMaxEmbargoDurationInMonths.test.ts: -------------------------------------------------------------------------------- 1 | import { GetMaxEmbargoDurationInMonths } from '../../../src/info/domain/useCases/GetMaxEmbargoDurationInMonths' 2 | import { IDataverseInfoRepository } from '../../../src/info/domain/repositories/IDataverseInfoRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | test('should return duration on repository success', async () => { 7 | const testDuration = 12 8 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 9 | dataverseInfoRepositoryStub.getMaxEmbargoDurationInMonths = jest 10 | .fn() 11 | .mockResolvedValue(testDuration) 12 | 13 | const sut = new GetMaxEmbargoDurationInMonths(dataverseInfoRepositoryStub) 14 | 15 | const actual = await sut.execute() 16 | 17 | expect(actual).toBe(testDuration) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 22 | dataverseInfoRepositoryStub.getMaxEmbargoDurationInMonths = jest 23 | .fn() 24 | .mockRejectedValue(new ReadError()) 25 | const sut = new GetMaxEmbargoDurationInMonths(dataverseInfoRepositoryStub) 26 | 27 | await expect(sut.execute()).rejects.toBeInstanceOf(ReadError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/info/GetZipDownloadLimit.test.ts: -------------------------------------------------------------------------------- 1 | import { GetZipDownloadLimit } from '../../../src/info/domain/useCases/GetZipDownloadLimit' 2 | import { IDataverseInfoRepository } from '../../../src/info/domain/repositories/IDataverseInfoRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | 5 | describe('execute', () => { 6 | test('should return successful result on repository success', async () => { 7 | const testZipDownloadLimit = 100 8 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 9 | dataverseInfoRepositoryStub.getZipDownloadLimit = jest 10 | .fn() 11 | .mockResolvedValue(testZipDownloadLimit) 12 | 13 | const sut = new GetZipDownloadLimit(dataverseInfoRepositoryStub) 14 | 15 | const actual = await sut.execute() 16 | 17 | expect(actual).toEqual(testZipDownloadLimit) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const dataverseInfoRepositoryStub: IDataverseInfoRepository = {} as IDataverseInfoRepository 22 | dataverseInfoRepositoryStub.getZipDownloadLimit = jest.fn().mockRejectedValue(new ReadError()) 23 | const sut = new GetZipDownloadLimit(dataverseInfoRepositoryStub) 24 | 25 | let actualError: ReadError | undefined = undefined 26 | await sut.execute().catch((e) => (actualError = e)) 27 | 28 | expect(actualError).toBeInstanceOf(ReadError) 29 | }) 30 | }) 31 | -------------------------------------------------------------------------------- /test/unit/metadataBlocks/GetAllMetadataBlocks.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src' 2 | import { createMetadataBlockModel } from '../../testHelpers/metadataBlocks/metadataBlockHelper' 3 | import { IMetadataBlocksRepository } from '../../../src/metadataBlocks/domain/repositories/IMetadataBlocksRepository' 4 | import { GetAllMetadataBlocks } from '../../../src/metadataBlocks/domain/useCases/GetAllMetadataBlocks' 5 | 6 | describe('execute', () => { 7 | test('should return metadata blocks on repository success', async () => { 8 | const testMetadataBlocks = [createMetadataBlockModel()] 9 | const metadataBlocksRepositoryStub: IMetadataBlocksRepository = {} as IMetadataBlocksRepository 10 | metadataBlocksRepositoryStub.getAllMetadataBlocks = jest 11 | .fn() 12 | .mockResolvedValue(testMetadataBlocks) 13 | const testGetAllMetadataBlocks = new GetAllMetadataBlocks(metadataBlocksRepositoryStub) 14 | 15 | const actual = await testGetAllMetadataBlocks.execute() 16 | 17 | expect(actual).toEqual(testMetadataBlocks) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const metadataBlocksRepositoryStub: IMetadataBlocksRepository = {} as IMetadataBlocksRepository 22 | metadataBlocksRepositoryStub.getAllMetadataBlocks = jest.fn().mockRejectedValue(new ReadError()) 23 | const testGetCollectionMetadataBlocks = new GetAllMetadataBlocks(metadataBlocksRepositoryStub) 24 | 25 | await expect(testGetCollectionMetadataBlocks.execute()).rejects.toThrow(ReadError) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/unit/metadataBlocks/GetCollectionMetadataBlocks.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src' 2 | import { createMetadataBlockModel } from '../../testHelpers/metadataBlocks/metadataBlockHelper' 3 | import { IMetadataBlocksRepository } from '../../../src/metadataBlocks/domain/repositories/IMetadataBlocksRepository' 4 | import { GetCollectionMetadataBlocks } from '../../../src/metadataBlocks/domain/useCases/GetCollectionMetadataBlocks' 5 | 6 | describe('execute', () => { 7 | test('should return collection metadata blocks on repository success', async () => { 8 | const testMetadataBlocks = [createMetadataBlockModel()] 9 | const metadataBlocksRepositoryStub: IMetadataBlocksRepository = {} as IMetadataBlocksRepository 10 | metadataBlocksRepositoryStub.getCollectionMetadataBlocks = jest 11 | .fn() 12 | .mockResolvedValue(testMetadataBlocks) 13 | const testGetCollectionMetadataBlocks = new GetCollectionMetadataBlocks( 14 | metadataBlocksRepositoryStub 15 | ) 16 | 17 | const actual = await testGetCollectionMetadataBlocks.execute(1) 18 | 19 | expect(actual).toEqual(testMetadataBlocks) 20 | }) 21 | 22 | test('should return error result on repository error', async () => { 23 | const metadataBlocksRepositoryStub: IMetadataBlocksRepository = {} as IMetadataBlocksRepository 24 | metadataBlocksRepositoryStub.getCollectionMetadataBlocks = jest 25 | .fn() 26 | .mockRejectedValue(new ReadError()) 27 | const testGetCollectionMetadataBlocks = new GetCollectionMetadataBlocks( 28 | metadataBlocksRepositoryStub 29 | ) 30 | 31 | await expect(testGetCollectionMetadataBlocks.execute(1)).rejects.toThrow(ReadError) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /test/unit/users/DeleteCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { IUsersRepository } from '../../../src/users/domain/repositories/IUsersRepository' 2 | import { WriteError } from '../../../src' 3 | import { DeleteCurrentApiToken } from '../../../src/users/domain/useCases/DeleteCurrentApiToken' 4 | 5 | describe('execute', () => { 6 | test('should return undefined on repository success', async () => { 7 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 8 | usersRepositoryStub.deleteCurrentApiToken = jest.fn().mockResolvedValue(undefined) 9 | const sut = new DeleteCurrentApiToken(usersRepositoryStub) 10 | 11 | const actual = await sut.execute() 12 | 13 | expect(actual).toBeUndefined() 14 | }) 15 | 16 | test('should return error result on repository error', async () => { 17 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 18 | usersRepositoryStub.deleteCurrentApiToken = jest.fn().mockRejectedValue(new WriteError()) 19 | const sut = new DeleteCurrentApiToken(usersRepositoryStub) 20 | 21 | await expect(sut.execute()).rejects.toThrow(WriteError) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/unit/users/GetCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { ReadError } from '../../../src' 2 | import { IUsersRepository } from '../../../src/users/domain/repositories/IUsersRepository' 3 | import { GetCurrentApiToken } from '../../../src/users/domain/useCases/GetCurrentApiToken' 4 | import { TestConstants } from '../../testHelpers/TestConstants' 5 | 6 | describe('execute', () => { 7 | test('should return API token on repository success', async () => { 8 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 9 | usersRepositoryStub.getCurrentApiToken = jest 10 | .fn() 11 | .mockResolvedValue(TestConstants.TEST_DUMMY_API_KEY) 12 | const sut = new GetCurrentApiToken(usersRepositoryStub) 13 | 14 | const actual = await sut.execute() 15 | 16 | expect(actual).toEqual(TestConstants.TEST_DUMMY_API_KEY) 17 | }) 18 | 19 | test('should return error result on repository error', async () => { 20 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 21 | usersRepositoryStub.getCurrentApiToken = jest.fn().mockRejectedValue(new ReadError()) 22 | const sut = new GetCurrentApiToken(usersRepositoryStub) 23 | 24 | await expect(sut.execute()).rejects.toThrow(ReadError) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/unit/users/GetCurrentAuthenticatedUser.test.ts: -------------------------------------------------------------------------------- 1 | import { GetCurrentAuthenticatedUser } from '../../../src/users/domain/useCases/GetCurrentAuthenticatedUser' 2 | import { IUsersRepository } from '../../../src/users/domain/repositories/IUsersRepository' 3 | import { ReadError } from '../../../src/core/domain/repositories/ReadError' 4 | import { createAuthenticatedUser } from '../../testHelpers/users/authenticatedUserHelper' 5 | 6 | describe('execute', () => { 7 | test('should return successful result with authenticated user on repository success', async () => { 8 | const testAuthenticatedUser = createAuthenticatedUser() 9 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 10 | usersRepositoryStub.getCurrentAuthenticatedUser = jest 11 | .fn() 12 | .mockResolvedValue(testAuthenticatedUser) 13 | const sut = new GetCurrentAuthenticatedUser(usersRepositoryStub) 14 | 15 | const actual = await sut.execute() 16 | 17 | expect(actual).toEqual(testAuthenticatedUser) 18 | }) 19 | 20 | test('should return error result on repository error', async () => { 21 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 22 | usersRepositoryStub.getCurrentAuthenticatedUser = jest.fn().mockRejectedValue(new ReadError()) 23 | const sut = new GetCurrentAuthenticatedUser(usersRepositoryStub) 24 | 25 | await expect(sut.execute()).rejects.toThrow(ReadError) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/unit/users/RecreateCurrentApiToken.test.ts: -------------------------------------------------------------------------------- 1 | import { IUsersRepository } from '../../../src/users/domain/repositories/IUsersRepository' 2 | import { RecreateCurrentApiToken } from '../../../src/users/domain/useCases/RecreateCurrentApiToken' 3 | import { WriteError } from '../../../src' 4 | import { ApiTokenInfo } from '../../../src/users/domain/models/ApiTokenInfo' 5 | import { TestConstants } from '../../testHelpers/TestConstants' 6 | 7 | describe('execute', () => { 8 | test('should return API token on repository success', async () => { 9 | const testNewTokenInfo: ApiTokenInfo = { 10 | apiToken: TestConstants.TEST_DUMMY_API_KEY, 11 | expirationDate: new Date() 12 | } 13 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 14 | usersRepositoryStub.recreateCurrentApiToken = jest.fn().mockResolvedValue(testNewTokenInfo) 15 | const sut = new RecreateCurrentApiToken(usersRepositoryStub) 16 | 17 | const actual = await sut.execute() 18 | 19 | expect(actual).toEqual(testNewTokenInfo) 20 | }) 21 | 22 | test('should return error result on repository error', async () => { 23 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 24 | usersRepositoryStub.recreateCurrentApiToken = jest.fn().mockRejectedValue(new WriteError()) 25 | const sut = new RecreateCurrentApiToken(usersRepositoryStub) 26 | 27 | await expect(sut.execute()).rejects.toThrow(WriteError) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /test/unit/users/RegisterUser.test.ts: -------------------------------------------------------------------------------- 1 | import { RegisterUser } from '../../../src/users/domain/useCases/RegisterUser' 2 | import { IUsersRepository } from '../../../src/users/domain/repositories/IUsersRepository' 3 | import { UserDTO, WriteError } from '../../../src' 4 | 5 | describe('execute', () => { 6 | const testUserDTO: UserDTO = { 7 | username: 'johndoe', 8 | firstName: 'John', 9 | lastName: 'Doe', 10 | emailAddress: 'johndoe@email.com', 11 | position: '', 12 | affiliation: '', 13 | termsAccepted: true 14 | } 15 | 16 | test('should return undefined on repository success', async () => { 17 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 18 | usersRepositoryStub.registerUser = jest.fn().mockResolvedValue(undefined) 19 | const testRegisterUser = new RegisterUser(usersRepositoryStub) 20 | 21 | const actual = await testRegisterUser.execute(testUserDTO) 22 | 23 | expect(actual).toEqual(undefined) 24 | }) 25 | 26 | test('should return error result on repository error', async () => { 27 | const usersRepositoryStub: IUsersRepository = {} as IUsersRepository 28 | usersRepositoryStub.registerUser = jest.fn().mockRejectedValue(new WriteError()) 29 | const testRegisterUser = new RegisterUser(usersRepositoryStub) 30 | 31 | await expect(testRegisterUser.execute(testUserDTO)).rejects.toThrow(WriteError) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "declarationDir": "./dist", 5 | "module": "commonjs", 6 | "noImplicitAny": true, 7 | "lib": ["es2017", "es7", "es6", "dom"], 8 | "moduleResolution": "node", 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "sourceMap": true, 12 | "target": "es2015", 13 | "outDir": "./dist", 14 | "esModuleInterop": true, 15 | "resolveJsonModule": true, 16 | "strict": true 17 | }, 18 | "include": ["src/**/*"], 19 | "exclude": [ 20 | "node_modules", 21 | "dist", 22 | "jest.config.integration.ts", 23 | "jest.config.ts", 24 | "jest.config.unit.ts", 25 | "jest.config.functional.ts" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "noEmit": true 5 | }, 6 | "include": [ 7 | "src/**/*", 8 | "test/**/*", 9 | "jest.config.integration.ts", 10 | "jest.config.ts", 11 | "jest.config.unit.ts", 12 | "jest.config.functional.ts" 13 | ], 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | --------------------------------------------------------------------------------