Skip to content

Commit

Permalink
fix: Principal JSON is compatible with @dfinity/utils jsonReviver helper
Browse files Browse the repository at this point in the history
  • Loading branch information
krpeacock committed Sep 22, 2023
1 parent 80ad8fb commit 8830c82
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/generated/changelog.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ <h1>Agent-JS Changelog</h1>
<section>
<h2>Version x.x.x</h2>
<ul>
<li>fix: Principal JSON is compatible with @dfinity/utils jsonReviver helper</li>
<li>feat: Principal class serializes to JSON</li>
<li>
feat: certificate checks validate that certificate time is not more than 5 minutes ahead
Expand Down
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"simple-cbor": "^0.4.1"
},
"devDependencies": {
"@dfinity/utils": "^0.0.22",
"@peculiar/webcrypto": "^1.4.3",
"@trust/webcrypto": "^0.9.2",
"@types/jest": "^28.1.4",
Expand Down
10 changes: 9 additions & 1 deletion packages/principal/src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Principal } from '.';
import { jsonReviver } from '@dfinity/utils';

describe('Principal', () => {
it('encodes properly', () => {
Expand Down Expand Up @@ -50,6 +51,13 @@ describe('Principal', () => {

it('serializes to JSON', () => {
const principal = Principal.fromText('ryjl3-tyaaa-aaaaa-aaaba-cai');
expect(JSON.stringify(principal)).toBe('"ryjl3-tyaaa-aaaaa-aaaba-cai"');

const json = principal.toJSON();
expect(json).toEqual({ __principal__: 'ryjl3-tyaaa-aaaaa-aaaba-cai' });

const stringified = JSON.stringify(principal);
expect(stringified).toEqual('{"__principal__":"ryjl3-tyaaa-aaaaa-aaaba-cai"}');

expect(JSON.parse(stringified, jsonReviver)).toEqual(principal);
});
});
26 changes: 20 additions & 6 deletions packages/principal/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { decode, encode } from './utils/base32';
import { getCrc32 } from './utils/getCrc';
import { sha224 } from './utils/sha224';

export const JSON_KEY_PRINCIPAL = '__principal__';
const SELF_AUTHENTICATING_SUFFIX = 2;
const ANONYMOUS_SUFFIX = 4;

Expand All @@ -13,6 +14,10 @@ const fromHexString = (hexString: string) =>
const toHexString = (bytes: Uint8Array) =>
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');

export type JsonnablePrincipal = {
[JSON_KEY_PRINCIPAL]: string;
};

export class Principal {
public static anonymous(): Principal {
return new this(new Uint8Array([ANONYMOUS_SUFFIX]));
Expand Down Expand Up @@ -50,15 +55,24 @@ export class Principal {
}

public static fromText(text: string): Principal {
const canisterIdNoDash = text.toLowerCase().replace(/-/g, '');
let maybePrincipal = text;
// If formatted as JSON string, parse it first
if (text.includes(JSON_KEY_PRINCIPAL)) {
const obj = JSON.parse(text);
if (JSON_KEY_PRINCIPAL in obj) {
maybePrincipal = obj[JSON_KEY_PRINCIPAL];
}
}

const canisterIdNoDash = maybePrincipal.toLowerCase().replace(/-/g, '');

let arr = decode(canisterIdNoDash);
arr = arr.slice(4, arr.length);

const principal = new this(arr);
if (principal.toText() !== text) {
if (principal.toText() !== maybePrincipal) {
throw new Error(
`Principal "${principal.toText()}" does not have a valid checksum (original value "${text}" may not be a valid Principal ID).`,
`Principal "${principal.toText()}" does not have a valid checksum (original value "${maybePrincipal}" may not be a valid Principal ID).`,
);
}

Expand Down Expand Up @@ -109,10 +123,10 @@ export class Principal {

/**
* Serializes to JSON
* @returns {string} string
* @returns {JsonnablePrincipal} a JSON object with a single key, {@link JSON_KEY_PRINCIPAL}, whose value is the principal as a string
*/
public toJSON(): string {
return this.toText();
public toJSON(): JsonnablePrincipal {
return { [JSON_KEY_PRINCIPAL]: this.toText() };
}

/**
Expand Down

0 comments on commit 8830c82

Please sign in to comment.