rxome-generator 0.1.0 → 0.1.3

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/- ADDED
@@ -0,0 +1 @@
1
+ answer to life the universe and everything
package/README.md CHANGED
@@ -1,7 +1,15 @@
1
- # rxome-qrcode-generator
2
- Generates QR codes containing medical information for use with the RxOME database.
1
+ # FindMe2Care (RxOME) QR-code generator
2
+ Generates QR codes containing medical information for use with the FindMe2Care database
3
+ (formerly called RxOME).
4
+ **Right now, it works only with the test API (-t, Coder.TESTAPI)**
3
5
 
4
- **Right now, it works only with the test API**
6
+ ## LICENSE
7
+
8
+ Copyright (c) 2022 MGZ-Tech GmbH
9
+
10
+ All rights reserved, unauthorized use prohibited.
11
+
12
+ ## Purpose
5
13
 
6
14
  This package offers a JavaScript library for generating QR codes from medical data as well as
7
15
  a command line tool as front end to this library. Both expect the medical data in JSON format
@@ -25,14 +33,19 @@ In case the patient already has a pseudonym that will be used for the QR code,
25
33
  the known pseudonym can be specified in the MetaData section of the input JSON data.
26
34
  Additionally, the command line tool
27
35
  offers a command line argument, `-p`, for specifying a known pseudonym.
36
+ Note that this pseudonym must be a valid RxOME pseudonym, that is, it has to be generated by
37
+ RxOME for a previous medical statement. Using an arbitrary pseudonym will render the
38
+ generated QR-Code useless, as it cannot be processed by RxOME.
28
39
 
29
40
  By default, the keywords in the JSON file are expected to be noted in camelCase. However, the tool
30
41
  can convert snake_case to camelCase (command line: -s, library: function convertToCamelCase).
31
42
 
43
+ ## Installation
44
+ > `npm install rxome-generator`
32
45
 
33
46
  ## Basic Usage
34
47
 
35
- ###Command Line Tool
48
+ ### Command Line Tool
36
49
 
37
50
  Generate a QR code *inputfile*.png from a JSON file *inputfile*.json containing all medical data in PhenoPacket format, meta data and credentials (using camelCase for keywords):
38
51
 
@@ -41,7 +54,7 @@ Generate a QR code *inputfile*.png from a JSON file *inputfile*.json containing
41
54
  For detailed descriptions see
42
55
  > `rxcode g --help`
43
56
 
44
- ###Library Functions
57
+ ### Library Functions
45
58
  Import the library with
46
59
  > `const Coder = require( 'rxome-generator' );`
47
60
 
@@ -94,7 +107,8 @@ Pleace specify *either* a file containing the API access key (keyFile, -k)
94
107
  *or* the key itself (key, -s).
95
108
 
96
109
  When a pseudonym is given (either in the meta data or with command line option `-P`),
97
- the QR code will be generated using this pseudonym. Otherwise, a new one will be
110
+ the QR code will be generated using this pseudonym (this must be a valid/known RxOME
111
+ pseudonym, see introduction). Otherwise, a new one will be
98
112
  fetched from the server. In both cases, the
99
113
  pseudonym used will be part of the output for futher processing or storing.
100
114
 
@@ -103,7 +117,7 @@ pseudonym used will be part of the output for futher processing or storing.
103
117
  ...
104
118
  metaData: {
105
119
  ...
106
- pseudonym: 'anonymous'
120
+ pseudonym: '19T5K7042'
107
121
  }
108
122
  credentials: {
109
123
  keyId: <lab-id/key-id, corresponding to private key>
@@ -172,20 +186,21 @@ The type of genetic test performed to obtain a variant can be specified in an ex
172
186
  "acmgPathogenicityClassification": "Pathogenic",
173
187
  "variationDescriptor": {
174
188
  "geneContext": {
175
- "expressions": [
176
- {
177
- "syntax": "hgvs.c",
178
- "value": "NM_017837.4(PIGV):c.1022C>A (p.Ala341Glu)"
179
- }
180
- ],
181
- "allelicState": {
182
- "id": "GENO_0000136"
183
- },
184
- "extensions": [
185
- {
186
- "name": "Single gene sequencing"
187
- }
188
- ]
189
+ "expressions": [
190
+ {
191
+ "syntax": "hgvs.c",
192
+ "value": "NM_017837.4(PIGV):c.1022C>A (p.Ala341Glu)"
193
+ }
194
+ ],
195
+ "allelicState": {
196
+ "id": "GENO_0000136"
197
+ },
198
+ "extensions": [
199
+ {
200
+ "name": "Single gene sequencing"
201
+ }
202
+ ]
203
+ }
189
204
  }
190
205
  }
191
206
  }
@@ -195,7 +210,7 @@ The type of genetic test performed to obtain a variant can be specified in an ex
195
210
 
196
211
  ### Additional Remarks
197
212
 
198
- Additional remarks can be specified in a *comment* field in the top level:
213
+ Additional remarks can be specified in a *comment* field on the top level:
199
214
 
