Skip to content

Commit 2519f8c

Browse files
authored
#147 - Added regex in JSON schema to check date-time properties accepted by DWN
1 parent 9b37d06 commit 2519f8c

File tree

13 files changed

+116
-78
lines changed

13 files changed

+116
-78
lines changed

json-schemas/definitions.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"did": {
1515
"type": "string",
1616
"pattern": "^did:([a-z0-9]+):((?:(?:[a-zA-Z0-9._-]|(?:%[0-9a-fA-F]{2}))*:)*((?:[a-zA-Z0-9._-]|(?:%[0-9a-fA-F]{2}))+))((;[a-zA-Z0-9_.:%-]+=[a-zA-Z0-9_.:%-]*)*)(\/[^#?]*)?([?][^#]*)?(#.*)?$"
17+
},
18+
"date-time": {
19+
"type": "string",
20+
"pattern": "^\\d{4}-[0-1]\\d-[0-3]\\dT(?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)\\.\\d{6}Z$"
1721
}
1822
}
19-
}
23+
}

json-schemas/hooks/hooks-write.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"type": "string"
3636
},
3737
"dateCreated": {
38-
"type": "string"
38+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
3939
},
4040
"schema": {
4141
"type": "string"

json-schemas/records/records-query.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"type": "string"
3535
},
3636
"dateCreated": {
37-
"type": "string"
37+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
3838
},
3939
"filter": {
4040
"type": "object",
@@ -71,10 +71,10 @@
7171
"additionalProperties": false,
7272
"properties": {
7373
"from": {
74-
"type": "string"
74+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
7575
},
7676
"to": {
77-
"type": "string"
77+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
7878
}
7979
}
8080
}

json-schemas/records/records-write.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,16 @@
121121
"type": "number"
122122
},
123123
"dateCreated": {
124-
"type": "string"
124+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
125125
},
126126
"dateModified": {
127-
"type": "string"
127+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
128128
},
129129
"published": {
130130
"type": "boolean"
131131
},
132132
"datePublished": {
133-
"type": "string"
133+
"$ref": "https://identity.foundation/dwn/json-schemas/defs.json#/definitions/date-time"
134134
},
135135
"dataFormat": {
136136
"type": "string"

package-lock.json

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tbd54566975/dwn-sdk-js",
3-
"version": "0.0.30",
3+
"version": "0.0.31",
44
"description": "A reference implementation of https://identity.foundation/decentralized-web-node/spec/",
55
"type": "module",
66
"types": "./dist/esm/src/index.d.ts",
@@ -60,7 +60,7 @@
6060
"@types/readable-stream": "2.3.15",
6161
"@types/secp256k1": "4.0.3",
6262
"abstract-level": "1.0.3",
63-
"ajv": "8.11.0",
63+
"ajv": "8.12.0",
6464
"blockstore-core": "3.0.0",
6565
"cross-fetch": "3.1.5",
6666
"date-fns": "2.28.0",
@@ -143,4 +143,4 @@
143143
"url": "https://github.com/TBD54566975/dwn-sdk-js/issues"
144144
},
145145
"homepage": "https://github.com/TBD54566975/dwn-sdk-js#readme"
146-
}
146+
}

tests/interfaces/records/handlers/records-query.spec.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ import { Message } from '../../../../src/core/message.js';
1919
import { MessageStoreLevel } from '../../../../src/store/message-store-level.js';
2020
import { RecordsQueryHandler } from '../../../../src/interfaces/records/handlers/records-query.js';
2121
import { StorageController } from '../../../../src/store/storage-controller.js';
22-
import { Temporal } from '@js-temporal/polyfill';
2322
import { TestDataGenerator } from '../../../utils/test-data-generator.js';
2423
import { TestStubGenerator } from '../../../utils/test-stub-generator.js';
24+
import { toTemporalInstant } from '@js-temporal/polyfill';
2525

