Skip to content

Commit 5d58ba0

Browse files
committed
[FIX] Update license headers & expose encrypt api
2 parents 8a32a1f + 41e03de commit 5d58ba0

98 files changed

Lines changed: 6771 additions & 3112 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yaml

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,30 +34,16 @@ jobs:
3434
run: npm install
3535

3636
- name: Run tests
37-
run: npm run test:ci
38-
env:
39-
NODE_OPTIONS: '--experimental-vm-modules'
40-
41-
- name: Publish Test Results
42-
uses: EnricoMi/publish-unit-test-result-action@v2.18.0
43-
if: always()
44-
with:
45-
files: ./coverage/test-report/test-report.xml
46-
47-
- name: Publish Code Coverage
48-
uses: irongut/CodeCoverageSummary@v1.3.0
49-
with:
50-
filename: coverage/cobertura-coverage.xml
51-
badge: true
52-
format: markdown
53-
output: both
54-
55-
- name: Add Coverage PR Comment
56-
uses: marocchino/sticky-pull-request-comment@v2
57-
if: github.event_name == 'pull_request'
37+
run: npm run test:coverage
38+
39+
- name: Report Coverage
40+
if: (!cancelled())
41+
uses: davelosert/vitest-coverage-report-action@v2
42+
43+
- name: Publish JUnit Test Report
44+
if: (!cancelled()) && (success() || failure())
45+
uses: mikepenz/action-junit-report@v5
5846
with:
59-
recreate: true
60-
path: code-coverage-results.md
47+
report_paths: reports/junit.xml
48+
check_name: 'Vitest JUnit'
6149

62-
- name: Write to job Summary
63-
run: cat code-coverage-results.md >> $GITHUB_STEP_SUMMARY

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ tmp
33
dist
44
.vscode
55
coverage
6+
reports
67

78
/lib
89
output/

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This repository is designed to be used with file-based data programmatically; fo
1010
📦common-identifier-algorithm-shared
1111
┣ 📂src
1212
┃ ┣ 📂config # functions related to the handling of configuration files
13+
┃ ┣ 📂crypto # functions related to encryption & signing of files
1314
┃ ┣ 📂decoding # reading and decoding files - CSV or XLSX
1415
┃ ┣ 📂encoding # encoding and writing files - CSV or XLSX
1516
┃ ┣ 📂hashing # base hashing logic and supporting utilities
@@ -46,4 +47,8 @@ This repository is designed to be used with file-based data programmatically; fo
4647
- The `src/processing` processing function identifies if the target is a mapping document based on the current configuration and the data in the file. Using the active configuration it collects data into `static` `to_translate` and `reference` buckets per-row and passes it to the active algorithm for processing
4748
- The active algorithm takes the `{ static:[...], to_translate:[...], reference: [...] }` per-row data and returns a map with the columns it wants to add -- ex: `{ USCADI: "....", DOCUMENT_HASH: "..." }`
4849
- The data returned by the algorithm is merged into the source rows so the encoders can package multiple different outputs
49-
- The `src/encoding` Encoders (CSV and XLSX) write the output based on the relevant `[destination]` / `[destination_map]` section of the active configuration.
50+
- The `src/encoding` Encoders (CSV and XLSX) write the output based on the relevant `[destination]` / `[destination_map]` section of the active configuration.
51+
52+
### Post-processing
53+
54+
- Any post-processing steps (e.g. encryption, signing, etc.) are handled after the encoding step, using the relevant classes in `src/crypto` (e.g. `GpgWrapper` for GPG encryption/signing)

docs/configuration-files.md

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,24 @@ Look in any of the existing algorithm implementation repositories for examples o
1010

1111
The meta section of the file contains information related to application versioning.
1212