200
215
  ```
201
216
  {
@@ -282,7 +297,7 @@ Options:
282
297
  -u, --user <user string> API access user (default: credentials.user
283
298
  or metaData.submittedBy or info@rxome.net)
284
299
  -c, --created <date> Date (default: input file, metaData.created)
285
- -l, --lab <lab> Laboratory name (default: input file, metaData.createdBy)
300
+ -l, --lab <lab> Laboratory name (default: input file, metaData.createdBy or lab name stored in the user account)
286
301
  -e, --email <email> Laboratory email (default: input file, metaData.submittedBy)
287
302
  -S, --snake Read payload formatted in snake_case (default: camelCase)
288
303
  -t, --test Use test API instead of production API
@@ -312,6 +327,11 @@ rxcode g -t -o qrcode.png demos/demo_data_full.json
312
327
 
313
328
  <img src="qrcode.png" width="400">
314
329
 
330
+ ## Testing
331
+ Codes generated by the test API (i.e., with the `-t` switch or `API=Coder.TESTAPI`) can be decoded online using the following tool:
332
+
333
+ Todo: URL Code-Reader Demo
334
+
315
335
  ## Debugging
316
336
  To check the connection to the API on RxOME server API use
317
337
 
@@ -139,11 +139,12 @@
139
139
  "created": "2021-05-14T10:35:00Z",
140
140
  "createdBy": "mgz",
141
141
  "submittedBy": "a_clinician@mgz-muenchen.de",
142
- "phenopacketSchemaVersion": "2.0"
142
+ "phenopacketSchemaVersion": "2.0",
143
+ "pseudonym": "_DEMO_PSEUDONYM_"
143
144
  },
144
145
  "credentials": {
145
- "key": "QhcoRruGBVP39XCh8BujCE+q42qCRy/tu2CQ4YmRBgg=",
146
- "keyId": "mgz",
146
+ "key": "lBSkSxe/+UBWOeF5OJdQgf9qZhiI85hYE6yJCuWjCNk=",
147
+ "keyId": "rxome",
147
148
  "user": "a_clinician@mgz-muenchen.de"
148
149
  }
149
150
  }
@@ -0,0 +1,100 @@
1
+ "subject": {
2
+ "id": "proband A",
3
+ "dateOfBirth": "1994-01-01T00:00:00Z",
4
+ "sex": "FEMALE"
5
+ },
6
+ "phenotypicFeatures":
7
+ [HPOin]
8
+ HP:0030084
9
+ HP:0000555
10
+ HP:0000486
11
+ HP:0000541
12
+ HP:0084369
13
+ HP:0112358
14
+ HP:0000145
15
+ HP:1234567
16
+ HP:9876543
17
+ HP:5678912
18
+
19
+ [HPOex]
20
+ HP:0031360
21
+ HP:0001234
22
+
23
+ "interpretations": [
24
+ {
25
+ "id": "interpretation.id",
26
+ "progressStatus": "SOLVED",
27
+ "diagnosis": {
28
+ "disease": {
29
+ "id": "OMIM:263750"
30
+ },
31
+ "genomicInterpretations": [
32
+ {
33
+ "variantInterpretation": {
34
+ "acmgPathogenicityClassification": "PATHOGENIC",
35
+ "variationDescriptor": {
36
+ "geneContext": {
37
+ "valueId": "HGNC:9884",
38
+ "symbol": "RB1"
39
+ },
40
+ "expressions": [
41
+ {
42
+ "syntax": "hgvs.c",
43
+ "value": "NM_000321.2:c.958C>T"
44
+ }
45
+ ],
46
+ "allelicState": {
47
+ "id": "GENO:0000135"
48
+ },
49
+ "extensions": [
50
+ {
51
+ "name": "test-type",
52
+ "value": "Exome, short read"
53
+ }
54
+ ]
55
+ }
56
+ }
57
+ },
58
+ {
59
+ "variantInterpretation": {
60
+ "acmgPathogenicityClassification": "LIKELY_PATHOGENIC",
61
+ "variationDescriptor": {
62
+ "geneContext": {
63
+ "valueId": "HGNC:9884",
64
+ "symbol": "RB1"
65
+ },
66
+ "expressions": [
67
+ {
68
+ "syntax": "hgvs.c",
69
+ "value": "NM_000321.2:c.1234A>G"
70
+ }
71
+ ],
72
+ "allelicState": {
73
+ "label": "heterozygous"
74
+ },
75
+ "extensions": [
76
+ {
77
+ "name": "test-type",
78
+ "value": "Exome, short read"
79
+ }
80
+ ]
81
+ }
82
+ }
83
+ }
84
+ ]
85
+ }
86
+ }
87
+ ],
88
+ "metaData": {
89
+ "created": "2021-05-14T10:35:00Z",
90
+ "createdBy": "mgz",
91
+ "submittedBy": "a_clinician@mgz-muenchen.de",
92
+ "phenopacketSchemaVersion": "2.0",
93
+ "pseudonym": "_DEMO_PSEUDONYM_"
94
+ },
95
+ "credentials": {
96
+ "key": "lBSkSxe/+UBWOeF5OJdQgf9qZhiI85hYE6yJCuWjCNk=",
97
+ "keyId": "rxome",
98
+ "user": "a_clinician@mgz-muenchen.de"
99
+ }
100
+ }
@@ -51,7 +51,8 @@
51
51
  "created": "2021-05-14T10:35:00Z",
52
52
  "createdBy": "mgz",
53
53
  "submittedBy": "a_clinician@mgz-muenchen.de",
54
- "phenopacketSchemaVersion": "2.0"
54
+ "phenopacketSchemaVersion": "2.0",
55
+ "pseudonym": "_DEMO_PSEUDONYM_"
55
56
  },
56
57
  "compressedFeatures": {
57
58
  "includes": [
@@ -69,8 +70,8 @@
69
70
  "excludes": ["HP:0031360", "HP:0001234"]
70
71
  },
71
72
  "credentials": {
72
- "key": "QhcoRruGBVP39XCh8BujCE+q42qCRy/tu2CQ4YmRBgg=",
73
- "keyId": "mgz",
73
+ "key": "lBSkSxe/+UBWOeF5OJdQgf9qZhiI85hYE6yJCuWjCNk=",
74
+ "keyId": "rxome",
74
75
  "user": "a_clinician@mgz-muenchen.de"
75
76
  }
76
77
  }
@@ -0,0 +1 @@
1
+ {"key":"\n-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nxYYEY8BGEhYJKwYBBAHaRw8BAQdAegfXQCYheo+FJXoIY0qPYAeP+67inq0u\n0AZE8yrEOJ3+CQMIQ3S9CuD3PUTgKF/q8xafzIvLlEuLTCAInll8oSg9zb+2\nMTFp3EEOZifKIA9YY1ujro17MzR3p/Bfdt2ayo+Pcxq0g9JNgrHfzUeIuTxG\nxM0VZGVtbyA8aW5mb0ByeG9tZS5uZXQ+wowEEBYKAB0FAmPARhIECwkHCAMV\nCAoEFgACAQIZAQIbAwIeAQAhCRABSM3I8Z0TohYhBGfCy2DdehZ+Frs2fgFI\nzcjxnROi9ukA/RmZoF4VKDJouTjxPxCEzIqbM+9ZfHFyLmkr9EtMR0D+AP9o\n5Rauo9ium/t88qxfeCpcPTULZ4qu5GBkBHg9XCYrAceLBGPARhISCisGAQQB\nl1UBBQEBB0A82B97nnZ60gGWR2v8mn319Yb8AdKGD85ier7hwSe/OgMBCAf+\nCQMIUzDpTTSoyZrg8AVwyRZVmrXQbE9mzRtcy2sM3fbNyZCW44Rz0rgxwC+z\n1EfRC442wQSDs6pat0eeBX1Eh6vqENIRsCQNK426BD8XWWLor8J4BBgWCAAJ\nBQJjwEYSAhsMACEJEAFIzcjxnROiFiEEZ8LLYN16Fn4WuzZ+AUjNyPGdE6In\nsAD9GNlDrmnRIz+IP0XGheue6IhMLgzss2TsvRv2K8HQGqsBAIC3k3/T/sCN\nMnR+TVPz/zYEEAbxEEurgfWdn1c8B0AM\n=Sgm6\n-----END PGP PRIVATE KEY BLOCK-----\n","cipher":"\n-----BEGIN PGP MESSAGE-----\n\nwV4DwTe9/0b1zzgSAQdAxe4Phva3n19MInlPhQS4OxG78y+3mTsNHVfsk5OF\nnGAwld5yaBCzLoudwGLQ17qIkABl9rhONf8VJMNoTOZOf8nPoouCKdXxktv7\ndG1wvV2s0lsBvUBUmaOzix1hEF6YeUr7mfc+MBBt/2gfoyT4Kujgrg25YoZ6\nqUgqga2eSZ6O+OhyjuFo3rZVXl1MFY6lfu+X+i4cCE2VWiAQOwVNnqFdY9FF\nuOPuzhz7UstK\n=KKxC\n-----END PGP MESSAGE-----\n"}
@@ -0,0 +1 @@
1
+ {"key":"\n-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxjMEY8BGEhYJKwYBBAHaRw8BAQdAegfXQCYheo+FJXoIY0qPYAeP+67inq0u\n0AZE8yrEOJ3NFWRlbW8gPGluZm9AcnhvbWUubmV0PsKMBBAWCgAdBQJjwEYS\nBAsJBwgDFQgKBBYAAgECGQECGwMCHgEAIQkQAUjNyPGdE6IWIQRnwstg3XoW\nfha7Nn4BSM3I8Z0TovbpAP0ZmaBeFSgyaLk48T8QhMyKmzPvWXxxci5pK/RL\nTEdA/gD/aOUWrqPYrpv7fPKsX3gqXD01C2eKruRgZAR4PVwmKwHOOARjwEYS\nEgorBgEEAZdVAQUBAQdAPNgfe552etIBlkdr/Jp99fWG/AHShg/OYnq+4cEn\nvzoDAQgHwngEGBYIAAkFAmPARhICGwwAIQkQAUjNyPGdE6IWIQRnwstg3XoW\nfha7Nn4BSM3I8Z0ToiewAP0Y2UOuadEjP4g/RcaF657oiEwuDOyzZOy9G/Yr\nwdAaqwEAgLeTf9P+wI0ydH5NU/P/NgQQBvEQS6uB9Z2fVzwHQAw=\n=w+2C\n-----END PGP PUBLIC KEY BLOCK-----\n","message":"answer to life the universe and everything"}
package/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export * from './lib/rxome-api';
2
- export * from './lib/rxome-api-demo';
3
- export * from './lib/rxome-generator';
1
+ export * from './lib/rxome-api.cjs';
2
+ export * from './lib/rxome-api-demo.cjs';
3
+ export * from './lib/rxome-generator.cjs';
@@ -35,8 +35,10 @@ VBvfFVhJAPoDhf5yKxJ+oJV3ls8IyoBBAeMeAYFBrA8r89fKT0nFBA==
35
35
  `
36
36
 
37
37
  const R_ID = 'rxome';
