@openneuro/server 4.47.6 → 5.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/package.json +10 -7
  2. package/src/app.ts +1 -1
  3. package/src/cache/__tests__/tree.spec.ts +212 -0
  4. package/src/cache/tree.ts +148 -0
  5. package/src/datalad/__tests__/dataRetentionNotifications.spec.ts +11 -0
  6. package/src/datalad/__tests__/files.spec.ts +249 -0
  7. package/src/datalad/dataRetentionNotifications.ts +5 -0
  8. package/src/datalad/dataset.ts +29 -1
  9. package/src/datalad/files.ts +362 -39
  10. package/src/datalad/snapshots.ts +29 -54
  11. package/src/graphql/resolvers/__tests__/response-status.spec.ts +42 -0
  12. package/src/graphql/resolvers/__tests__/user.spec.ts +55 -1
  13. package/src/graphql/resolvers/build-search-query.ts +391 -0
  14. package/src/graphql/resolvers/cache.ts +5 -1
  15. package/src/graphql/resolvers/dataset-search.ts +40 -23
  16. package/src/graphql/resolvers/datasetEvents.ts +48 -78
  17. package/src/graphql/resolvers/draft.ts +5 -2
  18. package/src/graphql/resolvers/holdDeletion.ts +21 -0
  19. package/src/graphql/resolvers/index.ts +6 -0
  20. package/src/graphql/resolvers/mutation.ts +2 -0
  21. package/src/graphql/resolvers/response-status.ts +43 -0
  22. package/src/graphql/resolvers/snapshots.ts +9 -18
  23. package/src/graphql/resolvers/summary.ts +17 -0
  24. package/src/graphql/resolvers/user.ts +1 -1
  25. package/src/graphql/schema.ts +54 -14
  26. package/src/handlers/datalad.ts +4 -0
  27. package/src/handlers/doi.ts +32 -36
  28. package/src/libs/doi/__tests__/doi.spec.ts +50 -12
  29. package/src/libs/doi/__tests__/validate.spec.ts +110 -0
  30. package/src/libs/doi/index.ts +108 -71
  31. package/src/libs/doi/metadata.ts +101 -0
  32. package/src/libs/doi/validate.ts +59 -0
  33. package/src/libs/presign.ts +137 -0
  34. package/src/models/dataset.ts +2 -0
  35. package/src/models/doi.ts +7 -0
  36. package/src/queues/producer-methods.ts +9 -5
  37. package/src/queues/queue-schedule.ts +1 -1
  38. package/src/queues/queues.ts +2 -2
  39. package/src/routes.ts +10 -2
  40. package/src/types/datacite/LICENSE +37 -0
  41. package/src/types/datacite/README.md +3 -0
  42. package/src/types/datacite/datacite-v4.5.json +643 -0
  43. package/src/types/datacite/datacite-v4.5.ts +281 -0
  44. package/src/types/datacite.ts +53 -63
  45. package/src/utils/datacite-mapper.ts +7 -3
  46. package/src/utils/datacite-utils.ts +12 -15
  47. package/src/libs/doi/__tests__/__snapshots__/doi.spec.ts.snap +0 -17
