upgrade to 1.1.0

This commit is contained in:
ben committed 2026-10-23 18:49:11 +00:00
1 parent 02c95d89bb
commit f5893a107d
46 files changed
+3762 -2889

No files matched your search

-2
View File
@@ -1,2 +0,0 @@
node_modules
.env
+1 -1
View File
@@ -1,2 +1,2 @@
node_modules node_modules
.env **/.env*
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2025 JLINC Labs Copyright (c) 2026 JLINC, Inc
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+722 -265
View File
File diff suppressed because it is too large. Load diff
+16
View File
@@ -0,0 +1,16 @@
export function produceAgreement({ data, shortName, role, archive }: {
data: any;
shortName: any;
role: any;
archive: any;
}): Promise<any>;
export function processAgreement({ agreementId, shortName, role, archive }: {
agreementId: any;
shortName: any;
role: any;
archive: any;
}): Promise<any>;
export function getAgreement({ agreementId, includeSignatures }: {
agreementId: any;
includeSignatures: any;
}): Promise<any>;
+36
View File
@@ -0,0 +1,36 @@
"use strict";
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function produceAgreement({ data, shortName, role, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(_getUrl('/api/v1/data/agreement/produce'), {
data,
shortName,
role,
archive: archive || defaultArchive,
}, { headers: _headers() })).data;
}
async function processAgreement({ agreementId, shortName, role, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(_getUrl('/api/v1/data/agreement/process'), {
agreementId: agreementId || getDefault('agreementId'),
shortName,
role,
archive: archive || defaultArchive,
}, { headers: _headers() })).data;
}
async function getAgreement({ agreementId, includeSignatures }) {
return (await axios.post(_getUrl('/api/v1/data/agreement/get'), { agreementId: agreementId || getDefault('agreementId'), includeSignatures }, { headers: _headers() })).data;
}
module.exports = { produceAgreement, processAgreement, getAgreement };
+9
View File
@@ -0,0 +1,9 @@
export function getAudits({ agreementId, eventId, meta }: {
agreementId: any;
eventId: any;
meta: any;
}): Promise<any>;
export function verifyAudit({ audits, shortNames }: {
audits: any;
shortNames: any;
}): Promise<any>;
+23
View File
@@ -0,0 +1,23 @@
"use strict";
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _getArchiveUrl(path) {
const cfg = getConfig();
return `${cfg.archiveUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function getAudits({ agreementId, eventId, meta }) {
return (await axios.post(_getArchiveUrl('/api/v1/audit/get'), { agreementId: agreementId || getDefault('agreementId'), eventId, meta }, { headers: _headers(getConfig().archiveKey) })).data;
}
async function verifyAudit({ audits, shortNames }) {
return (await axios.post(_getUrl('/api/v1/data/audit/verify'), { audits, shortNames }, { headers: _headers() })).data;
}
module.exports = { getAudits, verifyAudit };
+5
View File
@@ -0,0 +1,5 @@
export function auth({ subject, action, resource }: {
subject: any;
action: any;
resource: any;
}): Promise<any>;
+16
View File
@@ -0,0 +1,16 @@
"use strict";
const axios = require('axios');
const { getConfig } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function auth({ subject, action, resource }) {
return (await axios.post(_getUrl('/api/v1/auth'), { subject, action, resource }, { headers: _headers() })).data;
}
module.exports = { auth };
+4
View File
@@ -0,0 +1,4 @@
export function init(cfg: any, defaultOpts: any): void;
export function getConfig(): {};
export function getDefaults(): {};
export function getDefault(key: any): any;
+19
View File
@@ -0,0 +1,19 @@
"use strict";
let config = {};
let defaults = {};
function init(cfg, defaultOpts) {
config = { ...cfg };
if (defaultOpts) {
defaults = { ...defaultOpts };
}
}
function getConfig() {
return config;
}
function getDefaults() {
return defaults;
}
function getDefault(key) {
return defaults[key];
}
module.exports = { init, getConfig, getDefaults, getDefault };
+14
View File
@@ -0,0 +1,14 @@
export function getDomains({ fedidUrl, auth: authPayload }: {
fedidUrl: any;
auth: any;
}): Promise<any>;
export function getEntity({ shortName }: {
shortName: any;
}): Promise<axios.AxiosResponse<any, {
shortName: any;
}, {}, any>>;
export function createEntity({ fedidUrl, shortName }: {
fedidUrl: any;
shortName: any;
}): Promise<any>;
import axios = require("axios");
+30
View File
@@ -0,0 +1,30 @@
"use strict";
const axios = require('axios');
const { getConfig } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function getDomains({ fedidUrl, auth: authPayload }) {
const cfg = getConfig();
return (await axios.post(_getUrl('/api/v1/data/entity/domains/get'), {
fedidUrl: fedidUrl || cfg.fedidUrl,
auth: authPayload,
}, { headers: _headers() })).data;
}
async function createEntity({ fedidUrl, shortName }) {
const cfg = getConfig();
return (await axios.post(_getUrl('/api/v1/data/entity/create'), {
fedidUrl: fedidUrl || cfg.fedidUrl,
shortName,
}, { headers: _headers() })).data;
}
async function getEntity({ shortName }) {
return (await axios.post(_getUrl('/api/v1/data/entity/get'), { shortName }, { headers: _headers() }));
}
module.exports = { getDomains, getEntity, createEntity };
+18
View File
@@ -0,0 +1,18 @@
export function produceEvent({ type, senderShortName, recipientShortName, agreementId, meta, data, archive }: {
type: any;
senderShortName: any;
recipientShortName: any;
agreementId: any;
meta: any;
data: any;
archive: any;
}): Promise<any>;
export function getEvent({ eventId, meta }: {
eventId: any;
meta: any;
}): Promise<any>;
export function processEvent({ eventId, shortName, archive }: {
eventId: any;
shortName: any;
archive: any;
}): Promise<any>;
+38
View File
@@ -0,0 +1,38 @@
"use strict";
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function produceEvent({ type, senderShortName, recipientShortName, agreementId, meta, data, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(_getUrl('/api/v1/data/event/produce'), {
type,
senderShortName,
recipientShortName,
agreementId: agreementId || getDefault('agreementId'),
meta,
data,
archive: archive || defaultArchive,
}, { headers: _headers() })).data;
}
async function getEvent({ eventId, meta }) {
return (await axios.post(_getUrl('/api/v1/data/event/get'), { eventId, meta }, { headers: _headers() })).data;
}
async function processEvent({ eventId, shortName, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(_getUrl('/api/v1/data/event/process'), {
eventId,
shortName,
archive: archive || defaultArchive,
}, { headers: _headers() })).data;
}
module.exports = { produceEvent, getEvent, processEvent };
+13 -58
View File
@@ -1,58 +1,13 @@
export type JLINCConfig = { export let init: typeof import("./config.js").init;
debug?: boolean | undefined; export let auth: typeof import("./auth.js").auth;
systemPrefix?: string | undefined; export let getDomains: typeof import("./entity.js").getDomains;
defaultAgreementId?: string | undefined; export let getEntity: typeof import("./entity.js").getEntity;
domain?: string | null | undefined; export let createEntity: typeof import("./entity.js").createEntity;
dataStoreApiUrl?: string | null | undefined; export let produceAgreement: typeof import("./agreement.js").produceAgreement;
dataStoreApiKey?: string | null | undefined; export let processAgreement: typeof import("./agreement.js").processAgreement;
archiveApiUrl?: string | null | undefined; export let getAgreement: typeof import("./agreement.js").getAgreement;
archiveApiKey?: string | null | undefined; export let produceEvent: typeof import("./event.js").produceEvent;
}; export let getEvent: typeof import("./event.js").getEvent;
export type JLINCProduceEventData = { export let processEvent: typeof import("./event.js").processEvent;
to?: string | undefined; export let getAudits: typeof import("./audit.js").getAudits;
from?: string | undefined; export let verifyAudit: typeof import("./audit.js").verifyAudit;
agreementId?: string | null | undefined;
payload?: any;
auth?: any;
};
export type JLINCProcessEventData = {
to?: string | undefined;
eventId?: string | undefined;
auth?: any;
};
export type JLINCProduceAgreementData = {
requiredSigners?: string[] | undefined;
signer?: string | undefined;
uri?: string | undefined;
purposes?: string[] | undefined;
caveats?: string[] | undefined;
validRoles?: string[] | undefined;
role?: string | undefined;
auth?: any;
};
export type JLINCProcessAgreementData = {
agreementId?: string | undefined;
signer?: string | undefined;
role?: string | undefined;
auth?: any;
};
/**
* @param {JLINCConfig} data - The configuration object for the application.
*/
export function jlincInit(data: JLINCConfig): void;
/**
* @param {JLINCProduceEventData} data - The data required for producing an event.
*/
export function jlincProduceEvent(data: JLINCProduceEventData): Promise<any>;
/**
* @param {JLINCProcessEventData} data - The data required for processing an event.
*/
export function jlincProcessEvent(data: JLINCProcessEventData): Promise<any>;
/**
* @param {JLINCProduceAgreementData} data - The data required for producing an agreement.
*/
export function jlincProduceAgreement(data: JLINCProduceAgreementData): Promise<any>;
/**
* @param {JLINCProcessAgreementData} data - The data required for processing an agreement.
*/
export function jlincProcessAgreement(data: JLINCProcessAgreementData): Promise<any>;
+15 -262
View File
@@ -1,263 +1,16 @@
const axios = require("axios"); "use strict";
/**
* @typedef {Object} JLINCConfig
* @property {boolean} [debug]
* @property {string} [systemPrefix]
* @property {string} [defaultAgreementId]
* @property {string|null} [domain]
* @property {string|null} [dataStoreApiUrl]
* @property {string|null} [dataStoreApiKey]
* @property {string|null} [archiveApiUrl]
* @property {string|null} [archiveApiKey]
*/
/**
* @typedef {Object} JLINCProduceEventData
* @property {string} [to]
* @property {string} [from]
* @property {string|null} [agreementId]
* @property {any} [payload]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProcessEventData
* @property {string} [to]
* @property {string} [eventId]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProduceAgreementData
* @property {Array<string>} [requiredSigners]
* @property {string} [signer]
* @property {string} [uri]
* @property {Array<string>} [purposes]
* @property {Array<string>} [caveats]
* @property {Array<string>} [validRoles]
* @property {string} [role]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProcessAgreementData
* @property {string} [agreementId]
* @property {string} [signer]
* @property {string} [role]
* @property {any} [auth]
*/
/**
* @param {JLINCConfig} config - The configuration object for the library
*/
let config = {
debug: false,
systemPrefix: 'my-system',
defaultAgreementId: '00000000-0000-0000-0000-000000000000',
domain: null,
dataStoreApiUrl: null,
dataStoreApiKey: null,
archiveApiUrl: null,
archiveApiKey: null,
}
const entities = new Set();
/**
* @param {string} input
* @returns {string}
*/
function sanitize(input) {
return input.replace(/[^a-zA-Z0-9._-]/g, '_');
}
/**
* @param {string} dir
* @param {any} payload
* @returns {Promise<any>}
*/
async function postToApi(dir, payload) {
const response = await axios.post(
`${config.dataStoreApiUrl}/api/v1/data/${dir}`,
payload,
{
headers: {
'Authorization': `Bearer ${config.dataStoreApiKey}`,
}
}
);
return response.data;
}
/**
* @param {string} entityShortName
* @returns {Promise<string>}
*/
async function setEntity(name) {
const entityShortName = `${config.systemPrefix}-${sanitize(name)}@${config.domain}`;
if (!entities.has(entityShortName)) {
try {
await postToApi(`entity/get`, { shortName: entityShortName });
} catch (e) {
await postToApi(`entity/create`, { shortName: entityShortName });
}
entities.add(entityShortName);
}
return entityShortName
}
/**
* @param {JLINCConfig} data - The configuration object for the application.
*/
function jlincInit(data) {
config = {
...config,
...data,
}
}
async function getDomain() {
if (!config.domain) {
const domains = await postToApi(`entity/domains/get`, {});
config.domain = domains[0];
}
}
/**
* @param {JLINCProduceEventData} data - The data required for producing an event.
*/
async function jlincProduceEvent(data) {
let res = null;
try {
await getDomain();
const senderShortName = await setEntity(data.from)
const recipientShortName = await setEntity(data.to)
const agreementId = data.agreementId ?? config.defaultAgreementId;
const postData = {
type: 'data',
senderShortName,
recipientShortName,
agreementId,
data: data.payload,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`event/produce`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to produce event:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProcessEventData} data - The data required for processing an event.
*/
async function jlincProcessEvent(data) {
let res = null;
try {
await getDomain();
const shortName = await setEntity(data.to)
const postData = {
type: 'data',
shortName,
eventId: data.eventId,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`event/process`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to process event:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProduceAgreementData} data - The data required for producing an agreement.
*/
async function jlincProduceAgreement(data) {
let res = null;
try {
await getDomain();
const shortNames = [];
for (const id of data.requiredSigners) {
shortNames.push(await setEntity(id))
}
const shortName = await setEntity(data.signer)
const postData = {
data: {
uri: data.uri,
purposes: data.purposes,
caveats: data.caveats,
shortNames,
validRoles: data.validRoles,
},
shortName,
role: data.role,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`agreement/produce`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to produce agreement:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProcessAgreementData} data - The data required for processing an agreement.
*/
async function jlincProcessAgreement(data) {
let res = null;
try {
await getDomain();
const shortName = await setEntity(data.signer)
const postData = {
agreementId: data.agreementId,
shortName,
role: data.role,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`agreement/process`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to process agreement:", error.message);
if (config.debug) console.error(error)
}
return res;
}
module.exports = { module.exports = {
jlincInit, init: require('./config.js').init,
jlincProduceEvent, auth: require('./auth.js').auth,
jlincProcessEvent, getDomains: require('./entity.js').getDomains,
jlincProduceAgreement, getEntity: require('./entity.js').getEntity,
jlincProcessAgreement, createEntity: require('./entity.js').createEntity,
} produceAgreement: require('./agreement.js').produceAgreement,
processAgreement: require('./agreement.js').processAgreement,
getAgreement: require('./agreement.js').getAgreement,
produceEvent: require('./event.js').produceEvent,
getEvent: require('./event.js').getEvent,
processEvent: require('./event.js').processEvent,
getAudits: require('./audit.js').getAudits,
verifyAudit: require('./audit.js').verifyAudit,
};
+1
View File
@@ -0,0 +1 @@
export { init, getConfig, getDefaults, getDefault } from "./config.js";
+8
View File
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefault = exports.getDefaults = exports.getConfig = exports.init = void 0;
var config_js_1 = require("./config.js");
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return config_js_1.init; } });
Object.defineProperty(exports, "getConfig", { enumerable: true, get: function () { return config_js_1.getConfig; } });
Object.defineProperty(exports, "getDefaults", { enumerable: true, get: function () { return config_js_1.getDefaults; } });
Object.defineProperty(exports, "getDefault", { enumerable: true, get: function () { return config_js_1.getDefault; } });
+2
View File
@@ -0,0 +1,2 @@
const jlinc = require('./index.js');
module.exports = jlinc;
+16
View File
@@ -0,0 +1,16 @@
declare const _exports: {
init: (cfg: object, defaultOpts?: object) => void;
auth: (params: { subject: any; action: any; resource: any }) => Promise<any>;
getDomains: (params: { fedidUrl?: string; auth?: any }) => Promise<any>;
getEntity: (params: { shortName: string }) => Promise<any>;
createEntity: (params: { fedidUrl?: string; shortName: string }) => Promise<any>;
produceAgreement: (params: { data: any; shortName: string; role: string; archive?: any }) => Promise<any>;
processAgreement: (params: { agreementId?: string; shortName: string; role: string; archive?: any }) => Promise<any>;
getAgreement: (params: { agreementId?: string; includeSignatures?: boolean }) => Promise<any>;
produceEvent: (params: { type: string; senderShortName: string; recipientShortName: string; agreementId?: string; meta?: any; data: any; archive?: any }) => Promise<any>;
getEvent: (params: { eventId?: string; meta?: any }) => Promise<any>;
processEvent: (params: { eventId: string; shortName: string; archive?: any }) => Promise<any>;
getAudits: (params: { agreementId?: string; eventId?: string; meta?: any }) => Promise<any>;
verifyAudit: (params: { audits: string[]; shortNames: string[] }) => Promise<any>;
};
export = _exports;
+13
View File
@@ -0,0 +1,13 @@
export function init(cfg: object, defaultOpts?: object): void;
export function auth(params: { subject: any; action: any; resource: any }): Promise<any>;
export function getDomains(params: { fedidUrl?: string; auth?: any }): Promise<any>;
export function getEntity(params: { shortName: string }): Promise<any>;
export function createEntity(params: { fedidUrl?: string; shortName: string }): Promise<any>;
export function produceAgreement(params: { data: any; shortName: string; role: string; archive?: any }): Promise<any>;
export function processAgreement(params: { agreementId?: string; shortName: string; role: string; archive?: any }): Promise<any>;
export function getAgreement(params: { agreementId?: string; includeSignatures?: boolean }): Promise<any>;
export function produceEvent(params: { type: string; senderShortName: string; recipientShortName: string; agreementId?: string; meta?: any; data: any; archive?: any }): Promise<any>;
export function getEvent(params: { eventId?: string; meta?: any }): Promise<any>;
export function processEvent(params: { eventId: string; shortName: string; archive?: any }): Promise<any>;
export function getAudits(params: { agreementId?: string; eventId?: string; meta?: any }): Promise<any>;
export function verifyAudit(params: { audits: string[]; shortNames: string[] }): Promise<any>;
+9
View File
@@ -0,0 +1,9 @@
import jlinc from './index.js';
export const { init, getConfig, getDefaults, getDefault } = jlinc;
export const { auth } = jlinc;
export const { getDomains, getEntity, createEntity } = jlinc;
export const { produceAgreement, processAgreement, getAgreement } = jlinc;
export const { produceEvent, getEvent, processEvent } = jlinc;
export const { getAudits, verifyAudit } = jlinc;
export default jlinc;
-7
View File
@@ -1,7 +0,0 @@
JLINC_DATA_STORE_API_URL="http://localhost:9090"
JLINC_DATA_STORE_API_KEY=""
JLINC_ARCHIVE_API_URL="http://localhost:9090"
JLINC_ARCHIVE_API_KEY=""
JLINC_SYSTEM_PREFIX="TestJlinc"
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
};
+1441 -1609
View File
File diff suppressed because it is too large. Load diff
+31 -21
View File
@@ -1,17 +1,26 @@
{ {
"name": "@jlinc/sdk", "name": "@jlinc/sdk",
"version": "1.0.0", "version": "1.1.0",
"description": "JLINC SDK for utilizing the JLINC API.", "description": "JLINC SDK for utilizing the JLINC API.",
"main": "dist/index.js", "main": "./dist/jlinc.cjs",
"types": "dist/index.d.ts", "types": "./dist/jlinc.d.ts",
"scripts": { "exports": {
"test": "jest", ".": {
"build": "rm -rf dist && tsc --project tsconfig.build.json && cp src/index.js dist/" "import": {
"types": "./dist/jlinc.d.ts",
"default": "./dist/jlinc.mjs"
},
"require": {
"types": "./dist/jlinc.cjs.d.ts",
"default": "./dist/jlinc.cjs"
}
}
}, },
"homepage": "https://www.jlinc.com/", "scripts": {
"repository": { "build": "rm -rf dist && tsc --project tsconfig.build.json && cp src/jlinc.cjs dist/ && cp src/jlinc.mjs dist/ && cp src/jlinc.d.ts dist/ && cp src/jlinc.cjs.d.ts dist/",
"type": "git", "test": "jest --runInBand --testPathPattern \"index\\.test\\.js\"",
"url": "https://gitea.jlinc.io/jlinc-labs/jlinc-sdk-node" "test:integration": "jest --runInBand --testPathPattern \"integration/.*\\.test\\.js\"",
"test:integration-ts": "jest --runInBand --testPathPattern \"integration/.*\\.test\\.ts\""
}, },
"keywords": [ "keywords": [
"jlinc", "jlinc",
@@ -19,17 +28,18 @@
"governance", "governance",
"authorization" "authorization"
], ],
"author": "Ben Curtis",
"license": "MIT",
"dependencies": {
"axios": "^1.14.0"
},
"devDependencies": {
"dotenv": "^17.4.1",
"jest": "^30.3.0",
"typescript": "^6.0.2"
},
"files": [ "files": [
"dist/*" "dist/*"
] ],
"license": "MIT",
"dependencies": {
"axios": "^1.7.0"
},
"devDependencies": {
"@types/jest": "^29.0.0",
"@types/node": "^20.0.0",
"dotenv": "^16.0.0",
"jest": "^29.0.0",
"ts-jest": "^29.4.15"
}
} }
+53
View File
@@ -0,0 +1,53 @@
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function produceAgreement({ data, shortName, role, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(
_getUrl('/api/v1/data/agreement/produce'),
{
data,
shortName,
role,
archive: archive || defaultArchive,
},
{ headers: _headers() },
)).data;
}
async function processAgreement({ agreementId, shortName, role, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(
_getUrl('/api/v1/data/agreement/process'),
{
agreementId: agreementId || getDefault('agreementId'),
shortName,
role,
archive: archive || defaultArchive,
},
{ headers: _headers() },
)).data;
}
async function getAgreement({ agreementId, includeSignatures }) {
return (await axios.post(
_getUrl('/api/v1/data/agreement/get'),
{ agreementId: agreementId || getDefault('agreementId'), includeSignatures },
{ headers: _headers() },
)).data;
}
module.exports = { produceAgreement, processAgreement, getAgreement };
+36
View File
@@ -0,0 +1,36 @@
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _getArchiveUrl(path) {
const cfg = getConfig();
return `${cfg.archiveUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function getAudits({ agreementId, eventId, meta }) {
return (await axios.post(
_getArchiveUrl('/api/v1/audit/get'),
{ agreementId: agreementId || getDefault('agreementId'), eventId, meta },
{ headers: _headers(getConfig().archiveKey) },
)).data;
}
async function verifyAudit({ audits, shortNames }) {
return (await axios.post(
_getUrl('/api/v1/data/audit/verify'),
{ audits, shortNames },
{ headers: _headers() },
)).data;
}
module.exports = { getAudits, verifyAudit };
+23
View File
@@ -0,0 +1,23 @@
const axios = require('axios');
const { getConfig } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function auth({ subject, action, resource }) {
return (await axios.post(
_getUrl('/api/v1/auth'),
{ subject, action, resource },
{ headers: _headers() },
)).data;
}
module.exports = { auth };
+23
View File
@@ -0,0 +1,23 @@
let config = {};
let defaults = {};
function init(cfg, defaultOpts) {
config = { ...cfg };
if (defaultOpts) {
defaults = { ...defaultOpts };
}
}
function getConfig() {
return config;
}
function getDefaults() {
return defaults;
}
function getDefault(key) {
return defaults[key];
}
module.exports = { init, getConfig, getDefaults, getDefault };
+47
View File
@@ -0,0 +1,47 @@
const axios = require('axios');
const { getConfig } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function getDomains({ fedidUrl, auth: authPayload }) {
const cfg = getConfig();
return (await axios.post(
_getUrl('/api/v1/data/entity/domains/get'),
{
fedidUrl: fedidUrl || cfg.fedidUrl,
auth: authPayload,
},
{ headers: _headers() },
)).data;
}
async function createEntity({ fedidUrl, shortName }) {
const cfg = getConfig();
return (await axios.post(
_getUrl('/api/v1/data/entity/create'),
{
fedidUrl: fedidUrl || cfg.fedidUrl,
shortName,
},
{ headers: _headers() },
)).data;
}
async function getEntity({ shortName }) {
return (await axios.post(
_getUrl('/api/v1/data/entity/get'),
{ shortName },
{ headers: _headers() },
));
}
module.exports = { getDomains, getEntity, createEntity };
+55
View File
@@ -0,0 +1,55 @@
const axios = require('axios');
const { getConfig, getDefault } = require('./config.js');
function _getUrl(path) {
const cfg = getConfig();
return `${cfg.apiUrl}${path}`;
}
function _headers(token) {
return {
'Authorization': `Bearer ${token || getConfig().apiKey}`,
};
}
async function produceEvent({ type, senderShortName, recipientShortName, agreementId, meta, data, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(
_getUrl('/api/v1/data/event/produce'),
{
type,
senderShortName,
recipientShortName,
agreementId: agreementId || getDefault('agreementId'),
meta,
data,
archive: archive || defaultArchive,
},
{ headers: _headers() },
)).data;
}
async function getEvent({ eventId, meta }) {
return (await axios.post(
_getUrl('/api/v1/data/event/get'),
{ eventId, meta },
{ headers: _headers() },
)).data;
}
async function processEvent({ eventId, shortName, archive }) {
const cfg = getConfig();
const defaultArchive = { url: cfg.archiveUrl, key: cfg.archiveKey };
return (await axios.post(
_getUrl('/api/v1/data/event/process'),
{
eventId,
shortName,
archive: archive || defaultArchive,
},
{ headers: _headers() },
)).data;
}
module.exports = { produceEvent, getEvent, processEvent };
+14 -262
View File
@@ -1,263 +1,15 @@
const axios = require("axios");
/**
* @typedef {Object} JLINCConfig
* @property {boolean} [debug]
* @property {string} [systemPrefix]
* @property {string} [defaultAgreementId]
* @property {string|null} [domain]
* @property {string|null} [dataStoreApiUrl]
* @property {string|null} [dataStoreApiKey]
* @property {string|null} [archiveApiUrl]
* @property {string|null} [archiveApiKey]
*/
/**
* @typedef {Object} JLINCProduceEventData
* @property {string} [to]
* @property {string} [from]
* @property {string|null} [agreementId]
* @property {any} [payload]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProcessEventData
* @property {string} [to]
* @property {string} [eventId]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProduceAgreementData
* @property {Array<string>} [requiredSigners]
* @property {string} [signer]
* @property {string} [uri]
* @property {Array<string>} [purposes]
* @property {Array<string>} [caveats]
* @property {Array<string>} [validRoles]
* @property {string} [role]
* @property {any} [auth]
*/
/**
* @typedef {Object} JLINCProcessAgreementData
* @property {string} [agreementId]
* @property {string} [signer]
* @property {string} [role]
* @property {any} [auth]
*/
/**
* @param {JLINCConfig} config - The configuration object for the library
*/
let config = {
debug: false,
systemPrefix: 'my-system',
defaultAgreementId: '00000000-0000-0000-0000-000000000000',
domain: null,
dataStoreApiUrl: null,
dataStoreApiKey: null,
archiveApiUrl: null,
archiveApiKey: null,
}
const entities = new Set();
/**
* @param {string} input
* @returns {string}
*/
function sanitize(input) {
return input.replace(/[^a-zA-Z0-9._-]/g, '_');
}
/**
* @param {string} dir
* @param {any} payload
* @returns {Promise<any>}
*/
async function postToApi(dir, payload) {
const response = await axios.post(
`${config.dataStoreApiUrl}/api/v1/data/${dir}`,
payload,
{
headers: {
'Authorization': `Bearer ${config.dataStoreApiKey}`,
}
}
);
return response.data;
}
/**
* @param {string} entityShortName
* @returns {Promise<string>}
*/
async function setEntity(name) {
const entityShortName = `${config.systemPrefix}-${sanitize(name)}@${config.domain}`;
if (!entities.has(entityShortName)) {
try {
await postToApi(`entity/get`, { shortName: entityShortName });
} catch (e) {
await postToApi(`entity/create`, { shortName: entityShortName });
}
entities.add(entityShortName);
}
return entityShortName
}
/**
* @param {JLINCConfig} data - The configuration object for the application.
*/
function jlincInit(data) {
config = {
...config,
...data,
}
}
async function getDomain() {
if (!config.domain) {
const domains = await postToApi(`entity/domains/get`, {});
config.domain = domains[0];
}
}
/**
* @param {JLINCProduceEventData} data - The data required for producing an event.
*/
async function jlincProduceEvent(data) {
let res = null;
try {
await getDomain();
const senderShortName = await setEntity(data.from)
const recipientShortName = await setEntity(data.to)
const agreementId = data.agreementId ?? config.defaultAgreementId;
const postData = {
type: 'data',
senderShortName,
recipientShortName,
agreementId,
data: data.payload,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`event/produce`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to produce event:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProcessEventData} data - The data required for processing an event.
*/
async function jlincProcessEvent(data) {
let res = null;
try {
await getDomain();
const shortName = await setEntity(data.to)
const postData = {
type: 'data',
shortName,
eventId: data.eventId,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`event/process`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to process event:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProduceAgreementData} data - The data required for producing an agreement.
*/
async function jlincProduceAgreement(data) {
let res = null;
try {
await getDomain();
const shortNames = [];
for (const id of data.requiredSigners) {
shortNames.push(await setEntity(id))
}
const shortName = await setEntity(data.signer)
const postData = {
data: {
uri: data.uri,
purposes: data.purposes,
caveats: data.caveats,
shortNames,
validRoles: data.validRoles,
},
shortName,
role: data.role,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`agreement/produce`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to produce agreement:", error.message);
if (config.debug) console.error(error)
}
return res;
}
/**
* @param {JLINCProcessAgreementData} data - The data required for processing an agreement.
*/
async function jlincProcessAgreement(data) {
let res = null;
try {
await getDomain();
const shortName = await setEntity(data.signer)
const postData = {
agreementId: data.agreementId,
shortName,
role: data.role,
archive: {
url: config.archiveApiUrl,
key: config.archiveApiKey,
}
};
if (data.auth) postData.auth = data.auth;
res = await postToApi(`agreement/process`, postData);
if (config.debug) {
console.log(JSON.stringify({ res }, null, 2));
}
} catch (error) {
console.error("[JLINC] Failed to process agreement:", error.message);
if (config.debug) console.error(error)
}
return res;
}
module.exports = { module.exports = {
jlincInit, init: require('./config.js').init,
jlincProduceEvent, auth: require('./auth.js').auth,
jlincProcessEvent, getDomains: require('./entity.js').getDomains,
jlincProduceAgreement, getEntity: require('./entity.js').getEntity,
jlincProcessAgreement, createEntity: require('./entity.js').createEntity,
} produceAgreement: require('./agreement.js').produceAgreement,
processAgreement: require('./agreement.js').processAgreement,
getAgreement: require('./agreement.js').getAgreement,
produceEvent: require('./event.js').produceEvent,
getEvent: require('./event.js').getEvent,
processEvent: require('./event.js').processEvent,
getAudits: require('./audit.js').getAudits,
verifyAudit: require('./audit.js').verifyAudit,
};
+307 -164
View File
@@ -1,183 +1,326 @@
require('dotenv').config({ quiet: true }) const axios = require('axios');
const { const jlinc = require('./index');
jlincInit,
jlincProduceEvent,
jlincProcessEvent,
jlincProduceAgreement,
jlincProcessAgreement,
} = require("./index.js");
const printResults = false; jest.mock('axios');
function printResult(data) { const mockedAxios = axios;
if (printResults) {
console.log(`Result`);
console.log("---------------------------------------------");
console.log(JSON.stringify(data, null, 2));
console.log("---------------------------------------------");
}
}
function generateRandomString(length) { const mockResponse = (data) => ({ data });
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const mockPost = jest.fn();
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
async function main() { beforeEach(() => {
const config = { jest.clearAllMocks();
dataStoreApiUrl: process.env.JLINC_DATA_STORE_API_URL ?? "http://localhost:9090", mockedAxios.post = mockPost;
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY, mockPost.mockImplementation((url, data, config) => Promise.resolve(mockResponse({ ok: true, url, data, config })));
archiveApiUrl: process.env.JLINC_ARCHIVE_API_URL ?? "http://localhost:9090",
archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY,
defaultAgreementId: "00000000-0000-0000-0000-000000000000",
systemPrefix: process.env.JLINC_SYSTEM_PREFIX ?? "TestJlinc",
debug: false,
}
jlincInit(config);
try { jlinc.init(
{
apiKey: 'test-api-key',
apiUrl: 'http://localhost:9090',
archiveUrl: 'http://localhost:9090',
archiveKey: 'test-archive-key',
fedidUrl: 'http://localhost:8881',
},
{ agreementId: 'default-agreement-id' }
);
});
describe("JLINC transaction", () => { describe('auth', () => {
let data; it('should POST to /api/v1/auth with correct payload and headers', async () => {
let r; const subject = { type: 'user', id: 'tester' };
let agreementId; const action = { name: 'read' };
let eventId; const resource = { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } };
// Define participants await jlinc.auth({ subject, action, resource });
const p1 = generateRandomString(11);
const p2 = generateRandomString(11);
it(`Produce (create and sign) agreement`, async () => { expect(mockPost).toHaveBeenCalledWith(
data = { 'http://localhost:9090/api/v1/auth',
uri: 'http://localhost:9090/agreements/05c93a30cc699cfaee9185f23a5f68ffb12305acce1ca4b9444758fc4d594828', { subject, action, resource },
validRoles: ['provider', 'user'], { headers: { Authorization: 'Bearer test-api-key' } }
purposes: ['testing'], );
caveats: ['production'], });
requiredSigners: [p1], });
signer: p1,
role: 'provider',
}
r = await jlincProduceAgreement(data)
expect(r).toBeDefined();
printResult(r);
expect(r.created && r.processed).toBeDefined();
agreementId = r.created.agreementId
})
it(`Process (cross sign) agreement`, async () => { describe('getDomains', () => {
data = { it('should POST to /api/v1/data/entity/domains/get with fedidUrl and auth', async () => {
agreementId, const authPayload = { subject: { type: 'user', id: 'tester' }, action: { name: 'read' }, resource: { type: 'data', id: '1234' } };
signer: p2,
role: 'user',
}
r = await jlincProcessAgreement(data)
expect(r).toBeDefined();
printResult(r);
expect(r.auditData).toBeDefined();
expect(r.auditData.audit.agreementId === agreementId).toBe(true);
})
it(`Produce (create and sign) event for data to be sent`, async () => { await jlinc.getDomains({ fedidUrl: 'http://fedid.test', auth: authPayload });
data = {
from: p1,
to: p2,
agreementId,
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
expect(r).toBeDefined();
printResult(r);
expect(r.created && r.processed).toBeDefined();
expect(r.created.agreementId === agreementId).toBe(true);
eventId = r.created.eventId;
})
it(`Process (sign) by the receiver after data is received`, async () => { expect(mockPost).toHaveBeenCalledWith(
data = { 'http://localhost:9090/api/v1/data/entity/domains/get',
to: p2, { fedidUrl: 'http://fedid.test', auth: authPayload },
eventId, { headers: { Authorization: 'Bearer test-api-key' } }
} );
r = await jlincProcessEvent(data) });
expect(r).toBeDefined();
printResult(r);
expect(r.auditData).toBeDefined();
expect(r.auditData.audit.eventId === eventId).toBe(true);
})
it(`Produce (create and sign) event for data to be sent with successful auth`, async () => { it('should use default fedidUrl from config when not provided', async () => {
data = { const authPayload = { subject: { type: 'user', id: 'tester' }, action: { name: 'read' }, resource: { type: 'data', id: '1234' } };
from: p1,
to: p2, await jlinc.getDomains({ auth: authPayload });
auth: {
subject: { expect(mockPost).toHaveBeenCalledWith(
type: "user", 'http://localhost:9090/api/v1/data/entity/domains/get',
id: "tester", { fedidUrl: 'http://localhost:8881', auth: authPayload },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('getEntity', () => {
it('should POST to /api/v1/data/entity/get with shortName', async () => {
await jlinc.getEntity({ shortName: 'test-entity@test.domain' });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/entity/get',
{ shortName: 'test-entity@test.domain' },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('createEntity', () => {
it('should POST to /api/v1/data/entity/create with correct payload', async () => {
const shortName = 'test-entity';
await jlinc.createEntity({ fedidUrl: 'http://fedid.test', shortName });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/entity/create',
{ fedidUrl: 'http://fedid.test', shortName },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
it('should use default fedidUrl from config when not provided', async () => {
const shortName = 'test-entity';
await jlinc.createEntity({ shortName });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/entity/create',
{ fedidUrl: 'http://localhost:8881', shortName },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('produceAgreement', () => {
it('should POST to /api/v1/data/agreement/produce with correct payload and default archive', async () => {
const params = {
data: { references: ['ref1'], permitted: ['read'] },
shortName: 'provider',
role: 'provider',
};
await jlinc.produceAgreement(params);
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/agreement/produce',
{
...params,
archive: { url: 'http://localhost:9090', key: 'test-archive-key' },
}, },
action: { { headers: { Authorization: 'Bearer test-api-key' } }
name: "read", );
});
it('should use provided archive override', async () => {
const params = {
data: { references: ['ref1'] },
shortName: 'provider',
role: 'provider',
archive: { url: 'http://custom', key: 'custom-key' },
};
await jlinc.produceAgreement(params);
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/agreement/produce',
{ ...params, archive: { url: 'http://custom', key: 'custom-key' } },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('processAgreement', () => {
it('should POST to /api/v1/data/agreement/process with correct payload', async () => {
await jlinc.processAgreement({ agreementId: 'agr-1', shortName: 'user', role: 'user' });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/agreement/process',
{
agreementId: 'agr-1',
shortName: 'user',
role: 'user',
archive: { url: 'http://localhost:9090', key: 'test-archive-key' },
}, },
resource: { { headers: { Authorization: 'Bearer test-api-key' } }
type: "data", );
id: "1234", });
properties: {
ownerID: "tester@test.com",
}
}
},
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
expect(r).toBeDefined();
printResult(r);
expect(r.created && r.processed).toBeDefined();
expect(r.created.agreementId === config.defaultAgreementId).toBe(true);
})
it(`Produce (create and sign) event for data to be sent with failed auth`, async () => { it('should fall back to default agreementId when not provided', async () => {
data = { await jlinc.processAgreement({ shortName: 'user', role: 'user' });
from: p1,
to: p2, expect(mockPost).toHaveBeenCalledWith(
auth: { expect.any(String),
subject: { expect.objectContaining({ agreementId: 'default-agreement-id' }),
type: "user", { headers: { Authorization: 'Bearer test-api-key' } }
id: "tester", );
});
});
describe('getAgreement', () => {
it('should POST to /api/v1/data/agreement/get with correct payload', async () => {
await jlinc.getAgreement({ agreementId: 'agr-1', includeSignatures: true });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/agreement/get',
{ agreementId: 'agr-1', includeSignatures: true },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
it('should fall back to default agreementId when not provided', async () => {
await jlinc.getAgreement({ includeSignatures: true });
expect(mockPost).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({ agreementId: 'default-agreement-id' }),
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('produceEvent', () => {
it('should POST to /api/v1/data/event/produce with correct payload and default archive', async () => {
const params = {
type: 'data',
senderShortName: 'user1',
recipientShortName: 'provider1',
agreementId: 'agr-1',
meta: { id: '123' },
data: { query: 'test' },
};
await jlinc.produceEvent(params);
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/event/produce',
{ ...params, archive: { url: 'http://localhost:9090', key: 'test-archive-key' } },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
it('should fall back to default agreementId when not provided', async () => {
await jlinc.produceEvent({
type: 'data',
senderShortName: 'user1',
recipientShortName: 'provider1',
meta: {},
data: {},
});
expect(mockPost).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({ agreementId: 'default-agreement-id' }),
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('getEvent', () => {
it('should POST to /api/v1/data/event/get with eventId', async () => {
await jlinc.getEvent({ eventId: 'evt-1' });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/event/get',
{ eventId: 'evt-1', meta: undefined },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
it('should POST to /api/v1/data/event/get with meta filter', async () => {
await jlinc.getEvent({ meta: { id: '123' } });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/event/get',
{ eventId: undefined, meta: { id: '123' } },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('processEvent', () => {
it('should POST to /api/v1/data/event/process with correct payload and default archive', async () => {
await jlinc.processEvent({ eventId: 'evt-1', shortName: 'provider' });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/event/process',
{
eventId: 'evt-1',
shortName: 'provider',
archive: { url: 'http://localhost:9090', key: 'test-archive-key' },
}, },
action: { { headers: { Authorization: 'Bearer test-api-key' } }
name: "write", );
}, });
resource: { });
type: "data",
id: "1234",
properties: {
ownerID: "tester@test.com",
}
}
},
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
expect(r).toBeDefined();
printResult(r);
expect(r.decision).toBeDefined();
expect(r.decision).toBe(false);
})
})
} catch (err) { describe('getAudits', () => {
console.error("[*] ERROR:", err); it('should POST to /api/v1/audit/get with correct payload and archive auth', async () => {
} await jlinc.getAudits({ agreementId: 'agr-1' });
} expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/audit/get',
{ agreementId: 'agr-1', eventId: undefined, meta: undefined },
{ headers: { Authorization: 'Bearer test-archive-key' } }
);
});
main() it('should fall back to default agreementId when not provided', async () => {
await jlinc.getAudits({ eventId: 'evt-1' });
expect(mockPost).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({ agreementId: 'default-agreement-id' }),
{ headers: { Authorization: 'Bearer test-archive-key' } }
);
});
it('should use archiveKey from config for auth header', async () => {
await jlinc.getAudits({ eventId: 'evt-1' });
const callArgs = mockPost.mock.calls[0];
const headers = callArgs[2].headers;
expect(headers.Authorization).toBe('Bearer test-archive-key');
});
});
describe('verifyAudit', () => {
it('should POST to /api/v1/data/audit/verify with correct payload', async () => {
await jlinc.verifyAudit({ audits: ['a1', 'a2'], shortNames: ['s1', 's2'] });
expect(mockPost).toHaveBeenCalledWith(
'http://localhost:9090/api/v1/data/audit/verify',
{ audits: ['a1', 'a2'], shortNames: ['s1', 's2'] },
{ headers: { Authorization: 'Bearer test-api-key' } }
);
});
});
describe('config defaults', () => {
it('should use default agreementId from init second argument', async () => {
await jlinc.getAgreement({});
const callArgs = mockPost.mock.calls[0];
expect(callArgs[1].agreementId).toBe('default-agreement-id');
});
it('should allow overriding default agreementId per-call', async () => {
await jlinc.getAgreement({ agreementId: 'override-id' });
const callArgs = mockPost.mock.calls[0];
expect(callArgs[1].agreementId).toBe('override-id');
});
});
+1
View File
@@ -0,0 +1 @@
export { init, getConfig, getDefaults, getDefault } from './config.js';
+312
View File
@@ -0,0 +1,312 @@
require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') });
const jlinc = require('../index');
jlinc.init({
apiKey: process.env.JLINC_API_KEY,
apiUrl: process.env.JLINC_API_URL,
archiveUrl: process.env.JLINC_ARCHIVE_URL,
archiveKey: process.env.JLINC_ARCHIVE_KEY,
fedidUrl: process.env.JLINC_FEDID_URL,
});
let domain;
let provider;
let user;
let agreementId;
let agreement;
beforeAll(async () => {
const domains = await jlinc.getDomains({
fedidUrl: 'http://localhost:8881',
auth: {
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
},
});
domain = domains[0];
provider = await jlinc.createEntity({
fedidUrl: 'http://localhost:8881',
shortName: `provider-integ-${Date.now()}@${domain}`,
});
user = await jlinc.createEntity({
fedidUrl: 'http://localhost:8881',
shortName: `user-integ-${Date.now()}@${domain}`,
});
agreement = await jlinc.produceAgreement({
data: {
references: ['https://sisa.jlinc.org/v1/34020c5fb59ebc6507ebca4eb38090aec1097c6aec8d2ae2250ddfed4b4aa63c'],
permitted: ['data-sharing'],
prohibited: ['non-delegable'],
shortNames: [provider.didDoc.shortName],
validRoles: ['provider', 'user', 'third-party'],
},
shortName: provider.didDoc.shortName,
role: 'provider',
});
agreementId = agreement.created.agreementId;
await jlinc.processAgreement({
agreementId,
shortName: user.didDoc.shortName,
role: 'user',
});
});
describe('JLINC API Integration Tests', () => {
describe('auth', () => {
it('should authenticate successfully with read action', async () => {
const result = await jlinc.auth({
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
});
expect(result).toBeDefined();
});
it('should reject authentication with write action', async () => {
const result = await jlinc.auth({
subject: { type: 'user', id: 'tester' },
action: { name: 'write' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
});
expect(result).toBeDefined();
});
});
describe('domains', () => {
it('should get available domains', async () => {
const domains = await jlinc.getDomains({
fedidUrl: 'http://localhost:8881',
auth: {
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
},
});
expect(domains).toBeDefined();
expect(Array.isArray(domains)).toBe(true);
});
});
describe('entity creation', () => {
it('should create a provider entity', async () => {
expect(provider).toBeDefined();
expect(provider.didDoc).toBeDefined();
});
it('should create a user entity', async () => {
expect(user).toBeDefined();
expect(user.didDoc).toBeDefined();
});
});
describe('entity retrieval', () => {
it('should get a provider entity by shortName', async () => {
const result = await jlinc.getEntity({ shortName: provider.didDoc.shortName });
expect(result).toBeDefined();
expect(result.data).toBeDefined();
expect(result.data.didDoc).toBeDefined();
});
it('should get a user entity by shortName', async () => {
const result = await jlinc.getEntity({ shortName: user.didDoc.shortName });
expect(result).toBeDefined();
expect(result.data).toBeDefined();
expect(result.data.didDoc).toBeDefined();
});
});
describe('agreement lifecycle', () => {
it('should produce an agreement', async () => {
expect(agreement).toBeDefined();
expect(agreement.created).toBeDefined();
expect(agreementId).toBeDefined();
});
it('should process (cross-sign) the agreement as user', async () => {
const result = await jlinc.processAgreement({
agreementId,
shortName: user.didDoc.shortName,
role: 'user',
});
expect(result).toBeDefined();
});
it('should get the agreement', async () => {
const result = await jlinc.getAgreement({ agreementId, includeSignatures: true });
expect(result).toBeDefined();
});
});
describe('event lifecycle', () => {
it('should produce an event', async () => {
const result = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `integration-test-${Date.now()}` },
data: { query: 'Why am I making this?' },
});
expect(result).toBeDefined();
expect(result.created).toBeDefined();
});
it('should get an event by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `get-event-test-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get an event by meta ID', async () => {
const metaId = `meta-test-${Date.now()}`;
await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: metaId },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ meta: { myCustomId: metaId } });
expect(result).toBeDefined();
});
it('should process an event as provider', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `process-test-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.processEvent({
eventId: event.created.eventId,
shortName: provider.didDoc.shortName,
});
expect(result).toBeDefined();
});
});
describe('audit', () => {
it('should get agreement audits', async () => {
const result = await jlinc.getAudits({ agreementId });
expect(result).toBeDefined();
});
it('should get event audits by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `audit-event-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get event audits by meta ID', async () => {
const metaId = `audit-meta-${Date.now()}`;
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: metaId },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ meta: { myCustomId: metaId } });
expect(result).toBeDefined();
});
it('should verify audit records', async () => {
const audits = await jlinc.getAudits({ agreementId });
const result = await jlinc.verifyAudit({
audits: audits.auditRecords || [],
shortNames: [user.didDoc.shortName, provider.didDoc.shortName],
});
expect(result).toBeDefined();
});
});
describe('nil agreement events', () => {
it('should produce an event under the nil agreement', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-test-${Date.now()}` },
data: { query: 'Why am I making this?' },
});
expect(event).toBeDefined();
});
it('should get a nil agreement event by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-get-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get nil agreement event audits', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-audit-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should verify nil agreement audit', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-verify-${Date.now()}` },
data: { query: 'test' },
});
const audits = await jlinc.getAudits({ eventId: event.created.eventId });
const result = await jlinc.verifyAudit({
audits: audits.auditRecords || [],
shortNames: [user.didDoc.shortName, provider.didDoc.shortName],
});
expect(result).toBeDefined();
});
});
});
+310
View File
@@ -0,0 +1,310 @@
require('dotenv').config({ path: require('path').resolve(__dirname, '../.env') });
const jlinc = require('../index');
jlinc.init({
apiKey: process.env.JLINC_API_KEY,
apiUrl: process.env.JLINC_API_URL,
archiveUrl: process.env.JLINC_ARCHIVE_URL,
archiveKey: process.env.JLINC_ARCHIVE_KEY,
fedidUrl: process.env.JLINC_FEDID_URL,
});
let domain: string;
let provider: any;
let user: any;
let agreementId: string;
let agreement: any;
beforeAll(async () => {
const domains = await jlinc.getDomains({
fedidUrl: 'http://localhost:8881',
auth: {
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
},
});
domain = domains[0];
provider = await jlinc.createEntity({
fedidUrl: 'http://localhost:8881',
shortName: `provider-integ-${Date.now()}@${domain}`,
});
user = await jlinc.createEntity({
fedidUrl: 'http://localhost:8881',
shortName: `user-integ-${Date.now()}@${domain}`,
});
agreement = await jlinc.produceAgreement({
data: {
references: ['https://sisa.jlinc.org/v1/34020c5fb59ebc6507ebca4eb38090aec1097c6aec8d2ae2250ddfed4b4aa63c'],
permitted: ['data-sharing'],
prohibited: ['non-delegable'],
shortNames: [provider.didDoc.shortName],
validRoles: ['provider', 'user', 'third-party'],
},
shortName: provider.didDoc.shortName,
role: 'provider',
});
agreementId = agreement.created.agreementId;
await jlinc.processAgreement({
agreementId,
shortName: user.didDoc.shortName,
role: 'user',
});
});
describe('JLINC API Integration Tests', () => {
describe('auth', () => {
it('should authenticate successfully with read action', async () => {
const result = await jlinc.auth({
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
});
expect(result).toBeDefined();
});
it('should reject authentication with write action', async () => {
const result = await jlinc.auth({
subject: { type: 'user', id: 'tester' },
action: { name: 'write' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
});
expect(result).toBeDefined();
});
});
describe('domains', () => {
it('should get available domains', async () => {
const domains = await jlinc.getDomains({
fedidUrl: 'http://localhost:8881',
auth: {
subject: { type: 'user', id: 'tester' },
action: { name: 'read' },
resource: { type: 'data', id: '1234', properties: { ownerID: 'tester@test.com' } },
},
});
expect(domains).toBeDefined();
expect(Array.isArray(domains)).toBe(true);
});
});
describe('entity creation', () => {
it('should create a provider entity', async () => {
expect(provider).toBeDefined();
expect(provider.didDoc).toBeDefined();
});
it('should create a user entity', async () => {
expect(user).toBeDefined();
expect(user.didDoc).toBeDefined();
});
});
describe('entity retrieval', () => {
it('should get a provider entity by shortName', async () => {
const result = await jlinc.getEntity({ shortName: provider.didDoc.shortName });
expect(result).toBeDefined();
expect(result.data.didDoc).toBeDefined();
});
it('should get a user entity by shortName', async () => {
const result = await jlinc.getEntity({ shortName: user.didDoc.shortName });
expect(result).toBeDefined();
expect(result.data.didDoc).toBeDefined();
});
});
describe('agreement lifecycle', () => {
it('should produce an agreement', async () => {
expect(agreement).toBeDefined();
expect(agreement.created).toBeDefined();
expect(agreementId).toBeDefined();
});
it('should process (cross-sign) the agreement as user', async () => {
const result = await jlinc.processAgreement({
agreementId,
shortName: user.didDoc.shortName,
role: 'user',
});
expect(result).toBeDefined();
});
it('should get the agreement', async () => {
const result = await jlinc.getAgreement({ agreementId, includeSignatures: true });
expect(result).toBeDefined();
});
});
describe('event lifecycle', () => {
it('should produce an event', async () => {
const result = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `integration-test-${Date.now()}` },
data: { query: 'Why am I making this?' },
});
expect(result).toBeDefined();
expect(result.created).toBeDefined();
});
it('should get an event by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `get-event-test-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get an event by meta ID', async () => {
const metaId = `meta-test-${Date.now()}`;
await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: metaId },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ meta: { myCustomId: metaId } });
expect(result).toBeDefined();
});
it('should process an event as provider', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `process-test-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.processEvent({
eventId: event.created.eventId,
shortName: provider.didDoc.shortName,
});
expect(result).toBeDefined();
});
});
describe('audit', () => {
it('should get agreement audits', async () => {
const result = await jlinc.getAudits({ agreementId });
expect(result).toBeDefined();
});
it('should get event audits by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: `audit-event-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get event audits by meta ID', async () => {
const metaId = `audit-meta-${Date.now()}`;
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId,
meta: { myCustomId: metaId },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ meta: { myCustomId: metaId } });
expect(result).toBeDefined();
});
it('should verify audit records', async () => {
const audits = await jlinc.getAudits({ agreementId });
const result = await jlinc.verifyAudit({
audits: audits.auditRecords || [],
shortNames: [user.didDoc.shortName, provider.didDoc.shortName],
});
expect(result).toBeDefined();
});
});
describe('nil agreement events', () => {
it('should produce an event under the nil agreement', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-test-${Date.now()}` },
data: { query: 'Why am I making this?' },
});
expect(event).toBeDefined();
});
it('should get a nil agreement event by eventId', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-get-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getEvent({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should get nil agreement event audits', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-audit-${Date.now()}` },
data: { query: 'test' },
});
const result = await jlinc.getAudits({ eventId: event.created.eventId });
expect(result).toBeDefined();
});
it('should verify nil agreement audit', async () => {
const event = await jlinc.produceEvent({
type: 'data',
senderShortName: user.didDoc.shortName,
recipientShortName: provider.didDoc.shortName,
agreementId: '00000000-0000-0000-0000-000000000000',
meta: { myCustomId: `nil-verify-${Date.now()}` },
data: { query: 'test' },
});
const audits = await jlinc.getAudits({ eventId: event.created.eventId });
const result = await jlinc.verifyAudit({
audits: audits.auditRecords || [],
shortNames: [user.didDoc.shortName, provider.didDoc.shortName],
});
expect(result).toBeDefined();
});
});
});
+2
View File
@@ -0,0 +1,2 @@
const jlinc = require('./index.js');
module.exports = jlinc;
+16
View File
@@ -0,0 +1,16 @@
declare const _exports: {
init: (cfg: object, defaultOpts?: object) => void;
auth: (params: { subject: any; action: any; resource: any }) => Promise<any>;
getDomains: (params: { fedidUrl?: string; auth?: any }) => Promise<any>;
getEntity: (params: { shortName: string }) => Promise<any>;
createEntity: (params: { fedidUrl?: string; shortName: string }) => Promise<any>;
produceAgreement: (params: { data: any; shortName: string; role: string; archive?: any }) => Promise<any>;
processAgreement: (params: { agreementId?: string; shortName: string; role: string; archive?: any }) => Promise<any>;
getAgreement: (params: { agreementId?: string; includeSignatures?: boolean }) => Promise<any>;
produceEvent: (params: { type: string; senderShortName: string; recipientShortName: string; agreementId?: string; meta?: any; data: any; archive?: any }) => Promise<any>;
getEvent: (params: { eventId?: string; meta?: any }) => Promise<any>;
processEvent: (params: { eventId: string; shortName: string; archive?: any }) => Promise<any>;
getAudits: (params: { agreementId?: string; eventId?: string; meta?: any }) => Promise<any>;
verifyAudit: (params: { audits: string[]; shortNames: string[] }) => Promise<any>;
};
export = _exports;
+13
View File
@@ -0,0 +1,13 @@
export function init(cfg: object, defaultOpts?: object): void;
export function auth(params: { subject: any; action: any; resource: any }): Promise<any>;
export function getDomains(params: { fedidUrl?: string; auth?: any }): Promise<any>;
export function getEntity(params: { shortName: string }): Promise<any>;
export function createEntity(params: { fedidUrl?: string; shortName: string }): Promise<any>;
export function produceAgreement(params: { data: any; shortName: string; role: string; archive?: any }): Promise<any>;
export function processAgreement(params: { agreementId?: string; shortName: string; role: string; archive?: any }): Promise<any>;
export function getAgreement(params: { agreementId?: string; includeSignatures?: boolean }): Promise<any>;
export function produceEvent(params: { type: string; senderShortName: string; recipientShortName: string; agreementId?: string; meta?: any; data: any; archive?: any }): Promise<any>;
export function getEvent(params: { eventId?: string; meta?: any }): Promise<any>;
export function processEvent(params: { eventId: string; shortName: string; archive?: any }): Promise<any>;
export function getAudits(params: { agreementId?: string; eventId?: string; meta?: any }): Promise<any>;
export function verifyAudit(params: { audits: string[]; shortNames: string[] }): Promise<any>;
+9
View File
@@ -0,0 +1,9 @@
import jlinc from './index.js';
export const { init, getConfig, getDefaults, getDefault } = jlinc;
export const { auth } = jlinc;
export const { getDomains, getEntity, createEntity } = jlinc;
export const { produceAgreement, processAgreement, getAgreement } = jlinc;
export const { produceEvent, getEvent, processEvent } = jlinc;
export const { getAudits, verifyAudit } = jlinc;
export default jlinc;
-219
View File
@@ -1,219 +0,0 @@
require('dotenv').config({ quiet: true })
const {
jlincInit,
jlincProduceEvent,
jlincProcessEvent,
jlincProduceAgreement,
jlincProcessAgreement,
} = require("../src/index.js");
const printResults = true;
function generateRandomString(length) {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
async function main() {
const config = {
dataStoreApiUrl: process.env.JLINC_DATA_STORE_API_URL ?? "http://localhost:9090",
dataStoreApiKey: process.env.JLINC_DATA_STORE_API_KEY,
archiveApiUrl: process.env.JLINC_ARCHIVE_API_URL ?? "http://localhost:9090",
archiveApiKey: process.env.JLINC_ARCHIVE_API_KEY,
defaultAgreementId: "00000000-0000-0000-0000-000000000000",
systemPrefix: process.env.JLINC_SYSTEM_PREFIX ?? "TestJlinc",
debug: false,
}
jlincInit(config);
try {
let data;
let r;
// Define participants
const entity1 = generateRandomString(11);
const entity2 = generateRandomString(11);
console.log(`\nAction: Produce (create and sign) agreement`)
data = {
uri: 'http://localhost:9090/agreements/05c93a30cc699cfaee9185f23a5f68ffb12305acce1ca4b9444758fc4d594828',
validRoles: ['provider', 'user'],
purposes: ['testing'],
caveats: ['production'],
requiredSigners: [entity1],
signer: entity1,
role: 'provider',
}
r = await jlincProduceAgreement(data)
if (r) {
if (printResults) {
console.log(`Result:`)
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.created && r.processed) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
const agreementId = r.created.agreementId
console.log(`\nAction: Process (cross sign) agreement`)
data = {
agreementId,
signer: entity2,
role: 'user',
}
r = await jlincProcessAgreement(data)
if (r) {
if (printResults) {
console.log(`Result:`)
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.auditData && r.auditData.audit.agreementId === agreementId) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
console.log(`\nAction: Produce (create and sign) event for data to be sent`)
data = {
from: entity1,
to: entity2,
agreementId,
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
if (r) {
if (printResults) {
console.log(`Result:`)
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.created && r.processed && r.created.agreementId === agreementId) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
const eventId = r.created.eventId;
console.log(`\nAction: Process (sign) by the receiver after data is sent`)
data = {
to: entity2,
eventId,
}
r = await jlincProcessEvent(data)
if (r) {
console.log(`Result:`)
if (printResults) {
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.auditData && r.auditData.audit.eventId === eventId) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
console.log(`\nAction: Produce (create and sign) event for data to be sent with successful auth`)
data = {
from: entity1,
to: entity2,
auth: {
subject: {
type: "user",
id: "tester",
},
action: {
name: "read",
},
resource: {
type: "data",
id: "1234",
properties: {
ownerID: "tester@test.com",
}
}
},
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
if (r) {
if (printResults) {
console.log(`Result:`)
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.created && r.processed) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
console.log(`\nAction: Produce (create and sign) event for data to be sent with failed auth`)
data = {
from: entity1,
to: entity2,
auth: {
subject: {
type: "user",
id: "tester",
},
action: {
name: "write",
},
resource: {
type: "data",
id: "1234",
properties: {
ownerID: "tester@test.com",
}
}
},
payload: {
data: "testing",
},
}
r = await jlincProduceEvent(data)
if (r) {
if (printResults) {
console.log(`Result:`)
console.log(`---------------------------------------------`)
console.log(JSON.stringify(r, null, 2))
console.log(`---------------------------------------------`)
}
if (r.decision != undefined && r.decision == false) {
console.log('PASS')
} else {
throw new Error('FAIL')
}
}
} catch (err) {
console.error("[*] ERROR:", err);
}
}
main()
+16 -4
View File
@@ -1,6 +1,18 @@
{ {
"extends": "./tsconfig.json", "extends": "./tsconfig.json",
"exclude": [ "compilerOptions": {
"./src/*.test.*" "allowJs": true,
] "rootDir": "./src",
"declaration": true,
"declarationMap": false,
"sourceMap": false,
"types": ["node"]
},
"include": ["src/**/*.js", "src/**/*.d.ts"],
"exclude": [
"src/**/*.test.js",
"src/**/*.test.ts",
"src/integration",
"src/node_modules"
]
} }
+18 -14
View File
@@ -1,16 +1,20 @@
{ {
"compilerOptions": { "compilerOptions": {
"allowJs": true, "target": "ES2020",
"declaration": true, "module": "commonjs",
"emitDeclarationOnly": true, "lib": ["ES2020"],
"rootDir": "./src/", "outDir": "./dist",
"outDir": "dist", "rootDir": "./",
"target": "ESNext", "strict": true,
"module": "ESNext", "esModuleInterop": true,
"strict": true, "skipLibCheck": true,
"esModuleInterop": true "forceConsistentCasingInFileNames": true,
}, "resolveJsonModule": true,
"include": [ "declaration": true,
"src" "declarationMap": true,
] "sourceMap": true,
"types": ["jest", "node"]
},
"include": ["**/*.ts"],
"exclude": ["node_modules", "dist"]
} }