@openneuro/server 5.0.0 → 5.1.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 (98) hide show
  1. package/package.json +8 -5
  2. package/src/app.ts +2 -4
  3. package/src/cache/__tests__/tree.spec.ts +2 -0
  4. package/src/cache/tree.ts +4 -0
  5. package/src/cache/types.ts +1 -0
  6. package/src/datalad/__tests__/contributors.spec.ts +1 -1
  7. package/src/datalad/__tests__/dataRetentionNotifications.spec.ts +11 -11
  8. package/src/datalad/__tests__/files.spec.ts +31 -1
  9. package/src/datalad/__tests__/snapshots.spec.ts +4 -4
  10. package/src/datalad/contributors.ts +2 -2
  11. package/src/datalad/dataRetentionNotifications.ts +5 -5
  12. package/src/datalad/dataset.ts +9 -5
  13. package/src/datalad/description.ts +2 -2
  14. package/src/datalad/draft.ts +8 -3
  15. package/src/datalad/files.ts +71 -12
  16. package/src/datalad/readme.ts +2 -2
  17. package/src/datalad/snapshots.ts +19 -14
  18. package/src/elasticsearch/elastic-client.ts +11 -6
  19. package/src/elasticsearch/reindex-dataset.ts +8 -4
  20. package/src/graphql/__tests__/comment.spec.ts +3 -2
  21. package/src/graphql/__tests__/schema.spec.ts +28 -0
  22. package/src/graphql/builder.ts +42 -0
  23. package/src/graphql/resolvers/__tests__/dataset.spec.ts +6 -119
  24. package/src/graphql/resolvers/__tests__/importRemoteDataset.spec.ts +2 -1
  25. package/src/graphql/resolvers/__tests__/permssions.spec.ts +3 -2
  26. package/src/graphql/resolvers/__tests__/user.spec.ts +39 -11
  27. package/src/graphql/resolvers/brainInitiative.ts +4 -3
  28. package/src/graphql/resolvers/cache.ts +7 -6
  29. package/src/graphql/resolvers/comment.ts +35 -19
  30. package/src/graphql/resolvers/dataset-search.ts +7 -6
  31. package/src/graphql/resolvers/dataset.ts +77 -45
  32. package/src/graphql/resolvers/datasetEvents.ts +18 -16
  33. package/src/graphql/resolvers/description.ts +2 -1
  34. package/src/graphql/resolvers/draft.ts +14 -3
  35. package/src/graphql/resolvers/fileCheck.ts +5 -4
  36. package/src/graphql/resolvers/flaggedFiles.ts +2 -1
  37. package/src/graphql/resolvers/follow.ts +2 -1
  38. package/src/graphql/resolvers/git.ts +2 -1
  39. package/src/graphql/resolvers/gitEvents.ts +2 -1
  40. package/src/graphql/resolvers/history.ts +3 -1
  41. package/src/graphql/resolvers/holdDeletion.ts +1 -1
  42. package/src/graphql/resolvers/importRemoteDataset.ts +3 -2
  43. package/src/graphql/resolvers/issues.ts +2 -1
  44. package/src/graphql/resolvers/metadata.ts +3 -2
  45. package/src/graphql/resolvers/permissions.ts +26 -6
  46. package/src/graphql/resolvers/publish.ts +2 -1
  47. package/src/graphql/resolvers/readme.ts +2 -1
  48. package/src/graphql/resolvers/reexporter.ts +2 -1
  49. package/src/graphql/resolvers/relation.ts +3 -2
  50. package/src/graphql/resolvers/reset.ts +2 -1
  51. package/src/graphql/resolvers/reviewer.ts +14 -6
  52. package/src/graphql/resolvers/snapshots.ts +42 -10
  53. package/src/graphql/resolvers/stars.ts +2 -1
  54. package/src/graphql/resolvers/upload.ts +3 -2
  55. package/src/graphql/resolvers/user.ts +44 -32
  56. package/src/graphql/resolvers/validation.ts +6 -5
  57. package/src/graphql/resolvers/worker.ts +2 -1
  58. package/src/graphql/schema/analytics.ts +12 -0
  59. package/src/graphql/schema/comment.ts +30 -0
  60. package/src/graphql/schema/dataset-events.ts +91 -0
  61. package/src/graphql/schema/dataset-search.ts +32 -0
  62. package/src/graphql/schema/dataset.ts +167 -0
  63. package/src/graphql/schema/description.ts +44 -0
  64. package/src/graphql/schema/draft.ts +80 -0
  65. package/src/graphql/schema/enums.ts +55 -0
  66. package/src/graphql/schema/files.ts +44 -0
  67. package/src/graphql/schema/inputs.ts +231 -0
  68. package/src/graphql/schema/metadata.ts +86 -0
  69. package/src/graphql/schema/misc.ts +154 -0
  70. package/src/graphql/schema/mutation.ts +549 -0
  71. package/src/graphql/schema/pagination.ts +12 -0
  72. package/src/graphql/schema/permissions.ts +21 -0
  73. package/src/graphql/schema/query.ts +119 -0
  74. package/src/graphql/schema/refs.ts +23 -0
  75. package/src/graphql/schema/reviewer.ts +11 -0
  76. package/src/graphql/schema/scalars.ts +9 -0
  77. package/src/graphql/schema/snapshot.ts +111 -0
  78. package/src/graphql/schema/upload.ts +13 -0
  79. package/src/graphql/schema/user.ts +61 -0
  80. package/src/graphql/schema/validation.ts +70 -0
  81. package/src/graphql/schema/worker.ts +16 -0
  82. package/src/graphql/schema.ts +29 -1114
  83. package/src/handlers/subscriptions.ts +1 -1
  84. package/src/libs/apikey.ts +1 -1
  85. package/src/libs/authentication/crypto.ts +14 -9
  86. package/src/libs/notifications.ts +1 -1
  87. package/src/libs/presign.ts +32 -20
  88. package/src/libs/redis.ts +12 -2
  89. package/src/models/comment.ts +2 -4
  90. package/src/models/counter.ts +2 -2
  91. package/src/models/ingestDataset.ts +1 -2
  92. package/src/models/notification.ts +0 -2
  93. package/src/models/subscription.ts +0 -1
  94. package/src/models/user.ts +1 -2
  95. package/src/models/userMigration.ts +1 -1
  96. package/src/models/userNotificationStatus.ts +0 -1
  97. package/src/utils/__tests__/snapshots.spec.ts +120 -0
  98. package/src/utils/snapshots.ts +12 -0