13-
```
13+
```toml
1414
[meta]
15-
region="ABC" # application installations are region dependent, the value specified here MUST match the built-in region.
15+
id="ABC" # application installations are typically region dependent, the value specified here MUST match the built-in region.
1616
version="1.0.0" # the version information is shown in the top-right of the desktop UI for user visibility.
1717
signature = "aaabbb"
1818
```
19-
The signature is the calculated `md5` hash value of the configuration file itself, computed using the `src/config/generateConfigHash.ts` utility. This feature exists to reduce the likelihood of accidental changes to the config file causing issues in the deterministic processing of input data.
19+
The signature is the calculated `md5` hash value of the configuration file itself, computed using the [`generateConfigHash`](../src/config/utils.ts) utility. This feature exists to reduce the likelihood of accidental changes to the config file causing issues in the deterministic processing of input data.
2020

2121
When a change is made to the configuration file, a new signature value must be created to reflect its new content.
2222

23-
> TODO: git pre-commit hook to validate the config file on commit and throw an error if values don't match.
24-
2523
### Messages
2624

2725
> [!IMPORTANT]
2826
> If you are intending on using the UI component of this application, the messages section in the configuration file is NOT optional. The values for `terms_and_conditions`, `error_in_config`, and `error_in_salt` MUST be defined.
2927
3028
Messages are an optional field used to set the default error and terms & conditions messages within the UI application. Each of these fields supports `HTML` tag syntax.
3129

32-
```
30+
```toml
3331
[messages]
3432
# terms and conditions are shown to the user on first start and upon configuration file changes.
3533
terms_and_conditions="""
@@ -46,9 +44,7 @@ error_in_salt=""
4644

4745
The `source` sections defines the expected input columns in the source dataset. The `name` key is the human readable name in the header of the CSV file, `alias` is the more machine-friendly name used by the application internally, and `default` is the default value to use for empty cells where necessary.
4846

49-
> TODO: make `alias` an optional parameter - it is not relevant for programmatic usage.
50-
51-
```
47+
```toml
5248
[source]
5349
# an array of column names, their aliases, and default values where necessary.
5450
columns = [
@@ -62,7 +58,7 @@ columns = [
6258

6359
This file section details which validation rules to apply to which columns in the input file.
6460

65-
```
61+
```toml
6662
[validations]
6763
# per column name to apply validation rules to, define an array of validation rules
6864
column_name = [
@@ -74,7 +70,7 @@ column_name = [
7470
]
7571
```
7672

