rxome-generator 0.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/LICENSE +21 -0
- package/README.md +484 -0
- package/demos/demo_data_full.json +149 -0
- package/demos/demo_data_full_compressed.json +76 -0
- package/demos/demo_from_gui.json +64 -0
- package/demos/rxome.decrypt.json +1 -0
- package/demos/rxome.encrypt.json +1 -0
- package/demos/testmessage_cipher +19 -0
- package/index.js +3 -0
- package/lib/phenopackets.json +1948 -0
- package/lib/rxome-api-demo.js +56 -0
- package/lib/rxome-api.js +119 -0
- package/lib/rxome-api.test.js +22 -0
- package/lib/rxome-generator.js +349 -0
- package/lib/rxome-generator.test.js +379 -0
- package/package.json +50 -0
- package/rxcode +401 -0
- package/rxcode.test.js +104 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
const Coder = require('./rxome-generator');
|
|
2
|
+
const RxAPI = require( './rxome-api' );
|
|
3
|
+
const ApiDemo = require( './rxome-api-demo');
|
|
4
|
+
|
|
5
|
+
const FS = require('fs');
|
|
6
|
+
const Axios = require( 'axios' );
|
|
7
|
+
|
|
8
|
+
const RXAPI = RxAPI.API;
|
|
9
|
+
const RXTESTAPI = RxAPI.TESTAPI;
|
|
10
|
+
const RXLOCALAPI = 'http:localhost:3000';
|
|
11
|
+
|
|
12
|
+
const CREDENTIALS = {
|
|
13
|
+
id: ApiDemo.DEMO_API_ID,
|
|
14
|
+
user: 'test@rxome.net',
|
|
15
|
+
key: ApiDemo.DEMO_API_PRIVATE_KEY
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const DEMO_DATA = JSON.parse(FS.readFileSync('./demos/demo_data_full.json'));
|
|
19
|
+
|
|
20
|
+
const DEMO_DATA_COMPRESSED = JSON.parse(FS.readFileSync('./demos/demo_data_full_compressed.json'));
|
|
21
|
+
// compressed: without credentials due to whitelist/phenopacket filtering
|
|
22
|
+
|
|
23
|
+
const DEMO_PSEUDONYM = 'HANSMOTKAMP';
|
|
24
|
+
const EMPTY_PSEUDONYM = '';
|
|
25
|
+
|
|
26
|
+
const PRIVATE_KEY = ApiDemo.CRYPT_PRIVATE_KEY;
|
|
27
|
+
const PUBLIC_KEY = ApiDemo.CRYPT_PUBLIC_KEY;
|
|
28
|
+
const DEMO_API_PRIVATE_KEY = ApiDemo.DEMO_API_PRIVATE_KEY;
|
|
29
|
+
const DEMO_API_PUBLIC_KEY = ApiDemo.API_PUBLIC_KEY;
|
|
30
|
+
|
|
31
|
+
describe('API access', () => {
|
|
32
|
+
|
|
33
|
+
test('is able to connect to API', async () => {
|
|
34
|
+
await expect( Coder.fetchKey(CREDENTIALS, '', RXTESTAPI) ).resolves.not.toThrowError();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// test('should fetch key and pseudonyms', async () => {
|
|
38
|
+
// Coder.fetchKey(CREDENTIALS, '', RXTESTAPI)
|
|
39
|
+
// .then( result => {
|
|
40
|
+
// //console.log( PUBLIC_KEY )
|
|
41
|
+
// //console.log( result.key )
|
|
42
|
+
// expect(result.pseudonym).toBe(DEMO_PSEUDONYM);
|
|
43
|
+
// expect(result.key.trim()).toBe(PUBLIC_KEY.trim())
|
|
44
|
+
// })
|
|
45
|
+
// .catch( e => process.stderr.write( e.message ));
|
|
46
|
+
// });
|
|
47
|
+
|
|
48
|
+
test('should fetch key and pseudonyms', () => {
|
|
49
|
+
Coder.fetchKey(CREDENTIALS, '', RXTESTAPI)
|
|
50
|
+
.then(result => {
|
|
51
|
+
expect(result.pseudonym).toBe(DEMO_PSEUDONYM);
|
|
52
|
+
expect(result.key.trim()).toBe(PUBLIC_KEY.trim());
|
|
53
|
+
expect(result.version).toBe('0_1');
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('should not fetch pseudonyme for given lab ps', () => {
|
|
58
|
+
const lab_ps = (Math.random() + 1).toString(36).substring(2,12)
|
|
59
|
+
Coder.fetchKey(CREDENTIALS, lab_ps, RXTESTAPI).then(result => {
|
|
60
|
+
expect(result.pseudonym).toBe(lab_ps);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// error handling:
|
|
65
|
+
test('should raise an error when url is wrong', () => {
|
|
66
|
+
Coder.fetchKey(CREDENTIALS, '', 'https://testapi.gene-talk.de/api/v1/schnitzel/', false, 'fehler')
|
|
67
|
+
.then(data => { })
|
|
68
|
+
.catch(err => {
|
|
69
|
+
expect(err.message).toMatch('404');
|
|
70
|
+
expect(err.response.data).toBeDefined();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('should raise an error when id is wrong', () => {
|
|
75
|
+
const lab_ps = (Math.random() + 1).toString(36).substring(2,12);
|
|
76
|
+
const wrong_credentials = {
|
|
77
|
+
id: 'intruder',
|
|
78
|
+
user: 'test@rxome.net',
|
|
79
|
+
key: DEMO_API_PRIVATE_KEY
|
|
80
|
+
}
|
|
81
|
+
Coder.fetchKey(wrong_credentials, 'lab_ps', RXTESTAPI)
|
|
82
|
+
.then(data => { })
|
|
83
|
+
.catch(err => {
|
|
84
|
+
expect(err.response.status.toString().toMatch('403'));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('should raise an error when key is wrong', () => {
|
|
89
|
+
const lab_ps = (Math.random() + 1).toString(36).substring(2,12);
|
|
90
|
+
const wrong_credentials = {
|
|
91
|
+
id: 'rxome',
|
|
92
|
+
user: 'test@rxome.net',
|
|
93
|
+
key: DEMO_API_PRIVATE_KEY.split('').reverse().join('')
|
|
94
|
+
}
|
|
95
|
+
Coder.fetchKey(wrong_credentials, 'lab_ps', RXTESTAPI)
|
|
96
|
+
.then(data => { })
|
|
97
|
+
.catch(err => {
|
|
98
|
+
expect(err.response?.status.toString().toMatch('403'));
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
describe('fetchDemoKey', () => {
|
|
105
|
+
test('should fetch private key', () => {
|
|
106
|
+
Coder.fetchDemoPrivateKey(CREDENTIALS, RXTESTAPI )
|
|
107
|
+
.then(result => {
|
|
108
|
+
expect(result.private_key.trim()).toBe(PRIVATE_KEY.trim())
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
describe('Phenopackets preprocessor', () => {
|
|
115
|
+
test('converts camelCase to snake_case', () => {
|
|
116
|
+
const snake = Coder.convert_to_snake_case(DEMO_DATA);
|
|
117
|
+
const camel = Coder.convertToCamelCase(snake);
|
|
118
|
+
expect(JSON.stringify(snake).match(/_/g).length).toBe( 30 );
|
|
119
|
+
expect(camel).toEqual(DEMO_DATA);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('sanitizes sex record', () => {
|
|
123
|
+
expect(Coder.sanitizeSex('UNKNOWN_SEX')).toBe(0);
|
|
124
|
+
expect(Coder.sanitizeSex('UNKNOWN')).toBe(0);
|
|
125
|
+
expect(Coder.sanitizeSex('FEMALE')).toBe(1);
|
|
126
|
+
expect(Coder.sanitizeSex('female')).toBe(1);
|
|
127
|
+
expect(Coder.sanitizeSex('MALE')).toBe(2);
|
|
128
|
+
expect(Coder.sanitizeSex('MaLe')).toBe(2);
|
|
129
|
+
expect(Coder.sanitizeSex('OTHER_SEX')).toBe(3);
|
|
130
|
+
expect(Coder.sanitizeSex('OTHER')).toBe(3);
|
|
131
|
+
expect(Coder.sanitizeSex('FlyingSpaghettiMonster')).toBe(0);
|
|
132
|
+
expect(Coder.sanitizeSex('0')).toBe(0);
|
|
133
|
+
expect(Coder.sanitizeSex('1')).toBe(1);
|
|
134
|
+
expect(Coder.sanitizeSex('2')).toBe(2);
|
|
135
|
+
expect(Coder.sanitizeSex('3')).toBe(3);
|
|
136
|
+
expect(Coder.sanitizeSex(0)).toBe(0);
|
|
137
|
+
expect(Coder.sanitizeSex(1)).toBe(1);
|
|
138
|
+
expect(Coder.sanitizeSex(2)).toBe(2);
|
|
139
|
+
expect(Coder.sanitizeSex(3)).toBe(3);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('sanitizes progress status', () => {
|
|
143
|
+
expect(Coder.sanitizeProgState('UNKNOWN_PROGRESS')).toBe(0);
|
|
144
|
+
expect(Coder.sanitizeProgState('unknown_progress')).toBe(0);
|
|
145
|
+
expect(Coder.sanitizeProgState('IN_PROGRESS')).toBe(1);
|
|
146
|
+
expect(Coder.sanitizeProgState('In_PrOgReSs')).toBe(1);
|
|
147
|
+
expect(Coder.sanitizeProgState('COMPLETED')).toBe(2);
|
|
148
|
+
expect(Coder.sanitizeProgState('SOLVED')).toBe(3);
|
|
149
|
+
expect(Coder.sanitizeProgState('UNSOLVED')).toBe(4);
|
|
150
|
+
expect(Coder.sanitizeProgState('Its_Complicated')).toBe(0);
|
|
151
|
+
expect(Coder.sanitizeProgState('0')).toBe(0);
|
|
152
|
+
expect(Coder.sanitizeProgState('1')).toBe(1);
|
|
153
|
+
expect(Coder.sanitizeProgState('2')).toBe(2);
|
|
154
|
+
expect(Coder.sanitizeProgState('3')).toBe(3);
|
|
155
|
+
expect(Coder.sanitizeProgState('4')).toBe(4);
|
|
156
|
+
expect(Coder.sanitizeProgState(0)).toBe(0);
|
|
157
|
+
expect(Coder.sanitizeProgState(1)).toBe(1);
|
|
158
|
+
expect(Coder.sanitizeProgState(2)).toBe(2);
|
|
159
|
+
expect(Coder.sanitizeProgState(3)).toBe(3);
|
|
160
|
+
expect(Coder.sanitizeProgState(4)).toBe(4);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('sanitizes ACMG classification', () => {
|
|
164
|
+
expect(Coder.sanitizeACMG('NOT_PROVIDED')).toBe(0);
|
|
165
|
+
expect(Coder.sanitizeACMG('BENIGN')).toBe(1);
|
|
166
|
+
expect(Coder.sanitizeACMG('benign')).toBe(1);
|
|
167
|
+
expect(Coder.sanitizeACMG('LIKELY_BENIGN')).toBe(2);
|
|
168
|
+
expect(Coder.sanitizeACMG('LIKELY_BENIGN')).toBe(2);
|
|
169
|
+
expect(Coder.sanitizeACMG('UNCERTAIN_SIGNIFICANCE')).toBe(3);
|
|
170
|
+
expect(Coder.sanitizeACMG('LIKELY_PATHOGENIC')).toBe(4);
|
|
171
|
+
expect(Coder.sanitizeACMG('PATHOGENIC')).toBe(5);
|
|
172
|
+
expect(Coder.sanitizeACMG('Excellent ')).toBe(0);
|
|
173
|
+
expect(Coder.sanitizeACMG('Legen(wait for it)dary')).toBe(0);
|
|
174
|
+
expect(Coder.sanitizeACMG('0')).toBe(0);
|
|
175
|
+
expect(Coder.sanitizeACMG('1')).toBe(1);
|
|
176
|
+
expect(Coder.sanitizeACMG('2')).toBe(2);
|
|
177
|
+
expect(Coder.sanitizeACMG('3')).toBe(3);
|
|
178
|
+
expect(Coder.sanitizeACMG('4')).toBe(4);
|
|
179
|
+
expect(Coder.sanitizeACMG('5')).toBe(5);
|
|
180
|
+
expect(Coder.sanitizeACMG(0)).toBe(0);
|
|
181
|
+
expect(Coder.sanitizeACMG(1)).toBe(1);
|
|
182
|
+
expect(Coder.sanitizeACMG(2)).toBe(2);
|
|
183
|
+
expect(Coder.sanitizeACMG(3)).toBe(3);
|
|
184
|
+
expect(Coder.sanitizeACMG(4)).toBe(4);
|
|
185
|
+
expect(Coder.sanitizeACMG(5)).toBe(5);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test('sanitizes phenopackets', () => {
|
|
189
|
+
const data = Coder.sanitizePhenoPacket(DEMO_DATA);
|
|
190
|
+
expect(DEMO_DATA.subject.sex).toBe('FEMALE');
|
|
191
|
+
expect(data.subject.sex).toBe(1);
|
|
192
|
+
expect(DEMO_DATA.interpretations[0].progressStatus).toBe('SOLVED');
|
|
193
|
+
expect(data.interpretations[0].progressStatus).toBe(3);
|
|
194
|
+
expect(DEMO_DATA.interpretations[0].diagnosis.genomicInterpretations[0].variantInterpretation.acmgPathogenicityClassification).toBe('PATHOGENIC');
|
|
195
|
+
expect(data.interpretations[0].diagnosis.genomicInterpretations[0].variantInterpretation.acmgPathogenicityClassification).toBe(5);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('removes non-whitelisted items', () => {
|
|
199
|
+
const blackData = {
|
|
200
|
+
blackList: { badGuys: ['DarthVader', 'Sauron', 'Thanos'] },
|
|
201
|
+
...DEMO_DATA,
|
|
202
|
+
evilData: { name: 'Lore', firstEpisode: "Datalore"}
|
|
203
|
+
}
|
|
204
|
+
expect(Coder.whiteListPhenoPacket( blackData )).toEqual( DEMO_DATA );
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('works with phenopacket without phenotypicFeatures', () => {
|
|
208
|
+
const newData = { ...DEMO_DATA };
|
|
209
|
+
delete newData.phenotypicFeatures;
|
|
210
|
+
expect(Coder.compressPhenoPacket( newData )).toEqual( newData );
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test('compresses phenopackets', () => {
|
|
214
|
+
expect(Coder.compressPhenoPacket(DEMO_DATA)).toEqual(DEMO_DATA_COMPRESSED);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test('codes and decodes protobuf packets', async () => {
|
|
218
|
+
const newData = { ...DEMO_DATA };
|
|
219
|
+
delete newData.credentials;
|
|
220
|
+
const sanData = Coder.sanitizePhenoPacket(newData);
|
|
221
|
+
const protoBuf = Coder.encodePhenoPacket(sanData);
|
|
222
|
+
const phenoPrime = Coder.decodePhenoPacket(protoBuf); //decode inserts ENUM strings
|
|
223
|
+
const pheno = JSON.parse(JSON.stringify(phenoPrime));
|
|
224
|
+
expect(pheno).toEqual(newData);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test('codes and decodes compressed protobuf packets', async () => {
|
|
228
|
+
const newData = { ...DEMO_DATA_COMPRESSED };
|
|
229
|
+
delete newData.credentials;
|
|
230
|
+
const sanData = Coder.sanitizePhenoPacket(newData);
|
|
231
|
+
const protoBuf = Coder.encodePhenoPacket(sanData);
|
|
232
|
+
const phenoPrime = Coder.decodePhenoPacket(protoBuf); //decode inserts ENUM strings
|
|
233
|
+
const pheno = JSON.parse(JSON.stringify(phenoPrime));
|
|
234
|
+
expect(pheno).toEqual(newData);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
describe('Coder', () => {
|
|
240
|
+
test('should encode and decode', async () => {
|
|
241
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
242
|
+
const { privateKey, publicKey } = await Coder.generateRxomeKeys();
|
|
243
|
+
Coder.encode(publicKey, message)
|
|
244
|
+
.then(cipher => Coder.decode(privateKey, cipher))
|
|
245
|
+
.then(clear => expect(clear).toBe(message))
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('should encode and decode binary data', async () => {
|
|
249
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
250
|
+
const { privateKey, publicKey } = await Coder.generateRxomeKeys();
|
|
251
|
+
const textEncoder = new TextEncoder(); // to Uint8Array
|
|
252
|
+
const textDecoder = new TextDecoder(); //default: to utf8
|
|
253
|
+
const binaryMessage = textEncoder.encode( message );
|
|
254
|
+
|
|
255
|
+
Coder.encode( publicKey, binaryMessage, true )
|
|
256
|
+
.then( cipher => Coder.decode(privateKey, cipher, true))
|
|
257
|
+
.then( binaryClear => {
|
|
258
|
+
const clear = textDecoder.decode( binaryClear );
|
|
259
|
+
expect( binaryClear ).toEqual( binaryMessage );
|
|
260
|
+
expect( clear ).toBe( message );
|
|
261
|
+
})
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
test('should encode and decode with key from server', async () => {
|
|
265
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
266
|
+
const keypkg = await Coder.fetchKey(CREDENTIALS, '', RXTESTAPI);
|
|
267
|
+
expect(keypkg.pseudonym).toBe(DEMO_PSEUDONYM);
|
|
268
|
+
const cipher = await Coder.encode(keypkg.key, message);
|
|
269
|
+
const clear = await Coder.decode(PRIVATE_KEY, cipher);
|
|
270
|
+
expect(clear).toBe(message);
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
test('should encode and decode with key files', async () => {
|
|
274
|
+
const message = 'Be Sure To Drink Your Ovaltine';
|
|
275
|
+
await Coder.generateRxomeKeyFiles( 'testsuite' );
|
|
276
|
+
const privateKey = FS.readFileSync('./testsuite.private.key').toString();
|
|
277
|
+
const publicKey = FS.readFileSync('./testsuite.public.key').toString();
|
|
278
|
+
const cipher = await Coder.encode(publicKey, message);
|
|
279
|
+
const clear = await Coder.decode(privateKey, cipher);
|
|
280
|
+
expect(clear).toBe(message);
|
|
281
|
+
})
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
describe('Rails', () => {
|
|
286
|
+
test.skip('should be able to decode cipher', async() => {
|
|
287
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
288
|
+
const cipher = await Coder.encode(PUBLIC_KEY, message);
|
|
289
|
+
|
|
290
|
+
const res = await Axios({
|
|
291
|
+
method: 'GET',
|
|
292
|
+
url: 'http://localhost:3000/api/v1/decryptTest',
|
|
293
|
+
data: {
|
|
294
|
+
cipher: cipher
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
// @TODO: do not send to localhorst
|
|
298
|
+
expect( res.data?.clearText ).toEqual( message )
|
|
299
|
+
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
describe('Serial version of coder', () => {
|
|
305
|
+
test('should encode and decode', async () => {
|
|
306
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
307
|
+
const { privateKey, publicKey } = await Coder.generateRxomeKeys();
|
|
308
|
+
Coder.encode_serial(publicKey, message)
|
|
309
|
+
.then(cipher => Coder.decode_serial(privateKey, cipher))
|
|
310
|
+
.then(clear => expect(clear).toBe(message))
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
test('should encode and decode with key from server', async () => {
|
|
314
|
+
const message = 'A-well-a bird bird bird, bird is the word';
|
|
315
|
+
const keypkg = await Coder.fetchKey(CREDENTIALS, '', RXTESTAPI, false);
|
|
316
|
+
const { private_key } = await Coder.fetchDemoPrivateKey(CREDENTIALS);
|
|
317
|
+
expect(keypkg.pseudonym).toBe(DEMO_PSEUDONYM);
|
|
318
|
+
const cipher = await Coder.encode(keypkg.key, message);
|
|
319
|
+
const clear = await Coder.decode(private_key, cipher);
|
|
320
|
+
expect(clear).toBe(message);
|
|
321
|
+
})
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
describe('QR Code generator', () => {
|
|
326
|
+
test('should make QR code', async () => {
|
|
327
|
+
const newData = { ...DEMO_DATA, metaData: { ...DEMO_DATA.metaData } };
|
|
328
|
+
delete newData.metaData.pseudonym;
|
|
329
|
+
const { qr_code, pseudonym } = await Coder.makeQR(
|
|
330
|
+
newData,
|
|
331
|
+
RXTESTAPI
|
|
332
|
+
);
|
|
333
|
+
//console.log( qr_code.length );
|
|
334
|
+
//expect( qr_code.length ).toBe( 25750 );
|
|
335
|
+
expect(qr_code.startsWith(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiQAAAIkCAYAAAAu8zBwAAA2qElEQVR4AezBQQ4by5LAQFLw/a/MectcF')).toBeTruthy;
|
|
336
|
+
expect(pseudonym).toBe(DEMO_PSEUDONYM);
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
test('should make QR code when given lab ps', async () => {
|
|
340
|
+
const lab_ps = (Math.random() + 1).toString(36).substring(2,12);
|
|
341
|
+
const newData = {
|
|
342
|
+
...DEMO_DATA,
|
|
343
|
+
metaData: {
|
|
344
|
+
...DEMO_DATA.metaData,
|
|
345
|
+
pseudonym: lab_ps
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const { qr_code, pseudonym } = await Coder.makeQR(
|
|
350
|
+
newData,
|
|
351
|
+
RXTESTAPI
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
//expect( qr_code.length ).toBe( 25750 );
|
|
355
|
+
expect(qr_code.startsWith(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiQAAAIkCAYAAAAu8zBwAAA2qElEQVR4AezBQQ4by5LAQFLw/a/MectcF')).toBeTruthy;
|
|
356
|
+
//console.log(qr_code.length);
|
|
357
|
+
expect(pseudonym).toBe( lab_ps );
|
|
358
|
+
})
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
describe('QR Code generator', () => {
|
|
363
|
+
test('codes and decodes encrypted protobuf packets', async () => {
|
|
364
|
+
const newData = { ...DEMO_DATA_COMPRESSED, metaData: { ...DEMO_DATA_COMPRESSED.metaData } };
|
|
365
|
+
const sanitizedMedical = Coder.sanitizePhenoPacket( newData );
|
|
366
|
+
const compressedMedical = Coder.compressPhenoPacket( sanitizedMedical );
|
|
367
|
+
const protobufMedical = Coder.encodePhenoPacket( compressedMedical );
|
|
368
|
+
const protobufMedicalB64 = RxAPI.bufferToBase64( protobufMedical );
|
|
369
|
+
const cipher = await Coder.encode(PUBLIC_KEY, protobufMedicalB64)
|
|
370
|
+
|
|
371
|
+
const clear = await Coder.decode(PRIVATE_KEY, cipher)
|
|
372
|
+
const data = Uint8Array.from([...atob(clear) ].map( c => c.charCodeAt(0)));
|
|
373
|
+
// TODO: why not const data2 = RxAPI.base64ToBuffer( clear );
|
|
374
|
+
const pheno = Coder.decodePhenoPacket(data);
|
|
375
|
+
const phenoJ = JSON.parse(JSON.stringify(pheno));
|
|
376
|
+
delete newData.credentials;
|
|
377
|
+
expect( phenoJ ).toEqual( newData );
|
|
378
|
+
})
|
|
379
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rxome-generator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Generates QR codes containing medical information for use with RxOME database.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "jest --verbose",
|
|
9
|
+
"testcoder": "jest --verbose -t 'Coder'",
|
|
10
|
+
"testpreproc": "jest --verbose -t 'Phenopackets preprocessor'",
|
|
11
|
+
"testror": "jest -t 'Rails'",
|
|
12
|
+
"testcmd": "jest -t 'CmdLine'",
|
|
13
|
+
"testx": "jest --verbose -t 'pseudonyme'"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/GeneTalkTK/rxome-qrcode-generator.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"RxOME",
|
|
21
|
+
"genetics",
|
|
22
|
+
"variants",
|
|
23
|
+
"qr-codes",
|
|
24
|
+
"phenopacket"
|
|
25
|
+
],
|
|
26
|
+
"author": "Tom Kamphans",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/GeneTalkTK/rxome-qrcode-generator/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/GeneTalkTK/rxome-qrcode-generator#readme",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@noble/ed25519": "^1.7.1",
|
|
34
|
+
"axios": "^0.27.1",
|
|
35
|
+
"commander": "^9.4.0",
|
|
36
|
+
"json-key-converter": "^1.0.0",
|
|
37
|
+
"noble-ed25519": "^1.2.6",
|
|
38
|
+
"openpgp": "^5.2.1",
|
|
39
|
+
"protobufjs": "~7.1.0",
|
|
40
|
+
"protobufjs-cli": "^1.0.1",
|
|
41
|
+
"qrcode": "^1.5.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"jest": "^28.0.1",
|
|
45
|
+
"json-loader": "^0.5.7"
|
|
46
|
+
},
|
|
47
|
+
"bin": {
|
|
48
|
+
"rxcode": "./rxcode"
|
|
49
|
+
}
|
|
50
|
+
}
|