@openneuro/server 4.23.0 → 4.24.0-alpha.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/Dockerfile +1 -1
- package/package.json +11 -10
- package/src/cache/types.ts +1 -0
- package/src/datalad/__tests__/dataset.spec.ts +5 -2
- package/src/datalad/__tests__/pagination.spec.ts +5 -1
- package/src/datalad/__tests__/snapshots.spec.ts +7 -1
- package/src/datalad/description.ts +6 -0
- package/src/datalad/draft.ts +13 -8
- package/src/graphql/__tests__/__snapshots__/permissions.spec.ts.snap +2 -2
- package/src/graphql/__tests__/comment.spec.ts +5 -1
- package/src/graphql/resolvers/__tests__/dataset-search.spec.ts +11 -15
- package/src/graphql/resolvers/__tests__/dataset.spec.ts +6 -2
- package/src/graphql/resolvers/dataset-search.ts +21 -15
- package/src/graphql/schema.ts +2 -0
- package/src/handlers/datalad.ts +4 -1
- package/src/libs/__tests__/dataset.spec.ts +7 -3
- package/src/libs/doi/__tests__/__snapshots__/doi.spec.ts.snap +5 -5
- package/src/libs/email/templates/__tests__/__snapshots__/comment-created.spec.ts.snap +7 -7
- package/src/libs/email/templates/__tests__/__snapshots__/dataset-deleted.spec.ts.snap +3 -3
- package/src/libs/email/templates/__tests__/__snapshots__/owner-unsubscribed.spec.ts.snap +3 -3
- package/src/libs/email/templates/__tests__/__snapshots__/snapshot-created.spec.ts.snap +6 -6
- package/src/libs/email/templates/__tests__/__snapshots__/snapshot-reminder.spec.ts.snap +4 -4
package/Dockerfile
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openneuro/server",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.24.0-alpha.2",
|
|
4
4
|
"description": "Core service for the OpenNeuro platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -18,10 +18,10 @@
|
|
|
18
18
|
"@apollo/client": "3.7.2",
|
|
19
19
|
"@apollo/server": "4.9.3",
|
|
20
20
|
"@apollo/utils.keyvadapter": "3.0.0",
|
|
21
|
-
"@elastic/elasticsearch": "
|
|
21
|
+
"@elastic/elasticsearch": "8.13.1",
|
|
22
22
|
"@graphql-tools/schema": "^10.0.0",
|
|
23
23
|
"@keyv/redis": "^2.7.0",
|
|
24
|
-
"@openneuro/search": "^4.
|
|
24
|
+
"@openneuro/search": "^4.24.0-alpha.2",
|
|
25
25
|
"@passport-next/passport-google-oauth2": "^1.0.0",
|
|
26
26
|
"@sentry/node": "^4.5.3",
|
|
27
27
|
"base64url": "^3.0.0",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"date-fns": "^2.16.1",
|
|
31
31
|
"draft-js": "^0.11.7",
|
|
32
32
|
"draft-js-export-html": "^1.4.1",
|
|
33
|
-
"elastic-apm-node": "
|
|
34
|
-
"express": "4.
|
|
33
|
+
"elastic-apm-node": "4.5.4",
|
|
34
|
+
"express": "4.19.2",
|
|
35
35
|
"graphql": "16.8.1",
|
|
36
36
|
"graphql-bigint": "^1.0.0",
|
|
37
37
|
"graphql-compose": "9.0.10",
|
|
@@ -39,11 +39,12 @@
|
|
|
39
39
|
"graphql-tools": "9.0.0",
|
|
40
40
|
"immutable": "^3.8.2",
|
|
41
41
|
"ioredis": "4.17.3",
|
|
42
|
-
"jsdom": "
|
|
42
|
+
"jsdom": "24.0.0",
|
|
43
43
|
"jsonwebtoken": "^9.0.0",
|
|
44
44
|
"keyv": "^4.5.3",
|
|
45
45
|
"mime-types": "^2.1.19",
|
|
46
|
-
"
|
|
46
|
+
"mongodb-memory-server": "^9.2.0",
|
|
47
|
+
"mongoose": "6.12.8",
|
|
47
48
|
"morgan": "^1.6.1",
|
|
48
49
|
"node-mailjet": "^3.3.5",
|
|
49
50
|
"object-hash": "2.1.1",
|
|
@@ -76,14 +77,14 @@
|
|
|
76
77
|
"@types/semver": "^5",
|
|
77
78
|
"core-js": "^3.10.1",
|
|
78
79
|
"ioredis-mock": "^8.8.1",
|
|
79
|
-
"nodemon": "
|
|
80
|
+
"nodemon": "3.1.0",
|
|
80
81
|
"ts-node-dev": "1.1.6",
|
|
81
82
|
"tsc-watch": "^4.2.9",
|
|
82
|
-
"vitest": "
|
|
83
|
+
"vitest": "1.5.0",
|
|
83
84
|
"vitest-fetch-mock": "0.2.2"
|
|
84
85
|
},
|
|
85
86
|
"publishConfig": {
|
|
86
87
|
"access": "public"
|
|
87
88
|
},
|
|
88
|
-
"gitHead": "
|
|
89
|
+
"gitHead": "aa9717595d41e5e0eb55328c62fa59c80847b95e"
|
|
89
90
|
}
|
package/src/cache/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import request from "superagent"
|
|
|
3
3
|
import { createDataset, datasetsFilter, testBlacklist } from "../dataset"
|
|
4
4
|
import { getDatasetWorker } from "../../libs/datalad-service"
|
|
5
5
|
import { connect } from "mongoose"
|
|
6
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
6
7
|
|
|
7
8
|
// Mock requests to Datalad service
|
|
8
9
|
vi.mock("superagent")
|
|
@@ -13,9 +14,11 @@ vi.mock("../../libs/notifications")
|
|
|
13
14
|
|
|
14
15
|
describe("dataset model operations", () => {
|
|
15
16
|
describe("createDataset()", () => {
|
|
16
|
-
|
|
17
|
+
let mongod
|
|
18
|
+
beforeAll(async () => {
|
|
17
19
|
// Setup MongoDB with mongodb-memory-server
|
|
18
|
-
|
|
20
|
+
mongod = await MongoMemoryServer.create()
|
|
21
|
+
connect(mongod.getUri())
|
|
19
22
|
})
|
|
20
23
|
it("resolves to dataset id string", async () => {
|
|
21
24
|
const user = { id: "1234" }
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { vi } from "vitest"
|
|
2
2
|
vi.mock("ioredis")
|
|
3
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
3
4
|
import * as pagination from "../pagination.js"
|
|
4
5
|
import { connect, Types } from "mongoose"
|
|
5
6
|
import Dataset from "../../models/dataset"
|
|
@@ -37,8 +38,11 @@ describe("pagination model operations", () => {
|
|
|
37
38
|
})
|
|
38
39
|
})
|
|
39
40
|
describe("datasetsConnection()", () => {
|
|
41
|
+
let mongod
|
|
40
42
|
beforeAll(async () => {
|
|
41
|
-
|
|
43
|
+
// Setup MongoDB with mongodb-memory-server
|
|
44
|
+
mongod = await MongoMemoryServer.create()
|
|
45
|
+
connect(mongod.getUri())
|
|
42
46
|
const ds = new Dataset({
|
|
43
47
|
_id: new ObjectID("5bef51a1ed211400c08e5524"),
|
|
44
48
|
id: "ds001001",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { vi } from "vitest"
|
|
2
2
|
vi.mock("ioredis")
|
|
3
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
3
4
|
import request from "superagent"
|
|
4
5
|
import { createDataset } from "../dataset"
|
|
5
6
|
import { createSnapshot } from "../snapshots"
|
|
@@ -25,10 +26,15 @@ vi.mock("../../libs/notifications.ts")
|
|
|
25
26
|
|
|
26
27
|
describe("snapshot model operations", () => {
|
|
27
28
|
describe("createSnapshot()", () => {
|
|
29
|
+
let mongod
|
|
30
|
+
beforeAll(async () => {
|
|
31
|
+
// Setup MongoDB with mongodb-memory-server
|
|
32
|
+
mongod = await MongoMemoryServer.create()
|
|
33
|
+
connect(mongod.getUri())
|
|
34
|
+
})
|
|
28
35
|
it("posts to the DataLad /datasets/{dsId}/snapshots/{snapshot} endpoint", async () => {
|
|
29
36
|
const user = { id: "1234" }
|
|
30
37
|
const tag = "snapshot"
|
|
31
|
-
await connect(globalThis.__MONGO_URI__)
|
|
32
38
|
const { id: dsId } = await createDataset(user.id, user, {
|
|
33
39
|
affirmedDefaced: true,
|
|
34
40
|
affirmedConsent: true,
|
|
@@ -88,6 +88,12 @@ export const repairDescriptionTypes = (description) => {
|
|
|
88
88
|
newDescription.HowToAcknowledge =
|
|
89
89
|
JSON.stringify(description.HowToAcknowledge) || ""
|
|
90
90
|
}
|
|
91
|
+
if (
|
|
92
|
+
description.hasOwnProperty("DatasetType") &&
|
|
93
|
+
typeof description.DatasetType !== "string"
|
|
94
|
+
) {
|
|
95
|
+
newDescription.DatasetType = "raw"
|
|
96
|
+
}
|
|
91
97
|
return newDescription
|
|
92
98
|
}
|
|
93
99
|
|
package/src/datalad/draft.ts
CHANGED
|
@@ -4,16 +4,21 @@
|
|
|
4
4
|
import request from "superagent"
|
|
5
5
|
import Dataset from "../models/dataset"
|
|
6
6
|
import { getDatasetWorker } from "../libs/datalad-service"
|
|
7
|
+
import CacheItem, { CacheType } from "../cache/item"
|
|
8
|
+
import { redis } from "../libs/redis"
|
|
7
9
|
|
|
8
10
|
export const getDraftRevision = async (datasetId) => {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const cache = new CacheItem(redis, CacheType.draftRevision, [datasetId], 10)
|
|
12
|
+
return cache.get(async (_doNotCache): Promise<string> => {
|
|
13
|
+
const draftUrl = `http://${
|
|
14
|
+
getDatasetWorker(
|
|
15
|
+
datasetId,
|
|
16
|
+
)
|
|
17
|
+
}/datasets/${datasetId}/draft`
|
|
18
|
+
const response = await fetch(draftUrl)
|
|
19
|
+
const { hexsha } = await response.json()
|
|
20
|
+
return hexsha
|
|
21
|
+
})
|
|
17
22
|
}
|
|
18
23
|
|
|
19
24
|
export const updateDatasetRevision = (datasetId, gitRef) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`resolver permissions helpers > checkDatasetAdmin() > resolves to false for anonymous users 1`] = `
|
|
3
|
+
exports[`resolver permissions helpers > checkDatasetAdmin() > resolves to false for anonymous users 1`] = `[Error: You do not have admin access to this dataset.]`;
|
|
4
4
|
|
|
5
|
-
exports[`resolver permissions helpers > checkDatasetWrite() > resolves to false for anonymous users 1`] = `
|
|
5
|
+
exports[`resolver permissions helpers > checkDatasetWrite() > resolves to false for anonymous users 1`] = `[Error: You do not have access to modify this dataset.]`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { vi } from "vitest"
|
|
2
2
|
import { connect } from "mongoose"
|
|
3
3
|
import { deleteComment, flatten } from "../resolvers/comment"
|
|
4
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
4
5
|
import Comment from "../../models/comment"
|
|
5
6
|
|
|
6
7
|
vi.mock("ioredis")
|
|
@@ -16,8 +17,11 @@ describe("comment resolver helpers", () => {
|
|
|
16
17
|
user: "5678",
|
|
17
18
|
userInfo: { admin: false },
|
|
18
19
|
}
|
|
20
|
+
let mongod
|
|
19
21
|
beforeAll(async () => {
|
|
20
|
-
|
|
22
|
+
// Setup MongoDB with mongodb-memory-server
|
|
23
|
+
mongod = await MongoMemoryServer.create()
|
|
24
|
+
connect(mongod.getUri())
|
|
21
25
|
const comment = new Comment({
|
|
22
26
|
text: "a",
|
|
23
27
|
createDate: new Date().toISOString(),
|
|
@@ -28,11 +28,9 @@ describe("dataset search resolvers", () => {
|
|
|
28
28
|
describe("elasticRelayConnection()", () => {
|
|
29
29
|
it("returns a relay cursor for empty ApiResponse", async () => {
|
|
30
30
|
const emptyApiResponse = {
|
|
31
|
-
|
|
32
|
-
hits:
|
|
33
|
-
|
|
34
|
-
total: { value: 0 },
|
|
35
|
-
},
|
|
31
|
+
hits: {
|
|
32
|
+
hits: [],
|
|
33
|
+
total: { value: 0 },
|
|
36
34
|
},
|
|
37
35
|
}
|
|
38
36
|
|
|
@@ -60,20 +58,18 @@ describe("dataset search resolvers", () => {
|
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
const expectedApiResponse = {
|
|
63
|
-
|
|
64
|
-
hits:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
total: { value: 10 },
|
|
71
|
-
},
|
|
61
|
+
hits: {
|
|
62
|
+
hits: [
|
|
63
|
+
{ _source: { id: "testdataset1" } },
|
|
64
|
+
{ _source: { id: "testdataset2" } },
|
|
65
|
+
{ _source: { id: "testdataset3" }, sort: [1] },
|
|
66
|
+
],
|
|
67
|
+
total: { value: 10 },
|
|
72
68
|
},
|
|
73
69
|
}
|
|
74
70
|
|
|
75
71
|
const resultsRelayConnection = {
|
|
76
|
-
edges: expectedApiResponse.
|
|
72
|
+
edges: expectedApiResponse.hits.hits.map((hit) => {
|
|
77
73
|
// This skips the dataset resolver logic and passes this back
|
|
78
74
|
mockResolvers.dataset.mockReturnValueOnce(hit._source)
|
|
79
75
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { vi } from "vitest"
|
|
2
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
2
3
|
import { connect } from "mongoose"
|
|
3
4
|
import request from "superagent"
|
|
4
5
|
import * as ds from "../dataset"
|
|
@@ -9,8 +10,11 @@ vi.mock("../../../config.ts")
|
|
|
9
10
|
vi.mock("../../../libs/notifications.ts")
|
|
10
11
|
|
|
11
12
|
describe("dataset resolvers", () => {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
let mongod
|
|
14
|
+
beforeAll(async () => {
|
|
15
|
+
// Setup MongoDB with mongodb-memory-server
|
|
16
|
+
mongod = await MongoMemoryServer.create()
|
|
17
|
+
connect(mongod.getUri())
|
|
14
18
|
})
|
|
15
19
|
describe("createDataset()", () => {
|
|
16
20
|
it("createDataset mutation succeeds", async () => {
|
|
@@ -4,6 +4,7 @@ import Star from "../../models/stars"
|
|
|
4
4
|
import Subscription from "../../models/subscription"
|
|
5
5
|
import Permission from "../../models/permission"
|
|
6
6
|
import { hashObject } from "../../libs/authentication/crypto"
|
|
7
|
+
import util from "util"
|
|
7
8
|
|
|
8
9
|
const elasticIndex = "datasets"
|
|
9
10
|
|
|
@@ -37,7 +38,7 @@ export const decodeCursor = (cursor) =>
|
|
|
37
38
|
* @param {import ('@elastic/elasticsearch').ApiResponse} result
|
|
38
39
|
*/
|
|
39
40
|
export const elasticRelayConnection = (
|
|
40
|
-
|
|
41
|
+
body,
|
|
41
42
|
id,
|
|
42
43
|
size,
|
|
43
44
|
childResolvers = { dataset },
|
|
@@ -102,9 +103,9 @@ export const datasetSearchConnection = async (
|
|
|
102
103
|
index: elasticIndex,
|
|
103
104
|
size: first,
|
|
104
105
|
q: `${q} AND public:true`,
|
|
105
|
-
|
|
106
|
+
...requestBody,
|
|
106
107
|
})
|
|
107
|
-
return elasticRelayConnection(
|
|
108
|
+
return elasticRelayConnection(requestBody, searchId, first)
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
export const datasetSearch = {
|
|
@@ -215,6 +216,7 @@ export const advancedDatasetSearchConnection = async (
|
|
|
215
216
|
},
|
|
216
217
|
{ user, userInfo },
|
|
217
218
|
) => {
|
|
219
|
+
// Create an identity for this search (used to cache connections)
|
|
218
220
|
const searchId = hashObject({
|
|
219
221
|
query,
|
|
220
222
|
datasetType,
|
|
@@ -223,26 +225,30 @@ export const advancedDatasetSearchConnection = async (
|
|
|
223
225
|
user,
|
|
224
226
|
})
|
|
225
227
|
const sort = [{ _score: "desc" }, { id: "desc" }]
|
|
226
|
-
if (sortBy)
|
|
227
|
-
|
|
228
|
-
sort,
|
|
229
|
-
query: allDatasets
|
|
230
|
-
? query
|
|
231
|
-
: await parseQuery(query, datasetType, datasetStatus, user),
|
|
232
|
-
search_after: undefined,
|
|
228
|
+
if (sortBy) {
|
|
229
|
+
sort.unshift(sortBy)
|
|
233
230
|
}
|
|
231
|
+
// Parse out the decode token and add it to our query if successful
|
|
232
|
+
let search_after
|
|
234
233
|
if (after) {
|
|
235
234
|
try {
|
|
236
|
-
|
|
237
|
-
} catch (
|
|
235
|
+
search_after = decodeCursor(after)
|
|
236
|
+
} catch (_err) {
|
|
238
237
|
// Don't include search_after if parsing fails
|
|
239
238
|
}
|
|
240
239
|
}
|
|
241
|
-
const
|
|
240
|
+
const requestBody = {
|
|
242
241
|
index: elasticIndex,
|
|
243
242
|
size: first,
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
sort,
|
|
244
|
+
query: allDatasets
|
|
245
|
+
? query
|
|
246
|
+
: await parseQuery(query, datasetType, datasetStatus, user),
|
|
247
|
+
search_after,
|
|
248
|
+
}
|
|
249
|
+
// Run the query
|
|
250
|
+
const result = await elasticClient.search(requestBody)
|
|
251
|
+
// Extend with relay connection pagination
|
|
246
252
|
return elasticRelayConnection(
|
|
247
253
|
result,
|
|
248
254
|
searchId,
|
package/src/graphql/schema.ts
CHANGED
|
@@ -571,6 +571,8 @@ export const typeDefs = `
|
|
|
571
571
|
ReferencesAndLinks: [String]
|
|
572
572
|
# The Document Object Identifier of the dataset (not the corresponding paper).
|
|
573
573
|
DatasetDOI: String
|
|
574
|
+
# The BIDS DatasetType field defined as "raw" or "derivative"
|
|
575
|
+
DatasetType: String
|
|
574
576
|
# List of ethics committee approvals of the research protocols and/or protocol identifiers.
|
|
575
577
|
EthicsApprovals: [String]
|
|
576
578
|
}
|
package/src/handlers/datalad.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { Readable } from "node:stream"
|
|
|
3
3
|
import mime from "mime-types"
|
|
4
4
|
import { getFiles } from "../datalad/files"
|
|
5
5
|
import { getDatasetWorker } from "../libs/datalad-service"
|
|
6
|
+
import { getDraftRevision } from "../datalad/draft"
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Handlers for datalad dataset manipulation
|
|
@@ -21,7 +22,9 @@ export const getFile = async (req, res) => {
|
|
|
21
22
|
const worker = getDatasetWorker(datasetId)
|
|
22
23
|
// Find the right tree
|
|
23
24
|
const pathComponents = filename.split(":")
|
|
24
|
-
|
|
25
|
+
// Get the draft commit for cache busting
|
|
26
|
+
const draftCommit = await getDraftRevision(datasetId)
|
|
27
|
+
let tree = snapshotId || draftCommit
|
|
25
28
|
let file
|
|
26
29
|
for (const level of pathComponents) {
|
|
27
30
|
try {
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { vi } from "vitest"
|
|
2
|
-
import {
|
|
2
|
+
import { MongoMemoryServer } from "mongodb-memory-server"
|
|
3
|
+
import { connect, disconnect } from "mongoose"
|
|
3
4
|
import { getAccessionNumber } from "../dataset"
|
|
4
5
|
|
|
5
6
|
vi.mock("ioredis")
|
|
6
7
|
|
|
7
8
|
describe("libs/dataset", () => {
|
|
8
9
|
describe("getAccessionNumber", () => {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
let mongod
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
// Setup MongoDB with mongodb-memory-server
|
|
13
|
+
mongod = await MongoMemoryServer.create()
|
|
14
|
+
await connect(mongod.getUri())
|
|
11
15
|
})
|
|
12
16
|
it('returns strings starting with "ds"', async () => {
|
|
13
17
|
const ds = await getAccessionNumber()
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
3
|
exports[`DOI minting utils > template() > accepts expected arguments 1`] = `
|
|
4
|
-
"<?xml version
|
|
5
|
-
<resource xmlns:xsi
|
|
6
|
-
<identifier identifierType
|
|
4
|
+
"<?xml version="1.0" encoding="UTF-8"?>
|
|
5
|
+
<resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://datacite.org/schema/kernel-4" xsi:schemaLocation="http://datacite.org/schema/kernel-4 http://schema.datacite.org/meta/kernel-4/metadata.xsd">
|
|
6
|
+
<identifier identifierType="DOI">12345</identifier>
|
|
7
7
|
<creators>
|
|
8
8
|
<creator><creatorName>A. User</creatorName></creator><creator><creatorName>B. User</creatorName></creator>
|
|
9
9
|
</creators>
|
|
10
10
|
<titles>
|
|
11
|
-
<title xml:lang
|
|
11
|
+
<title xml:lang="en-us">Test Dataset</title>
|
|
12
12
|
</titles>
|
|
13
13
|
<publisher>Openneuro</publisher>
|
|
14
14
|
<publicationYear>1999</publicationYear>
|
|
15
|
-
<resourceType resourceTypeGeneral
|
|
15
|
+
<resourceType resourceTypeGeneral="Dataset">fMRI</resourceType>
|
|
16
16
|
</resource>"
|
|
17
17
|
`;
|
|
@@ -56,22 +56,22 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
56
56
|
</style>
|
|
57
57
|
</head>
|
|
58
58
|
<body>
|
|
59
|
-
<div class
|
|
60
|
-
<img src
|
|
59
|
+
<div class="top-bar">
|
|
60
|
+
<img src="https://openneuro.org/assets/email-header.1cb8bf76.png" />
|
|
61
61
|
</div>
|
|
62
|
-
<div class
|
|
62
|
+
<div class="content">
|
|
63
63
|
<h2>Hi, J. Doe</h2>
|
|
64
64
|
|
|
65
65
|
<p>
|
|
66
66
|
A new new has been posted on a dataset you follow, <b>Not Real Dataset</b>.
|
|
67
67
|
</p>
|
|
68
|
-
<div class
|
|
68
|
+
<div class="comment">
|
|
69
69
|
<p>By: <b>56789</b> on 2063-04-05</p>
|
|
70
70
|
|
|
71
71
|
Test comment, please ignore
|
|
72
72
|
</div>
|
|
73
|
-
<div class
|
|
74
|
-
<a class
|
|
73
|
+
<div class="link-div">
|
|
74
|
+
<a class="dataset-link" href="https://openneuro.org/datasets/ds1245678#comment-12345">Click here to view this comment on OpenNeuro »</a>
|
|
75
75
|
</div>
|
|
76
76
|
|
|
77
77
|
<p>
|
|
@@ -82,7 +82,7 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
82
82
|
</div>
|
|
83
83
|
</body>
|
|
84
84
|
<footer>
|
|
85
|
-
If you would like to stop receiving notifications about this dataset, please <a class='link' href
|
|
85
|
+
If you would like to stop receiving notifications about this dataset, please <a class='link' href="https://openneuro.org/datasets/ds1245678">visit the dataset page</a> and click the 'unfollow' icon.
|
|
86
86
|
</footer>
|
|
87
87
|
<html>"
|
|
88
88
|
`;
|
|
@@ -38,10 +38,10 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
38
38
|
</style>
|
|
39
39
|
</head>
|
|
40
40
|
<body>
|
|
41
|
-
<div class
|
|
42
|
-
<img src
|
|
41
|
+
<div class="top-bar">
|
|
42
|
+
<img src="https://openneuro.org/assets/email-header.1cb8bf76.png" />
|
|
43
43
|
</div>
|
|
44
|
-
<div class
|
|
44
|
+
<div class="content">
|
|
45
45
|
<h2>Hi, J. Doe</h2>
|
|
46
46
|
|
|
47
47
|
<p>
|
|
@@ -38,10 +38,10 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
38
38
|
</style>
|
|
39
39
|
</head>
|
|
40
40
|
<body>
|
|
41
|
-
<div class
|
|
42
|
-
<img src
|
|
41
|
+
<div class="top-bar">
|
|
42
|
+
<img src="https://openneuro.org/assets/email-header.1cb8bf76.png" />
|
|
43
43
|
</div>
|
|
44
|
-
<div class
|
|
44
|
+
<div class="content">
|
|
45
45
|
<h2>Hi, J. Doe</h2>
|
|
46
46
|
|
|
47
47
|
<p>
|
|
@@ -56,10 +56,10 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
56
56
|
</style>
|
|
57
57
|
</head>
|
|
58
58
|
<body>
|
|
59
|
-
<div class
|
|
60
|
-
<img src
|
|
59
|
+
<div class="top-bar">
|
|
60
|
+
<img src="https://openneuro.org/assets/email-header.1cb8bf76.png" />
|
|
61
61
|
</div>
|
|
62
|
-
<div class
|
|
62
|
+
<div class="content">
|
|
63
63
|
<h2>Hi, J. Doe</h2>
|
|
64
64
|
|
|
65
65
|
<p>
|
|
@@ -67,12 +67,12 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
67
67
|
</p>
|
|
68
68
|
|
|
69
69
|
<div>
|
|
70
|
-
<a class='dataset-link' href
|
|
70
|
+
<a class='dataset-link' href="https://openneuro.org/datasets/ds1245678/versions/1.2.4">Click here to view this snapshot on OpenNeuro »</a>
|
|
71
71
|
</div>
|
|
72
72
|
|
|
73
73
|
<div>
|
|
74
74
|
<h4>Changelog</h4>
|
|
75
|
-
<p class
|
|
75
|
+
<p class="changelog">New changes...</p>
|
|
76
76
|
</div>
|
|
77
77
|
|
|
78
78
|
<p>
|
|
@@ -82,7 +82,7 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
82
82
|
</div>
|
|
83
83
|
</body>
|
|
84
84
|
<footer>
|
|
85
|
-
If you would like to stop receiving notifications about this dataset, please <a class='link' href
|
|
85
|
+
If you would like to stop receiving notifications about this dataset, please <a class='link' href="https://openneuro.org/datasets/ds1245678">visit the dataset page</a> and click the 'unfollow' icon.
|
|
86
86
|
</footer>
|
|
87
87
|
<html>"
|
|
88
88
|
`;
|
|
@@ -38,10 +38,10 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
38
38
|
</style>
|
|
39
39
|
</head>
|
|
40
40
|
<body>
|
|
41
|
-
<div class
|
|
42
|
-
<img src
|
|
41
|
+
<div class="top-bar">
|
|
42
|
+
<img src="https://openneuro.org/assets/email-header.1cb8bf76.png" />
|
|
43
43
|
</div>
|
|
44
|
-
<div class
|
|
44
|
+
<div class="content">
|
|
45
45
|
<h2>Hi, J. Doe</h2>
|
|
46
46
|
|
|
47
47
|
<p>
|
|
@@ -53,7 +53,7 @@ exports[`email template -> comment created > renders with expected arguments 1`]
|
|
|
53
53
|
The CRN Team
|
|
54
54
|
</p>
|
|
55
55
|
|
|
56
|
-
<a class
|
|
56
|
+
<a class="dataset-link" href="https://openneuro.org/datasets/ds12345678/snapshot">Create a snapshot. »</a>
|
|
57
57
|
</div>
|
|
58
58
|
</body>
|
|
59
59
|
<html>"
|