77-
```
73+
```toml
7874
# the structure of a validation rule is as follows:
7975
{
8076
# the name of the validation rule, from the supported list.
@@ -107,7 +103,7 @@ This is the list of currently supported validation rules, these are further desc
107103

108104
### Algorithm
109105

110-
```
106+
```toml
111107
[algorithm]
112108
# the aliased columns to use as part of the algo implementation.
113109
[algorithm.columns]
@@ -119,7 +115,7 @@ static = [ "col_b" ]
119115
reference = [ "col_c" ]
120116
```
121117

122-
```
118+
```toml
123119
[algorithm.hash]
124120
# the hashing algorithm to use, currently only SHA256 is supported
125121
strategy = "SHA256"
@@ -146,7 +142,7 @@ darwin = "$APPDATA/<path_to_file>/file.asc"
146142

147143
Define the columns to include in the output file, including the human-readable names to convert to where necessary.
148144

149-
```
145+
```toml
150146
[destination]
151147
# array of column names and aliases to include in the output file
152148
columns = [
@@ -163,7 +159,7 @@ postfix = "_OUTPUT"
163159

164160
Define the columns to include in the output mapping file, including the human-readable names to convert to where necessary.
165161

166-
```
162+
```toml
167163
[destination_map]
168164
# array of column names and aliases to include in the output mapping file
169165
columns = [
@@ -183,7 +179,7 @@ Define the columns to include in the error report, including the human-readable
183179
> [!IMPORTANT]
184180
> Make sure to include the `Errors | errors` column in the output configuration, otherwise they will not be included in the output file.
185181
186-
```
182+
```toml
187183
[destination_errors]
188184
# array of column names and aliases to include in the errors file
189185
columns = [
@@ -195,4 +191,15 @@ columns = [
195191
# suffix that is appended to the errors filename -> <input_file_name><postfix>.csv
196192
# for XLSX, this is also the name of the sheet containing the final data
197193
postfix = "_ERRORS"
194+
```
195+
196+
### Post-processing
197+
198+
#### Encryption / Signing
199+
200+
```toml
201+
[post_processing]
202+
[post_processing.encryption]
203+
recipient = "QWERTY" # the fingerprint or identifier of the recipient to encrypt the file for
204+
gpgBinaryPath = "" # optional path to gpg binary, overrides system path lookup
198205
```

examples/example_algorithm/_generic_hasher.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1-
// Common Identifier Application
2-
// Copyright (C) 2024 World Food Programme
3-
4-
// This program is free software: you can redistribute it and/or modify
5-
// it under the terms of the GNU Affero General Public License as published by
6-
// the Free Software Foundation, either version 3 of the License, or
7-
// (at your option) any later version.
8-
9-
// This program is distributed in the hope that it will be useful,
10-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
// GNU Affero General Public License for more details.
13-
14-
// You should have received a copy of the GNU Affero General Public License
15-
// along with this program. If not, see <http://www.gnu.org/licenses/>.
1+
/* ************************************************************************
2+
* Common Identifier Application
3+
* Copyright (C) 2026 World Food Programme
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU Affero General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public License
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
************************************************************************ */
1618

1719
import { joinFieldsForHash, cleanValueList, extractAlgoColumnsFromObject, BaseHasher } from '../../src';
1820
import type { Config, Validator, makeHasherFunction } from '../../src';

examples/example_algorithm/config.toml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[meta]
22
id="ANY"
33
version="1.0.0"
4-
signature="a36d008f81060928709a6704da61bbd6"
4+
signature="f2789d9afdb774d88be5998aa37bdb78"
55

66
[source]
77
columns = [
@@ -35,18 +35,25 @@ strategy = "SHA256"
3535
columns = [
3636
{ name = "Common Identifier", alias = "hashed_id" },
3737
]
38-
postfix = "-OUTPUT-{{yyyy-MM-dd--HH-mm-ss}}"
38+
prefix = "OUTPUT-"
39+
postfix = "-{{yyyyMMddHHmm}}"
3940

4041
[destination_map]
4142
columns = [
4243
{ name = "ID", alias = "id" },
4344
{ name = "Common Identifier", alias = "hashed_id" }
4445
]
45-
postfix = "-MAPPING-{{yyyy-MM-dd--HH-mm-ss}}"
46+
prefix = "MAPPING-"
47+
postfix = "-{{yyyyMMddHHmm}}"
4648

4749
[destination_errors]
4850
columns = [
4951
{ name = "Errors", alias = "errors" },
5052
{ name = "ID", alias = "id" },
5153
]
52-
postfix = "-ERRORS-{{yyyy-MM-dd--HH-mm-ss}}"
54+
prefix = "ERRORS-"
55+
postfix = "-{{yyyyMMddHHmm}}"
56+
57+
# [post_processing]
58+
# [post_processing.encryption]
59+
# recipient = "SOME_PUBLIC_KEY_FINGERPRINT"

examples/file_based_usage.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// REPLACE ALL REFERENCES TO "_generic_hasher" WITH THE DESIRED ALGORITHM IN THE ALGORITHMS DIRECTORY.
22

3-
import { loadConfig, preprocessFile, processFile } from '../src/index';
3+
import { existsSync, rmSync } from 'node:fs';
4+
import { loadConfig, postprocessFile, preprocessFile, processFile } from '../src/index';
45
import { makeHasher, ALGORITHM_ID } from './example_algorithm/_generic_hasher';
5-
import { dirname, join } from 'node:path';
6+
import { dirname, join, parse, resolve } from 'node:path';
67
import { fileURLToPath } from 'node:url';
78

89
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -12,31 +13,52 @@ const INPUT_PATH = join(__dirname, 'example_algorithm', 'input_10.csv');
1213
const OUTPUT_PATH = join(__dirname, 'output', 'output_10.csv');
1314
const VALIDATION_ERRORS_PATH = join(__dirname, 'output', 'validation_errors.csv');
1415

15-
// load configuration from file
16+
if (existsSync(resolve(__dirname, "output"))) rmSync(join(__dirname, "output"), { recursive: true, force: true});
17+
18+
// 1. load configuration from file
1619
const configLoadResult = loadConfig({
1720
configPath: CONFIG_PATH,
1821
algorithmId: ALGORITHM_ID,
1922
validateConfig: true,
2023
embeddedSalt: { source: "STRING", value: "SOME_SALT_VALUE" }
2124
});
22-
if (!configLoadResult.success) throw new Error(`ERROR: Unable to load configuration file >> ${configLoadResult.error}`);
25+
if (!configLoadResult.success)
26+
throw new Error(`ERROR: Unable to load configuration file >> ${configLoadResult.error}`);
2327

24-
// validate the input file against all configured validation rules.
28+
// 2. validate the input file against all configured validation rules.
2529
const preprocessResult = await preprocessFile({
2630
config: configLoadResult.config,
2731
inputFilePath: INPUT_PATH,
2832
errorFileOutputPath: VALIDATION_ERRORS_PATH,
2933
});
3034

3135
if (!preprocessResult.isValid)
32-
throw new Error('Validation errors found in input file, review error file output.');
36+
throw new Error('ERROR: Validation errors found in input file, review error file output.');
3337

34-
// validate the input file against all configured validation rules.
38+
// 3. process the input file according to the configuration.
3539
const processFileResult = await processFile({
3640
config: configLoadResult.config,
3741
inputFilePath: INPUT_PATH,
3842
outputPath: OUTPUT_PATH,
3943
hasherFactory: makeHasher,
4044
});
41-
// print the result, save the result, etc.
42-
console.dir(processFileResult, { depth: 3 });
45+
if (!processFileResult.outputFilePath)
46+
throw new Error(`ERROR: Unable to process input file`);
47+
48+
// 4. print the result, save the result, etc.
49+
console.dir(processFileResult, { depth: 3 });
50+
51+
52+
// 5. postprocess the output file according to the configuration.
53+
const postprocessResult = await postprocessFile({
54+
config: configLoadResult.config,
55+
inputPath: processFileResult.outputFilePath,
56+
outputPath: join(parse(processFileResult.outputFilePath).dir, "ENCRYPTED.gpg"),
57+
options: {
58+
signer: "" // OPTIONAL: Signing key
59+
}
60+
});
61+
if (!postprocessResult.success) {
62+
const errors = postprocessResult.steps.filter(step => !step.success);
63+
throw new Error(`ERROR: Postprocessing failed, one or more steps failed >> ${JSON.stringify(errors)}`);
64+
}

examples/programmatic_usage.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// REPLACE ALL REFERENCES TO "_generic_hasher" WITH THE DESIRED ALGORITHM IN THE ALGORITHMS DIRECTORY.
2-
import { generateHashesForDocument, validateConfigCore, validateDocument, type CidDocument, type Config } from '../src/index';
3-
import { SUPPORTED_VALIDATORS } from '../src/validation/Validation';
2+
import { generateHashesForDocument, validateConfigCore, validateDocument, type CidDocument, type Config } from '@/index';
3+
import { SUPPORTED_VALIDATORS } from '@/validation/Validation';
44
import { makeHasher } from './example_algorithm/_generic_hasher';
55

66
/*
@@ -54,7 +54,7 @@ const doc: CidDocument = {
5454
]
5555
}
5656

57-
function main() {
57+
async function main() {
5858
const configValidationResult = validateConfigCore(config, "UNKNOWN");
5959
if (!!configValidationResult) {
6060
console.log(`ERROR: ${configValidationResult}`);
@@ -75,6 +75,9 @@ function main() {
7575

7676
// print the results, save the results, up to you.
7777
console.dir(result, { depth: 5 });
78+
79+
// NOTE: do any postprocessing steps as required, e.g. encryption, signing, etc.
80+
// see the GpgWrapper class in src/crypto/gpg.ts for example usage.
7881
}
7982

8083
main();

0 commit comments

Comments
 (0)