2626
import { constructRecordsWriteIndexes } from '../../../../src/interfaces/records/handlers/records-write.js';
2727
import { DataStream, DidResolver, Dwn, HdKey, KeyDerivationScheme, Records } from '../../../../src/index.js';
2828
import { DateSort, RecordsQuery } from '../../../../src/interfaces/records/messages/records-query.js';
2929

3030
chai.use(chaiAsPromised);
3131

32+
function createDateString(d: Date): string {
33+
return toTemporalInstant.call(d).toString({ smallestUnit: 'microseconds' });
34+
}
35+
3236
describe('RecordsQueryHandler.handle()', () => {
3337
describe('functional tests', () => {
3438
let didResolver: DidResolver;
@@ -183,11 +187,12 @@ describe('RecordsQueryHandler.handle()', () => {
183187
expect(reply3.entries?.length).to.equal(0);
184188
});
185189

190+
186191
it('should be able to range query by `dateCreated`', async () => {
187192
// scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema
188-
const firstDayOf2021 = Temporal.PlainDateTime.from({ year: 2021, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
189-
const firstDayOf2022 = Temporal.PlainDateTime.from({ year: 2022, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
190-
const firstDayOf2023 = Temporal.PlainDateTime.from({ year: 2023, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
193+
const firstDayOf2021 = createDateString(new Date(2021, 1, 1));
194+
const firstDayOf2022 = createDateString(new Date(2022, 1, 1));
195+
const firstDayOf2023 = createDateString(new Date(2023, 1, 1));
191196
const alice = await DidKeyResolver.generate();
192197
const write1 = await TestDataGenerator.generateRecordsWrite({ requester: alice, dateCreated: firstDayOf2021, dateModified: firstDayOf2021 });
193198
const write2 = await TestDataGenerator.generateRecordsWrite({ requester: alice, dateCreated: firstDayOf2022, dateModified: firstDayOf2022 });
@@ -202,7 +207,7 @@ describe('RecordsQueryHandler.handle()', () => {
202207
expect(writeReply3.status.code).to.equal(202);
203208

204209
// testing `from` range
205-
const lastDayOf2021 = Temporal.PlainDateTime.from({ year: 2021, month: 12, day: 31 }).toString({ smallestUnit: 'microseconds' });
210+
const lastDayOf2021 = createDateString(new Date(2021, 12, 31));
206211
const recordsQuery1 = await TestDataGenerator.generateRecordsQuery({
207212
requester : alice,
208213
filter : { dateCreated: { from: lastDayOf2021 } },
@@ -214,7 +219,7 @@ describe('RecordsQueryHandler.handle()', () => {
214219
expect(reply1.entries![1].encodedData).to.equal(Encoder.bytesToBase64Url(write3.dataBytes!));
215220

216221
// testing `to` range
217-
const lastDayOf2022 = Temporal.PlainDateTime.from({ year: 2022, month: 12, day: 31 }).toString({ smallestUnit: 'microseconds' });
222+
const lastDayOf2022 = createDateString(new Date(2022, 12, 31));
218223
const recordsQuery2 = await TestDataGenerator.generateRecordsQuery({
219224
requester : alice,
220225
filter : { dateCreated: { to: lastDayOf2022 } },
@@ -226,7 +231,7 @@ describe('RecordsQueryHandler.handle()', () => {
226231
expect(reply2.entries![1].encodedData).to.equal(Encoder.bytesToBase64Url(write2.dataBytes!));
227232

228233
// testing `from` and `to` range
229-
const lastDayOf2023 = Temporal.PlainDateTime.from({ year: 2023, month: 12, day: 31 }).toString({ smallestUnit: 'microseconds' });
234+
const lastDayOf2023 = createDateString(new Date(2023, 12, 31));
230235
const recordsQuery3 = await TestDataGenerator.generateRecordsQuery({
231236
requester : alice,
232237
filter : { dateCreated: { from: lastDayOf2022, to: lastDayOf2023 } },
@@ -249,9 +254,9 @@ describe('RecordsQueryHandler.handle()', () => {
249254

250255
it('should be able use range and exact match queries at the same time', async () => {
251256
// scenario: 3 records authored by alice, created on first of 2021, 2022, and 2023 respectively, only the first 2 records share the same schema
252-
const firstDayOf2021 = Temporal.PlainDateTime.from({ year: 2021, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
253-
const firstDayOf2022 = Temporal.PlainDateTime.from({ year: 2022, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
254-
const firstDayOf2023 = Temporal.PlainDateTime.from({ year: 2023, month: 1, day: 1 }).toString({ smallestUnit: 'microseconds' });
257+
const firstDayOf2021 = createDateString(new Date(2021, 1, 1));
258+
const firstDayOf2022 = createDateString(new Date(2022, 1, 1));
259+
const firstDayOf2023 = createDateString(new Date(2023, 1, 1));
255260
const alice = await DidKeyResolver.generate();
256261
const schema = '2021And2022Schema';
257262
const write1 = await TestDataGenerator.generateRecordsWrite({
@@ -273,8 +278,8 @@ describe('RecordsQueryHandler.handle()', () => {
273278
expect(writeReply3.status.code).to.equal(202);
274279

275280
// testing range criterion with another exact match
276-
const lastDayOf2021 = Temporal.PlainDateTime.from({ year: 2021, month: 12, day: 31 }).toString({ smallestUnit: 'microseconds' });
277-
const lastDayOf2023 = Temporal.PlainDateTime.from({ year: 2023, month: 12, day: 31 }).toString({ smallestUnit: 'microseconds' });
281+
const lastDayOf2021 = createDateString(new Date(2021, 12, 31));
282+
const lastDayOf2023 = createDateString(new Date(2023, 12, 31));
278283
const recordsQuery5 = await TestDataGenerator.generateRecordsQuery({
279284
requester : alice,
280285
filter : {

tests/interfaces/records/handlers/records-write.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ describe('RecordsWriteHandler.handle()', () => {
425425

426426
it('should return 400 if `dateCreated` and `dateModified` are not the same in an initial write', async () => {
427427
const { requester, message, dataStream } = await TestDataGenerator.generateRecordsWrite({
428-
dateCreated : '2023-01-10T10:20:30.405060',
428+
dateCreated : '2023-01-10T10:20:30.405060Z',
429429
dateModified : getCurrentTimeInHighPrecision() // this always generate a different timestamp
430430
});
431431
const tenant = requester.did;

tests/interfaces/records/messages/records-write.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ describe('RecordsWrite', () => {
2525
recipient : alice.did,
2626
data : TestDataGenerator.randomBytes(10),
2727
dataFormat : 'application/json',
28-
dateCreated : '2022-10-14T10:20:30.405060',
28+
dateCreated : '2022-10-14T10:20:30.405060Z',
2929
recordId : await TestDataGenerator.randomCborSha256Cid(),
3030
authorizationSignatureInput : Jws.createSignatureInput(alice)
3131
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import Ajv from 'ajv';
2+
import definitions from '../../../json-schemas/definitions.json' assert { type: 'json' };
3+
4+
import { expect } from 'chai';
5+
6+
describe('date-time schema', async () => {
7+
8+
const ajv = new Ajv.default();
9+
const validateDateTime = ajv.compile(definitions.definitions['date-time']);
10+
11+
it('should accept ISO 8601 date-time strings accepted by DWN', () => {
12+
expect(validateDateTime('2022-04-29T10:30:00.123456Z')).to.be.true;
13+
});
14+
15+
it('should reject ISO 8601 date-time strings not accepted by DWN', () => {
16+
const unacceptableDateTimeStrings = [
17+
'2023-04-27T13:30:00.123456',
18+
'2023-04-27T13:30:00.123456z',
19+
'2023-04-27T13:30:00.1234Z',
20+
'2023-04-27T13:30:00Z',
21+
'2023-04-27T13:30:00.000000+00:00',
22+
'2023-04-27 13:30:00.000000Z'
23+
];
24+
25+
for (const dateTime of unacceptableDateTimeStrings) {
26+
expect(validateDateTime(dateTime)).to.be.false;
27+
}
28+
});
29+
});

0 commit comments

Comments
 (0)