Skip to content

Commit 556284e

Browse files
Feat/#472 normalize new lines (#151)
* normalize schemas * fix test fixtures * fix string replacement * refactor to count for script being an array
1 parent 8c963eb commit 556284e

File tree

6 files changed

+142
-5
lines changed

6 files changed

+142
-5
lines changed

src/services/dictionaryService.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import { Dictionary, DictionaryDocument } from '../models/Dictionary';
2121
import { ConflictError, BadRequestError, NotFoundError } from '../utils/errors';
22-
import { validate } from '../services/schemaService';
22+
import { normalizeSchema, validate } from '../services/schemaService';
2323
import { incrementMajor, incrementMinor, isValidVersion, isGreater } from '../utils/version';
2424
import logger from '../config/logger';
2525

@@ -103,7 +103,9 @@ export const create = async (newDict: {
103103

104104
if (!isGreater(newDict.version, latest)) {
105105
logger.warn(
106-
`Rejected ${newDict.name} due to version ${newDict.version} being lower than latest: ${latest}`,
106+
`Rejected ${newDict.name} due to version ${
107+
newDict.version
108+
} being lower than latest: ${latest}`,
107109
);
108110
throw new BadRequestError(
109111
`New version for ${newDict.name} is not greater than latest existing version`,
@@ -116,11 +118,13 @@ export const create = async (newDict: {
116118
if (!result.valid) throw new BadRequestError(JSON.stringify(result.errors));
117119
});
118120

121+
const normalizedSchemas = newDict.schemas.map(schema => normalizeSchema(schema));
122+
119123
// Save new dictionary version
120124
const dict = new Dictionary({
121125
name: newDict.name,
122126
version: newDict.version,
123-
schemas: newDict.schemas,
127+
schemas: normalizedSchemas,
124128
references: newDict.references || {},
125129
});
126130
const saved = await dict.save();
@@ -149,8 +153,10 @@ export const addSchema = async (id: string, schema: any): Promise<DictionaryDocu
149153
const entities = doc.schemas.map(s => s['name']);
150154
if (entities.includes(schema['name'])) throw new ConflictError('This schema already exists.');
151155

156+
const normalizedSchema = normalizeSchema(schema);
157+
152158
const schemas = doc.schemas;
153-
schemas.push(schema);
159+
schemas.push(normalizedSchema);
154160
// Save new dictionary version
155161
const dict = new Dictionary({
156162
name: doc.name,
@@ -193,7 +199,10 @@ export const updateSchema = async (
193199

194200
// Filter out one to update
195201
const schemas = doc.schemas.filter(s => !(s['name'] === schema['name']));
196-
schemas.push(schema);
202+
203+
const normalizedSchema = normalizeSchema(schema);
204+
205+
schemas.push(normalizedSchema);
197206

198207
// Increment Version
199208
const nextVersion = major ? incrementMajor(doc.version) : incrementMinor(doc.version);

src/services/schemaService.ts

+29
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,32 @@ export function validate(schema: any, references: any) {
3333

3434
return { valid: validate(schemaWithReplacements), errors: validate.errors };
3535
}
36+
37+
function normalizeScript(script: string | string[]) {
38+
if (typeof script === 'string') {
39+
return script.replace(/\r\n/g, '\n');
40+
} else if (Array.isArray(script)) {
41+
return script.map((s: string) => s.replace(/\r\n/g, '\n'));
42+
} else {
43+
return script;
44+
}
45+
}
46+
47+
export function normalizeSchema(schema: any) {
48+
if (!schema.fields) return schema;
49+
return {
50+
...schema,
51+
fields: schema.fields.map((field: any) => {
52+
if (field.restrictions && field.restrictions.script) {
53+
return {
54+
...field,
55+
restrictions: {
56+
...field.restrictions,
57+
script: normalizeScript(field.restrictions.script),
58+
},
59+
};
60+
}
61+
return field;
62+
}),
63+
};
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "ARGO Dictionary",
3+
"version": "1.2",
4+
"schemas": [
5+
{
6+
"name": "donor",
7+
"description": "Donor Entity",
8+
"fields": [
9+
{
10+
"name": "cause_of_death",
11+
"valueType": "string",
12+
"description": "Cause of Donor Death",
13+
"restrictions": {
14+
"script": "(function(){\nreturn true;\n\n\n\n}())"
15+
}
16+
}
17+
]
18+
}
19+
]
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "ARGO Dictionary",
3+
"version": "1.2",
4+
"schemas": [
5+
{
6+
"name": "donor",
7+
"description": "Donor Entity",
8+
"fields": [
9+
{
10+
"name": "cause_of_death",
11+
"valueType": "string",
12+
"description": "Cause of Donor Death",
13+
"restrictions": {
14+
"script": "(function(){\r\nreturn true;\r\n\r\n\r\n\r\n}())"
15+
}
16+
}
17+
]
18+
}
19+
]
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "ARGO Dictionary",
3+
"version": "1.2",
4+
"schemas": [
5+
{
6+
"name": "donor",
7+
"description": "Donor Entity",
8+
"fields": [
9+
{
10+
"name": "cause_of_death",
11+
"valueType": "string",
12+
"description": "Cause of Donor Death",
13+
"restrictions": {
14+
"script": "(function(){\nreturn true;\n\n\n\n}())"
15+
}
16+
}
17+
]
18+
}
19+
]
20+
}

test/functional/schemaTest.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2020 The Ontario Institute for Cancer Research. All rights reserved
3+
*
4+
* This program and the accompanying materials are made available under the terms of
5+
* the GNU Affero General Public License v3.0. You should have received a copy of the
6+
* GNU Affero General Public License along with this program.
7+
* If not, see <http://www.gnu.org/licenses/>.
8+
*
9+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
10+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
11+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
12+
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
13+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
14+
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
15+
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
16+
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
17+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18+
*/
19+
20+
import App from '../../src/app';
21+
import { expect } from 'chai';
22+
import { AppConfig, getAppConfig } from '../../src/config/appConfig';
23+
import { DictionaryDocument } from '../../src/models/Dictionary';
24+
import { normalizeSchema } from '../../src/services/schemaService';
25+
26+
const input1 = require('./fixtures/linebreak/input1.json') as DictionaryDocument;
27+
const input2 = require('./fixtures/linebreak/input2.json') as DictionaryDocument;
28+
const output = require('./fixtures/linebreak/output.json') as DictionaryDocument;
29+
30+
describe('Verify different EOL are normalized', () => {
31+
it('Should not alter already formatted files', () => {
32+
const normalizedSchema = normalizeSchema(input1.schemas[0]);
33+
expect(normalizedSchema).to.deep.eq(output.schemas[0]);
34+
});
35+
it('Should output consistent script property', () => {
36+
const normalizedSchema = normalizeSchema(input2.schemas[0]);
37+
expect(normalizedSchema).to.deep.eq(output.schemas[0]);
38+
});
39+
});

0 commit comments

Comments
 (0)