@@ -0,0 +1,281 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * This file was automatically generated by json-schema-to-typescript.
4
+ * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
5
+ * and run json-schema-to-typescript to regenerate this file.
6
+ */
7
+
8
+ export type ResourceTypeGeneral =
9
+ | "Audiovisual"
10
+ | "Book"
11
+ | "BookChapter"
12
+ | "Collection"
13
+ | "ComputationalNotebook"
14
+ | "ConferencePaper"
15
+ | "ConferenceProceeding"
16
+ | "DataPaper"
17
+ | "Dataset"
18
+ | "Dissertation"
19
+ | "Event"
20
+ | "Image"
21
+ | "Instrument"
22
+ | "InteractiveResource"
23
+ | "Journal"
24
+ | "JournalArticle"
25
+ | "Model"
26
+ | "OutputManagementPlan"
27
+ | "PeerReview"
28
+ | "PhysicalObject"
29
+ | "Preprint"
30
+ | "Report"
31
+ | "Service"
32
+ | "Software"
33
+ | "Sound"
34
+ | "Standard"
35
+ | "StudyRegistration"
36
+ | "Text"
37
+ | "Workflow"
38
+ | "Other"
39
+ export type Creator = Person
40
+ export type NameType = "Organizational" | "Personal"
41
+ export type NameIdentifiers = {
42
+ nameIdentifier: string
43
+ nameIdentifierScheme: string
44
+ schemeUri?: string
45
+ }[]
46
+ export type Affiliation = {
47
+ name: string
48
+ affiliationIdentifier?: string
49
+ affiliationIdentifierScheme?: string
50
+ schemeUri?: string
51
+ }[]
52
+ export type TitleType =
53
+ | "AlternativeTitle"
54
+ | "Subtitle"
55
+ | "TranslatedTitle"
56
+ | "Other"
57
+ export type PublicationYear = string
58
+ export type Contributor = Person & {
59
+ contributorType: ContributorType
60
+ [k: string]: unknown
61
+ }
62
+ export type ContributorType =
63
+ | "ContactPerson"
64
+ | "DataCollector"
65
+ | "DataCurator"
66
+ | "DataManager"
67
+ | "Distributor"
68
+ | "Editor"
69
+ | "HostingInstitution"
70
+ | "Producer"
71
+ | "ProjectLeader"
72
+ | "ProjectManager"
73
+ | "ProjectMember"
74
+ | "RegistrationAgency"
75
+ | "RegistrationAuthority"
76
+ | "RelatedPerson"
77
+ | "Researcher"
78
+ | "ResearchGroup"
79
+ | "RightsHolder"
80
+ | "Sponsor"
81
+ | "Supervisor"
82
+ | "WorkPackageLeader"
83
+ | "Other"
84
+ export type Date = {
85
+ [k: string]: unknown
86
+ }
87
+ export type Date1 = string
88
+ export type DateType =
89
+ | "Accepted"
90
+ | "Available"
91
+ | "Copyrighted"
92
+ | "Collected"
93
+ | "Created"
94
+ | "Issued"
95
+ | "Submitted"
96
+ | "Updated"
97
+ | "Valid"
98
+ | "Withdrawn"
99
+ | "Other"
100
+ export type RelationType =
101
+ | "IsCitedBy"
102
+ | "Cites"
103
+ | "IsCollectedBy"
104
+ | "Collects"
105
+ | "IsSupplementTo"
106
+ | "IsSupplementedBy"
107
+ | "IsContinuedBy"
108
+ | "Continues"
109
+ | "IsDescribedBy"
110
+ | "Describes"
111
+ | "HasMetadata"
112
+ | "IsMetadataFor"
113
+ | "HasVersion"
114
+ | "IsVersionOf"
115
+ | "IsNewVersionOf"
116
+ | "IsPartOf"
117
+ | "IsPreviousVersionOf"
118
+ | "IsPublishedIn"
119
+ | "HasPart"
120
+ | "IsReferencedBy"
121
+ | "References"
122
+ | "IsDocumentedBy"
123
+ | "Documents"
124
+ | "IsCompiledBy"
125
+ | "Compiles"
126
+ | "IsVariantFormOf"
127
+ | "IsOriginalFormOf"
128
+ | "IsIdenticalTo"
129
+ | "IsReviewedBy"
130
+ | "Reviews"
131
+ | "IsDerivedFrom"
132
+ | "IsSourceOf"
133
+ | "IsRequiredBy"
134
+ | "Requires"
135
+ | "IsObsoletedBy"
136
+ | "Obsoletes"
137
+ export type DescriptionType =
138
+ | "Abstract"
139
+ | "Methods"
140
+ | "SeriesInformation"
141
+ | "TableOfContents"
142
+ | "TechnicalInfo"
143
+ | "Other"
144
+ export type Longitude = number
145
+ export type Latitude = number
146
+ export type FunderIdentifierType =
147
+ | "ISNI"
148
+ | "GRID"
149
+ | "Crossref Funder ID"
150
+ | "ROR"
151
+ | "Other"
152
+
153
+ /**
154
+ * JSON representation of the DataCite v4.5 schema.
155
+ */
156
+ export interface DataCiteV45 {
157
+ doi?: string
158
+ prefix?: string
159
+ suffix?: string
160
+ event?: "hide" | "register" | "publish"
161
+ url?: string
162
+ types: {
163
+ resourceType?: string
164
+ resourceTypeGeneral: ResourceTypeGeneral
165
+ }
166
+ /**
167
+ * @minItems 1
168
+ */
169
+ creators: [Creator, ...Creator[]]
170
+ /**
171
+ * @minItems 1
172
+ */
173
+ titles: [
174
+ {
175
+ title: string
176
+ titleType?: TitleType
177
+ lang?: string
178
+ },
179
+ ...{
180
+ title: string
181
+ titleType?: TitleType
182
+ lang?: string
183
+ }[],
184
+ ]
185
+ publisher: {
186
+ name: string
187
+ publisherIdentifier?: string
188
+ publisherIdentifierScheme?: string
189
+ schemeUri?: string
190
+ lang?: string
191
+ }
192
+ publicationYear: PublicationYear
193
+ subjects?: {
194
+ subject: string
195
+ subjectScheme?: string
196
+ schemeUri?: string
197
+ valueUri?: string
198
+ classificationCode?: string
199
+ lang?: string
200
+ }[]
201
+ contributors?: Contributor[]
202
+ dates?: {
203
+ date: Date & Date1
204
+ dateType: DateType
205
+ dateInformation?: string
206
+ }[]
207
+ language?: string
208
+ alternateIdentifiers?: {
209
+ alternateIdentifier: string
210
+ alternateIdentifierType: string
211
+ }[]
212
+ relatedIdentifiers?: RelatedObject[]
213
+ relatedItems?: RelatedObject[]
214
+ sizes?: string[]
215
+ formats?: string[]
216
+ version?: string
217
+ rightsList?: {
218
+ rights?: string
219
+ rightsUri?: string
220
+ rightsIdentifier?: string
221
+ rightsIdentifierScheme?: string
222
+ schemeUri?: string
223
+ lang?: string
224
+ }[]
225
+ descriptions?: {
226
+ description: string
227
+ descriptionType: DescriptionType
228
+ lang?: string
229
+ }[]
230
+ geoLocations?: {
231
+ geoLocationPlace?: string
232
+ geoLocationPoint?: GeoLocationPoint
233
+ geoLocationBox?: {
234
+ westBoundLongitude: Longitude
235
+ eastBoundLongitude: Longitude
236
+ southBoundLatitude: Latitude
237
+ northBoundLatitude: Latitude
238
+ }
239
+ geoLocationPolygon?: {
240
+ polygonPoint?: GeoLocationPoint
241
+ inPolygonPoint?: GeoLocationPoint
242
+ }[]
243
+ }[]
244
+ fundingReferences?: {
245
+ funderName: string
246
+ funderIdentifier?: string
247
+ funderIdentifierType?: FunderIdentifierType
248
+ awardNumber?: string
249
+ awardUri?: string
250
+ awardTitle?: string
251
+ }[]
252
+ schemaVersion: "http://datacite.org/schema/kernel-4"
253
+ container?: {
254
+ type?: string
255
+ title?: string
256
+ firstPage?: string
257
+ [k: string]: unknown
258
+ }
259
+ }
260
+ export interface Person {
261
+ name: string
262
+ nameType?: NameType
263
+ givenName?: string
264
+ familyName?: string
265
+ nameIdentifiers?: NameIdentifiers
266
+ affiliation?: Affiliation
267
+ lang?: string
268
+ [k: string]: unknown
269
+ }
270
+ export interface RelatedObject {
271
+ relationType: RelationType
272
+ relatedMetadataScheme?: string
273
+ schemeUri?: string
274
+ schemeType?: string
275
+ resourceTypeGeneral?: ResourceTypeGeneral
276
+ [k: string]: unknown
277
+ }
278
+ export interface GeoLocationPoint {
279
+ pointLongitude: Longitude
280
+ pointLatitude: Latitude
281
+ }
@@ -1,29 +1,44 @@
1
1
  /**
2
- * Interfaces for working with datacite.yml metadata.
3
- * Both contributors and creators
2
+ * DataCite types.
3
+ *
4
+ * Schema-derived types are re-exported from the generated datacite-v4.5 module.
5
+ * Only app-specific types that don't exist in the DataCite schema are defined here.
4
6
  */
