@json-editor/json-editor 2.8.0 → 2.9.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/README.md +14 -2
- package/dist/jsoneditor.js +2 -2
- package/dist/nonmin/jsoneditor.js +267 -44
- package/dist/nonmin/jsoneditor.js.map +1 -1
- package/package.json +1 -1
- package/src/schemaloader.js +112 -19
- package/tests/codeceptjs/core_test.js +21 -0
- package/tests/pages/references.html +6 -0
- package/tests/unit/schemaloader.spec.js +108 -2
package/package.json
CHANGED
package/src/schemaloader.js
CHANGED
|
@@ -421,27 +421,40 @@ export class SchemaLoader {
|
|
|
421
421
|
waiting++
|
|
422
422
|
|
|
423
423
|
let url = this._joinUrl(uri, fileBase)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
resolve(r)
|
|
424
|
+
|
|
425
|
+
let externalSchema
|
|
426
|
+
if (this.options.ajax_cache_responses) {
|
|
427
|
+
const schemaFromCache = this.cacheGet(url)
|
|
428
|
+
if (schemaFromCache) {
|
|
429
|
+
externalSchema = schemaFromCache
|
|
431
430
|
}
|
|
432
|
-
|
|
433
|
-
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (!externalSchema) {
|
|
434
|
+
const response = await new Promise(resolve => {
|
|
435
|
+
const r = new XMLHttpRequest()
|
|
436
|
+
if (this.options.ajaxCredentials) r.withCredentials = this.options.ajaxCredentials
|
|
437
|
+
r.overrideMimeType('application/json')
|
|
438
|
+
r.open('GET', url, true)
|
|
439
|
+
r.onload = () => {
|
|
440
|
+
resolve(r)
|
|
441
|
+
}
|
|
442
|
+
r.onerror = (e) => {
|
|
443
|
+
resolve(undefined)
|
|
444
|
+
}
|
|
445
|
+
r.send()
|
|
446
|
+
})
|
|
447
|
+
if (typeof response === 'undefined') throw new Error(`Failed to fetch ref via ajax - ${uri}`)
|
|
448
|
+
try {
|
|
449
|
+
externalSchema = JSON.parse(response.responseText)
|
|
450
|
+
if (this.options.ajax_cache_responses) {
|
|
451
|
+
this.cacheSet(url, externalSchema)
|
|
452
|
+
}
|
|
453
|
+
} catch (e) {
|
|
454
|
+
// eslint-disable-next-line no-console
|
|
455
|
+
console.log(e)
|
|
456
|
+
throw new Error(`Failed to parse external ref ${url}`)
|
|
434
457
|
}
|
|
435
|
-
r.send()
|
|
436
|
-
})
|
|
437
|
-
if (typeof response === 'undefined') throw new Error(`Failed to fetch ref via ajax - ${uri}`)
|
|
438
|
-
let externalSchema
|
|
439
|
-
try {
|
|
440
|
-
externalSchema = JSON.parse(response.responseText)
|
|
441
|
-
} catch (e) {
|
|
442
|
-
// eslint-disable-next-line no-console
|
|
443
|
-
console.log(e)
|
|
444
|
-
throw new Error(`Failed to parse external ref ${url}`)
|
|
445
458
|
}
|
|
446
459
|
|
|
447
460
|
if (!(typeof externalSchema === 'boolean' || typeof externalSchema === 'object') || externalSchema === null || Array.isArray(externalSchema)) {
|
|
@@ -528,4 +541,84 @@ export class SchemaLoader {
|
|
|
528
541
|
})
|
|
529
542
|
return extended
|
|
530
543
|
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Gets a cache key namespaced for JSON Editor.
|
|
547
|
+
*
|
|
548
|
+
* @param {*} key
|
|
549
|
+
* The schema's key, e.g., URL.
|
|
550
|
+
* @returns {string}
|
|
551
|
+
* A namespaced cache key, by prefixing "je-cache::".
|
|
552
|
+
*/
|
|
553
|
+
getCacheKey (key) {
|
|
554
|
+
return ['je-cache', key].join('::')
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Returns the schema cache buster from JSON Editor settings.
|
|
559
|
+
*
|
|
560
|
+
* @returns {string}
|
|
561
|
+
* The configured cache buster, if any. Otherwise, returns the current date
|
|
562
|
+
* in ISO 8601 simplified format (e.g., 2011-10-05 for October 5, 2011).
|
|
563
|
+
*/
|
|
564
|
+
getCacheBuster () {
|
|
565
|
+
return this.options.ajax_cache_buster || new Date().toISOString().slice(0, 10)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Sets a schema into localStorage cache.
|
|
570
|
+
*
|
|
571
|
+
* @param {string} key
|
|
572
|
+
* The schema's key, e.g., URL.
|
|
573
|
+
* @param {mixed} data
|
|
574
|
+
* The schema to store. Can be any data type.
|
|
575
|
+
*/
|
|
576
|
+
cacheSet (key, data) {
|
|
577
|
+
try {
|
|
578
|
+
window.localStorage.setItem(this.getCacheKey(key), JSON.stringify({
|
|
579
|
+
cacheBuster: this.getCacheBuster(),
|
|
580
|
+
schema: data
|
|
581
|
+
}))
|
|
582
|
+
} catch (e) {
|
|
583
|
+
// eslint-disable-next-line no-console
|
|
584
|
+
console.error(e)
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Fetches a schema from localStorage cache.
|
|
590
|
+
*
|
|
591
|
+
* @param {string} key
|
|
592
|
+
* The schema's key, e.g., URL.
|
|
593
|
+
*
|
|
594
|
+
* @returns {mixed}
|
|
595
|
+
* If found, returns the schema.
|
|
596
|
+
*/
|
|
597
|
+
cacheGet (key) {
|
|
598
|
+
try {
|
|
599
|
+
const resultRaw = window.localStorage.getItem(this.getCacheKey(key))
|
|
600
|
+
if (resultRaw) {
|
|
601
|
+
const resultDecoded = JSON.parse(resultRaw)
|
|
602
|
+
if (resultDecoded.cacheBuster && resultDecoded.schema) {
|
|
603
|
+
if (resultDecoded.cacheBuster === this.getCacheBuster()) {
|
|
604
|
+
return resultDecoded.schema
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
this.cacheDelete(key)
|
|
608
|
+
}
|
|
609
|
+
} catch (e) {
|
|
610
|
+
// eslint-disable-next-line no-console
|
|
611
|
+
console.error(e)
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Deletes a schema from localStorage cache.
|
|
617
|
+
*
|
|
618
|
+
* @param {string} key
|
|
619
|
+
* The schema's key, e.g., URL.
|
|
620
|
+
*/
|
|
621
|
+
cacheDelete (key) {
|
|
622
|
+
window.localStorage.removeItem(this.getCacheKey(key))
|
|
623
|
+
}
|
|
531
624
|
}
|
|
@@ -247,6 +247,27 @@ Scenario('should load internal schema definitions, external schema definitions a
|
|
|
247
247
|
|
|
248
248
|
// external schema properties
|
|
249
249
|
I.waitForElement('[data-schemapath="root.link.street_address"]')
|
|
250
|
+
|
|
251
|
+
const currentUrl = await I.grabCurrentUrl()
|
|
252
|
+
const currentPath = currentUrl.replace('references.html', '')
|
|
253
|
+
|
|
254
|
+
// Ensures that external schemas were stored in cache. (This does not assert that the loader actually fetched them from cache.)
|
|
255
|
+
const schemaPaths = [
|
|
256
|
+
'../fixtures/string.json',
|
|
257
|
+
'../fixtures/definitions.json',
|
|
258
|
+
'../fixtures/basic_person.json',
|
|
259
|
+
'../fixtures/person.json',
|
|
260
|
+
]
|
|
261
|
+
for (const path of schemaPaths) {
|
|
262
|
+
let key = 'je-cache::' + currentPath + path;
|
|
263
|
+
|
|
264
|
+
const item = await I.executeScript(function (storageKey) {
|
|
265
|
+
return window.localStorage.getItem(storageKey);
|
|
266
|
+
}, key)
|
|
267
|
+
const itemDecoded = JSON.parse(item)
|
|
268
|
+
assert.equal(itemDecoded.cacheBuster, 'abc123');
|
|
269
|
+
assert(itemDecoded, 'Cached schema found');
|
|
270
|
+
}
|
|
250
271
|
})
|
|
251
272
|
|
|
252
273
|
Scenario('should override error messages if specified in schema options @core @errors-messages', async (I) => {
|
|
@@ -58,6 +58,12 @@
|
|
|
58
58
|
// Enable fetching schemas via ajax
|
|
59
59
|
ajax: true,
|
|
60
60
|
|
|
61
|
+
// Enable caching schemas from via ajax
|
|
62
|
+
ajax_cache_responses: true,
|
|
63
|
+
|
|
64
|
+
// The cache-buster for cached caches
|
|
65
|
+
ajax_cache_buster: 'abc123',
|
|
66
|
+
|
|
61
67
|
// The schema for the editor
|
|
62
68
|
schema: {
|
|
63
69
|
$schema: "https://json-schema.org/draft-04/schema",
|
|
@@ -80,7 +80,7 @@ describe('SchemaLoader', () => {
|
|
|
80
80
|
})
|
|
81
81
|
|
|
82
82
|
describe('when external absolute ref exists', () => {
|
|
83
|
-
it('should set
|
|
83
|
+
it('should set option { ajax: true }', async () => {
|
|
84
84
|
const response = {
|
|
85
85
|
type: 'string',
|
|
86
86
|
minLength: 4
|
|
@@ -117,7 +117,7 @@ describe('SchemaLoader', () => {
|
|
|
117
117
|
})
|
|
118
118
|
|
|
119
119
|
describe('when external relative $ref exists', () => {
|
|
120
|
-
it('should set
|
|
120
|
+
it('should set option { ajax: true }', async () => {
|
|
121
121
|
const response = {
|
|
122
122
|
type: 'string',
|
|
123
123
|
minLength: 4
|
|
@@ -467,4 +467,110 @@ describe('SchemaLoader', () => {
|
|
|
467
467
|
expect(Object.keys(loader.refs).length).toBe(1)
|
|
468
468
|
})
|
|
469
469
|
})
|
|
470
|
+
|
|
471
|
+
describe('when schemas caching is enabled', () => {
|
|
472
|
+
beforeEach(() => {
|
|
473
|
+
// Mocks window.localStorage system.
|
|
474
|
+
// Thanks to https://stackoverflow.com/a/32911774.
|
|
475
|
+
var localStorageMock = (function () {
|
|
476
|
+
var store = {}
|
|
477
|
+
return {
|
|
478
|
+
getItem: function (key) {
|
|
479
|
+
return store[key]
|
|
480
|
+
},
|
|
481
|
+
setItem: function (key, value) {
|
|
482
|
+
store[key] = value.toString()
|
|
483
|
+
},
|
|
484
|
+
clear: function () {
|
|
485
|
+
store = {}
|
|
486
|
+
},
|
|
487
|
+
removeItem: function (key) {
|
|
488
|
+
delete store[key]
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
})()
|
|
492
|
+
Object.defineProperty(window, 'localStorage', { value: localStorageMock })
|
|
493
|
+
})
|
|
494
|
+
it('should store and retrieve cached items', async () => {
|
|
495
|
+
const schema = {
|
|
496
|
+
type: 'string',
|
|
497
|
+
minLength: 4
|
|
498
|
+
}
|
|
499
|
+
const cacheKey = 'myItem'
|
|
500
|
+
loader = new SchemaLoader({ ajax: true, ajax_cache_responses: true, ajax_cache_buster: 'abc123' })
|
|
501
|
+
loader.cacheSet(cacheKey, schema)
|
|
502
|
+
|
|
503
|
+
const schemaCached = loader.cacheGet(cacheKey)
|
|
504
|
+
expect(schemaCached).toEqual(schema)
|
|
505
|
+
})
|
|
506
|
+
it('should not retrieve cached item with invalid cache buster', async () => {
|
|
507
|
+
const schema = {
|
|
508
|
+
type: 'string',
|
|
509
|
+
minLength: 4
|
|
510
|
+
}
|
|
511
|
+
const cacheKey = 'myItem'
|
|
512
|
+
loader = new SchemaLoader({ ajax: true, ajax_cache_responses: true, ajax_cache_buster: 'abc123' })
|
|
513
|
+
loader.cacheSet(cacheKey, schema)
|
|
514
|
+
|
|
515
|
+
loader.options.ajax_cache_buster = 'not-abc123'
|
|
516
|
+
const schemaCached = loader.cacheGet(cacheKey)
|
|
517
|
+
expect(schemaCached).toBeUndefined()
|
|
518
|
+
})
|
|
519
|
+
it('should fetch schemas from cache { ajax: true, ajax_cache_responses: true }', async () => {
|
|
520
|
+
// Runs two passes, the first to warm cache and second to fetch from cache.
|
|
521
|
+
const response = {
|
|
522
|
+
type: 'string',
|
|
523
|
+
minLength: 4
|
|
524
|
+
}
|
|
525
|
+
const server = createFakeServer()
|
|
526
|
+
server.autoRespond = true
|
|
527
|
+
window.XMLHttpRequest = server.xhr
|
|
528
|
+
server.respondWith('/string.json', [
|
|
529
|
+
200,
|
|
530
|
+
{ 'Content-Type': 'application/json' },
|
|
531
|
+
JSON.stringify(response)
|
|
532
|
+
])
|
|
533
|
+
fetchUrl =
|
|
534
|
+
document.location.origin + document.location.pathname.toString()
|
|
535
|
+
loader = new SchemaLoader({ ajax: true, ajax_cache_responses: true })
|
|
536
|
+
fileBase = loader._getFileBase(document.location.toString())
|
|
537
|
+
|
|
538
|
+
// Pass one: Warm the cache.
|
|
539
|
+
const schema = {
|
|
540
|
+
type: 'object',
|
|
541
|
+
properties: {
|
|
542
|
+
fname: { $ref: '/string.json' },
|
|
543
|
+
lname: { $ref: '/string.json' }
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
await loader.load(
|
|
547
|
+
schema,
|
|
548
|
+
fetchUrl,
|
|
549
|
+
fileBase
|
|
550
|
+
)
|
|
551
|
+
const urls = Object.keys(loader.refs)
|
|
552
|
+
expect(urls.length).toEqual(1)
|
|
553
|
+
|
|
554
|
+
// Tears down the mock Ajax endpoint to ensure any schemas get fetched from cache.
|
|
555
|
+
server.restore()
|
|
556
|
+
|
|
557
|
+
// Pass two: Should fetch external schemas from cache.
|
|
558
|
+
// Requires a fresh loader because SchemaLoader.refs can return stale data.
|
|
559
|
+
const loaderFresh = new SchemaLoader({ ajax: true, ajax_cache_responses: true })
|
|
560
|
+
const schemaFromCache = {
|
|
561
|
+
type: 'object',
|
|
562
|
+
properties: {
|
|
563
|
+
fname: { $ref: '/string.json' },
|
|
564
|
+
lname: { $ref: '/string.json' }
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
await loaderFresh.load(
|
|
568
|
+
schemaFromCache,
|
|
569
|
+
fetchUrl,
|
|
570
|
+
fileBase
|
|
571
|
+
)
|
|
572
|
+
const urlsFromCache = Object.keys(loaderFresh.refs)
|
|
573
|
+
expect(urlsFromCache.length).toEqual(1)
|
|
574
|
+
})
|
|
575
|
+
})
|
|
470
576
|
})
|