@isrd-isi-edu/ermrestjs 2.0.0 → 2.0.1

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/js/core.js CHANGED
@@ -205,6 +205,10 @@ import {
205
205
  */
206
206
  function Catalogs(server) {
207
207
  this._server = server;
208
+
209
+ /**
210
+ * @type {{[catalogId: string]: Catalog}}
211
+ */
208
212
  this._catalogs = {};
209
213
  }
210
214
 
@@ -243,21 +247,28 @@ import {
243
247
  get: function (id, dontFetchSchema) {
244
248
  // do introspection here and return a promise
245
249
 
246
- var self = this, defer = ConfigService.q.defer(), catalog;
250
+ const defer = ConfigService.q.defer();
251
+ let catalog;
247
252
 
248
253
  // create a new catalog object if the object has not been created before
249
254
  if (id in this._catalogs) {
250
- catalog = self._catalogs[id];
255
+ catalog = this._catalogs[id];
251
256
  } else {
252
- catalog = new Catalog(self._server, id);
257
+ catalog = new Catalog(this._server, id);
253
258
  }
254
259
 
255
260
  // make sure the catalog is introspected.
256
261
  // the introspect function might or might not
257
- catalog._introspect(dontFetchSchema).then(function () {
258
- self._catalogs[id] = catalog;
262
+ catalog._introspect(dontFetchSchema).then(() => {
263
+ /**
264
+ * TODO the catalog id might have changed if the version was corrected.
265
+ * with the current implementation, the next time this function is called,
266
+ * it will not use the cached catalog and will create a new one (so a new request).
267
+ * We might be able to improve this in the future.
268
+ */
269
+ this._catalogs[id] = catalog;
259
270
  defer.resolve(catalog);
260
- }).catch(function (error) {
271
+ }).catch((error) => {
261
272
  defer.reject(error);
262
273
  });
263
274
 
@@ -318,6 +329,11 @@ import {
318
329
  // this property is needed by _determineDisplayName
319
330
  this.name = id;
320
331
 
332
+ /**
333
+ * Indicates whether the version in the catalog ID was corrected to match the server's snaptime.
334
+ */
335
+ this.versionCorrected = false;
336
+
321
337
  this._nameStyle = {}; // Used in the displayname to store the name styles.
322
338
 
323
339
  // NOTE we still haven't fetched the catalog, so we don't have the catalog annotation here.
@@ -460,21 +476,28 @@ import {
460
476
  * @private
461
477
  */
462
478
  _introspect: function (dontFetchSchema) {
463
- var defer = ConfigService.q.defer(), self = this;
479
+ var defer = ConfigService.q.defer();
464
480
 
465
481
  // load the catalog (or use the one that is cached)
466
- this._get().then(function(response) {
467
- self.snaptime = response.snaptime;
482
+ this._get().then((response) => {
483
+ this.snaptime = response.snaptime;
484
+
485
+ let versionCorrected = false;
486
+ if (isStringAndNotEmpty(this.version) && this.version !== this.snaptime) {
487
+ this.version = this.snaptime;
488
+ this.id = this.id.split("@")[0] + "@" + this.version;
489
+ this.versionCorrected = true;
490
+ }
468
491
 
469
492
  if ("features" in response) {
470
- for (var k in self.features) {
471
- self.features[k] = response.features[k];
493
+ for (var k in this.features) {
494
+ this.features[k] = response.features[k];
472
495
  }
473
496
  }
474
497
 
475
- self.annotations = new Annotations();
498
+ this.annotations = new Annotations();
476
499
  for (var uri in response.annotations) {
477
- self.annotations._push(new Annotation("catalog", uri, response.annotations[uri]));
500
+ this.annotations._push(new Annotation("catalog", uri, response.annotations[uri]));
478
501
  }
479
502
 
480
503
  /**
@@ -482,7 +505,7 @@ import {
482
505
  * This should be done before initializing tables because tables require this field.
483
506
  * @type {boolean|null}
484
507
  */
485
- self.isGenerated = _processACLAnnotation(self.annotations, _annotations.GENERATED, false);
508
+ this.isGenerated = _processACLAnnotation(this.annotations, _annotations.GENERATED, false);
486
509
 
487
510
  /**
488
511
  * whether catalog is immutable.
@@ -491,32 +514,32 @@ import {
491
514
  * null: annotation is not defined
492
515
  * @type {boolean|null}
493
516
  */
494
- self.isImmutable = _processACLAnnotation(self.annotations, _annotations.IMMUTABLE, null);
517
+ this.isImmutable = _processACLAnnotation(this.annotations, _annotations.IMMUTABLE, null);
495
518
 
496
519
  /**
497
520
  * whether catalog is non-deletable
498
521
  * @type {boolean}
499
522
  */
500
- self.isNonDeletable = _processACLAnnotation(self.annotations, _annotations.NON_DELETABLE, false);
523
+ this.isNonDeletable = _processACLAnnotation(this.annotations, _annotations.NON_DELETABLE, false);
501
524
 
502
525
  /**
503
526
  * this will make sure the nameStyle is populated on the catalog as well,
504
527
  * so schema can use it.
505
528
  */
506
- _determineDisplayName(self, true);
529
+ _determineDisplayName(this, true);
507
530
 
508
- if (dontFetchSchema === true || self._schemaFetched) {
509
- defer.resolve();
531
+ if (dontFetchSchema === true || this._schemaFetched) {
532
+ defer.resolve({ versionCorrected });
510
533
  } else {
511
534
  // load all schemas
512
- self._fetchSchema().then(function () {
513
- defer.resolve();
514
- }).catch(function (err) {
535
+ this._fetchSchema().then(() => {
536
+ defer.resolve({ versionCorrected });
537
+ }).catch((err) => {
515
538
  throw err;
516
539
  });
517
540
  }
518
541
 
519
- }).catch(function (response) {
542
+ }).catch((response) => {
520
543
  defer.reject(ErrorService.responseToError(response));
521
544
  });
522
545
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@isrd-isi-edu/ermrestjs",
3
3
  "description": "ERMrest client library in JavaScript",
4
- "version": "2.0.0",
4
+ "version": "2.0.1",
5
5
  "license": "Apache-2.0",
6
6
  "engines": {
7
7
  "node": ">= 20.0.0",
@@ -54,7 +54,7 @@
54
54
  "spark-md5": "^3.0.0",
55
55
  "terser": "^5.43.1",
56
56
  "typescript": "~5.8.3",
57
- "vite": "^6.3.6",
57
+ "vite": "^6.4.1",
58
58
  "vite-plugin-compression2": "^2.2.1"
59
59
  },
60
60
  "devDependencies": {
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  ConflictError,
17
17
  IntegrityConflictError,
18
18
  DuplicateConflictError,
19
+ SnapshotNotFoundError,
19
20
  PreconditionFailedError,
20
21
  InternalServerError,
21
22
  BadGatewayError,
@@ -170,6 +171,7 @@ export {
170
171
  ConflictError,
171
172
  IntegrityConflictError,
172
173
  DuplicateConflictError,
174
+ SnapshotNotFoundError,
173
175
  PreconditionFailedError,
174
176
  InternalServerError,
175
177
  BadGatewayError,
@@ -96,6 +96,12 @@ export class DuplicateConflictError extends ConflictError {
96
96
  }
97
97
  }
98
98
 
99
+ export class SnapshotNotFoundError extends ERMrestError {
100
+ constructor(status: string, message: string, subMessage?: string) {
101
+ super(_HTTPErrorCodes.CONFLICT, _errorStatus.SNAPSHOT_NOT_FOUND, message, subMessage);
102
+ }
103
+ }
104
+
99
105
  export class PreconditionFailedError extends ERMrestError {
100
106
  constructor(status: string, message: string) {
101
107
  const usedStatus = isStringAndNotEmpty(status) ? status : _errorStatus.PRECONDITION_FAILED;
@@ -122,13 +122,15 @@ export const resolve = async (uri: string, contextHeaderParams?: any): Promise<R
122
122
  await onload();
123
123
  //added try block to make sure it rejects all parse() related error
124
124
  // It should have been taken care by outer try but did not work
125
- const location = parse(uri);
125
+ const loc = parse(uri);
126
126
 
127
- const server = ermrestFactory.getServer(location.service, contextHeaderParams);
127
+ console.log(loc.catalog);
128
128
 
129
- const catalog = await server.catalogs.get(location.catalog);
129
+ const server = ermrestFactory.getServer(loc.service, contextHeaderParams);
130
130
 
131
- return new Reference(location, catalog);
131
+ const catalog = await server.catalogs.get(loc.catalog);
132
+
133
+ return new Reference(loc, catalog);
132
134
  };
133
135
 
134
136
  /**
@@ -1,7 +1,9 @@
1
+ import moment from 'moment-timezone';
2
+
1
3
  // models
2
4
  import { Reference } from '@isrd-isi-edu/ermrestjs/src/models/reference';
3
5
 
4
- import { contextHeaderName, _operationsFlag } from '@isrd-isi-edu/ermrestjs/src/utils/constants';
6
+ import { contextHeaderName, _operationsFlag, _dataFormats } from '@isrd-isi-edu/ermrestjs/src/utils/constants';
5
7
  import {
6
8
  ERMrestError,
7
9
  NoConnectionError,
@@ -18,12 +20,15 @@ import {
18
20
  IntegrityConflictError,
19
21
  DuplicateConflictError,
20
22
  ConflictError,
23
+ SnapshotNotFoundError,
21
24
  } from '@isrd-isi-edu/ermrestjs/src/models/errors';
22
25
  import { fixedEncodeURIComponent } from '@isrd-isi-edu/ermrestjs/src/utils/value-utils';
23
26
 
24
27
  // legacy
28
+
25
29
  import { parse } from '@isrd-isi-edu/ermrestjs/js/parser';
26
30
  import { ermrestFactory } from '@isrd-isi-edu/ermrestjs/js/core';
31
+ import HistoryService from '@isrd-isi-edu/ermrestjs/src/services/history';
27
32
 
28
33
  export default class ErrorService {
29
34
  /**
@@ -121,7 +126,20 @@ export default class ErrorService {
121
126
  const conflictErrorPrefix = ['409 Conflict\nThe request conflicts with the state of the server. ', 'Request conflicts with state of server.'];
122
127
  let siteAdminMsg = '\nIf you have trouble removing dependencies please contact the site administrator.';
123
128
 
124
- if (generatedErrMessage.indexOf('violates foreign key constraint') > -1 && actionFlag === _operationsFlag.DELETE) {
129
+ if (generatedErrMessage.indexOf('Requested catalog revision ') > -1 && generatedErrMessage.indexOf('is prior to any known revision.') > -1) {
130
+ const match = generatedErrMessage.match(/Requested catalog revision "([^"]+)"/);
131
+ const snapshot = match ? match[1] : '';
132
+ let formattedTime = '';
133
+ if (snapshot) {
134
+ formattedTime = HistoryService.snapshotToDatetimeISO(snapshot, true);
135
+ if (formattedTime) {
136
+ formattedTime = moment(formattedTime).format(_dataFormats.DATETIME.display);
137
+ }
138
+ }
139
+ const newMessage = `The requested snapshot time ${formattedTime ? '(' + formattedTime + ') ' : ''}is older than any available history.`;
140
+
141
+ return new SnapshotNotFoundError(errorStatusText, newMessage);
142
+ } else if (generatedErrMessage.indexOf('violates foreign key constraint') > -1 && actionFlag === _operationsFlag.DELETE) {
125
143
  let referenceTable: any = '';
126
144
 
127
145
  let detail: string | number = generatedErrMessage.search(/DETAIL:/g);
@@ -327,6 +327,7 @@ export const _errorStatus = Object.freeze({
327
327
  INVALID_PAGE: 'Invalid Page Criteria',
328
328
  INVALID_SERVER_RESPONSE: 'Invalid Server Response',
329
329
  UNSUPPORTED_FILTERS: 'Unsupported Filters',
330
+ SNAPSHOT_NOT_FOUND: 'Snapshot Not Found',
330
331
  });
331
332
 
332
333
  export const _errorMessage = Object.freeze({