@@ -0,0 +1,23 @@
1
+ import { builder } from "../builder"
2
+ import type { FlattenMaps } from "mongoose"
3
+ import type { CommentDocument } from "../../models/comment"
4
+ import type { DatasetDocument } from "../../models/dataset"
5
+ import type { DatasetEventType } from "../../models/datasetEvents"
6
+ import type { GraphQLUserType } from "../resolvers/user"
7
+ import type { EnrichedDatasetEvent } from "../resolvers/datasetEvents"
8
+ import type { SnapshotShape } from "../resolvers/snapshots"
9
+ import type { DraftShape } from "../resolvers/draft"
10
+
11
+ export const UserRef = builder.objectRef<GraphQLUserType>("User")
12
+ export const DatasetRef = builder.objectRef<FlattenMaps<DatasetDocument>>(
13
+ "Dataset",
14
+ )
15
+ export const DraftRef = builder.objectRef<DraftShape>("Draft")
16
+ export const SnapshotRef = builder.objectRef<SnapshotShape>("Snapshot")
17
+ export const CommentRef = builder.objectRef<CommentDocument>("Comment")
18
+ export const DatasetEventDescriptionRef = builder.objectRef<DatasetEventType>(
19
+ "DatasetEventDescription",
20
+ )
21
+ export const DatasetEventRef = builder.objectRef<EnrichedDatasetEvent>(
22
+ "DatasetEvent",
23
+ )
@@ -0,0 +1,11 @@
1
+ import { builder } from "../builder"
2
+
3
+ export const DatasetReviewer = builder.simpleObject("DatasetReviewer", {
4
+ description: "Anonymous dataset reviewer",
5
+ fields: (t) => ({
6
+ id: t.id({ nullable: false }),
7
+ datasetId: t.id({ nullable: false }),
8
+ expiration: t.field({ type: "DateTime" }),
9
+ url: t.string({ nullable: false }),
10
+ }),
11
+ })
@@ -0,0 +1,9 @@
1
+ import { builder } from "../builder"
2
+ import { GraphQLDate, GraphQLDateTime } from "graphql-iso-date"
3
+ import GraphQLBigInt from "graphql-bigint"
4
+ import GraphQLJSON from "graphql-type-json"
5
+
6
+ builder.addScalarType("Date", GraphQLDate)
7
+ builder.addScalarType("DateTime", GraphQLDateTime)
8
+ builder.addScalarType("BigInt", GraphQLBigInt)
9
+ builder.addScalarType("JSON", GraphQLJSON)
@@ -0,0 +1,111 @@
1
+ import { DatasetRef, SnapshotRef } from "./refs"
2
+ import { Summary } from "./metadata"
3
+ import {
4
+ DatasetValidation,
5
+ ValidationIssue,
6
+ ValidationIssueStatus,
7
+ } from "./validation"
8
+ import { DatasetFile } from "./files"
9
+ import { Description } from "./description"
10
+ import { Contributor } from "./description"
11
+ import { Analytic } from "./analytics"
12
+ import { DeprecatedSnapshot, RelatedObject } from "./misc"
13
+ import SnapshotFields from "../resolvers/snapshots"
14
+
15
+ SnapshotRef.implement({
16
+ directives: { cacheControl: { maxAge: 3600 } },
17
+ fields: (t) => ({
18
+ id: t.id({ nullable: false, resolve: (obj) => obj.id }),
19
+ tag: t.string({ nullable: false, resolve: (obj) => obj.tag }),
20
+ dataset: t.field({
21
+ type: DatasetRef,
22
+ nullable: false,
23
+ resolve: (obj) => obj.dataset() as never,
24
+ }),
25
+ created: t.field({
26
+ type: "DateTime",
27
+ resolve: (obj) => obj.created ?? null,
28
+ }),
29
+ summary: t.field({
30
+ type: Summary,
31
+ resolve: (obj) => obj.summary() as never,
32
+ }),
33
+ issues: t.field({
34
+ type: [ValidationIssue],
35
+ nullable: { list: true, items: true },
36
+ resolve: (obj) => SnapshotFields.issues(obj),
37
+ }),
38
+ issuesStatus: t.field({
39
+ type: ValidationIssueStatus,
40
+ resolve: (obj) => SnapshotFields.issuesStatus(obj),
41
+ }),
42
+ validation: t.field({
43
+ type: DatasetValidation,
44
+ resolve: (obj, _args, ctx) =>
45
+ SnapshotFields.validation(obj, null, ctx) as never,
46
+ }),
47
+ files: t.field({
48
+ type: [DatasetFile],
49
+ nullable: { list: true, items: true },
50
+ args: {
51
+ tree: t.arg.string(),
52
+ recursive: t.arg.boolean(),
53
+ },
54
+ resolve: (obj, args) =>
55
+ (typeof obj.files === "function"
56
+ ? obj.files({ tree: args.tree, recursive: args.recursive })
57
+ : obj.files) as never,
58
+ }),
59
+ description: t.field({
60
+ type: Description,
61
+ resolve: (obj) =>
62
+ typeof obj.description === "function"
63
+ ? obj.description()
64
+ : obj.description,
65
+ }),
66
+ analytics: t.field({
67
+ type: Analytic,
68
+ resolve: (obj) => SnapshotFields.analytics(obj),
69
+ }),
70
+ readme: t.string({
71
+ directives: { cacheControl: { maxAge: 31536000 } },
72
+ resolve: (obj) =>
73
+ (typeof obj.readme === "function" ? obj.readme() : obj.readme) as never,
74
+ }),
75
+ hexsha: t.string({
76
+ resolve: (obj) => obj.hexsha ?? null,
77
+ }),
78
+ deprecated: t.field({
79
+ type: DeprecatedSnapshot,
80
+ resolve: (obj) =>
81
+ typeof obj.deprecated === "function"
82
+ ? obj.deprecated()
83
+ : obj.deprecated,
84
+ }),
85
+ related: t.field({
86
+ type: [RelatedObject],
87
+ nullable: { list: true, items: true },
88
+ resolve: (obj) =>
89
+ (typeof obj.related === "function"
90
+ ? obj.related()
91
+ : obj.related) as never,
92
+ }),
93
+ onBrainlife: t.boolean({
94
+ directives: { cacheControl: { maxAge: 10080 } },
95
+ resolve: (obj) =>
96
+ typeof obj.onBrainlife === "function"
97
+ ? obj.onBrainlife()
98
+ : obj.onBrainlife,
99
+ }),
100
+ size: t.field({
101
+ type: "BigInt",
102
+ resolve: (obj) =>
103
+ (typeof obj.size === "function" ? obj.size() : obj.size) as never,
104
+ }),
105
+ contributors: t.field({
106
+ type: [Contributor],
107
+ nullable: { list: true, items: true },
108
+ resolve: (obj) => SnapshotFields.contributors(obj),
109
+ }),
110
+ }),
111
+ })
@@ -0,0 +1,13 @@
1
+ import { builder } from "../builder"
2
+
3
+ export const UploadMetadata = builder.simpleObject("UploadMetadata", {
4
+ description: "Client metadata needed to complete an upload",
5
+ fields: (t) => ({
6
+ id: t.id({ nullable: false }),
7
+ datasetId: t.id({ nullable: false }),
8
+ complete: t.boolean({ nullable: false }),
9
+ estimatedSize: t.field({ type: "BigInt" }),
10
+ token: t.string(),
11
+ endpoint: t.int(),
12
+ }),
13
+ })
@@ -0,0 +1,61 @@
1
+ import { builder } from "../builder"
2
+ import { DatasetEventRef, UserRef } from "./refs"
3
+ import { UserProvider } from "./enums"
4
+ import { notifications } from "../resolvers/user"
5
+
6
+ UserRef.implement({
7
+ description: "OpenNeuro user records from all providers",
8
+ fields: (t) => ({
9
+ id: t.id({ resolve: (obj) => obj.id }),
10
+ provider: t.field({
11
+ type: UserProvider,
12
+ resolve: (obj) => obj.provider,
13
+ }),
14
+ avatar: t.string({ resolve: (obj) => obj.avatar }),
15
+ orcid: t.string({ resolve: (obj) => obj.orcid }),
16
+ created: t.field({
17
+ type: "DateTime",
18
+ nullable: false,
19
+ resolve: (obj) => obj.created,
20
+ }),
21
+ modified: t.field({
22
+ type: "DateTime",
23
+ resolve: (obj) => obj.updatedAt,
24
+ }),
25
+ lastSeen: t.field({
26
+ type: "DateTime",
27
+ resolve: (obj) => obj.lastSeen,
28
+ }),
29
+ email: t.string({ resolve: (obj) => obj.email }),
30
+ name: t.string({ resolve: (obj) => obj.name }),
31
+ admin: t.boolean({ resolve: (obj) => obj.admin }),
32
+ blocked: t.boolean({ resolve: (obj) => obj.blocked }),
33
+ location: t.string({ resolve: (obj) => obj.location }),
34
+ institution: t.string({ resolve: (obj) => obj.institution }),
35
+ github: t.string({ resolve: (obj) => obj.github }),
36
+ githubSynced: t.field({
37
+ type: "Date",
38
+ resolve: (obj) => obj.githubSynced,
39
+ }),
40
+ links: t.stringList({
41
+ nullable: { list: true, items: true },
42
+ resolve: (obj) => obj.links,
43
+ }),
44
+ notifications: t.field({
45
+ type: [DatasetEventRef],
46
+ nullable: { list: true, items: false },
47
+ resolve: (obj, args, ctx) => notifications(obj, args, ctx),
48
+ }),
49
+ orcidConsent: t.boolean({ resolve: (obj) => obj.orcidConsent }),
50
+ }),
51
+ })
52
+
53
+ export const UserList = builder.simpleObject("UserList", {
54
+ fields: (t) => ({
55
+ users: t.field({
56
+ type: [UserRef],
57
+ nullable: { list: false, items: false },
58
+ }),
59
+ totalCount: t.int({ nullable: false }),
60
+ }),
61
+ })
@@ -0,0 +1,70 @@
1
+ import { builder } from "../builder"
2
+ import { Severity } from "./enums"
3
+ import { ValidationIssueFile } from "./files"
4
+
5
+ export const ValidatorCodeMessage = builder.simpleObject(
6
+ "ValidatorCodeMessage",
7
+ {
8
+ fields: (t) => ({
9
+ code: t.string({ nullable: false }),
10
+ message: t.string({ nullable: false }),
11
+ }),
12
+ },
13
+ )
14
+
15
+ export const ValidatorIssue = builder.simpleObject("ValidatorIssue", {
16
+ description: "BIDS Validator (schema) issues",
17
+ fields: (t) => ({
18
+ code: t.string({ nullable: false }),
19
+ subCode: t.string(),
20
+ location: t.string(),
21
+ severity: t.field({ type: Severity }),
22
+ rule: t.string(),
23
+ issueMessage: t.string(),
24
+ affects: t.string(),
25
+ line: t.int(),
26
+ }),
27
+ })
28
+
29
+ export const DatasetValidation = builder.simpleObject("DatasetValidation", {
30
+ fields: (t) => ({
31
+ id: t.string(),
32
+ datasetId: t.string(),
33
+ issues: t.field({
34
+ type: [ValidatorIssue],
35
+ nullable: { list: true, items: true },
36
+ }),
37
+ codeMessages: t.field({
38
+ type: [ValidatorCodeMessage],
39
+ nullable: { list: true, items: true },
40
+ }),
41
+ errors: t.int(),
42
+ warnings: t.int(),
43
+ }),
44
+ })
45
+
46
+ export const ValidationIssue = builder.simpleObject("ValidationIssue", {
47
+ fields: (t) => ({
48
+ severity: t.field({ type: Severity, nullable: false }),
49
+ key: t.string({ nullable: false }),
50
+ code: t.int(),
51
+ reason: t.string({ nullable: false }),
52
+ files: t.field({
53
+ type: [ValidationIssueFile],
54
+ nullable: { list: true, items: true },
55
+ }),
56
+ additionalFileCount: t.int(),
57
+ helpUrl: t.string(),
58
+ }),
59
+ })
60
+
61
+ export const ValidationIssueStatus = builder.simpleObject(
62
+ "ValidationIssueStatus",
63
+ {
64
+ description: "Legacy validator count of errors and warnings",
65
+ fields: (t) => ({
66
+ errors: t.int(),
67
+ warnings: t.int(),
68
+ }),
69
+ },
70
+ )
@@ -0,0 +1,16 @@
1
+ import { builder } from "../builder"
2
+
3
+ export const WorkerTask = builder.simpleObject("WorkerTask", {
4
+ fields: (t) => ({
5
+ id: t.id({ nullable: false }),
6
+ args: t.field({ type: "JSON" }),
7
+ kwargs: t.field({ type: "JSON" }),
8
+ taskName: t.string(),
9
+ worker: t.string(),
10
+ queuedAt: t.field({ type: "DateTime" }),
11
+ startedAt: t.field({ type: "DateTime" }),
12
+ finishedAt: t.field({ type: "DateTime" }),
13
+ error: t.string(),
14
+ executionTime: t.int(),
15
+ }),
16
+ })