Skip to content

Commit 22962bd

Browse files
authored
Merge pull request #514 from olgn/merged-dictionary-schema-check
schema checks are made against merged json dictionaries
2 parents cbe3f5e + e98216b commit 22962bd

File tree

5 files changed

+377
-258
lines changed

5 files changed

+377
-258
lines changed

tests/json.spec.js

Lines changed: 125 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,39 @@ describe('JSON', function() {
66
name: 'task-rest_bold.json',
77
relativePath: '/task-rest_bold.json',
88
}
9-
10-
it('should catch missing closing brackets', function() {
11-
validate.JSON(file, '{', function(issues) {
12-
assert(issues && issues.length > 0)
13-
})
14-
})
9+
var jsonDict = {}
1510

1611
it('sidecars should have key/value pair for "RepetitionTime" expressed in seconds', function() {
17-
var jsonObj =
18-
'{"RepetitionTime": 1.2, "echo_time": 0.005, "flip_angle": 90, "TaskName": "Rest"}'
19-
validate.JSON(file, jsonObj, function(issues) {
12+
var jsonObj = {
13+
RepetitionTime: 1.2,
14+
echo_time: 0.005,
15+
flip_angle: 90,
16+
TaskName: 'Rest',
17+
}
18+
jsonDict[file.relativePath] = jsonObj
19+
validate.JSON(file, jsonDict, function(issues) {
2020
assert(issues.length === 0)
2121
})
22-
var jsonObjInval =
23-
'{"RepetitionTime": 1200, "echo_time": 0.005, "flip_angle": 90, "TaskName": "Rest"}'
24-
validate.JSON(file, jsonObjInval, function(issues) {
22+
var jsonObjInval = {
23+
RepetitionTime: 1200,
24+
echo_time: 0.005,
25+
flip_angle: 90,
26+
TaskName: 'Rest',
27+
}
28+
jsonDict[file.relativePath] = jsonObjInval
29+
validate.JSON(file, jsonDict, function(issues) {
2530
assert(issues && issues.length === 1)
2631
})
2732
})
2833

2934
it('should detect negative value for SliceTiming', function() {
30-
var jsonObj =
31-
'{"RepetitionTime": 1.2, "SliceTiming": [-1.0, 0.0, 1.0], "TaskName": "Rest"}'
32-
validate.JSON(file, jsonObj, function(issues) {
35+
var jsonObj = {
36+
RepetitionTime: 1.2,
37+
SliceTiming: [-1.0, 0.0, 1.0],
38+
TaskName: 'Rest',
39+
}
40+
jsonDict[file.relativePath] = jsonObj
41+
validate.JSON(file, jsonDict, function(issues) {
3342
assert(issues.length === 1 && issues[0].code == 55)
3443
})
3544
})
@@ -40,17 +49,24 @@ describe('JSON', function() {
4049
}
4150

4251
it('*_meg.json sidecars should have required key/value pairs', function() {
43-
var jsonObj =
44-
'{"TaskName": "Audiovis", "SamplingFrequency": 1000, ' +
45-
' "PowerLineFrequency": 50, "DewarPosition": "Upright", ' +
46-
' "SoftwareFilters": "n/a", "DigitizedLandmarks": true,' +
47-
' "DigitizedHeadPoints": false}'
48-
validate.JSON(meg_file, jsonObj, function(issues) {
52+
var jsonObj = {
53+
TaskName: 'Audiovis',
54+
SamplingFrequency: 1000,
55+
PowerLineFrequency: 50,
56+
DewarPosition: 'Upright',
57+
SoftwareFilters: 'n/a',
58+
DigitizedLandmarks: true,
59+
DigitizedHeadPoints: false,
60+
}
61+
jsonDict[meg_file.relativePath] = jsonObj
62+
validate.JSON(meg_file, jsonDict, function(issues) {
4963
assert(issues.length === 0)
5064
})
5165

52-
var jsonObjInval = jsonObj.replace(/"SamplingFrequency": 1000, /g, '')
53-
validate.JSON(meg_file, jsonObjInval, function(issues) {
66+
var jsonObjInval = jsonObj
67+
jsonObjInval['SamplingFrequency'] = ''
68+
jsonDict[meg_file.relativePath] = jsonObjInval
69+
validate.JSON(meg_file, jsonDict, function(issues) {
5470
assert(issues && issues.length === 1)
5571
})
5672
})
@@ -61,16 +77,95 @@ describe('JSON', function() {
6177
}
6278

6379
it('*_ieeg.json sidecars should have required key/value pairs', function() {
64-
var jsonObj =
65-
'{"TaskName": "Audiovis", "Manufacturer": "TDT", ' +
66-
' "PowerLineFrequency": 50, "SamplingFrequency": 10, "iEEGReference": "reference"}'
67-
validate.JSON(ieeg_file, jsonObj, function(issues) {
80+
var jsonObj = {
81+
TaskName: 'Audiovis',
82+
Manufacturer: 'TDT',
83+
PowerLineFrequency: 50,
84+
SamplingFrequency: 10,
85+
iEEGReference: 'reference',
86+
}
87+
jsonDict[ieeg_file.relativePath] = jsonObj
88+
validate.JSON(ieeg_file, jsonDict, function(issues) {
6889
assert(issues.length === 0)
6990
})
70-
71-
var jsonObjInval = jsonObj.replace(/"Manufacturer": "TDT", /g, '')
72-
validate.JSON(ieeg_file, jsonObjInval, function(issues) {
91+
var jsonObjInval = jsonObj
92+
jsonObjInval['Manufacturer'] = ''
93+
jsonDict[ieeg_file.relativePath] = jsonObjInval
94+
validate.JSON(ieeg_file, jsonDict, function(issues) {
7395
assert(issues && issues.length === 1)
7496
})
7597
})
98+
99+
it('should use inherited sidecars to find missing fields', function() {
100+
const multiEntryJsonDict = {}
101+
102+
// this json file is missing the SamplingFrequency field
103+
const partialJsonObj = {
104+
TaskName: 'Audiovis',
105+
PowerLineFrequency: 50,
106+
DewarPosition: 'Upright',
107+
SoftwareFilters: 'n/a',
108+
DigitizedLandmarks: true,
109+
DigitizedHeadPoints: false,
110+
}
111+
multiEntryJsonDict[meg_file.relativePath] = partialJsonObj
112+
113+
// this json file (sitting at the root directory level)
114+
// provides the missing json field
115+
const inheritedMegFile = {
116+
name: 'meg.json',
117+
relativePath: '/meg.json',
118+
}
119+
120+
const restOfJsonObj = {
121+
SamplingFrequency: 2000,
122+
}
123+
multiEntryJsonDict[inheritedMegFile.relativePath] = restOfJsonObj
124+
125+
// json validation will pass because (when merged) there are no
126+
// missing data fields
127+
validate.JSON(meg_file, multiEntryJsonDict, function(issues) {
128+
assert(issues.length == 0)
129+
})
130+
})
131+
132+
it('should favor the sidecar on the directory level closest to the file being validated', function() {
133+
const multiEntryJsonDict = {}
134+
const lowLevelFile = {
135+
name: 'run-01_meg.json',
136+
relativePath: '/sub-01/run-01_meg.json',
137+
}
138+
139+
// this json file has a good SamplingFrequency field
140+
const partialJsonObj = {
141+
TaskName: 'Audiovis',
142+
SamplingFrequency: 1000,
143+
PowerLineFrequency: 50,
144+
DewarPosition: 'Upright',
145+
SoftwareFilters: 'n/a',
146+
DigitizedLandmarks: true,
147+
DigitizedHeadPoints: false,
148+
}
149+
multiEntryJsonDict[lowLevelFile.relativePath] = partialJsonObj
150+
151+
// this json file (sitting at the root directory level)
152+
// also has a SamplingFrequency field, but it is wrong.
153+
const inheritedMegFile = {
154+
name: 'meg.json',
155+
relativePath: '/meg.json',
156+
}
157+
158+
const restOfJsonObj = {
159+
SamplingFrequency: '',
160+
}
161+
multiEntryJsonDict[inheritedMegFile.relativePath] = restOfJsonObj
162+
163+
// json validation will pass because merged dictionaries prefer
164+
// field values of the json sidecar furthest from the root.
165+
// /meg.json is closer to the root than /sub-01/run-01_meg.json
166+
// and so the values of the latter should be preferred.
167+
validate.JSON(lowLevelFile, multiEntryJsonDict, function(issues) {
168+
assert(issues.length == 0)
169+
})
170+
})
76171
})

utils/files.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,34 +43,34 @@ var fileUtils = {
4343
* In node the file should be a path to a file.
4444
*
4545
*/
46-
function readFile(file, callback) {
47-
if (fs) {
48-
testFile(file, function(issue) {
49-
if (issue) {
50-
process.nextTick(function() {
51-
callback(issue, null)
52-
})
53-
return
54-
}
55-
fs.readFile(file.path, 'utf8', function(err, data) {
56-
process.nextTick(function() {
57-
callback(null, data)
46+
function readFile(file) {
47+
return new Promise((resolve, reject) => {
48+
if (fs) {
49+
testFile(file, function(issue) {
50+
if (issue) {
51+
process.nextTick(function() {
52+
return reject(issue)
53+
})
54+
}
55+
fs.readFile(file.path, 'utf8', function(err, data) {
56+
process.nextTick(function() {
57+
return resolve(data)
58+
})
5859
})
5960
})
60-
})
61-
} else {
62-
var reader = new FileReader()
63-
reader.onloadend = function(e) {
64-
if (e.target.readyState == FileReader.DONE) {
65-
if (!e.target.result) {
66-
callback(new Issue({ code: 44, file: file }), null)
67-
return
61+
} else {
62+
var reader = new FileReader()
63+
reader.onloadend = function(e) {
64+
if (e.target.readyState == FileReader.DONE) {
65+
if (!e.target.result) {
66+
return reject(new Issue({ code: 44, file: file }))
67+
}
68+
return resolve(e.target.result)
6869
}
69-
callback(null, e.target.result)
7070
}
71+
reader.readAsBinaryString(file)
7172
}
72-
reader.readAsBinaryString(file)
73-
}
73+
})
7474
}
7575

7676
function getBIDSIgnoreFileObjNode(dir) {
@@ -120,7 +120,7 @@ function getBIDSIgnore(dir, callback) {
120120

121121
var bidsIgnoreFileObj = getBIDSIgnoreFileObj(dir)
122122
if (bidsIgnoreFileObj) {
123-
readFile(bidsIgnoreFileObj, function(issue, content) {
123+
readFile(bidsIgnoreFileObj).then(content => {
124124
ig = ig.add(content)
125125
callback(ig)
126126
})

utils/options.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,14 @@ module.exports = {
3131
loadConfig: function(config, callback) {
3232
if (typeof config === 'string') {
3333
// load file
34-
files.readFile({ path: config }, function(issue, contents) {
35-
if (issue) {
36-
callback([issue], { path: config }, null)
37-
} else {
34+
files
35+
.readFile({ path: config })
36+
.then(contents => {
3837
callback(null, { path: config }, contents)
39-
}
40-
})
38+
})
39+
.catch(issue => {
40+
callback([issue], { path: config }, null)
41+
})
4142
} else if (typeof config === 'object') {
4243
callback(null, { path: 'config' }, JSON.stringify(config))
4344
}

0 commit comments

Comments
 (0)