5
7
 
6
- /**
7
- * Unique identifier for a person or organization.
8
- */
9
- export interface NameIdentifier {
10
- nameIdentifier: string
11
- nameIdentifierScheme: string
12
- schemeUri?: string
13
- }
8
+ export type {
9
+ Affiliation,
10
+ ContributorType,
11
+ Creator,
12
+ DescriptionType,
13
+ NameIdentifiers,
14
+ NameType,
15
+ Person,
16
+ PublicationYear,
17
+ ResourceTypeGeneral,
18
+ TitleType,
19
+ } from "./datacite/datacite-v4.5"
20
+
21
+ export type { Contributor as DataciteContributor } from "./datacite/datacite-v4.5"
22
+
23
+ import type { Creator, DataCiteV45, TitleType } from "./datacite/datacite-v4.5"
14
24
 
15
25
  /**
16
- * An interface for an organizational or institutional affiliation.
26
+ * Re-export the versioned schema type under a version-agnostic name.
27
+ *
28
+ * Titles and creators are omitted and we separately check minItems:1
17
29
  */
18
- export interface Affiliation {
19
- name: string
20
- schemeUri?: string
21
- affiliationIdentifier?: string
22
- affiliationIdentifierScheme?: string
30
+ export type DataCite = Omit<DataCiteV45, "creators" | "titles"> & {
31
+ creators: Creator[]
32
+ titles: {
33
+ title: string
34
+ titleType?: TitleType
35
+ lang?: string
36
+ }[]
23
37
  }
24
38
 
25
39
  /**
26
- * Contributor object (normalized form used internally in app).
40
+ * Internal app contributor type with fields not in the DataCite schema
41
+ * (orcid shorthand, display order, linked user ID).
27
42
  */
28
43
  export interface Contributor {
29
44
  name: string
@@ -36,62 +51,37 @@ export interface Contributor {
36
51
  }
37
52
 
38
53
  /**
39
- * Base interface shared by both creators and contributors in datacite.yml
40
- */
41
- export interface RawDataciteBaseContributor {
42
- name: string
43
- nameType: "Personal" | "Organizational"
44
- givenName?: string
45
- familyName?: string
46
- nameIdentifiers?: NameIdentifier[]
47
- affiliation?: Affiliation[]
48
- }
49
-
50
- /**
51
- * Raw Creator object as it appears in datacite.yml creators array.
52
- * Does NOT have contributorType.
53
- */
54
- export type RawDataciteCreator = RawDataciteBaseContributor
55
-
56
- /**
57
- * Raw Contributor object as it appears in datacite.yml contributors array.
58
- * Adds contributorType, which is required.
54
+ * DOI states tracked in our database (response-only, not in the DataCite request schema).
59
55
  */
60
- export interface RawDataciteContributor extends RawDataciteBaseContributor {
61
- contributorType: string
62
- }
56
+ export type DoiState = "draft" | "registered" | "findable"
63
57
 
64
- /**
65
- * An interface for the resource types.
66
- */
67
- export interface RawDataciteTypes {
68
- resourceType?: string
69
- resourceTypeGeneral: string
58
+ export interface DatasetWithDescription {
59
+ dataset_description?: {
60
+ Description?: string
61
+ }
70
62
  }
71
63
 
72
64
  /**
73
- * The main attributes section of the datacite.yml file.
74
- */
75
- export interface RawDataciteAttributes {
76
- contributors?: RawDataciteContributor[]
77
- creators?: RawDataciteCreator[]
78
- types: RawDataciteTypes
79
- descriptions?: {
80
- description: string
81
- descriptionType: string
82
- }[]
83
- }
84
- /**
85
- * The top-level interface for the entire datacite.yml file structure.
65
+ * The datacite.yml file structure stored in dataset repositories.
66
+ * Uses a subset of DataCiteV45 fields wrapped in a JSON:API-like envelope.
86
67
  */
87
68
  export interface RawDataciteYml {
88
69
  data: {
89
- attributes: RawDataciteAttributes
70
+ attributes: Partial<
71
+ Pick<
72
+ DataCite,
73
+ "contributors" | "creators" | "types" | "descriptions"
74
+ >
75
+ >
90
76
  }
91
77
  }
92
78
 
93
- export interface DatasetWithDescription {
94
- dataset_description?: {
95
- Description?: string
79
+ /**
80
+ * JSON:API request envelope for the DataCite REST API.
81
+ */
82
+ export interface DataciteDoiRequest {
83
+ data: {
84
+ type: "dois"
85
+ attributes: DataCite
96
86
  }
97
87
  }
@@ -1,11 +1,15 @@
1
- import type { Contributor, RawDataciteContributor } from "../types/datacite"
1
+ import type {
2
+ Contributor,
3
+ ContributorType,
4
+ DataciteContributor,
5
+ } from "../types/datacite"
2
6
 
3
7
  export const mapToRawContributor = (
4
8
  c: Contributor,
5
- ): RawDataciteContributor => ({
9
+ ): DataciteContributor => ({
6
10
  name: c.name,
7
11
  nameType: "Personal",
8
- contributorType: c.contributorType || "Researcher",
12
+ contributorType: (c.contributorType || "Researcher") as ContributorType,
9
13
  givenName: c.givenName,
10
14
  familyName: c.familyName,
11
15
  nameIdentifiers: c.orcid
@@ -7,7 +7,9 @@ import { commitFiles } from "../datalad/dataset"
7
7
  import { getDatasetWorker } from "../libs/datalad-service"
8
8
  import type {
9
9
  Contributor,
10
- RawDataciteContributor,
10
+ ContributorType,
11
+ Creator,
12
+ DataciteContributor,
11
13
  RawDataciteYml,
12
14
  } from "../types/datacite"
13
15
  import { validateOrcid } from "../utils/orcid-utils"
@@ -131,11 +133,11 @@ export const saveDataciteYmlToRepo = async (
131
133
  }
132
134
 
133
135
  /**
134
- * Converts RawDataciteContributor -> internal Contributor type.
136
+ * Converts DataciteContributor -> internal Contributor type.
135
137
  * Optionally attaches a `userId` if the contributor exists as a site user.
136
138
  */
137
139
  export const normalizeRawContributors = async (
138
- raw: RawDataciteContributor[] | undefined,
140
+ raw: DataciteContributor[] | undefined,
139
141
  ): Promise<Contributor[]> => {
140
142
  if (!Array.isArray(raw)) return []
141
143
 
@@ -183,14 +185,14 @@ export const updateContributors = async (
183
185
  dataciteData = await emptyDataciteYml({ datasetId, revision })
184
186
  }
185
187
 
186
- // Map contributors to RawDataciteContributor format
187
- const rawContributors: RawDataciteContributor[] = newContributors.map((
188
+ // Map contributors to DataciteContributor format
189
+ const rawContributors: DataciteContributor[] = newContributors.map((
188
190
  c,
189
191
  ) => ({
190
192
  name: c.name,
191
193
  givenName: c.givenName,
192
194
  familyName: c.familyName,
193
- contributorType: c.contributorType || "Researcher",
195
+ contributorType: (c.contributorType || "Researcher") as ContributorType,
194
196
  nameType: "Personal" as const,
195
197
  nameIdentifiers: c.orcid
196
198
  ? [{ nameIdentifier: c.orcid, nameIdentifierScheme: "ORCID" }]
@@ -223,7 +225,7 @@ export const updateContributorsUtil = async (
223
225
  //
224
226
  // 1. Build contributors (full form, includes `order`)
225
227
  //
226
- const contributorsCopy: RawDataciteContributor[] = newContributors.map((
228
+ const contributorsCopy: DataciteContributor[] = newContributors.map((
227
229
  c,
228
230
  index,
229
231
  ) => ({
@@ -240,7 +242,7 @@ export const updateContributorsUtil = async (
240
242
  schemeUri: "https://orcid.org",
241
243
  }]
242
244
  : [],
243
- contributorType: c.contributorType || "Researcher",
245
+ contributorType: (c.contributorType || "Researcher") as ContributorType,
244
246
  order: index + 1,
245
247
  }))
246
248
 
@@ -249,13 +251,8 @@ export const updateContributorsUtil = async (
249
251
  //
250
252
  // 2. Build creators (strictly filtered / no empty fields / no order)
251
253
  //
252
- type RawDataciteCreator = Omit<
253
- RawDataciteContributor,
254
- "contributorType" | "order"
255
- >
256
-
257
- const creators: RawDataciteCreator[] = contributorsCopy.map((c) => {
258
- const creator: RawDataciteCreator = {
254
+ const creators: Creator[] = contributorsCopy.map((c) => {
255
+ const creator: Creator = {
259
256
  name: c.name,
260
257
  nameType: "Personal",
261
258
  }
@@ -1,17 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`DOI minting utils > template() > accepts expected arguments 1`] = `
4
- "<?xml version="1.0" encoding="UTF-8"?>
5
- <resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://datacite.org/schema/kernel-4" xsi:schemaLocation="http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4/metadata.xsd">
6
- <identifier identifierType="DOI">12345</identifier>
7
- <creators>
8
- <creator><creatorName>A. User</creatorName></creator><creator><creatorName>B. User</creatorName></creator>
9
- </creators>
10
- <titles>
11
- <title xml:lang="en-us">Test Dataset</title>
12
- </titles>
13
- <publisher>Openneuro</publisher>
14
- <publicationYear>1999</publicationYear>
15
- <resourceType resourceTypeGeneral="Dataset">fMRI</resourceType>
16
- </resource>"
17
- `;