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/- +1 -0
- package/README.md +43 -23
- package/demos/demo_data_full.json +4 -3
- package/demos/demo_data_full.yaml +100 -0
- package/demos/demo_data_full_compressed.json +4 -3
- package/demos/demo_decrypt.json +1 -0
- package/demos/demo_encrypt.json +1 -0
- package/index.js +3 -3
- package/lib/{rxome-api-demo.js → rxome-api-demo.cjs} +4 -2
- package/lib/{rxome-api.js → rxome-api.cjs} +11 -3
- package/lib/rxome-api.test.js +7 -7
- package/lib/{rxome-generator.js → rxome-generator.cjs} +9 -9
- package/lib/rxome-generator.test.js +28 -23
- package/package.json +3 -3
- package/{rxcode → rxcode.js} +73 -36
- package/rxcode.test.js +132 -42
- package/LICENSE +0 -21
- package/demos/rxome.decrypt.json +0 -1
- package/demos/rxome.encrypt.json +0 -1
package/README.md
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
#
|
|
2
|
-
Generates QR codes containing medical information for use with the
|
|
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
|
-
|
|
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
|
|
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: '
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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": "
|
|
146
|
-
"keyId": "
|
|
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": "
|
|
73
|
-
"keyId": "
|
|
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
|
|
39
|
-
const
|
|
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 = '
|
|
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,
|
package/lib/rxome-api.test.js
CHANGED
|
@@ -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( '
|
|
8
|
-
expect( FS.existsSync('
|
|
9
|
-
expect( FS.existsSync('
|
|
10
|
-
expect( FS.statSync('
|
|
11
|
-
expect( FS.statSync('
|
|
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('
|
|
16
|
-
const pubKey = RxAPI.unpack([...RxAPI.base64ToBuffer( FS.readFileSync('
|
|
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
|
-
|
|
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:
|
|
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
|
|
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,
|
|
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,
|
|
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, '', '
|
|
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(
|
|
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,
|
|
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
|
|
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
|
|
292
|
-
url:
|
|
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,
|
|
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
|
|
333
|
+
//const { qr_code, pseudonym } = await
|
|
334
|
+
Coder.makeQR(
|
|
330
335
|
newData,
|
|
331
336
|
RXTESTAPI
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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.
|
|
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": "
|
|
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": "
|
|
27
|
+
"license" : "SEE LICENSE IN README.md",
|
|
28
28
|
"bugs": {
|
|
29
29
|
"url": "https://github.com/GeneTalkTK/rxome-qrcode-generator/issues"
|
|
30
30
|
},
|
package/{rxcode → rxcode.js}
RENAMED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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)'
|
|
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
|
-
|
|
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)'
|
|
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', '
|
|
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
|
-
|
|
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
|
|
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('-
|
|
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('
|
|
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)'
|
|
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
|
-
|
|
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('
|
|
235
|
+
program.command('decrypt')
|
|
211
236
|
.alias('d')
|
|
212
|
-
.description('
|
|
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)'
|
|
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
|
-
|
|
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('
|
|
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)'
|
|
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
|
-
|
|
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)'
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
=
|
|
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
|
-
|
|
40
|
-
FS.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
package/demos/rxome.decrypt.json
DELETED
|
@@ -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"}
|
package/demos/rxome.encrypt.json
DELETED
|
@@ -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"}
|