38
- const R_PRIVATE_KEY = 'NamaTB+xwDFxtkQyBBkjRr5GEaXNtCw/G4qydnhQk5Y=';
39
- const R_PUBLIC_KEY = 'XvbhLWKbA1wfKsx3B7FKQuDQsZTZ/dMXWiD1MehBxZg=';
38
+ const R_PUBLIC_KEY = '60uReCXTn7KTEIExM4KveKstBGI3TaSrQss4biaesNs=';
39
+ const R_PRIVATE_KEY = 'lBSkSxe/+UBWOeF5OJdQgf9qZhiI85hYE6yJCuWjCNk=';
40
+ // const R_PRIVATE_KEY = 'NamaTB+xwDFxtkQyBBkjRr5GEaXNtCw/G4qydnhQk5Y=';
41
+ // const R_PUBLIC_KEY = 'XvbhLWKbA1wfKsx3B7FKQuDQsZTZ/dMXWiD1MehBxZg=';
40
42
 
41
43
  const J_ID = 'rxomej'
42
44
  const J_PRIVATE_KEY = 'QhcoRruGBVP39XCh8BujCE+q42qCRy/tu2CQ4YmRBgg=';
@@ -1,16 +1,20 @@
1
1
  const Axios = require( 'axios' );
2
2
  const ED = require( 'noble-ed25519' );
3
3
  const Protobuf = require('protobufjs');
4
+ const { stringify } = require('querystring');
4
5
  //const BASE64 = require('@protobufjs/base64')
5
6
 
6
7
  const API = 'https://www.rxome.net';
7
- const TESTAPI = 'https://testapi.gene-talk.de';
8
+ const TESTAPI = 'http://stage.rxome.net';
8
9
  const APIENTRY = 'api/v1';
9
10
  const VSTR = 'API1.0'
11
+ const IS_DEMO = '_DEMO_PSEUDONYM_';
12
+
10
13
  exports.API = API;
11
14
  exports.TESTAPI = TESTAPI;
12
15
  exports.APIENTRY = APIENTRY;
13
16
  exports.VSTR = VSTR;
17
+ exports.IS_DEMO = IS_DEMO;
14
18
 
15
19
  /************************************************************************************
16
20
  * Helper functions
@@ -70,6 +74,8 @@ catch (e) {
70
74
  }
71
75
  }
72
76
 
77
+ exports.readSigKey = readSigKey;
78
+
73
79
  /************************************************************************************/
74
80
 
