@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
|
@@ -10,9 +10,16 @@ import { filterRemovedAnnexObjects } from "../utils/file.js"
|
|
|
10
10
|
import { validation } from "./validation"
|
|
11
11
|
import FileCheck from "../../models/fileCheck"
|
|
12
12
|
import { contributors } from "../../datalad/contributors"
|
|
13
|
+
import type { GraphQLContext } from "../builder"
|
|
14
|
+
|
|
15
|
+
export type DraftShape = { id: string; revision?: string; modified?: Date }
|
|
13
16
|
|
|
14
17
|
// A draft must have a dataset parent
|
|
15
|
-
export const draftFiles = async (
|
|
18
|
+
export const draftFiles = async (
|
|
19
|
+
dataset: DraftShape,
|
|
20
|
+
args: { tree?: string | null; recursive?: boolean | null },
|
|
21
|
+
{ userInfo }: Pick<GraphQLContext, "userInfo">,
|
|
22
|
+
) => {
|
|
16
23
|
const hexsha = await getDraftRevision(dataset.id)
|
|
17
24
|
const treeish = args.tree || hexsha
|
|
18
25
|
const files = args.recursive
|
|
@@ -32,14 +39,18 @@ const draftSize = async (dataset, args, { userInfo }) => {
|
|
|
32
39
|
/**
|
|
33
40
|
* Mutation to move the draft HEAD reference forward or backward
|
|
34
41
|
*/
|
|
35
|
-
export const revalidate = async (
|
|
42
|
+
export const revalidate = async (
|
|
43
|
+
obj: unknown,
|
|
44
|
+
{ datasetId }: { datasetId: string },
|
|
45
|
+
{ user, userInfo }: GraphQLContext,
|
|
46
|
+
) => {
|
|
36
47
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/**
|
|
40
51
|
* Resolve any issues with files recorded for the current commit
|
|
41
52
|
*/
|
|
42
|
-
export const fileCheck = async (dataset) => {
|
|
53
|
+
export const fileCheck = async (dataset: DraftShape) => {
|
|
43
54
|
const hexsha = await getDraftRevision(dataset.id)
|
|
44
55
|
// For drafts, obtain the local check results
|
|
45
56
|
const checks = await FileCheck.findOne({
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import config from "../../config"
|
|
2
2
|
import { generateDataladCookie } from "../../libs/authentication/jwt"
|
|
3
3
|
import { getDatasetWorker } from "../../libs/datalad-service"
|
|
4
|
-
import {
|
|
4
|
+
import { getRedlock } from "../../libs/redis"
|
|
5
5
|
import FileCheck from "../../models/fileCheck"
|
|
6
6
|
import { checkDatasetAdmin, checkDatasetWrite } from "../permissions"
|
|
7
|
+
import type { GraphQLContext } from "../builder"
|
|
7
8
|
|
|
8
9
|
export const updateFileCheck = async (
|
|
9
10
|
obj,
|
|
10
11
|
{ datasetId, hexsha, refs, remote, annexFsck },
|
|
11
|
-
{ user, userInfo },
|
|
12
|
+
{ user, userInfo }: GraphQLContext,
|
|
12
13
|
) => {
|
|
13
14
|
await checkDatasetAdmin(datasetId, user, userInfo)
|
|
14
15
|
return await FileCheck.findOneAndUpdate(
|
|
@@ -28,7 +29,7 @@ export const fsckUrl = (datasetId) => {
|
|
|
28
29
|
}/datasets/${datasetId}/fsck`
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
export const fsckDataset = async (_, { datasetId }, { user, userInfo }) => {
|
|
32
|
+
export const fsckDataset = async (_, { datasetId }, { user, userInfo }: GraphQLContext) => {
|
|
32
33
|
// Anonymous users can't trigger fsck
|
|
33
34
|
try {
|
|
34
35
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
@@ -37,7 +38,7 @@ export const fsckDataset = async (_, { datasetId }, { user, userInfo }) => {
|
|
|
37
38
|
}
|
|
38
39
|
try {
|
|
39
40
|
// Lock for 30 minutes to avoid stacking fsck requests
|
|
40
|
-
await
|
|
41
|
+
await getRedlock().lock(`openneuro:fsck-local-lock:${datasetId}`, 1800000)
|
|
41
42
|
const response = await fetch(fsckUrl(datasetId), {
|
|
42
43
|
method: "POST",
|
|
43
44
|
body: JSON.stringify({}),
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import BadAnnexObject from "../../models/badAnnexObject"
|
|
2
2
|
import { checkAdmin } from "../permissions"
|
|
3
|
+
import type { GraphQLContext } from "../builder"
|
|
3
4
|
|
|
4
5
|
export const flaggedFiles = async (
|
|
5
6
|
obj,
|
|
6
7
|
{ flagged = true, deleted = false },
|
|
7
|
-
{ user, userInfo },
|
|
8
|
+
{ user, userInfo }: GraphQLContext,
|
|
8
9
|
) => {
|
|
9
10
|
await checkAdmin(user, userInfo)
|
|
10
11
|
return BadAnnexObject.find({ flagged, removed: deleted })
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Subscription from "../../models/subscription"
|
|
2
|
+
import type { GraphQLContext } from "../builder"
|
|
2
3
|
|
|
3
|
-
export const followDataset = async (obj, { datasetId }, { user }) => {
|
|
4
|
+
export const followDataset = async (obj, { datasetId }, { user }: GraphQLContext) => {
|
|
4
5
|
const following = await Subscription.findOne({
|
|
5
6
|
datasetId,
|
|
6
7
|
userId: user,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { checkDatasetRead, checkDatasetWrite } from "../permissions.js"
|
|
2
2
|
import { generateRepoToken } from "../../libs/authentication/jwt"
|
|
3
3
|
import { getDatasetEndpoint } from "../../libs/datalad-service.js"
|
|
4
|
+
import type { GraphQLContext } from "../builder"
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Generate a short lived token for git operations
|
|
@@ -14,7 +15,7 @@ import { getDatasetEndpoint } from "../../libs/datalad-service.js"
|
|
|
14
15
|
export async function prepareRepoAccess(
|
|
15
16
|
_: Record<string, unknown>,
|
|
16
17
|
{ datasetId }: { datasetId: string },
|
|
17
|
-
{ user, userInfo }:
|
|
18
|
+
{ user, userInfo }: GraphQLContext,
|
|
18
19
|
): Promise<{ token: string; endpoint: number }> {
|
|
19
20
|
try {
|
|
20
21
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createEvent } from "../../libs/events"
|
|
2
2
|
import { checkDatasetWrite } from "../permissions"
|
|
3
|
+
import type { GraphQLContext } from "../builder"
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Create a git event
|
|
@@ -7,7 +8,7 @@ import { checkDatasetWrite } from "../permissions"
|
|
|
7
8
|
export const createGitEvent = async (
|
|
8
9
|
obj,
|
|
9
10
|
{ datasetId, commit, reference },
|
|
10
|
-
{ user, userInfo },
|
|
11
|
+
{ user, userInfo }: GraphQLContext,
|
|
11
12
|
) => {
|
|
12
13
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
13
14
|
const event = await createEvent(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { FlattenMaps } from "mongoose"
|
|
1
2
|
import { getDatasetWorker } from "../../libs/datalad-service"
|
|
3
|
+
import type { DatasetDocument } from "../../models/dataset"
|
|
2
4
|
|
|
3
|
-
export const history = async (obj) => {
|
|
5
|
+
export const history = async (obj: FlattenMaps<DatasetDocument>) => {
|
|
4
6
|
const datasetId = obj.id
|
|
5
7
|
const historyUrl = `http://${
|
|
6
8
|
getDatasetWorker(
|
|
@@ -5,7 +5,7 @@ import Dataset from "../../models/dataset"
|
|
|
5
5
|
* Requires site admin access.
|
|
6
6
|
*/
|
|
7
7
|
export async function holdDeletion(
|
|
8
|
-
_obj:
|
|
8
|
+
_obj: unknown,
|
|
9
9
|
{ datasetId, hold }: { datasetId: string; hold: boolean },
|
|
10
10
|
{ userInfo }: { userInfo: { admin: boolean } },
|
|
11
11
|
): Promise<boolean> {
|
|
@@ -4,6 +4,7 @@ import { getDatasetWorker } from "../../libs/datalad-service"
|
|
|
4
4
|
import { generateDataladCookie } from "../../libs/authentication/jwt"
|
|
5
5
|
import notifications from "../../libs/notifications"
|
|
6
6
|
import config from "../../config"
|
|
7
|
+
import type { GraphQLContext } from "../builder"
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Test if a URL is allowed to be imported
|
|
@@ -34,7 +35,7 @@ export function allowedImportUrl(raw: string): boolean {
|
|
|
34
35
|
export async function importRemoteDataset(
|
|
35
36
|
_: Record<string, unknown>,
|
|
36
37
|
{ datasetId, url }: { datasetId: string; url: string },
|
|
37
|
-
{ user, userInfo }:
|
|
38
|
+
{ user, userInfo }: GraphQLContext,
|
|
38
39
|
): Promise<string | null> {
|
|
39
40
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
40
41
|
if (!allowedImportUrl(url)) {
|
|
@@ -66,7 +67,7 @@ export async function finishImportRemoteDataset(
|
|
|
66
67
|
_: Record<string, unknown>,
|
|
67
68
|
{ id, success, message }: { id: string; success: boolean; message: string },
|
|
68
69
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
|
69
|
-
{ user, userInfo }:
|
|
70
|
+
{ user, userInfo }: GraphQLContext,
|
|
70
71
|
): Promise<boolean> {
|
|
71
72
|
const ingest = await IngestDataset.findById(id)
|
|
72
73
|
ingest.imported = success
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Issue from "../../models/issue"
|
|
2
2
|
import { datasetType } from "./datasetType"
|
|
3
3
|
import { revalidate } from "./validation.js"
|
|
4
|
+
import type { GraphQLContext } from "../builder"
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Issues resolver
|
|
7
8
|
*/
|
|
8
|
-
export const issues = async (dataset, _, { userInfo }) => {
|
|
9
|
+
export const issues = async (dataset, _, { userInfo }: GraphQLContext) => {
|
|
9
10
|
return Issue.findOne({
|
|
10
11
|
id: dataset.revision,
|
|
11
12
|
datasetId: dataset.id,
|
|
@@ -5,6 +5,7 @@ import MetadataModel from "../../models/metadata"
|
|
|
5
5
|
import type { MetadataDocument } from "../../models/metadata"
|
|
6
6
|
import { latestSnapshot } from "./snapshots"
|
|
7
7
|
import { permissions } from "./permissions"
|
|
8
|
+
import type { GraphQLContext } from "../builder"
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Summary resolver
|
|
@@ -14,7 +15,7 @@ import { permissions } from "./permissions"
|
|
|
14
15
|
export const metadata = async (
|
|
15
16
|
dataset,
|
|
16
17
|
_,
|
|
17
|
-
context,
|
|
18
|
+
context: GraphQLContext,
|
|
18
19
|
): Promise<FlattenMaps<MetadataDocument>> => {
|
|
19
20
|
const record = await MetadataModel.findOne({
|
|
20
21
|
datasetId: dataset.id,
|
|
@@ -79,7 +80,7 @@ export async function publicMetadata(
|
|
|
79
80
|
}).lean()
|
|
80
81
|
const dsMetadata: FlattenMaps<MetadataDocument>[] = []
|
|
81
82
|
for (const ds of datasets) {
|
|
82
|
-
dsMetadata.push(await metadata(ds, null, {}))
|
|
83
|
+
dsMetadata.push(await metadata(ds, null, {} as GraphQLContext))
|
|
83
84
|
}
|
|
84
85
|
return dsMetadata
|
|
85
86
|
}
|
|
@@ -3,22 +3,31 @@ import type { UserDocument } from "../../models/user"
|
|
|
3
3
|
import Permission from "../../models/permission"
|
|
4
4
|
import type { PermissionDocument } from "../../models/permission"
|
|
5
5
|
import { checkDatasetAdmin } from "../permissions"
|
|
6
|
+
import type { GraphQLContext } from "../builder"
|
|
6
7
|
import { user } from "./user"
|
|
7
8
|
import { createEvent, updateEvent } from "../../libs/events"
|
|
8
9
|
|
|
9
|
-
interface DatasetPermission {
|
|
10
|
+
export interface DatasetPermission {
|
|
10
11
|
id: string
|
|
11
12
|
userPermissions: (PermissionDocument & { user: Promise<UserDocument> })[]
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export async function permissions(
|
|
15
|
+
export async function permissions(
|
|
16
|
+
ds: { id: string },
|
|
17
|
+
_: unknown,
|
|
18
|
+
context: Partial<GraphQLContext> | null,
|
|
19
|
+
): Promise<DatasetPermission> {
|
|
15
20
|
const permissions = await Permission.find({ datasetId: ds.id }).exec()
|
|
16
21
|
return {
|
|
17
22
|
id: ds.id,
|
|
18
23
|
userPermissions: permissions.map(
|
|
19
24
|
(userPermission) => ({
|
|
20
25
|
...userPermission.toJSON(),
|
|
21
|
-
user: user(
|
|
26
|
+
user: user(
|
|
27
|
+
ds,
|
|
28
|
+
{ id: userPermission.userId },
|
|
29
|
+
context as Partial<GraphQLContext>,
|
|
30
|
+
),
|
|
22
31
|
} as unknown as PermissionDocument & { user: Promise<UserDocument> }),
|
|
23
32
|
),
|
|
24
33
|
}
|
|
@@ -71,7 +80,11 @@ async function updateUsers(datasetId: string, level: string, users) {
|
|
|
71
80
|
/**
|
|
72
81
|
* Update user permissions on a dataset
|
|
73
82
|
*/
|
|
74
|
-
export const updatePermissions = async (
|
|
83
|
+
export const updatePermissions = async (
|
|
84
|
+
obj: unknown,
|
|
85
|
+
args: { datasetId: string; userEmail: string; level: string },
|
|
86
|
+
{ user, userInfo }: GraphQLContext,
|
|
87
|
+
) => {
|
|
75
88
|
await checkDatasetAdmin(args.datasetId, user, userInfo)
|
|
76
89
|
// get all users the the email specified by permissions arg
|
|
77
90
|
const users = await User.find({ email: args.userEmail })
|
|
@@ -88,7 +101,11 @@ export const updatePermissions = async (obj, args, { user, userInfo }) => {
|
|
|
88
101
|
/**
|
|
89
102
|
* ORCID variant of updatePermissions
|
|
90
103
|
*/
|
|
91
|
-
export const updateOrcidPermissions = async (
|
|
104
|
+
export const updateOrcidPermissions = async (
|
|
105
|
+
obj: unknown,
|
|
106
|
+
args: { datasetId: string; userOrcid: string; level: string },
|
|
107
|
+
{ user, userInfo }: GraphQLContext,
|
|
108
|
+
) => {
|
|
92
109
|
await checkDatasetAdmin(args.datasetId, user, userInfo)
|
|
93
110
|
// Get all users associated with the ORCID provided
|
|
94
111
|
const users = await User.find({
|
|
@@ -110,7 +127,10 @@ export const updateOrcidPermissions = async (obj, args, { user, userInfo }) => {
|
|
|
110
127
|
/**
|
|
111
128
|
* Remove user permissions on a dataset
|
|
112
129
|
*/
|
|
113
|
-
export const removePermissions = (
|
|
130
|
+
export const removePermissions = (
|
|
131
|
+
obj: unknown,
|
|
132
|
+
args: { datasetId: string; userId: string },
|
|
133
|
+
) => {
|
|
114
134
|
return Permission.deleteOne({
|
|
115
135
|
datasetId: args.datasetId,
|
|
116
136
|
userId: args.userId,
|
|
@@ -4,8 +4,9 @@ import { updatePublic } from "../../datalad/dataset"
|
|
|
4
4
|
import { checkDatasetWrite } from "../permissions.js"
|
|
5
5
|
import { generateDataladCookie } from "../../libs/authentication/jwt"
|
|
6
6
|
import { getDatasetWorker } from "../../libs/datalad-service"
|
|
7
|
+
import type { GraphQLContext } from "../builder"
|
|
7
8
|
|
|
8
|
-
export const publishDataset = (obj, { datasetId }, { user, userInfo }) => {
|
|
9
|
+
export const publishDataset = (obj, { datasetId }, { user, userInfo }: GraphQLContext) => {
|
|
9
10
|
return checkDatasetWrite(datasetId, user, userInfo).then(async () => {
|
|
10
11
|
await updatePublic(datasetId, true, userInfo)
|
|
11
12
|
const uri = `${getDatasetWorker(datasetId)}/datasets/${datasetId}/publish`
|
|
@@ -4,13 +4,14 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { setReadme } from "../../datalad/readme"
|
|
6
6
|
import { checkDatasetWrite } from "../permissions"
|
|
7
|
+
import type { GraphQLContext } from "../builder"
|
|
7
8
|
export { readme } from "../../datalad/readme"
|
|
8
9
|
import { draftFiles } from "./draft"
|
|
9
10
|
|
|
10
11
|
export async function updateReadme(
|
|
11
12
|
obj,
|
|
12
13
|
{ datasetId, value },
|
|
13
|
-
{ user, userInfo },
|
|
14
|
+
{ user, userInfo }: GraphQLContext,
|
|
14
15
|
) {
|
|
15
16
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
16
17
|
const files = await draftFiles({ id: datasetId }, { tree: "HEAD" }, {
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { checkAdmin } from "../permissions"
|
|
5
5
|
import { runReexporter } from "../../datalad/reexporter"
|
|
6
|
+
import type { GraphQLContext } from "../builder"
|
|
6
7
|
|
|
7
8
|
export const reexportRemotes = async (
|
|
8
9
|
obj,
|
|
9
10
|
{ datasetId },
|
|
10
|
-
{ user, userInfo },
|
|
11
|
+
{ user, userInfo }: GraphQLContext,
|
|
11
12
|
) => {
|
|
12
13
|
await checkAdmin(user, userInfo)
|
|
13
14
|
await runReexporter(datasetId)
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import Dataset from "../../models/dataset"
|
|
2
2
|
import { checkDatasetAdmin } from "../permissions"
|
|
3
3
|
import { DOIPattern } from "../../libs/doi/normalize"
|
|
4
|
+
import type { GraphQLContext } from "../builder"
|
|
4
5
|
|
|
5
6
|
export const createRelation = async (
|
|
6
7
|
obj,
|
|
7
8
|
{ datasetId, doi, relation, kind, description },
|
|
8
|
-
{ user, userInfo },
|
|
9
|
+
{ user, userInfo }: GraphQLContext,
|
|
9
10
|
) => {
|
|
10
11
|
await checkDatasetAdmin(datasetId, user, userInfo)
|
|
11
12
|
// Validate the right DOI format
|
|
@@ -27,7 +28,7 @@ export const createRelation = async (
|
|
|
27
28
|
export const deleteRelation = async (
|
|
28
29
|
obj,
|
|
29
30
|
{ datasetId, doi },
|
|
30
|
-
{ user, userInfo },
|
|
31
|
+
{ user, userInfo }: GraphQLContext,
|
|
31
32
|
) => {
|
|
32
33
|
await checkDatasetAdmin(datasetId, user, userInfo)
|
|
33
34
|
return await Dataset.findOneAndUpdate(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { checkDatasetWrite } from "../permissions"
|
|
2
2
|
import { resetDraft as resetDraftTask } from "../../datalad/draft"
|
|
3
|
+
import type { GraphQLContext } from "../builder"
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Mutation to move the draft HEAD reference forward or backward
|
|
@@ -7,7 +8,7 @@ import { resetDraft as resetDraftTask } from "../../datalad/draft"
|
|
|
7
8
|
export const resetDraft = async (
|
|
8
9
|
obj,
|
|
9
10
|
{ datasetId, ref },
|
|
10
|
-
{ user, userInfo },
|
|
11
|
+
{ user, userInfo }: GraphQLContext,
|
|
11
12
|
) => {
|
|
12
13
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
13
14
|
try {
|
|
@@ -2,6 +2,7 @@ import config from "../../config"
|
|
|
2
2
|
import Reviewer from "../../models/reviewer"
|
|
3
3
|
import { checkDatasetAdmin } from "../permissions.js"
|
|
4
4
|
import { generateReviewerToken } from "../../libs/authentication/jwt"
|
|
5
|
+
import type { GraphQLContext } from "../builder"
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Create an anonymous read-only access key
|
|
@@ -12,7 +13,11 @@ import { generateReviewerToken } from "../../libs/authentication/jwt"
|
|
|
12
13
|
* @param {string} context.user User id
|
|
13
14
|
* @param {object} context.userInfo Decoded userInfo from token
|
|
14
15
|
*/
|
|
15
|
-
export async function createReviewer(
|
|
16
|
+
export async function createReviewer(
|
|
17
|
+
obj: unknown,
|
|
18
|
+
{ datasetId }: { datasetId: string },
|
|
19
|
+
{ user, userInfo }: GraphQLContext,
|
|
20
|
+
) {
|
|
16
21
|
await checkDatasetAdmin(datasetId, user, userInfo)
|
|
17
22
|
const reviewer = new Reviewer({ datasetId, creator: user })
|
|
18
23
|
await reviewer.save()
|
|
@@ -26,9 +31,9 @@ export async function createReviewer(obj, { datasetId }, { user, userInfo }) {
|
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
export async function deleteReviewer(
|
|
29
|
-
obj,
|
|
30
|
-
{ datasetId, id },
|
|
31
|
-
{ user, userInfo },
|
|
34
|
+
obj: unknown,
|
|
35
|
+
{ datasetId, id }: { datasetId: string; id: string },
|
|
36
|
+
{ user, userInfo }: GraphQLContext,
|
|
32
37
|
) {
|
|
33
38
|
await checkDatasetAdmin(datasetId, user, userInfo)
|
|
34
39
|
return Reviewer.findOneAndDelete({ id }).lean().exec()
|
|
@@ -37,7 +42,10 @@ export async function deleteReviewer(
|
|
|
37
42
|
/**
|
|
38
43
|
* Resolver for dataset reviewers
|
|
39
44
|
*/
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
export function reviewers(
|
|
46
|
+
obj: { id: string },
|
|
47
|
+
_: unknown,
|
|
48
|
+
_ctx: GraphQLContext,
|
|
49
|
+
) {
|
|
42
50
|
return Reviewer.find({ datasetId: obj.id }).lean().exec()
|
|
43
51
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as datalad from "../../datalad/snapshots"
|
|
2
|
-
import { analytics, dataset
|
|
2
|
+
import { analytics, dataset } from "./dataset.js"
|
|
3
|
+
import { snapshotCreationComparison } from "../../utils/snapshots"
|
|
3
4
|
import { onBrainlife } from "./brainlife"
|
|
4
5
|
import { checkDatasetRead, checkDatasetWrite } from "../permissions"
|
|
5
6
|
import { readme } from "./readme.js"
|
|
@@ -11,23 +12,49 @@ import Summary from "../../models/summary"
|
|
|
11
12
|
import DatasetModel from "../../models/dataset"
|
|
12
13
|
import { filterRemovedAnnexObjects } from "../utils/file"
|
|
13
14
|
import DeprecatedSnapshot from "../../models/deprecatedSnapshot"
|
|
14
|
-
import {
|
|
15
|
+
import { getRedis } from "../../libs/redis"
|
|
15
16
|
import CacheItem, { CacheType } from "../../cache/item"
|
|
16
17
|
import { normalizeDOI } from "../../libs/doi/normalize"
|
|
17
18
|
import { snapshotValidation } from "./validation"
|
|
18
19
|
import { advancedDatasetSearchConnection } from "./dataset-search"
|
|
19
20
|
import { contributors } from "../../datalad/contributors"
|
|
20
21
|
import { getDraftInfo } from "../../datalad/draft"
|
|
22
|
+
import type { GraphQLContext } from "../builder"
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shape returned by the snapshot() resolver, combining plain data
|
|
26
|
+
* from the HTTP API with lazy-loaded thunk fields.
|
|
27
|
+
*/
|
|
28
|
+
export interface SnapshotShape {
|
|
29
|
+
id: string
|
|
30
|
+
datasetId: string
|
|
31
|
+
tag: string
|
|
32
|
+
created?: Date
|
|
33
|
+
hexsha?: string
|
|
34
|
+
dataset: () => Promise<unknown>
|
|
35
|
+
description: () => Promise<unknown>
|
|
36
|
+
readme: () => Promise<unknown>
|
|
37
|
+
summary: () => Promise<unknown>
|
|
38
|
+
files: (args: {
|
|
39
|
+
tree?: string | null
|
|
40
|
+
recursive?: boolean | null
|
|
41
|
+
}) => Promise<unknown>
|
|
42
|
+
size: () => Promise<unknown>
|
|
43
|
+
deprecated: () => Promise<unknown>
|
|
44
|
+
related: () => Promise<unknown>
|
|
45
|
+
onBrainlife: () => Promise<boolean>
|
|
46
|
+
}
|
|
21
47
|
|
|
22
48
|
export const snapshots = (obj) => {
|
|
23
49
|
return datalad.getSnapshots(obj.id)
|
|
24
50
|
}
|
|
25
51
|
|
|
26
|
-
export const snapshot = (obj, { datasetId, tag }, context) => {
|
|
52
|
+
export const snapshot = (obj, { datasetId, tag }, context: GraphQLContext) => {
|
|
27
53
|
return checkDatasetRead(datasetId, context.user, context.userInfo).then(
|
|
28
54
|
() => {
|
|
29
55
|
return datalad.getSnapshot(datasetId, tag).then((snapshot) => ({
|
|
30
56
|
...snapshot,
|
|
57
|
+
created: snapshot.created ? new Date(snapshot.created) : undefined,
|
|
31
58
|
datasetId,
|
|
32
59
|
dataset: () => dataset(snapshot, { id: datasetId }, context),
|
|
33
60
|
description: () => description(snapshot),
|
|
@@ -100,7 +127,7 @@ export const matchKnownObjects = (desc) => {
|
|
|
100
127
|
export const deprecateSnapshot = async (
|
|
101
128
|
obj,
|
|
102
129
|
{ datasetId, tag, reason },
|
|
103
|
-
{ user, userInfo },
|
|
130
|
+
{ user, userInfo }: GraphQLContext,
|
|
104
131
|
) => {
|
|
105
132
|
const id = `${datasetId}:${tag}`
|
|
106
133
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
@@ -129,7 +156,7 @@ export const deprecateSnapshot = async (
|
|
|
129
156
|
export const undoDeprecateSnapshot = async (
|
|
130
157
|
obj,
|
|
131
158
|
{ datasetId, tag },
|
|
132
|
-
{ user, userInfo },
|
|
159
|
+
{ user, userInfo }: GraphQLContext,
|
|
133
160
|
) => {
|
|
134
161
|
const id = `${datasetId}:${tag}`
|
|
135
162
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
@@ -148,7 +175,7 @@ const brainInitiativeQuery = {
|
|
|
148
175
|
export const participantCount = (obj, { modality }) => {
|
|
149
176
|
const cacheKey = modality || "all"
|
|
150
177
|
const cache = new CacheItem(
|
|
151
|
-
|
|
178
|
+
getRedis(),
|
|
152
179
|
CacheType.participantCount,
|
|
153
180
|
[cacheKey],
|
|
154
181
|
86400,
|
|
@@ -172,11 +199,12 @@ export const participantCount = (obj, { modality }) => {
|
|
|
172
199
|
datasetStatus: "",
|
|
173
200
|
after,
|
|
174
201
|
first: 100,
|
|
175
|
-
}, { user: null, userInfo: {} })
|
|
202
|
+
}, { user: null, userInfo: {} } as unknown as GraphQLContext)
|
|
176
203
|
nihDatasets.push(...results.edges.map((edge) => edge.id))
|
|
177
204
|
if (!results.pageInfo.hasNextPage) {
|
|
178
205
|
break
|
|
179
206
|
} else {
|
|
207
|
+
// eslint-disable-next-line no-useless-assignment
|
|
180
208
|
after = results.pageInfo.endCursor
|
|
181
209
|
}
|
|
182
210
|
}
|
|
@@ -261,7 +289,7 @@ export const filterLatestSnapshot = (snapshots) => {
|
|
|
261
289
|
}
|
|
262
290
|
}
|
|
263
291
|
|
|
264
|
-
export const latestSnapshot = async (obj, _, context) => {
|
|
292
|
+
export const latestSnapshot = async (obj, _, context: GraphQLContext) => {
|
|
265
293
|
const snapshots = await datalad.getSnapshots(obj.id)
|
|
266
294
|
const snapshotTag = filterLatestSnapshot(snapshots)
|
|
267
295
|
if (snapshotTag) {
|
|
@@ -282,7 +310,7 @@ export const latestSnapshot = async (obj, _, context) => {
|
|
|
282
310
|
export const createSnapshot = (
|
|
283
311
|
obj,
|
|
284
312
|
{ datasetId, tag, changes },
|
|
285
|
-
{ user, userInfo },
|
|
313
|
+
{ user, userInfo }: GraphQLContext,
|
|
286
314
|
) => {
|
|
287
315
|
return checkDatasetWrite(datasetId, user, userInfo).then(async () => {
|
|
288
316
|
return datalad.createSnapshot(datasetId, tag, userInfo, {}, changes)
|
|
@@ -292,7 +320,11 @@ export const createSnapshot = (
|
|
|
292
320
|
/**
|
|
293
321
|
* Remove a tag from a dataset
|
|
294
322
|
*/
|
|
295
|
-
export const deleteSnapshot = (
|
|
323
|
+
export const deleteSnapshot = (
|
|
324
|
+
obj,
|
|
325
|
+
{ datasetId, tag },
|
|
326
|
+
{ user, userInfo }: GraphQLContext,
|
|
327
|
+
) => {
|
|
296
328
|
return checkDatasetWrite(datasetId, user, userInfo).then(() => {
|
|
297
329
|
return datalad.deleteSnapshot(datasetId, tag)
|
|
298
330
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Star from "../../models/stars"
|
|
2
|
+
import type { GraphQLContext } from "../builder"
|
|
2
3
|
|
|
3
|
-
export const starDataset = async (obj, { datasetId }, { user }) => {
|
|
4
|
+
export const starDataset = async (obj, { datasetId }, { user }: GraphQLContext) => {
|
|
4
5
|
const star = await Star.findOne({ datasetId, userId: user }).exec()
|
|
5
6
|
const newStar = {
|
|
6
7
|
datasetId,
|
|
@@ -3,6 +3,7 @@ import { checkDatasetWrite } from "../permissions.js"
|
|
|
3
3
|
import { generateUploadToken } from "../../libs/authentication/jwt"
|
|
4
4
|
import { finishUploadRequest } from "../../datalad/upload.js"
|
|
5
5
|
import { getDatasetEndpoint } from "../../libs/datalad-service.js"
|
|
6
|
+
import type { GraphQLContext } from "../builder"
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Track initial state for a new upload
|
|
@@ -19,7 +20,7 @@ import { getDatasetEndpoint } from "../../libs/datalad-service.js"
|
|
|
19
20
|
export async function prepareUpload(
|
|
20
21
|
obj,
|
|
21
22
|
{ datasetId, uploadId },
|
|
22
|
-
{ user, userInfo },
|
|
23
|
+
{ user, userInfo }: GraphQLContext,
|
|
23
24
|
) {
|
|
24
25
|
await checkDatasetWrite(datasetId, user, userInfo)
|
|
25
26
|
const upload = await Upload.findOneAndUpdate(
|
|
@@ -47,7 +48,7 @@ export async function prepareUpload(
|
|
|
47
48
|
* @param {string} context.user User id
|
|
48
49
|
* @param {object} context.userInfo Decoded userInfo from token
|
|
49
50
|
*/
|
|
50
|
-
export async function finishUpload(obj, { uploadId }, { user, userInfo }) {
|
|
51
|
+
export async function finishUpload(obj, { uploadId }, { user, userInfo }: GraphQLContext) {
|
|
51
52
|
const upload = await Upload.findOne({ id: uploadId })
|
|
52
53
|
const datasetId = upload.datasetId
|
|
53
54
|
await checkDatasetWrite(datasetId, user, userInfo)
|