@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.
- package/package.json +8 -5
- package/src/app.ts +2 -4
- package/src/cache/__tests__/tree.spec.ts +2 -0
- package/src/cache/tree.ts +4 -0
- package/src/cache/types.ts +1 -0
- package/src/datalad/__tests__/contributors.spec.ts +1 -1
- package/src/datalad/__tests__/dataRetentionNotifications.spec.ts +11 -11
- package/src/datalad/__tests__/files.spec.ts +31 -1
- package/src/datalad/__tests__/snapshots.spec.ts +4 -4
- package/src/datalad/contributors.ts +2 -2
- package/src/datalad/dataRetentionNotifications.ts +5 -5
- package/src/datalad/dataset.ts +9 -5
- package/src/datalad/description.ts +2 -2
- package/src/datalad/draft.ts +8 -3
- package/src/datalad/files.ts +71 -12
- package/src/datalad/readme.ts +2 -2
- package/src/datalad/snapshots.ts +19 -14
- package/src/elasticsearch/elastic-client.ts +11 -6
- package/src/elasticsearch/reindex-dataset.ts +8 -4
- package/src/graphql/__tests__/comment.spec.ts +3 -2
- package/src/graphql/__tests__/schema.spec.ts +28 -0
- package/src/graphql/builder.ts +42 -0
- package/src/graphql/resolvers/__tests__/dataset.spec.ts +6 -119
- package/src/graphql/resolvers/__tests__/importRemoteDataset.spec.ts +2 -1
- package/src/graphql/resolvers/__tests__/permssions.spec.ts +3 -2
- package/src/graphql/resolvers/__tests__/user.spec.ts +39 -11
- package/src/graphql/resolvers/brainInitiative.ts +4 -3
- package/src/graphql/resolvers/cache.ts +7 -6
- package/src/graphql/resolvers/comment.ts +35 -19
- package/src/graphql/resolvers/dataset-search.ts +7 -6
- package/src/graphql/resolvers/dataset.ts +77 -45
- package/src/graphql/resolvers/datasetEvents.ts +18 -16
- package/src/graphql/resolvers/description.ts +2 -1
- package/src/graphql/resolvers/draft.ts +14 -3
- package/src/graphql/resolvers/fileCheck.ts +5 -4
- package/src/graphql/resolvers/flaggedFiles.ts +2 -1
- package/src/graphql/resolvers/follow.ts +2 -1
- package/src/graphql/resolvers/git.ts +2 -1
- package/src/graphql/resolvers/gitEvents.ts +2 -1
- package/src/graphql/resolvers/history.ts +3 -1
- package/src/graphql/resolvers/holdDeletion.ts +1 -1
- package/src/graphql/resolvers/importRemoteDataset.ts +3 -2
- package/src/graphql/resolvers/issues.ts +2 -1
- package/src/graphql/resolvers/metadata.ts +3 -2
- package/src/graphql/resolvers/permissions.ts +26 -6
- package/src/graphql/resolvers/publish.ts +2 -1
- package/src/graphql/resolvers/readme.ts +2 -1
- package/src/graphql/resolvers/reexporter.ts +2 -1
- package/src/graphql/resolvers/relation.ts +3 -2
- package/src/graphql/resolvers/reset.ts +2 -1
- package/src/graphql/resolvers/reviewer.ts +14 -6
- package/src/graphql/resolvers/snapshots.ts +42 -10
- package/src/graphql/resolvers/stars.ts +2 -1
- package/src/graphql/resolvers/upload.ts +3 -2
- package/src/graphql/resolvers/user.ts +44 -32
- package/src/graphql/resolvers/validation.ts +6 -5
- package/src/graphql/resolvers/worker.ts +2 -1
- package/src/graphql/schema/analytics.ts +12 -0
- package/src/graphql/schema/comment.ts +30 -0
- package/src/graphql/schema/dataset-events.ts +91 -0
- package/src/graphql/schema/dataset-search.ts +32 -0
- package/src/graphql/schema/dataset.ts +167 -0
- package/src/graphql/schema/description.ts +44 -0
- package/src/graphql/schema/draft.ts +80 -0
- package/src/graphql/schema/enums.ts +55 -0
- package/src/graphql/schema/files.ts +44 -0
- package/src/graphql/schema/inputs.ts +231 -0
- package/src/graphql/schema/metadata.ts +86 -0
- package/src/graphql/schema/misc.ts +154 -0
- package/src/graphql/schema/mutation.ts +549 -0
- package/src/graphql/schema/pagination.ts +12 -0
- package/src/graphql/schema/permissions.ts +21 -0
- package/src/graphql/schema/query.ts +119 -0
- package/src/graphql/schema/refs.ts +23 -0
- package/src/graphql/schema/reviewer.ts +11 -0
- package/src/graphql/schema/scalars.ts +9 -0
- package/src/graphql/schema/snapshot.ts +111 -0
- package/src/graphql/schema/upload.ts +13 -0
- package/src/graphql/schema/user.ts +61 -0
- package/src/graphql/schema/validation.ts +70 -0
- package/src/graphql/schema/worker.ts +16 -0
- package/src/graphql/schema.ts +29 -1114
- package/src/handlers/subscriptions.ts +1 -1
- package/src/libs/apikey.ts +1 -1
- package/src/libs/authentication/crypto.ts +14 -9
- package/src/libs/notifications.ts +1 -1
- package/src/libs/presign.ts +32 -20
- package/src/libs/redis.ts +12 -2
- package/src/models/comment.ts +2 -4
- package/src/models/counter.ts +2 -2
- package/src/models/ingestDataset.ts +1 -2
- package/src/models/notification.ts +0 -2
- package/src/models/subscription.ts +0 -1
- package/src/models/user.ts +1 -2
- package/src/models/userMigration.ts +1 -1
- package/src/models/userNotificationStatus.ts +0 -1
- package/src/utils/__tests__/snapshots.spec.ts +120 -0
- 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
|
+
})
|