It provides utility to
- Create a stable representation of a JSON object (all keys are ordered)
- Makes a Hash string of this stable representation (default SHA256)
- Creates a unique key to finds back the data item
Returns a "stable" JSON reprersentation of an event as described bellow.
Returns a hash of "stable" JSON reprersentation of an event, prefixed with 'EVENT:O' and encoded in base64 following subresource integrity (SRI) specifcation See W3.org/TR/SRI. By default SHA256 is used.
Example: EVENT:0:sha256-X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
Returns a unique key for this event version.
Returns an object equivalent of { key: key(event), hash: hash(event, algorithm) }
{
key: 'EVENT:0:ckoxy0giw000hrs9yvi3rnlfm:1621578570.151',
integrity: 'EVENT:0:sha256-X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE='
}
In this example we will use javascript as pseudo code examples.
- Representation is a valid JSON object.
JSON.parse(stableRep)
must be valid. (stableRep;
is a stable representation of a JSON object as a string.) - JSON representation should not have any 'spaces' between seprators.
{"keyA":"valueA","keyB":["item0B","item1B"]}
is valid,
while{"keyA": "valueA", "keyB": ["item0B", "item1B"]}
is not. - Numbers fields are represented without
"
.
{"keyForNumericalValue":12.23}
is valid,
while{"keyForNumericalValue":"12.23"}
is not. - Boolean fields are represented without
"
.
{"keyForBooleanValue":false}
is valid,
while{"keyForBooleanValue":"false"}
is not. - Array are kept is their original order. If order is modified stable representation differs.
- Object fields are ordered by their UTF16 characters codes value.
The folowing code exposes the logic implemented in javascript to be consumed by a
sort()
function.
/**
* Compare two strings based on the UTF16 code value of their characters.
* Characters are consumed one by one on each string.
* if the shortest string equals the first characters of the longest one, the shortest is before.
* @param a
* @param b
* @returns {number} -1 if a is before b, 1 if b is before a, 0 if they are equals.
*/
function utf16StrCompare(a, b) {
var lA = a.length;
var lB = b.length;
// default A is shorter and equals firstsB chars: A first
var l = lA;
var dres = -1;
// B is shorter and equals firstsA chars: B first
if (lA > lB) { l = lB; dres = -1; }
// A and B are the same size
if (lA === lB) {dres = 0; }
var res;
for (var i = 0; i < l; i++) {
res = a.charCodeAt(i) - b.charCodeAt(i);
if (res !== 0) {
return res;
}
}
return dres;
}
. key
. structure: {item code}:{item code version}:{item id}:{item version}
usually the item version is the modified
date property
. They key is unique it is used to find a corresponding checksum.
. integrity
. structure: {item code}:{item code version}:{algorithm}-{checksum base64 encode}
. EVENT:0:
correspond to item id = event.id
and item version = event.modified
. If event.modified
is not present, then event.deleted
is used.
. ACCESS:0:
correspond to item id =access.id
and item version = access.modified
. If access.modified
is not present, then access.deleted
is used.
. EVENT:0:
correspond to encoding of current implementation of stableRepresentation.event.stringify0(event)
. ACCESS:0:
correspond to encoding of current implementation of stableRepresentation.access.stringify0(access)
Properties with a null values, will be ignored from the representation.
Special attention is required for
- integrity: Obviously integrity is not taken for computation...
- apiEndpoint: is ignored from computation (full domain name is part of settings not data)
- lastUsed: is ignored as too dynamic...
- calls: is ignored as too dynamic...
Properties with a null values, will be ignored from the representation.
Special attention is required for
- integrity: Obviously integrity is not taken for computation...
- tags: !! Are always ignored as deprecated and now included in streamIds
- trashed: if
false
is ignored from the representation. - deleted: if
null
is ignored from the representation. - duration: if
0
is ignored from the representation <=> toundefined
. - streamId: if exists is replaced by
streamIds
whith the value[streamId]
- attachments.readToken: is ignored in the representation.
Example
{
"id": "ciusga35r000sgwg4o1sr1j5q",
"time": 1477575221.247,
"streamId": "diary",
"duration": 0,
"type": "picture/attached",
"tags": [],
"description": "test\"te\"st",
"attachments": [
{
"id": "ciusga35r000tgwg4hcz2i22u",
"fileName": "photo.jpg",
"type": "image/jpeg",
"size": 2561,
"readToken": "cjasdashdhgad-asdjhasdhsdh"
},
{
"id": "ciusga35r000tgwg4hcz2i32u",
"fileName": "photo.jpg",
"type": "image/jpeg",
"size": 2561,
"readToken": "cjasdashdhgad-asdjhasdhsdh"
}
],
"created": 1477575221.247,
"createdBy": "ciusga33w0004gwg436uhtqs2",
"modified": 1477575221.247,
"modifiedBy": "ciusga33w0004gwg436uhtqs2",
"trashed": false,
"clientData": {
"key2": "value2",
"key1": "value1"
}
}
Stable representation:
{"attachments":[{"fileName":"photo.jpg","id":"ciusga35r000tgwg4hcz2i22u","size":2561,"type":"image/jpeg"},{"fileName":"photo.jpg","id":"ciusga35r000tgwg4hcz2i32u","size":2561,"type":"image/jpeg"}],"clientData":{"key1":"value1","key2":"value2"},"created":1477575221.247,"createdBy":"ciusga33w0004gwg436uhtqs2","description":"test\"te\"st","id":"ciusga35r000sgwg4o1sr1j5q","modified":1477575221.247,"modifiedBy":"ciusga33w0004gwg436uhtqs2","streamId":"diary","time":1477575221.247,"type":"picture/attached"}
Copyright (C) 2020-2021 Pryv S.A. https://pryv.com This file is part of Open-Pryv.io and released under BSD-Clause-3 License SPDX-License-Identifier: BSD-3-Clause