@openneuro/server 5.1.0 → 5.1.2
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 +4 -4
- package/src/datalad/__tests__/dataset.spec.ts +2 -2
- package/src/datalad/__tests__/snapshots.spec.ts +1 -1
- package/src/datalad/dataset.ts +18 -6
- package/src/graphql/resolvers/__tests__/derivatives.spec.ts +11 -0
- package/src/graphql/resolvers/dataset.ts +1 -1
- package/src/graphql/resolvers/derivatives.ts +5 -1
- package/src/graphql/resolvers/user.ts +5 -1
- package/src/graphql/schema/mutation.ts +4 -2
- package/src/graphql/schema/user.ts +1 -0
- package/src/models/user.ts +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openneuro/server",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.2",
|
|
4
4
|
"description": "Core service for the OpenNeuro platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@elastic/elasticsearch": "8.13.1",
|
|
23
23
|
"@graphql-tools/schema": "^10.0.0",
|
|
24
24
|
"@keyv/redis": "^4.5.0",
|
|
25
|
-
"@openneuro/search": "^5.1.
|
|
25
|
+
"@openneuro/search": "^5.1.2",
|
|
26
26
|
"@pothos/core": "^4.12.0",
|
|
27
27
|
"@pothos/plugin-directives": "^4.3.0",
|
|
28
28
|
"@pothos/plugin-simple-objects": "^4.1.3",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"ts-node": "10.9.2",
|
|
73
73
|
"typescript": "5.6.3",
|
|
74
74
|
"underscore": "^1.8.3",
|
|
75
|
-
"uuid": "
|
|
75
|
+
"uuid": "11.1.1",
|
|
76
76
|
"xmldoc": "^1.1.0"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
"publishConfig": {
|
|
96
96
|
"access": "public"
|
|
97
97
|
},
|
|
98
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "bccaddf537a2950100549b992a846768c32d683e"
|
|
99
99
|
}
|
|
@@ -23,7 +23,7 @@ describe("dataset model operations", () => {
|
|
|
23
23
|
connect(mongod.getUri())
|
|
24
24
|
})
|
|
25
25
|
it("resolves to dataset id string", async () => {
|
|
26
|
-
const user = { id: "1234" }
|
|
26
|
+
const user = { id: "1234", userId: "1234", admin: false }
|
|
27
27
|
const { id: dsId } = await createDataset(user.id, user, {
|
|
28
28
|
affirmedDefaced: true,
|
|
29
29
|
affirmedConsent: true,
|
|
@@ -32,7 +32,7 @@ describe("dataset model operations", () => {
|
|
|
32
32
|
expect(dsId.slice(0, 2)).toBe("ds")
|
|
33
33
|
})
|
|
34
34
|
it("posts to the DataLad /datasets/{dsId} endpoint", async () => {
|
|
35
|
-
const user = { id: "1234" }
|
|
35
|
+
const user = { id: "1234", userId: "1234", admin: false }
|
|
36
36
|
// Reset call count for request.post
|
|
37
37
|
request.post.mockClear()
|
|
38
38
|
const { id: dsId } = await createDataset(user.id, user, {
|
|
@@ -34,7 +34,7 @@ describe("snapshot model operations", () => {
|
|
|
34
34
|
connect(mongod.getUri())
|
|
35
35
|
})
|
|
36
36
|
it("posts to the DataLad /datasets/{dsId}/snapshots/{snapshot} endpoint", async () => {
|
|
37
|
-
const user = { id: "1234" }
|
|
37
|
+
const user = { id: "1234", userId: "1234", admin: false }
|
|
38
38
|
const tag = "snapshot"
|
|
39
39
|
const { id: dsId } = await createDataset(user.id, user, {
|
|
40
40
|
affirmedDefaced: true,
|
package/src/datalad/dataset.ts
CHANGED
|
@@ -28,8 +28,9 @@ import { getDatasetWorker } from "../libs/datalad-service"
|
|
|
28
28
|
import { createEvent, updateEvent } from "../libs/events"
|
|
29
29
|
import Doi from "../models/doi"
|
|
30
30
|
import { hideDoi, publishDoi } from "../libs/doi/index"
|
|
31
|
+
import type { UserInfo } from "../graphql/builder"
|
|
31
32
|
|
|
32
|
-
export const giveUploaderPermission = (datasetId, userId) => {
|
|
33
|
+
export const giveUploaderPermission = (datasetId: string, userId: string) => {
|
|
33
34
|
const permission = new Permission({ datasetId, userId, level: "admin" })
|
|
34
35
|
return permission.save()
|
|
35
36
|
}
|
|
@@ -46,8 +47,11 @@ export const giveUploaderPermission = (datasetId, userId) => {
|
|
|
46
47
|
*/
|
|
47
48
|
export const createDataset = async (
|
|
48
49
|
uploader: string,
|
|
49
|
-
userInfo,
|
|
50
|
-
{ affirmedDefaced, affirmedConsent }
|
|
50
|
+
userInfo: UserInfo,
|
|
51
|
+
{ affirmedDefaced, affirmedConsent }: {
|
|
52
|
+
affirmedDefaced: boolean
|
|
53
|
+
affirmedConsent: boolean
|
|
54
|
+
},
|
|
51
55
|
) => {
|
|
52
56
|
// Obtain an accession number
|
|
53
57
|
const datasetId = await getAccessionNumber()
|
|
@@ -87,7 +91,9 @@ type DatasetWithRevision = Mongoose.FlattenMaps<DatasetDocument> & {
|
|
|
87
91
|
/**
|
|
88
92
|
* Fetch dataset document and related fields
|
|
89
93
|
*/
|
|
90
|
-
export const getDataset = async (
|
|
94
|
+
export const getDataset = async (
|
|
95
|
+
id: string,
|
|
96
|
+
): Promise<DatasetWithRevision | null> => {
|
|
91
97
|
const dataset = await Dataset.findOne({ id }).lean()
|
|
92
98
|
return dataset && {
|
|
93
99
|
...dataset,
|
|
@@ -98,7 +104,7 @@ export const getDataset = async (id): Promise<DatasetWithRevision | null> => {
|
|
|
98
104
|
/**
|
|
99
105
|
* Delete dataset and associated documents
|
|
100
106
|
*/
|
|
101
|
-
export const deleteDataset = async (datasetId, user) => {
|
|
107
|
+
export const deleteDataset = async (datasetId: string, user: UserInfo) => {
|
|
102
108
|
const event = await createEvent(
|
|
103
109
|
datasetId,
|
|
104
110
|
user.id,
|
|
@@ -498,7 +504,11 @@ export const flagAnnexObject = (
|
|
|
498
504
|
/**
|
|
499
505
|
* Update public state and transition DOI states accordingly.
|
|
500
506
|
*/
|
|
501
|
-
export async function updatePublic(
|
|
507
|
+
export async function updatePublic(
|
|
508
|
+
datasetId: string,
|
|
509
|
+
publicFlag: boolean,
|
|
510
|
+
user: UserInfo,
|
|
511
|
+
): Promise<boolean> {
|
|
502
512
|
const event = await createEvent(
|
|
503
513
|
datasetId,
|
|
504
514
|
user.id,
|
|
@@ -535,6 +545,8 @@ export async function updatePublic(datasetId, publicFlag, user) {
|
|
|
535
545
|
} catch (err) {
|
|
536
546
|
Sentry.captureException(err)
|
|
537
547
|
}
|
|
548
|
+
// Return the new public state
|
|
549
|
+
return publicFlag
|
|
538
550
|
}
|
|
539
551
|
|
|
540
552
|
export const getDatasetAnalytics = (datasetId, _tag) => {
|
|
@@ -33,4 +33,15 @@ describe("GraphQL derivatives", () => {
|
|
|
33
33
|
})
|
|
34
34
|
})
|
|
35
35
|
})
|
|
36
|
+
it("returns expected values for fitlins", () => {
|
|
37
|
+
expect(derivativeObject("ds000002", "fitlins")).toEqual({
|
|
38
|
+
dataladUrl: new URL(
|
|
39
|
+
"https://github.com/OpenNeuroDerivatives/ds000002-fitlins.git",
|
|
40
|
+
),
|
|
41
|
+
local: false,
|
|
42
|
+
name: "ds000002-fitlins",
|
|
43
|
+
s3Url: new URL("s3://openneuro-derivatives/fitlins/ds000002-fitlins"),
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
36
47
|
})
|
|
@@ -220,7 +220,7 @@ export const updatePublic = (
|
|
|
220
220
|
{ user, userInfo }: GraphQLContext,
|
|
221
221
|
) => {
|
|
222
222
|
return checkDatasetWrite(datasetId, user, userInfo).then(() => {
|
|
223
|
-
return datalad.updatePublic(datasetId, publicFlag,
|
|
223
|
+
return datalad.updatePublic(datasetId, publicFlag, userInfo)
|
|
224
224
|
})
|
|
225
225
|
}
|
|
226
226
|
|
|
@@ -6,7 +6,7 @@ const S3_BUCKET = "openneuro-derivatives"
|
|
|
6
6
|
const GITHUB_ORGANIZATION = "OpenNeuroDerivatives"
|
|
7
7
|
|
|
8
8
|
// Available derivatives at this time
|
|
9
|
-
type GitHubDerivative = "mriqc" | "fmriprep"
|
|
9
|
+
type GitHubDerivative = "mriqc" | "fmriprep" | "fitlins"
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Test for a derivative on GitHub via API
|
|
@@ -90,5 +90,9 @@ export const derivatives = async (
|
|
|
90
90
|
if (await githubDerivative(datasetId, "fmriprep")) {
|
|
91
91
|
available.push(derivativeObject(datasetId, "fmriprep"))
|
|
92
92
|
}
|
|
93
|
+
if (await githubDerivative(datasetId, "fitlins")) {
|
|
94
|
+
available.push(derivativeObject(datasetId, "fitlins"))
|
|
95
|
+
}
|
|
96
|
+
|
|
93
97
|
return available
|
|
94
98
|
}
|
|
@@ -25,6 +25,7 @@ export type GraphQLUserType = {
|
|
|
25
25
|
githubSynced: Date
|
|
26
26
|
links: string[]
|
|
27
27
|
orcidConsent: boolean | null
|
|
28
|
+
profilePrivate: boolean
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export async function user(
|
|
@@ -50,6 +51,7 @@ export async function user(
|
|
|
50
51
|
githubSynced: oneWeekAgo,
|
|
51
52
|
links: [],
|
|
52
53
|
orcidConsent: true,
|
|
54
|
+
profilePrivate: false,
|
|
53
55
|
created: oneWeekAgo,
|
|
54
56
|
lastSeen: new Date(),
|
|
55
57
|
updatedAt: oneWeekAgo,
|
|
@@ -232,12 +234,13 @@ export const setBlocked = (
|
|
|
232
234
|
|
|
233
235
|
export const updateUser = async (
|
|
234
236
|
obj: unknown,
|
|
235
|
-
{ id, location, institution, links, orcidConsent }: {
|
|
237
|
+
{ id, location, institution, links, orcidConsent, profilePrivate }: {
|
|
236
238
|
id: string
|
|
237
239
|
location?: string
|
|
238
240
|
institution?: string
|
|
239
241
|
links?: string[]
|
|
240
242
|
orcidConsent?: boolean
|
|
243
|
+
profilePrivate?: boolean
|
|
241
244
|
},
|
|
242
245
|
{ userInfo }: GraphQLContext,
|
|
243
246
|
) => {
|
|
@@ -270,6 +273,7 @@ export const updateUser = async (
|
|
|
270
273
|
if (institution !== undefined) user.institution = institution
|
|
271
274
|
if (links !== undefined) user.links = links
|
|
272
275
|
if (orcidConsent !== undefined) user.orcidConsent = orcidConsent
|
|
276
|
+
if (profilePrivate !== undefined) user.profilePrivate = profilePrivate
|
|
273
277
|
|
|
274
278
|
await user.save()
|
|
275
279
|
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
ValidatorInput,
|
|
26
26
|
} from "./inputs"
|
|
27
27
|
|
|
28
|
-
import Mutation from
|
|
28
|
+
import Mutation from "../resolvers/mutation"
|
|
29
29
|
|
|
30
30
|
builder.mutationType({
|
|
31
31
|
fields: (t) => ({
|
|
@@ -142,7 +142,8 @@ builder.mutationType({
|
|
|
142
142
|
datasetId: t.arg.id({ required: true }),
|
|
143
143
|
userId: t.arg.string({ required: true }),
|
|
144
144
|
},
|
|
145
|
-
resolve: (root, args) =>
|
|
145
|
+
resolve: (root, args) =>
|
|
146
|
+
Mutation.removePermissions(root, args as never) as never,
|
|
146
147
|
}),
|
|
147
148
|
removeUser: t.boolean({
|
|
148
149
|
args: {
|
|
@@ -177,6 +178,7 @@ builder.mutationType({
|
|
|
177
178
|
institution: t.arg.string(),
|
|
178
179
|
links: t.arg.stringList(),
|
|
179
180
|
orcidConsent: t.arg.boolean(),
|
|
181
|
+
profilePrivate: t.arg.boolean(),
|
|
180
182
|
},
|
|
181
183
|
resolve: (root, args, ctx) =>
|
|
182
184
|
Mutation.updateUser(root, args as never, ctx),
|
package/src/models/user.ts
CHANGED
|
@@ -45,6 +45,8 @@ export interface UserDocument extends Document {
|
|
|
45
45
|
githubSynced: Date
|
|
46
46
|
// Defaults to NULL populated from ORCID Consent Form Mutation
|
|
47
47
|
orcidConsent?: boolean | null
|
|
48
|
+
// Whether the user has opted out of public profile visibility
|
|
49
|
+
profilePrivate?: boolean
|
|
48
50
|
givenName?: string
|
|
49
51
|
familyName?: string
|
|
50
52
|
}
|
|
@@ -73,6 +75,10 @@ const userSchema = new Schema({
|
|
|
73
75
|
type: Boolean,
|
|
74
76
|
default: null,
|
|
75
77
|
},
|
|
78
|
+
profilePrivate: {
|
|
79
|
+
type: Boolean,
|
|
80
|
+
default: false,
|
|
81
|
+
},
|
|
76
82
|
}, { timestamps: { createdAt: false, updatedAt: true } })
|
|
77
83
|
|
|
78
84
|
userSchema.index({ id: 1, provider: 1 }, { unique: true })
|