@openneuro/server 4.38.3 → 4.39.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.
- package/Dockerfile +1 -1
- package/package.json +6 -5
- package/src/cache/types.ts +1 -0
- package/src/datalad/__tests__/contributors.spec.ts +177 -0
- package/src/datalad/contributors.ts +153 -0
- package/src/datalad/dataset.ts +1 -1
- package/src/graphql/resolvers/__tests__/importRemoteDataset.spec.ts +2 -6
- package/src/graphql/resolvers/__tests__/user.spec.ts +18 -3
- package/src/graphql/resolvers/cache.ts +13 -9
- package/src/graphql/resolvers/datasetEvents.ts +470 -35
- package/src/graphql/resolvers/draft.ts +2 -0
- package/src/graphql/resolvers/mutation.ts +15 -1
- package/src/graphql/resolvers/snapshots.ts +10 -0
- package/src/graphql/resolvers/user.ts +182 -31
- package/src/graphql/schema.ts +98 -5
- package/src/libs/events.ts +5 -0
- package/src/models/datasetEvents.ts +126 -22
- package/src/models/user.ts +8 -0
- package/src/models/userNotificationStatus.ts +37 -0
- package/src/types/datacite.ts +97 -0
- package/src/utils/datacite-mapper.ts +21 -0
- package/src/utils/datacite-utils.ts +256 -0
- package/src/utils/orcid-utils.ts +17 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
* User resolvers
|
|
3
|
-
*/
|
|
1
|
+
import type { PipelineStage } from "mongoose"
|
|
4
2
|
import User from "../../models/user"
|
|
3
|
+
import DatasetEvent from "../../models/datasetEvents"
|
|
4
|
+
import type { UserNotificationStatusDocument } from "../../models/userNotificationStatus"
|
|
5
5
|
|
|
6
6
|
function isValidOrcid(orcid: string): boolean {
|
|
7
7
|
return /^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]$/.test(orcid || "")
|
|
@@ -15,12 +15,16 @@ export async function user(
|
|
|
15
15
|
let user
|
|
16
16
|
if (isValidOrcid(id)) {
|
|
17
17
|
user = await User.findOne({
|
|
18
|
-
$or: [{
|
|
18
|
+
$or: [{ provider: "orcid", providerId: id }],
|
|
19
19
|
}).exec()
|
|
20
20
|
} else {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
user = await User.findOne({ id }).exec()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!user) {
|
|
25
|
+
return null // Fail silently
|
|
23
26
|
}
|
|
27
|
+
|
|
24
28
|
if (userInfo?.admin || user.id === userInfo?.id) {
|
|
25
29
|
return user.toObject()
|
|
26
30
|
} else {
|
|
@@ -37,6 +41,7 @@ export interface UserInfo {
|
|
|
37
41
|
provider?: string
|
|
38
42
|
providerId?: string
|
|
39
43
|
blocked?: boolean
|
|
44
|
+
orcidConsent?: boolean | null
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
export interface GraphQLContext {
|
|
@@ -50,16 +55,18 @@ type MongoOperatorValue =
|
|
|
50
55
|
| RegExp
|
|
51
56
|
| (string | number | boolean | RegExp)[]
|
|
52
57
|
|
|
53
|
-
type MongoQueryOperator<T> =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
type MongoQueryOperator<T> =
|
|
59
|
+
| T
|
|
60
|
+
| {
|
|
61
|
+
$ne?: T
|
|
62
|
+
$regex?: string
|
|
63
|
+
$options?: string
|
|
64
|
+
$gt?: T
|
|
65
|
+
$gte?: T
|
|
66
|
+
$lt?: T
|
|
67
|
+
$lte?: T
|
|
68
|
+
$in?: T[]
|
|
69
|
+
}
|
|
63
70
|
|
|
64
71
|
type MongoFilterValue =
|
|
65
72
|
| MongoOperatorValue
|
|
@@ -68,9 +75,17 @@ type MongoFilterValue =
|
|
|
68
75
|
interface MongoQueryCondition {
|
|
69
76
|
[key: string]: MongoFilterValue
|
|
70
77
|
}
|
|
78
|
+
|
|
71
79
|
export const users = async (
|
|
72
80
|
obj: unknown,
|
|
73
|
-
{
|
|
81
|
+
{
|
|
82
|
+
isAdmin,
|
|
83
|
+
isBlocked,
|
|
84
|
+
search,
|
|
85
|
+
limit = 100,
|
|
86
|
+
offset = 0,
|
|
87
|
+
orderBy,
|
|
88
|
+
}: {
|
|
74
89
|
isAdmin?: boolean
|
|
75
90
|
isBlocked?: boolean
|
|
76
91
|
search?: string
|
|
@@ -80,13 +95,9 @@ export const users = async (
|
|
|
80
95
|
},
|
|
81
96
|
context: GraphQLContext,
|
|
82
97
|
) => {
|
|
83
|
-
|
|
84
|
-
if (!context.userInfo?.admin) {
|
|
85
|
-
return Promise.reject(
|
|
86
|
-
new Error("You must be a site admin to retrieve users"),
|
|
87
|
-
)
|
|
88
|
-
}
|
|
98
|
+
const isSiteAdmin = context.userInfo?.admin === true
|
|
89
99
|
|
|
100
|
+
// Build filter for all users (admin or not)
|
|
90
101
|
const filter: {
|
|
91
102
|
admin?: MongoQueryOperator<boolean>
|
|
92
103
|
blocked?: MongoQueryOperator<boolean>
|
|
@@ -105,6 +116,7 @@ export const users = async (
|
|
|
105
116
|
filter.$or = [
|
|
106
117
|
{ name: { $regex: search, $options: "i" } },
|
|
107
118
|
{ email: { $regex: search, $options: "i" } },
|
|
119
|
+
{ orcid: { $regex: search, $options: "i" } },
|
|
108
120
|
]
|
|
109
121
|
}
|
|
110
122
|
|
|
@@ -133,9 +145,18 @@ export const users = async (
|
|
|
133
145
|
|
|
134
146
|
const users = await query.exec()
|
|
135
147
|
|
|
148
|
+
// If the requester is not a site admin, hide sensitive fields
|
|
149
|
+
const sanitizedUsers = isSiteAdmin ? users : users.map((u) => {
|
|
150
|
+
const obj = u.toObject()
|
|
151
|
+
obj.email = null
|
|
152
|
+
obj.blocked = null
|
|
153
|
+
obj.admin = null
|
|
154
|
+
return obj
|
|
155
|
+
})
|
|
156
|
+
|
|
136
157
|
return {
|
|
137
|
-
users:
|
|
138
|
-
totalCount
|
|
158
|
+
users: sanitizedUsers,
|
|
159
|
+
totalCount,
|
|
139
160
|
}
|
|
140
161
|
}
|
|
141
162
|
|
|
@@ -165,16 +186,19 @@ export const setBlocked = (obj, { id, blocked }, { userInfo }) => {
|
|
|
165
186
|
}
|
|
166
187
|
}
|
|
167
188
|
|
|
168
|
-
export const updateUser = async (
|
|
189
|
+
export const updateUser = async (
|
|
190
|
+
obj,
|
|
191
|
+
{ id, location, institution, links, orcidConsent },
|
|
192
|
+
) => {
|
|
169
193
|
try {
|
|
170
|
-
let user
|
|
194
|
+
let user
|
|
171
195
|
|
|
172
196
|
if (isValidOrcid(id)) {
|
|
173
197
|
user = await User.findOne({
|
|
174
|
-
$or: [{
|
|
198
|
+
$or: [{ orcid: id }, { providerId: id }],
|
|
175
199
|
}).exec()
|
|
176
200
|
} else {
|
|
177
|
-
user = await User.findOne({
|
|
201
|
+
user = await User.findOne({ id: id }).exec()
|
|
178
202
|
}
|
|
179
203
|
|
|
180
204
|
if (!user) {
|
|
@@ -185,16 +209,141 @@ export const updateUser = async (obj, { id, location, institution, links }) => {
|
|
|
185
209
|
if (location !== undefined) user.location = location
|
|
186
210
|
if (institution !== undefined) user.institution = institution
|
|
187
211
|
if (links !== undefined) user.links = links
|
|
212
|
+
if (orcidConsent !== undefined) user.orcidConsent = orcidConsent
|
|
188
213
|
|
|
189
|
-
// Save the updated user
|
|
190
214
|
await user.save()
|
|
191
215
|
|
|
192
|
-
return user
|
|
216
|
+
return user
|
|
193
217
|
} catch (err) {
|
|
194
218
|
throw new Error("Failed to update user: " + err.message)
|
|
195
219
|
}
|
|
196
220
|
}
|
|
197
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Get all events associated with a specific user (for their notifications feed).
|
|
224
|
+
* Uses a single aggregation pipeline for improved performance.
|
|
225
|
+
*/
|
|
226
|
+
export async function notifications(obj, _, { userInfo }) {
|
|
227
|
+
const userId = obj.id
|
|
228
|
+
|
|
229
|
+
// --- authorization ---
|
|
230
|
+
if (!userInfo || (userInfo.id !== userId && !userInfo.admin)) {
|
|
231
|
+
throw new Error("Not authorized to view these notifications.")
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// --- get user and orcid ---
|
|
235
|
+
const currentUser = await User.findOne({ id: userId }).exec()
|
|
236
|
+
const orcid = currentUser?.orcid
|
|
237
|
+
|
|
238
|
+
// --- base match conditions: either the user created it OR it targets them ---
|
|
239
|
+
const matchConditions: Record<string, unknown>[] = [
|
|
240
|
+
{ userId },
|
|
241
|
+
{ "event.targetUserId": userId },
|
|
242
|
+
]
|
|
243
|
+
if (orcid) matchConditions.push({ "event.targetUserId": orcid })
|
|
244
|
+
|
|
245
|
+
const pipeline: PipelineStage[] = [
|
|
246
|
+
{ $match: { $or: matchConditions } },
|
|
247
|
+
{
|
|
248
|
+
$lookup: {
|
|
249
|
+
from: "permissions",
|
|
250
|
+
let: { datasetId: "$datasetId", currentUserId: userId },
|
|
251
|
+
pipeline: [
|
|
252
|
+
{
|
|
253
|
+
$match: {
|
|
254
|
+
$expr: {
|
|
255
|
+
$and: [
|
|
256
|
+
{ $eq: ["$datasetId", "$$datasetId"] },
|
|
257
|
+
{ $eq: ["$userId", "$$currentUserId"] },
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
as: "permissions",
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{ $unwind: { path: "$permissions", preserveNullAndEmptyArrays: true } },
|
|
267
|
+
{ $sort: { timestamp: -1 } },
|
|
268
|
+
{
|
|
269
|
+
$lookup: {
|
|
270
|
+
from: "users",
|
|
271
|
+
localField: "userId",
|
|
272
|
+
foreignField: "id",
|
|
273
|
+
as: "user",
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{ $unwind: { path: "$user", preserveNullAndEmptyArrays: true } },
|
|
277
|
+
{
|
|
278
|
+
$lookup: {
|
|
279
|
+
from: "usernotificationstatuses",
|
|
280
|
+
let: { eventId: "$id" },
|
|
281
|
+
pipeline: [
|
|
282
|
+
{
|
|
283
|
+
$match: {
|
|
284
|
+
$expr: {
|
|
285
|
+
$and: [
|
|
286
|
+
{ $eq: ["$datasetEventId", "$$eventId"] },
|
|
287
|
+
{ $eq: ["$userId", userId] },
|
|
288
|
+
],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
],
|
|
293
|
+
as: "notificationStatus",
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
$unwind: {
|
|
298
|
+
path: "$notificationStatus",
|
|
299
|
+
preserveNullAndEmptyArrays: true,
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
]
|
|
303
|
+
|
|
304
|
+
const events = await DatasetEvent.aggregate(pipeline).exec()
|
|
305
|
+
|
|
306
|
+
// --- apply visibility rules ---
|
|
307
|
+
const filtered = events.filter((ev) => {
|
|
308
|
+
if (!ev.event || !ev.event.type) return false
|
|
309
|
+
|
|
310
|
+
const type = ev.event.type
|
|
311
|
+
const targetId = ev.event.targetUserId
|
|
312
|
+
const isDatasetAdmin = userInfo.admin || ev.permissions?.level === "admin"
|
|
313
|
+
const isTargetUser = targetId === userId || (orcid && targetId === orcid)
|
|
314
|
+
|
|
315
|
+
switch (type) {
|
|
316
|
+
case "contributorRequest":
|
|
317
|
+
return isDatasetAdmin
|
|
318
|
+
case "contributorCitation":
|
|
319
|
+
return isTargetUser
|
|
320
|
+
case "contributorRequestResponse":
|
|
321
|
+
case "contributorCitationResponse":
|
|
322
|
+
return isDatasetAdmin || isTargetUser
|
|
323
|
+
// Reduce the notification noise by hiding non-actionable events
|
|
324
|
+
case "created":
|
|
325
|
+
case "versioned":
|
|
326
|
+
case "deleted":
|
|
327
|
+
case "published":
|
|
328
|
+
case "permissionChange":
|
|
329
|
+
case "git":
|
|
330
|
+
case "upload":
|
|
331
|
+
return false
|
|
332
|
+
default:
|
|
333
|
+
return isDatasetAdmin
|
|
334
|
+
}
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
// --- map results with notification status ---
|
|
338
|
+
return filtered.map((event) => {
|
|
339
|
+
const notificationStatus = event.notificationStatus
|
|
340
|
+
? event.notificationStatus
|
|
341
|
+
: ({ status: "UNREAD" } as UserNotificationStatusDocument)
|
|
342
|
+
|
|
343
|
+
return { ...event, notificationStatus }
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
|
|
198
347
|
const UserResolvers = {
|
|
199
348
|
id: (obj) => obj.id,
|
|
200
349
|
provider: (obj) => obj.provider,
|
|
@@ -209,7 +358,9 @@ const UserResolvers = {
|
|
|
209
358
|
location: (obj) => obj.location,
|
|
210
359
|
institution: (obj) => obj.institution,
|
|
211
360
|
links: (obj) => obj.links,
|
|
361
|
+
orcidConsent: (obj) => obj.orcidConsent,
|
|
212
362
|
modified: (obj) => obj.updatedAt,
|
|
363
|
+
notifications: notifications,
|
|
213
364
|
}
|
|
214
365
|
|
|
215
366
|
export default UserResolvers
|
package/src/graphql/schema.ts
CHANGED
|
@@ -110,6 +110,7 @@ export const typeDefs = `
|
|
|
110
110
|
): [FlaggedFile]
|
|
111
111
|
# All public dataset metadata
|
|
112
112
|
publicMetadata: [Metadata] @cacheControl(maxAge: 86400, scope: PUBLIC)
|
|
113
|
+
orcidConsent: Boolean
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
type Mutation {
|
|
@@ -146,7 +147,7 @@ export const typeDefs = `
|
|
|
146
147
|
# Sets a users admin status
|
|
147
148
|
setBlocked(id: ID!, blocked: Boolean!): User
|
|
148
149
|
# Mutation for updating user data
|
|
149
|
-
updateUser(id: ID!, location: String, institution: String, links: [String]): User
|
|
150
|
+
updateUser(id: ID!, location: String, institution: String, links: [String], orcidConsent: Boolean): User
|
|
150
151
|
# Tracks a view or download for a dataset
|
|
151
152
|
trackAnalytics(datasetId: ID!, tag: String, type: AnalyticTypes): Boolean
|
|
152
153
|
# Follow dataset
|
|
@@ -175,8 +176,8 @@ export const typeDefs = `
|
|
|
175
176
|
prepareUpload(datasetId: ID!, uploadId: ID!): UploadMetadata
|
|
176
177
|
# Add files from a completed upload to the dataset draft
|
|
177
178
|
finishUpload(uploadId: ID!): Boolean
|
|
178
|
-
# Drop
|
|
179
|
-
cacheClear(datasetId: ID
|
|
179
|
+
# Drop cached data for a dataset - requires site admin access
|
|
180
|
+
cacheClear(datasetId: ID!): Boolean
|
|
180
181
|
# Rerun the latest validator on a given commit
|
|
181
182
|
revalidate(datasetId: ID!, ref: String!): Boolean
|
|
182
183
|
# Request a temporary token for git access
|
|
@@ -205,6 +206,16 @@ export const typeDefs = `
|
|
|
205
206
|
saveAdminNote(id: ID, datasetId: ID!, note: String!): DatasetEvent
|
|
206
207
|
# Create a git event log for dataset changes
|
|
207
208
|
createGitEvent(datasetId: ID!, commit: String!, reference: String!): DatasetEvent
|
|
209
|
+
# Request contributor status for a dataset
|
|
210
|
+
createContributorRequestEvent(datasetId: ID!): DatasetEvent
|
|
211
|
+
# Save contributor request response data
|
|
212
|
+
processContributorRequest(
|
|
213
|
+
datasetId: ID!
|
|
214
|
+
targetUserId: ID!
|
|
215
|
+
requestId: ID!
|
|
216
|
+
resolutionStatus: String!
|
|
217
|
+
reason: String
|
|
218
|
+
): DatasetEvent
|
|
208
219
|
# Create or update a fileCheck document
|
|
209
220
|
updateFileCheck(
|
|
210
221
|
datasetId: ID!
|
|
@@ -213,6 +224,21 @@ export const typeDefs = `
|
|
|
213
224
|
annexFsck: [AnnexFsckInput!]!
|
|
214
225
|
remote: String
|
|
215
226
|
): FileCheck
|
|
227
|
+
# Profile Event Status updates
|
|
228
|
+
updateEventStatus(eventId: ID!, status: NotificationStatusType!): UserNotificationStatus
|
|
229
|
+
updateContributors(
|
|
230
|
+
datasetId: String!
|
|
231
|
+
newContributors: [ContributorInput!]!
|
|
232
|
+
): UpdateContributorsPayload!
|
|
233
|
+
createContributorCitationEvent(
|
|
234
|
+
datasetId: ID!
|
|
235
|
+
targetUserId: ID!
|
|
236
|
+
contributorData: ContributorInput!
|
|
237
|
+
): DatasetEvent
|
|
238
|
+
processContributorCitation(
|
|
239
|
+
eventId: ID!
|
|
240
|
+
status: String!
|
|
241
|
+
): DatasetEvent
|
|
216
242
|
# Update worker task queue status
|
|
217
243
|
updateWorkerTask(
|
|
218
244
|
id: ID!,
|
|
@@ -351,7 +377,7 @@ export const typeDefs = `
|
|
|
351
377
|
|
|
352
378
|
# OpenNeuro user records from all providers
|
|
353
379
|
type User {
|
|
354
|
-
id: ID
|
|
380
|
+
id: ID
|
|
355
381
|
provider: UserProvider
|
|
356
382
|
avatar: String
|
|
357
383
|
orcid: String
|
|
@@ -367,6 +393,8 @@ export const typeDefs = `
|
|
|
367
393
|
github: String
|
|
368
394
|
githubSynced: Date
|
|
369
395
|
links: [String]
|
|
396
|
+
notifications: [DatasetEvent!]
|
|
397
|
+
orcidConsent: Boolean
|
|
370
398
|
}
|
|
371
399
|
|
|
372
400
|
type UserList {
|
|
@@ -561,6 +589,8 @@ export const typeDefs = `
|
|
|
561
589
|
size: BigInt
|
|
562
590
|
# File issues
|
|
563
591
|
fileCheck: FileCheck
|
|
592
|
+
# Contributors list from datacite.yml
|
|
593
|
+
contributors: [Contributor]
|
|
564
594
|
}
|
|
565
595
|
|
|
566
596
|
# Tagged snapshot of a draft
|
|
@@ -600,6 +630,8 @@ export const typeDefs = `
|
|
|
600
630
|
size: BigInt
|
|
601
631
|
# Single list of files to download this snapshot (only available on snapshots)
|
|
602
632
|
downloadFiles: [DatasetFile]
|
|
633
|
+
# Contributors list from datacite.yml
|
|
634
|
+
contributors: [Contributor]
|
|
603
635
|
}
|
|
604
636
|
|
|
605
637
|
# RelatedObject nature of relationship
|
|
@@ -669,6 +701,33 @@ export const typeDefs = `
|
|
|
669
701
|
EthicsApprovals: [String]
|
|
670
702
|
}
|
|
671
703
|
|
|
704
|
+
|
|
705
|
+
# Defines the Contributor type in contributors.ts
|
|
706
|
+
type Contributor {
|
|
707
|
+
name: String!
|
|
708
|
+
givenName: String
|
|
709
|
+
familyName: String
|
|
710
|
+
orcid: String
|
|
711
|
+
contributorType: String!
|
|
712
|
+
order: Int
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
# ContributorInput input type
|
|
716
|
+
input ContributorInput {
|
|
717
|
+
name: String
|
|
718
|
+
givenName: String
|
|
719
|
+
familyName: String
|
|
720
|
+
orcid: String
|
|
721
|
+
contributorType: String
|
|
722
|
+
order: Int
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
type UpdateContributorsPayload {
|
|
726
|
+
success: Boolean!
|
|
727
|
+
dataset: Dataset
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
|
|
672
731
|
# User permissions on a dataset
|
|
673
732
|
type Permission {
|
|
674
733
|
datasetId: ID!
|
|
@@ -903,12 +962,18 @@ export const typeDefs = `
|
|
|
903
962
|
version: String
|
|
904
963
|
public: Boolean
|
|
905
964
|
target: User
|
|
965
|
+
targetUserId: ID
|
|
906
966
|
level: String
|
|
907
967
|
ref: String
|
|
908
968
|
message: String
|
|
969
|
+
requestId: ID
|
|
970
|
+
reason: String
|
|
971
|
+
datasetId: ID
|
|
972
|
+
resolutionStatus: String
|
|
973
|
+
contributorData: Contributor
|
|
909
974
|
}
|
|
910
975
|
|
|
911
|
-
|
|
976
|
+
# Dataset events
|
|
912
977
|
type DatasetEvent {
|
|
913
978
|
# Unique identifier for the event
|
|
914
979
|
id: ID
|
|
@@ -922,8 +987,36 @@ export const typeDefs = `
|
|
|
922
987
|
success: Boolean
|
|
923
988
|
# Notes associated with the event
|
|
924
989
|
note: String
|
|
990
|
+
# top-level datasetId field
|
|
991
|
+
datasetId: ID
|
|
992
|
+
# User's notification status event
|
|
993
|
+
notificationStatus: UserNotificationStatus
|
|
994
|
+
responseStatus: String
|
|
995
|
+
hasBeenRespondedTo: Boolean
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
# Possible statuses for user notification/events
|
|
1000
|
+
enum NotificationStatusType {
|
|
1001
|
+
UNREAD
|
|
1002
|
+
SAVED
|
|
1003
|
+
ARCHIVED
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
# Define the enum for responseStatus
|
|
1007
|
+
enum ResponseStatusType {
|
|
1008
|
+
PENDING
|
|
1009
|
+
ACCEPTED
|
|
1010
|
+
DENIED
|
|
925
1011
|
}
|
|
926
1012
|
|
|
1013
|
+
# User's notification status
|
|
1014
|
+
type UserNotificationStatus {
|
|
1015
|
+
status: NotificationStatusType!
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
|
|
927
1020
|
type FileCheck {
|
|
928
1021
|
datasetId: String!
|
|
929
1022
|
hexsha: String!
|
package/src/libs/events.ts
CHANGED