75
81
  exports.signData = async( keyId, user, keyB64, created, debug = false ) => {
@@ -97,13 +103,15 @@ exports.fetchData = async ( url, credentials, pseudonym = '', debug = false ) =>
97
103
  const user = credentials.user || `${keyId}@rxome.net`;
98
104
  const keyB64 = credentials.key || readSigKey( process.cwd()+'/'+credentials.keyFile );
99
105
 
100
- auth = await exports.signData( keyId, user, keyB64, created, debug );
106
+ const auth = await exports.signData( keyId, user, keyB64, created, debug );
101
107
 
108
+ FS = require( 'fs' );
102
109
  return Axios({
103
110
  url: url,
104
111
  method: 'GET',
105
112
  params: {
106
- pslab: !!pseudonym.trim()
113
+ pslab: !!pseudonym.trim(),
114
+ demo: pseudonym === IS_DEMO
107
115
  },
108
116
  headers: {
109
117
  Authorization: auth,
@@ -4,16 +4,16 @@ const ED = require( 'noble-ed25519' );
4
4
 
5
5
  describe('API access', () => {
6
6
  test.skip('generates valid API access keys', async () => {
7
- await RxAPI.generateApiKeys( 'jesttest' );
8
- expect( FS.existsSync('jesttest.private.apikey') ).toBe( true );
9
- expect( FS.existsSync('jesttest.public.apikey') ).toBe( true );
10
- expect( FS.statSync('jesttest.private.apikey').size - 44 ).toBeLessThan( 2 );
11
- expect( FS.statSync('jesttest.public.apikey').size - 44 ).toBeLessThan( 2 );
7
+ await RxAPI.generateApiKeys( '__TESTSUITE_jesttest' );
8
+ expect( FS.existsSync('__TESTSUITE_jesttest.private.apikey') ).toBe( true );
9
+ expect( FS.existsSync('__TESTSUITE_jesttest.public.apikey') ).toBe( true );
10
+ expect( FS.statSync('__TESTSUITE_jesttest.private.apikey').size - 44 ).toBeLessThan( 2 );
11
+ expect( FS.statSync('__TESTSUITE_jesttest.public.apikey').size - 44 ).toBeLessThan( 2 );
12
12
 
13
13
  const message='Answer to life the universe and everything';
14
14
  const messageUi8 = RxAPI.unpack(Array.from(message));
15
- const privKey = RxAPI.unpack([...RxAPI.base64ToBuffer( FS.readFileSync('jesttest.private.apikey'))])
16
- const pubKey = RxAPI.unpack([...RxAPI.base64ToBuffer( FS.readFileSync('jesttest.public.apikey'))])
15
+ const privKey = RxAPI.unpack([...RxAPI.base64ToBuffer( FS.readFileSync('__TESTSUITE_jesttest.private.apikey'))])
16
+ const pubKey = RxAPI.unpack([...RxAPI.base64ToBuffer( FS.readFileSync('__TESTSUITE_jesttest.public.apikey'))])
17
17
  const signature = await ED.sign(messageUi8, privKey);
18
18
  const isValid = await ED.verify(signature, messageUi8, pubKey);
19
19
  expect( isValid ).toBeTruthy;
@@ -10,7 +10,7 @@ const PhenoPacketDescriptor = require("./phenopackets.json");
10
10
 
11
11
  //const { constants } = require('buffer');
12
12
 
13
- const RxAPI= require('./rxome-api');
13
+ const RxAPI= require('./rxome-api.cjs');
14
14
 
15
15
  const apiVer = '1.0';
16
16
  const RXAPI = RxAPI.API;
@@ -55,7 +55,7 @@ try {
55
55
  const {qrData, pseudonym} = await exports.prepareQR( data, api, apiEntry );
56
56
  //const base64Data = qr_code.replace(/^data:image\/png;base64,/, "");
57
57
  //FS.writeFile(filename, base64Data, 'base64', (err) => {console.log(err)} );
58
- QRCode.toFile( filename, qrData, { type: 'png'} )
58
+ QRCode.toFile( filename, JSON.stringify( qrData ), { type: 'png'} )
59
59
  return pseudonym
60
60
  }
61
61
  }
@@ -291,13 +291,12 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
291
291
  const key = await this.fetchKey( credentials, metaData.pseudonym || '', api, false, apiEntry );
292
292
 
293
293
  // check:
294
- const buff = RxAPI.base64ToBuffer( base64Medical );
295
- const pheno = exports.decodePhenoPacket( buff );
294
+ //const buff = RxAPI.base64ToBuffer( base64Medical );
295
+ //const pheno = exports.decodePhenoPacket( buff );
296
296
  //const medicalDeciphered = JSON.parse( JSON.stringify( pheno ));
297
297
  //console.log( JSON.stringify(medicalDeciphered,' ', 2 ))
298
298
 
299
299
  const cipher = await exports.encode( key.key, base64Medical );
300
- //console.log( 'Cipher:', JSON.stringify(cipher) );
301
300
 
302
301
  //delete metaData.pseudonym;
303
302
  const newMetaData = Object.fromEntries(
@@ -305,17 +304,18 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
305
304
  )
306
305
 
307
306
  const qrData = {
307
+ createdBy: key.lab,
308
308
  ...newMetaData,
309
+ labid: key.lab_id,
309
310
  keyver: key.version,
310
311
  apiver: apiVer,
311
312
  pseudonym: key.pseudonym,
312
313
  payload: cipher.toString()
313
314
  }
314
- // console.log( qrData );
315
- // console.log( "QR-Data " , JSON.stringify( qrData ).length );
315
+ console.log( "[QR Generator lib ]", qrData, "\nLength: ", JSON.stringify( qrData ).length );
316
316
 
317
317
  return {
318
- qrData: JSON.stringify( qrData ),
318
+ qrData: qrData,
319
319
  pseudonym: key.pseudonym
320
320
  }
321
321
  }
@@ -324,7 +324,7 @@ exports.prepareQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
324
324
  exports.makeQR = async ( data, api = RXAPI, apiEntry = APIENTRY ) => {
325
325
  const {qrData, pseudonym} = await exports.prepareQR( data, api, apiEntry );
326
326
  return {
327
- qr_code: await QRCode.toDataURL( qrData ),
327
+ qr_code: await QRCode.toDataURL( JSON.stringify( qrData )),
328
328
  pseudonym: pseudonym,
329
329
  qr_data: qrData
330
330
  }
@@ -7,7 +7,8 @@ const Axios = require( 'axios' );
7
7
 
8
8
  const RXAPI = RxAPI.API;
9
9
  const RXTESTAPI = RxAPI.TESTAPI;
10
- const RXLOCALAPI = 'http:localhost:3000';
10
+ // const RXTESTAPI = 'http://localhost:3000';
11
+ const RXLOCALAPI = 'http://localhost:3000';
11
12
 
12
13
  const CREDENTIALS = {
13
14
  id: ApiDemo.DEMO_API_ID,
@@ -28,13 +29,15 @@ const PUBLIC_KEY = ApiDemo.CRYPT_PUBLIC_KEY;
28
29
  const DEMO_API_PRIVATE_KEY = ApiDemo.DEMO_API_PRIVATE_KEY;
29
30
  const DEMO_API_PUBLIC_KEY = ApiDemo.API_PUBLIC_KEY;
30
31
 
32
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
33
+
31
34
  describe('API access', () => {
32
35
 
33
36
  test('is able to connect to API', async () => {
34
- await expect( Coder.fetchKey(CREDENTIALS, '', RXTESTAPI) ).resolves.not.toThrowError();
37
+ await expect( Coder.fetchKey(CREDENTIALS, RxAPI.IS_DEMO, RXTESTAPI) ).resolves.not.toThrowError();
35
38
  });
36
39
 
37
- // test('should fetch key and pseudonyms', async () => {
40
+ // test.skip('should fetch key and pseudonyms', async () => {
38
41
  // Coder.fetchKey(CREDENTIALS, '', RXTESTAPI)
39
42
  // .then( result => {
40
43
  // //console.log( PUBLIC_KEY )
@@ -46,7 +49,7 @@ describe('API access', () => {
46
49
  // });
47
50
 
48
51
  test('should fetch key and pseudonyms', () => {
49
- Coder.fetchKey(CREDENTIALS, '', RXTESTAPI)
52
+ Coder.fetchKey(CREDENTIALS, RxAPI.IS_DEMO, RXTESTAPI)
50
53
  .then(result => {
51
54
  expect(result.pseudonym).toBe(DEMO_PSEUDONYM);
52
55
  expect(result.key.trim()).toBe(PUBLIC_KEY.trim());
@@ -63,7 +66,7 @@ describe('API access', () => {
63
66
 
64
67
  // error handling:
65
68
  test('should raise an error when url is wrong', () => {
66
- Coder.fetchKey(CREDENTIALS, '', 'https://testapi.gene-talk.de/api/v1/schnitzel/', false, 'fehler')
69
+ Coder.fetchKey(CREDENTIALS, '', RXTESTAPI, false, '/api/v1/errortest')
67
70
  .then(data => { })
68
71
  .catch(err => {
69
72
  expect(err.message).toMatch('404');
@@ -106,7 +109,8 @@ describe('fetchDemoKey', () => {
106
109
  Coder.fetchDemoPrivateKey(CREDENTIALS, RXTESTAPI )
107
110
  .then(result => {
108
111
  expect(result.private_key.trim()).toBe(PRIVATE_KEY.trim())
109
- });
112
+ })
113
+ .catch( err => { process.stderr.write( JSON.stringify( err ))});
110
114
  });
111
115
  });
112
116
 
@@ -115,7 +119,7 @@ describe('Phenopackets preprocessor', () => {
115
119
  test('converts camelCase to snake_case', () => {
116
120
  const snake = Coder.convert_to_snake_case(DEMO_DATA);
117
121
  const camel = Coder.convertToCamelCase(snake);
118
- expect(JSON.stringify(snake).match(/_/g).length).toBe( 30 );
122
+ expect(JSON.stringify(snake).match(/_/g).length).toBe( 33 );
119
123
  expect(camel).toEqual(DEMO_DATA);
120
124
  });
121
125
 
@@ -263,7 +267,7 @@ describe('Coder', () => {
263
267
 
264
268
  test('should encode and decode with key from server', async () => {
265
269
  const message = 'A-well-a bird bird bird, bird is the word';
266
- const keypkg = await Coder.fetchKey(CREDENTIALS, '', RXTESTAPI);
270
+ const keypkg = await Coder.fetchKey(CREDENTIALS, RxAPI.IS_DEMO, RXTESTAPI);
267
271
  expect(keypkg.pseudonym).toBe(DEMO_PSEUDONYM);
268
272
  const cipher = await Coder.encode(keypkg.key, message);
269
273
  const clear = await Coder.decode(PRIVATE_KEY, cipher);
@@ -283,20 +287,18 @@ describe('Coder', () => {
283
287
 
284
288
 
285
289
  describe('Rails', () => {
286
- test.skip('should be able to decode cipher', async() => {
290
+ test('should be able to decode cipher', async() => {
287
291
  const message = 'A-well-a bird bird bird, bird is the word';
288
292
  const cipher = await Coder.encode(PUBLIC_KEY, message);
289
293
 
290
294
  const res = await Axios({
291
- method: 'GET',
292
- url: 'http://localhost:3000/api/v1/decryptTest',
295
+ method$: 'GET',
296
+ url: `${RXTESTAPI}/api/v1/decryptTest`,
293
297
  data: {
294
298
  cipher: cipher
295
299
  }
296
300
  })
297
- // @TODO: do not send to localhorst
298
301
  expect( res.data?.clearText ).toEqual( message )
299
-
300
302
  });
301
303
  });
302
304
 
@@ -312,8 +314,10 @@ describe('Serial version of coder', () => {
312
314
 
313
315
  test('should encode and decode with key from server', async () => {
314
316
  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
+ const keypkg = await Coder.fetchKey(CREDENTIALS, RxAPI.IS_DEMO, RXTESTAPI, false);
318
+ const { private_key } = await Coder.fetchDemoPrivateKey(CREDENTIALS, RXTESTAPI)
319
+ //.then( data => {console.log( data )})
320
+ //.catch( err => { process.stderr.write( JSON.stringify( err ))})
317
321
  expect(keypkg.pseudonym).toBe(DEMO_PSEUDONYM);
318
322
  const cipher = await Coder.encode(keypkg.key, message);
319
323
  const clear = await Coder.decode(private_key, cipher);
@@ -326,14 +330,17 @@ describe('QR Code generator', () => {
326
330
  test('should make QR code', async () => {
327
331
  const newData = { ...DEMO_DATA, metaData: { ...DEMO_DATA.metaData } };
328
332
  delete newData.metaData.pseudonym;
329
- const { qr_code, pseudonym } = await Coder.makeQR(
333
+ //const { qr_code, pseudonym } = await
334
+ Coder.makeQR(
330
335
  newData,
331
336
  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
+ .then( data => {
339
+ expect( data.qr_code.startsWith(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiQAAAIkCAYAAAAu8zBwAAA2qElEQVR4AezBQQ4by5LAQFLw/a/MectcF')).toBeTruthy;
340
+ expect( data.pseudonym ).toBe(DEMO_PSEUDONYM);
341
+ expect( data.qr_data ).toBe( newData );
342
+ })
343
+ .catch( err => { process.stderr.write( JSON.stringify( err ))})
337
344
  })
338
345
 
339
346
  test('should make QR code when given lab ps', async () => {
@@ -350,8 +357,6 @@ describe('QR Code generator', () => {
350
357
  newData,
351
358
  RXTESTAPI
352
359
  );
353
-
354
- //expect( qr_code.length ).toBe( 25750 );
355
360
  expect(qr_code.startsWith(' data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAiQAAAIkCAYAAAAu8zBwAAA2qElEQVR4AezBQQ4by5LAQFLw/a/MectcF')).toBeTruthy;
356
361
  //console.log(qr_code.length);
357
362
  expect(pseudonym).toBe( lab_ps );
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "rxome-generator",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Generates QR codes containing medical information for use with RxOME database.",
5
5
  "main": "index.js",
6
- "type": "commonjs",
6
+ "type": "module",
7
7
  "scripts": {
8
8
  "test": "jest --verbose",
9
9
  "testcoder": "jest --verbose -t 'Coder'",
@@ -24,7 +24,7 @@
24
24
  "phenopacket"
25
25
  ],
26
26
  "author": "Tom Kamphans",
27
- "license": "MIT",
27
+ "license" : "SEE LICENSE IN README.md",
28
28
  "bugs": {
29
29
  "url": "https://github.com/GeneTalkTK/rxome-qrcode-generator/issues"
30
30
  },
@@ -1,13 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const Coder = require( './lib/rxome-generator' );
4
- const ApiDemo = require( './lib/rxome-api-demo' );
5
- const RxAPI = require( './lib/rxome-api' );
6
- const { program } = require('commander');
7
-
8
- const FS = require( 'fs' );
9
- const Path = require('path');
10
- const { cp } = require('fs/promises');
3
+ import * as FS from 'fs';
4
+ // const Coder = require( './lib/rxome-generator' );
5
+ import * as Coder from './lib/rxome-generator.cjs';
6
+ import * as ApiDemo from './lib/rxome-api-demo.cjs' ;
7
+ import * as RxAPI from './lib/rxome-api.cjs';
8
+
9
+ import { program } from 'commander';
10
+ import * as Path from 'path';
11
+ // import { cp } from 'fs/promises';
12
+
13
+ // const FS = require( 'fs' );
14
+ // const Coder = require( './lib/rxome-generator' );
15
+ // const ApiDemo = require( './lib/rxome-api-demo' );
16
+ // const RxAPI = require( './lib/rxome-api' );
17
+ // const { program } = require('commander');
18
+
19
+ // const Path = require('path');
20
+ // const { cp } = require('fs/promises');
21
+
11
22
  //const TXT = require( './assets/scripts/modules/texte.js')
12
23
 
13
24
  const DEMO_CREDENTIALS = ApiDemo.DEMO_CREDENTIALS
@@ -42,10 +53,11 @@ Output: prints the given or new pseudonym.`)
42
53
  .option('-s, --key <key string>', 'API access key (default: input file, credentials.key)')
43
54
  .option('-u, --user <user string>', 'API access user (default: credentials.user or metaData.submittedBy or info@rxome.net)')
44
55
  .option('-c, --created <date>', 'Date (default: input file, metaData.created)')
45
- .option('-l, --lab <lab>', 'Laboratory name (default: input file, metaData.createdBy)')
56
+ .option('-l, --lab <lab>', 'Laboratory name (default: input file, metaData.createdBy or lab name stored in the user account)')
46
57
  .option('-e, --email <email>', 'Laboratory email (default: input file, metaData.submittedBy)')
47
58
  .option('-S, --snake', 'Read payload formatted in snake_case (default: camelCase)')
48
59
  .option('-t, --test', 'Use test API instead of production API')
60
+ .option('-L, --localhost', 'Connect to localhost API')
49
61
  .option('-D, --debug', 'Some output for debugging')
50
62
  .action( async (inputfile, options) => {
51
63
  const rawdata = FS.readFileSync( inputfile || '/dev/stdin' );
@@ -57,7 +69,9 @@ Output: prints the given or new pseudonym.`)
57
69
  options.created && (qrData.metaData.created = options.created);
58
70
  options.email && (qrData.metaData.submittedBy = options.email);
59
71
  options.pseudonym && (qrData.metaData.pseudonym = options.pseudonym);
60
- const qrApi = options.test ? RxAPI.TESTAPI : RxAPI.API;
72
+ let qrApi = RxAPI.API;
73
+ options.test && (qrApi = RxAPI.TESTAPI);
74
+ options.localhost && (qrApi = 'http://localhost:3000/');
61
75
 
62
76
  ('credentials' in qrData) || (qrData.credentials = {});
63
77
  if ( options.key ) {
@@ -91,7 +105,7 @@ program.command('convert')
91
105
  .alias('c')
92
106
  .description('convert case style of keys in JSON files from snake_case to camelCase (and vice versa)')
93
107
  .argument('[input file]', 'Input JSON file (default: STDIN)')
94
- .option('-o, --output <output file>', 'Output JSON file (default: stdout)', '/dev/stdout')
108
+ .option('-o, --output <output file>', 'Output JSON file (default: stdout)')
95
109
  .option('-s, --snake', 'Convert to snake_case (default: convert to camelCase')
96
110
  .action( async (inputfile, options) => {
97
111
  const data = JSON.parse(FS.readFileSync( inputfile || '/dev/stdin' ));
@@ -101,7 +115,11 @@ program.command('convert')
101
115
  } else {
102
116
  newData = Coder.convertToCamelCase( data );
103
117
  }
104
- FS.writeFileSync( options.output, JSON.stringify( newData ))
118
+ if ( options.output ) {
119
+ const stream = FS.createWriteStream( options.output );
120
+ process.stdout.write = stream.write.bind( stream );
121
+ }
122
+ process.stdout.write( JSON.stringify( newData ) );
105
123
  });
106
124
 
107
125
 
@@ -109,10 +127,10 @@ program.command('preprocess')
109
127
  .alias('p')
110
128
  .description('perform preprocessing steps')
111
129
  .argument('[input file]', 'Input JSON file (default: STDIN)')
112
- .option('-o, --output <output file>', 'Output JSON file (default: stdout)', '/dev/stdout')
130
+ .option('-o, --output <output file>', 'Output JSON file (default: stdout)')
113
131
  .option('-C, --case', 'Apply case style converter from snake_case to camelCase')
114
132
  .option('-w, --whitelist', 'Apply whitelist filtering (remove unnecessary sections)')
115
- .option('-s, --sanitize', 'A pply sanitizing step (remove common mistakes)')
133
+ .option('-s, --sanitize', 'Apply sanitizing step (remove common mistakes)')
116
134
  .option('-c, --compress', 'Compact HPO term list')
117
135
  .action( async (inputfile, options) => {
118
136
  let data = JSON.parse(FS.readFileSync( inputfile || '/dev/stdin' ));
@@ -120,7 +138,11 @@ program.command('preprocess')
120
138
  options.whitelist && (data = Coder.whiteListPhenoPacket( data ));
121
139
  options.sanitize && (data = Coder.sanitizePhenoPacket( data ));
122
140
  options.compress && (data = Coder.compressPhenoPacket( data ));
123
- FS.writeFileSync( options.output, JSON.stringify( data ))
141
+ if ( options.output ) {
142
+ const stream = FS.createWriteStream( options.output );
143
+ process.stdout.write = stream.write.bind( stream );
144
+ }
145
+ process.stdout.write( JSON.stringify( data ) );
124
146
  })
125
147
 
126
148
 
@@ -149,10 +171,10 @@ program.command('apikeys')
149
171
  program.command('ping')
150
172
  .summary('Ping API/check API credentials')
151
173
  .alias('P')
152
- .argument('id', 'API access key ID (default: rxome)')
174
+ .argument('id', 'API access key ID')
153
175
  .argument('key', 'API access key')
154
176
  .option('-t, --test', 'Connect to test API')
155
- .option('-l, --localhost', 'Connect to localhost API')
177
+ .option('-L, --localhost', 'Connect to localhost API')
156
178
  .option('-D, --debug', 'Some output for debugging')
157
179
  .action( async (id, key, options) => {
158
180
  let qrApi = RxAPI.API;
@@ -163,15 +185,14 @@ program.command('ping')
163
185
  key: key,
164
186
  user: 'info@rxome.net'
165
187
  }
166
-
167
188
  options.debug && console.log( "Sending " , credentials , " to ", qrApi );
168
189
 
169
190
  Coder.fetchKey( credentials, 'HANSMOTKAMP', qrApi, options.debug )
170
- .then( result => {console.log( (result?.key && result?.pseudonym === '') ? 'OK' : 'An error occured')} )
191
+ .then( result => { console.log('[RESULT] ', result);console.log( (result?.key && result?.pseudonym === 'HANSMOTKAMP') ? 'OK' : 'An error occured')} )
171
192
  .catch( error => {
172
- console.log( error.code );
193
+ console.log( '[Error] ', error.code );
173
194
  if (error.response) { // status !== 2xx
174
- options.debug && console.log('\nResp. Data:\n', error.response.data);
195
+ // options.debug && console.log('\nResp. Data:\n', error.response.data);
175
196
  options.debug && console.log('\nResp. Headers:\n', error.response.headers);
176
197
  console.log('\nError ', error.response.status);
177
198
  } else if (error.request) { // request send, but no response
@@ -183,11 +204,11 @@ program.command('ping')
183
204
  });
184
205
 
185
206
 
186
- program.command('encode')
207
+ program.command('encrypt')
187
208
  .alias('e')
188
209
  .description('encrypt message (just for testing)')
189
210
  .argument('[input file]', 'Input text file (default: STDIN)')
190
- .option('-o, --output <output file>', 'Output file (default: stdout)', '/dev/stdout')
211
+ .option('-o, --output <output file>', 'Output file (default: stdout)')
191
212
  .option('-k, --keyfile <keyfile>', 'name of public data encryption key file (not to be confused with the API access key!)', 'rxome.public.key')
192
213
  .option('-j, --json', 'treat inputfile as json {key: ..., message: ...}', false)
193
214
  .action( async (inputfile, options) => {
@@ -203,15 +224,19 @@ program.command('encode')
203
224
  message = data.toString();
204
225
  }
205
226
  const cipher = await Coder.encode( key, message );
206
- FS.writeFileSync( options.output, cipher );
227
+ if ( options.output ) {
228
+ const stream = FS.createWriteStream( options.output );
229
+ process.stdout.write = stream.write.bind( stream );
230
+ }
231
+ process.stdout.write( cipher );
207
232
  });
208
233
 
209
234
 
210
- program.command('decode')
235
+ program.command('decrypt')
211
236
  .alias('d')
212
- .description('decode coded message or medical data')
237
+ .description('decrypt coded message or medical data')
213
238
  .argument('[input file]', 'Input cipher file (default: STDIN)')
214
- .option('-o, --output <output file>', 'Output file (default: stdout)', '/dev/stdout')
239
+ .option('-o, --output <output file>', 'Output file (default: stdout)')
215
240
  .option('-k, --keyfile <keyfile>', 'name of private data encryption key file', 'rxome.private.key')
216
241
  .option('-j, --json', 'treat inputfile as json {key: ..., cipher: ...}', false)
217
242
  .option('-c, --complete', 'also unpack PhenoPacket', false )
@@ -242,12 +267,16 @@ program.command('decode')
242
267
  } else {
243
268
  result = data;
244
269
  }
245
- FS.writeFileSync( options.output, result );
246
-
270
+ if ( options.output ) {
271
+ const stream = FS.createWriteStream( options.output );
272
+ process.stdout.write = stream.write.bind( stream );
273
+ }
274
+ process.stdout.write( result );
247
275
  });
248
276
 
249
277
 
250
- program.command('rxome-keys')
278
+ program.command('data-keys')
279
+ .alias('K')
251
280
  .description('generate key pair for data encryption (see -e, -d; just for testing)')
252
281
  .argument('[file prefix]', 'Prefix for file names (default: rxome)')
253
282
  .option('-d, --directory <dir>', 'output directory', '.')
@@ -260,7 +289,7 @@ program.command('pheno2proto')
260
289
  .alias('E')
261
290
  .description('encode PhenoPacket to protobuf (just for testing)')
262
291
  .argument('[input file]', 'Input text file (default: STDIN)')
263
- .option('-o, --output <output file>', 'Output file (default: stdout)', '/dev/stdout')
292
+ .option('-o, --output <output file>', 'Output file (default: stdout)')
264
293
  .option('-b, --base64', 'Write output base64 encoded')
265
294
  .option('-W, --whitelist', 'Preprocess: whitelist', false )
266
295
  .option('-S, --sanitize', 'Preprocess: sanitize', false )
@@ -273,7 +302,11 @@ program.command('pheno2proto')
273
302
  const compressedMedical = options.compress ? Coder.compressPhenoPacket( sanitizedMedical ) : sanitizedMedical;
274
303
  const protobufMedical = Coder.encodePhenoPacket( compressedMedical );
275
304
  const outData = (options.base64 ? RxAPI.bufferToBase64( protobufMedical ) : protobufMedical );
276
- FS.writeFileSync( options.output, outData );
305
+ if ( options.output ) {
306
+ const stream = FS.createWriteStream( options.output );
307
+ process.stdout.write = stream.write.bind( stream );
308
+ }
309
+ process.stdout.write( outData );
277
310
  });
278
311
 
279
312
 
@@ -281,7 +314,7 @@ program.command('proto2pheno')
281
314
  .alias('D')
282
315
  .description('decode protobuf to PhenoPacket (just for testing)')
283
316
  .argument('[input file]', 'Input cipher file (default: STDIN)')
284
- .option('-o, --output <output file>', 'Output file (default: stdout)', '/dev/stdout')
317
+ .option('-o, --output <output file>', 'Output file (default: stdout)')
285
318
  .option('-b, --base64', 'Read input base64 encoded')
286
319
  .option('-p, --pretty', 'Pretty print output}')
287
320
 
@@ -289,7 +322,11 @@ program.command('proto2pheno')
289
322
  const input = FS.readFileSync( inputfile || '/dev/stdin' );
290
323
  const data = ( options.base64 ? Uint8Array.from([...atob(input) ].map( c => c.charCodeAt(0))): input)
291
324
  const pheno = Coder.decodePhenoPacket(data);
292
- FS.writeFileSync( options.output, JSON.stringify(pheno, ' ', options.pretty ? 2 : 0) );
325
+ if ( options.output ) {
326
+ const stream = FS.createWriteStream( options.output );
327
+ process.stdout.write = stream.write.bind( stream );
328
+ }
329
+ process.stdout.write( JSON.stringify(pheno, ' ', options.pretty ? 2 : 0) );
293
330
  });
294
331
 
295
332
 
@@ -391,8 +428,8 @@ program.command('statistics')
391
428
  // return Coder.decode(privateKey, cipher)
392
429
  // })
393
430
  // .then(clear => {console.log( JSON.parse(clear) )})
394
-
395
- // console.log( JSON.stringify( Coder.fetchKey( DEMO_CREDENTIALS, '', RxAPI.TESTAPI )));
431
+ // .action( async() => {
432
+ // console.log( JSON.stringify( await Coder.fetchDemoPrivateKey( DEMO_CREDENTIALS, '', RxAPI.TESTAPI, true )));
396
433
 
397
434
  // });
398
435
 
package/rxcode.test.js CHANGED
@@ -3,22 +3,27 @@ const FS = require( 'fs' );
3
3
  //const { default: test } = require("node:test");
4
4
  const Coder = require( './lib/rxome-generator' );
5
5
 
6
+ DEMO_TEXT = 'answer to life the universe and everything'
7
+
6
8
  DEMO_CIPHER = `
7
9
  -----BEGIN PGP MESSAGE-----
8
10
 
9
- wV4Dhx/ATfJT0nQSAQdAQB3UcIAE5hMbtWJLiaDIrIiuNBRYzYPPGe4Ry40A
10
- oCkw9Tf0OlDdDEqrCfGWseuYgdbgpqUSYJMTgmnmj8S2KxqU30eW10fu+lzt
11
- 0a8vvetS0loB8ktDgqeRfFeusC8L5vBVD6r5PixaZYWjhcaL1ZrcEl211ArW
12
- Qvk3mk17r9xwp26rVXk5NX/feTNXr19907RdPM1oT7zWetTpwx8ow6KZZrGB
13
- ZcabBZ5ti/o=
14
- =6tRv
11
+ wV4DwTe9/0b1zzgSAQdAxe4Phva3n19MInlPhQS4OxG78y+3mTsNHVfsk5OF
12
+ nGAwld5yaBCzLoudwGLQ17qIkABl9rhONf8VJMNoTOZOf8nPoouCKdXxktv7
13
+ dG1wvV2s0lsBvUBUmaOzix1hEF6YeUr7mfc+MBBt/2gfoyT4Kujgrg25YoZ6
14
+ qUgqga2eSZ6O+OhyjuFo3rZVXl1MFY6lfu+X+i4cCE2VWiAQOwVNnqFdY9FF
15
+ uOPuzhz7UstK
16
+ =KKxC
15
17
  -----END PGP MESSAGE-----
16
18
  `
17
19
 
18
20
  const PRIVATE_KEY = Coder.DEMO_PRIVATE_KEY;
19
21
  const PUBLIC_KEY = Coder.DEMO_PUBLIC_KEY;
20
22
 
23
+ jest.setTimeout(70000)
24
+
21
25
  async function execCmd( cmd ) {
26
+ // process.stderr.write( `Executing ${cmd}` )
22
27
  const exec = require('child_process').exec;
23
28
  return new Promise((resolve, reject) => {
24
29
  exec(cmd, (error, stdout, stderr) => {
@@ -36,41 +41,132 @@ async function execCmd( cmd ) {
36
41
  }
37
42
 
38
43
  describe('CmdLine generate', () => {
39
- it.skip('generates a qr code from demo data', () => {
40
- FS.rm( 'zzz.png' );
41
- execCmd( 'rxcode g -t -o zzz.png demos/demo_data_full.json' )
42
- .then( result => {
43
- console.log( result.stdout )
44
- console.log( result.stderr )
45
- });
44
+ test('generates a qr code from demo data', () => {
45
+ if ( FS.existsSync( '__TESTSUITE_IMG.png' )) {
46
+ FS.rmSync( '__TESTSUITE_IMG.png' );
47
+ }
48
+ execCmd( 'rxcode g -t -o __TESTSUITE_IMG.png demos/demo_data_full.json' )
49
+ // execCmd( 'rxcode g -L -o __TESTSUITE_IMG.png demos/demo_data_full.json' )
50
+ .then( res => {
51
+ expect( res.error ).toBeNull();
52
+ expect( res.stdout ).toBe('HANSMOTKAMP\n');
53
+ expect( res.stderr ).toBe('');
54
+ expect( FS.existsSync( '__TESTSUITE_IMG.png' )).toBeTruthy();
55
+ const fileStats = FS.statSync( '__TESTSUITE_IMG.png' );
56
+ expect( Math.floor( fileStats.size / 100 )-215).toBeLessThan( 4 );
57
+ })
58
+ .catch( err => { process.stderr.write( JSON.stringify( err ))})
46
59
  });
47
60
  });
48
61
 
49
62
 
50
- // describe('generate keys', () => {
51
- // test.skip('creates two files', async () => {
52
- // const dir = '.';
53
- // const ex = await execCmd( 'node rxcode.js generate testsuite foo@bar.baz ' );
54
- // const publicKeyFile = `${dir}/testsuite.public.key`;
55
- // const privateKeyFile = `${dir}/testsuite.private.key`;
56
- // expect( ex.error ).toBeNull();
57
- // expect( ex.stdout ).toBe('');
58
- // expect( ex.stderr ).toBe('');
59
- // expect( FS.existsSync( publicKeyFile )).toBeTruthy();
60
- // expect( FS.existsSync( privateKeyFile )).toBeTruthy();
61
- // const privStats = FS.statSync( privateKeyFile );
62
- // expect( privStats.size ).toBe( 850 );
63
- // const pubStats = FS.statSync( publicKeyFile );
64
- // expect( pubStats.size ).toBe( 624 );
65
- // const privateKey = FS.readFileSync( privateKeyFile, { encoding: 'utf8' }, err => { console.log(err) } );
66
- // expect( privateKey ).toContain( 'BEGIN PGP PRIVATE KEY BLOCK' );
67
- // const publicKey = FS.readFileSync( publicKeyFile, { encoding: 'utf8' }, err => { console.log(err) } );
68
- // expect( publicKey ).toContain( 'BEGIN PGP PUBLIC KEY BLOCK' );
69
- // })
63
+ describe('generate keys', () => {
64
+ test('creates two files', async () => {
65
+ const dir = '.';
66
+ const ex = await execCmd( 'rxcode K __TESTSUITE' );
67
+ const publicKeyFile = `${dir}/__TESTSUITE.public.key`;
68
+ const privateKeyFile = `${dir}/__TESTSUITE.private.key`;
69
+ expect( ex.error ).toBeNull();
70
+ expect( ex.stdout ).toBe('');
71
+ expect( ex.stderr ).toBe('');
72
+ expect( FS.existsSync( publicKeyFile )).toBeTruthy();
73
+ expect( FS.existsSync( privateKeyFile )).toBeTruthy();
74
+ const privStats = FS.statSync( privateKeyFile );
75
+ expect( Math.floor( privStats.size / 100 )).toBe( 8 );
76
+ const pubStats = FS.statSync( publicKeyFile );
77
+ expect( Math.floor( pubStats.size / 10 )).toBe( 62 );
78
+ const privateKey = FS.readFileSync( privateKeyFile, { encoding: 'utf8' }, err => { console.log(err) } );
79
+ expect( privateKey ).toContain( 'BEGIN PGP PRIVATE KEY BLOCK' );
80
+ const publicKey = FS.readFileSync( publicKeyFile, { encoding: 'utf8' }, err => { console.log(err) } );
81
+ expect( publicKey ).toContain( 'BEGIN PGP PUBLIC KEY BLOCK' );
82
+ })
70
83
 
71
- // });
84
+ });
72
85
 
73
- // //describe('encode', () => {
86
+ describe('Cmdline encode', () => {
87
+ test('encodes json input', async() => {
88
+ execCmd( './rxcode e -j ./demos/demo_encrypt.json > __TESTSUITE_FILE_1' )
89
+ .then( res => {
90
+ expect( res.error ).toBeNull();
91
+ expect( res.stdout ).toBe('');
92
+ expect( res.stderr ).toBe('');
93
+ expect( FS.existsSync( '__TESTSUITE_FILE_1' )).toBeTruthy();
94
+ const fileStats = FS.statSync( '__TESTSUITE_FILE_1' );
95
+ expect( Math.floor( fileStats.size / 100 )).toBe( 3 );
96
+ const fileContent = FS.readFileSync( '__TESTSUITE_FILE_1', { encoding: 'utf8' }, err => { process.stderr.write(err) } );
97
+ expect( fileContent ).toContain( '-----BEGIN PGP MESSAGE-----' );
98
+ //FS.rmSync( '__TESTSUITE_FILE_1')
99
+ })
100
+ .catch( err => { process.stderr.write( JSON.stringify( err ))})
101
+ })
102
+
103
+ test('decodes json input', async() => {
104
+ execCmd( './rxcode d -j ./demos/demo_decrypt.json > __TESTSUITE_FILE_2' )
105
+ .then( res => {
106
+ expect( res.error ).toBeNull();
107
+ expect( res.stdout ).toBe('');
108
+ expect( res.stderr ).toBe('');
109
+ expect( FS.existsSync( '__TESTSUITE_FILE_2' )).toBeTruthy();
110
+ const fileStats = FS.statSync( '__TESTSUITE_FILE_2' );
111
+ expect( fileStats.size ).toBe( 42 );
112
+ const fileContent = FS.readFileSync( '__TESTSUITE_FILE_2', { encoding: 'utf8' }, err => { process.stderr.write(err) } );
113
+ expect( fileContent ).toContain( DEMO_TEXT );
114
+ //FS.rmSync( '__TESTSUITE_FILE_1')
115
+ })
116
+ .catch( err => { process.stderr.write( JSON.stringify( err ))})
117
+ })
118
+
119
+ test('encodes and decodes a text file', async() => {
120
+ FS.writeFileSync( '__TESTSUITE_FILE_3', DEMO_TEXT );
121
+ execCmd( './rxcode e -k ./demos/demo.public.key -o __TESTSUITE_FILE_4 __TESTSUITE_FILE_3' )
122
+ .then( res => {
123
+ expect( res.error ).toBeNull();
124
+ expect( res.stdout ).toBe('');
125
+ expect( res.stderr ).toBe('');
126
+ expect( FS.existsSync( '__TESTSUITE_FILE_4' )).toBeTruthy();
127
+
128
+ execCmd( './rxcode d -k ./demos/demo.private.key -o __TESTSUITE_FILE_5 __TESTSUITE_FILE_4' )
129
+ .then( res => {
130
+ expect( res.error ).toBeNull();
131
+ expect( res.stdout ).toBe('');
132
+ expect( res.stderr ).toBe('');
133
+ expect( FS.existsSync( '__TESTSUITE_FILE_5' )).toBeTruthy();
134
+ const fileStats = FS.statSync( '__TESTSUITE_FILE_5' );
135
+ expect( fileStats.size ).toBe( 42 );
136
+ const fileContent = FS.readFileSync( '__TESTSUITE_FILE_5', { encoding: 'utf8' } );
137
+ expect( fileContent ).toContain( DEMO_TEXT );
138
+ //FS.rmSync( '__TESTSUITE_FILE_1')
139
+ })
140
+ //.catch( err => { process.stderr.write( JSON.stringify( err ))})
141
+ })
142
+ //.catch( err => { process.stderr.write( JSON.stringify( err ))})
143
+ }, 100000)
144
+
145
+ test('encodes and decodes from stdin to stdout file', async() => {
146
+ FS.writeFileSync( '__TESTSUITE_FILE_6', DEMO_TEXT );
147
+ execCmd( './rxcode e -k ./demos/demo.public.key < __TESTSUITE_FILE_6 > __TESTSUITE_FILE_7' )
148
+ .then( res => {
149
+ expect( res.error ).toBeNull();
150
+ expect( res.stdout ).toBe('');
151
+ expect( res.stderr ).toBe('');
152
+ expect( FS.existsSync( '__TESTSUITE_FILE_7' )).toBeTruthy();
153
+
154
+ execCmd( './rxcode d -k ./demos/demo.private.key < __TESTSUITE_FILE_7 > __TESTSUITE_FILE_8' )
155
+ .then( res => {
156
+ expect( res.error ).toBeNull();
157
+ expect( res.stdout ).toBe('');
158
+ expect( res.stderr ).toBe('');
159
+ expect( FS.existsSync( '__TESTSUITE_FILE_8' )).toBeTruthy();
160
+ const fileStats = FS.statSync( '__TESTSUITE_FILE_8' );
161
+ expect( fileStats.size ).toBe( 42 );
162
+ const fileContent = FS.readFileSync( '__TESTSUITE_FILE_8', { encoding: 'utf8' } );
163
+ expect( fileContent ).toContain( DEMO_TEXT );
164
+ //FS.rmSync( '__TESTSUITE_FILE_1')
165
+ })
166
+ //.catch( err => { process.stderr.write( JSON.stringify( err ))})
167
+ });
168
+ })
169
+ });
74
170
 
75
171
  // //});
76
172
  // describe('CmdLine help', async () => {
@@ -95,10 +191,4 @@ rxcode d -j rxome.decrypt.json
95
191
  rxcode d -j rxome.decrypt.json
96
192
  rxcode d -j < rxome.decrypt
97
193
 
98
- rxcode P test Cs67c7aXYhZQbjU2W/4q6sBu/oIQ8zONSBURUH8g2wQ=
99
- OK
100
- rxcode P mgz QhcoRruGBVP39XCh8BujCE+q42qCRy/tu2CQ4YmRBgg=
101
- OK
102
- rxcode P rxome NamaTB+xwDFxtkQyBBkjRr5GEaXNtCw/G4qydnhQk5Y=
103
- OK
104
- */
194
+ */
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 Tom Kamphans, GeneTalk GmbH
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1 +0,0 @@
1
- {"key":"\n-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nxYYEYmgyNxYJKwYBBAHaRw8BAQdA9l4CTUgpfKMpdhmIsu1+mFZh96Mch+5w\nZnd3+fvIoz7+CQMI2zrQtpZGqZvgA0VIaMqedg6MCtUWVZR7Eb6FOhizqvo/\ngNeBu4IvoHEqv8NECSfy10WFSMuDN2D4dm9E21VBcpdryNo5WOTE2qwUX7sp\ntc0UZm9vYmFyIDxmb29AYmFyLmJhej7CjAQQFgoAHQUCYmgyNwQLCQcIAxUI\nCgQWAAIBAhkBAhsDAh4BACEJELz7rrggWl5DFiEExPymzQI26MvyIw/1vPuu\nuCBaXkOMogEAhQlj7f+riRrlpFtmL13//BHZJdAy8hNO4AUOkNjxEzcBALqS\nrEoMvCeXPjju60hBzyb0rzmzWHyUKQE1R4mgJzwEx4sEYmgyNxIKKwYBBAGX\nVQEFAQEHQNVKEkYa4QPXrI5553FfN1Uio4fsorJoIya8EivEbLIyAwEIB/4J\nAwhP5xKSOD3XVODBWc+ZZVn9sJyvyQeyZWZ0em15ixtmxO3BF+1AzNj/1bcx\nx/BsSYyX6tnEh/AjMNCZV4Cmvp+pBie+x+BBN6zJRHM837PYwngEGBYIAAkF\nAmJoMjcCGwwAIQkQvPuuuCBaXkMWIQTE/KbNAjboy/IjD/W8+664IFpeQyUr\nAP0VhT+Xa4lt2EvapPlXeEsdTyI5gx/87pSw6Cs21GyUwwEApgU2q4qfjsFC\nyOcQrcylWRQ4NvdK9SqsEomwxTwHkQQ=\n=YCih\n-----END PGP PRIVATE KEY BLOCK-----\n","cipher":"\n-----BEGIN PGP MESSAGE-----\n\nwV4Dhx/ATfJT0nQSAQdAkPgYRHClE5eUyl58gXBQrmZoiTtkvrOZLjnaLvFq\ncicw/28IAdW/0wuYtbd0CQf6gFMMLVmF+4Qx6OeQHehI8t+5mi7Keve1eaJp\nj2WL1TiN0lsBBbYvVdInUt3VbiywTxa+sMvfT+0crmTtsfJ6FrbD7nR22L/v\n9RgUEaY6cDrNu+PzcKGVYQj/WCqhlFflcAn+/cfcu0mRkllmbghN2uYbymff\n4M44RC1yZ0j1\n=x+N1\n-----END PGP MESSAGE-----\n"}
@@ -1 +0,0 @@
1
- {"key":"\n-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nxYYEYmgyNxYJKwYBBAHaRw8BAQdA9l4CTUgpfKMpdhmIsu1+mFZh96Mch+5w\nZnd3+fvIoz7+CQMI2zrQtpZGqZvgA0VIaMqedg6MCtUWVZR7Eb6FOhizqvo/\ngNeBu4IvoHEqv8NECSfy10WFSMuDN2D4dm9E21VBcpdryNo5WOTE2qwUX7sp\ntc0UZm9vYmFyIDxmb29AYmFyLmJhej7CjAQQFgoAHQUCYmgyNwQLCQcIAxUI\nCgQWAAIBAhkBAhsDAh4BACEJELz7rrggWl5DFiEExPymzQI26MvyIw/1vPuu\nuCBaXkOMogEAhQlj7f+riRrlpFtmL13//BHZJdAy8hNO4AUOkNjxEzcBALqS\nrEoMvCeXPjju60hBzyb0rzmzWHyUKQE1R4mgJzwEx4sEYmgyNxIKKwYBBAGX\nVQEFAQEHQNVKEkYa4QPXrI5553FfN1Uio4fsorJoIya8EivEbLIyAwEIB/4J\nAwhP5xKSOD3XVODBWc+ZZVn9sJyvyQeyZWZ0em15ixtmxO3BF+1AzNj/1bcx\nx/BsSYyX6tnEh/AjMNCZV4Cmvp+pBie+x+BBN6zJRHM837PYwngEGBYIAAkF\nAmJoMjcCGwwAIQkQvPuuuCBaXkMWIQTE/KbNAjboy/IjD/W8+664IFpeQyUr\nAP0VhT+Xa4lt2EvapPlXeEsdTyI5gx/87pSw6Cs21GyUwwEApgU2q4qfjsFC\nyOcQrcylWRQ4NvdK9SqsEomwxTwHkQQ=\n=YCih\n-----END PGP PRIVATE KEY BLOCK-----\n","message":"answer to life the universe and everything"}