diff --git a/RELEASE.md b/RELEASE.md index a51b49280..c79b7c8a5 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -16,7 +16,7 @@ This document is a summary of code changes in Chaise. This is the vocabulary use - [Changed] how we encode search filter in the url to use the new `search-box` sourcekey. - [Added] `comment` support to display annotation. - [Changed] `comment` behavior in annotations so that `false` is treated the same as empty string. - - [Added] `elapsed_s` attribute to `Deriva-Client-Context` header object for all the http requests. + - [Added] `elapsed_ms` attribute to `Deriva-Client-Context` header object for all the http requests. - [Added] `show_foreign_key_link` support. - [Changed] `show_nulls` to `show_null` in display annotation. diff --git a/docs/dev-docs/api.md b/docs/dev-docs/api.md index cb844f089..6bd6c9719 100644 --- a/docs/dev-docs/api.md +++ b/docs/dev-docs/api.md @@ -92,7 +92,8 @@ to use for ERMrest JavaScript agents. * [.Schema](#ERMrest.Schema) * [new Schema(catalog, jsonSchema)](#new_ERMrest.Schema_new) * [.catalog](#ERMrest.Schema+catalog) : [Catalog](#ERMrest.Catalog) - * [.name](#ERMrest.Schema+name) + * [.name](#ERMrest.Schema+name) : string + * [.RID](#ERMrest.Schema+RID) : string * [.ignore](#ERMrest.Schema+ignore) : boolean * [.annotations](#ERMrest.Schema+annotations) : [Annotations](#ERMrest.Annotations) * [.rights](#ERMrest.Schema+rights) : Object @@ -109,7 +110,8 @@ to use for ERMrest JavaScript agents. * [new Table(schema, jsonTable)](#new_ERMrest.Table_new) * _instance_ * [.schema](#ERMrest.Table+schema) : [Schema](#ERMrest.Schema) - * [.name](#ERMrest.Table+name) + * [.name](#ERMrest.Table+name) : string + * [.RID](#ERMrest.Table+RID) : string * [.entity](#ERMrest.Table+entity) : [Entity](#ERMrest.Table.Entity) * [.ignore](#ERMrest.Table+ignore) : boolean * [._baseTable](#ERMrest.Table+_baseTable) : [Table](#ERMrest.Table) @@ -171,6 +173,7 @@ to use for ERMrest JavaScript agents. * [.isSystemColumn](#ERMrest.Column+isSystemColumn) : Boolean * [.isImmutable](#ERMrest.Column+isImmutable) : Boolean * [.name](#ERMrest.Column+name) : string + * [.RID](#ERMrest.Column+RID) : string * [.type](#ERMrest.Column+type) : [Type](#ERMrest.Type) * [.ignore](#ERMrest.Column+ignore) : boolean * [.annotations](#ERMrest.Column+annotations) : [Annotations](#ERMrest.Annotations) @@ -210,6 +213,7 @@ to use for ERMrest JavaScript agents. * [.colset](#ERMrest.Key+colset) : [ColSet](#ERMrest.ColSet) * [.annotations](#ERMrest.Key+annotations) : [Annotations](#ERMrest.Annotations) * [.comment](#ERMrest.Key+comment) : string + * [.RID](#ERMrest.Key+RID) : string * [.constraint_names](#ERMrest.Key+constraint_names) : Array * [.name](#ERMrest.Key+name) : string * [.simple](#ERMrest.Key+simple) : boolean @@ -236,6 +240,8 @@ to use for ERMrest JavaScript agents. * [.get(colset)](#ERMrest.ForeignKeys+get) ⇒ [Array.<ForeignKeyRef>](#ERMrest.ForeignKeyRef) * [.ForeignKeyRef](#ERMrest.ForeignKeyRef) * [new ForeignKeyRef(table, jsonFKR)](#new_ERMrest.ForeignKeyRef_new) + * [.table](#ERMrest.ForeignKeyRef+table) : [Table](#ERMrest.Table) + * [.RID](#ERMrest.ForeignKeyRef+RID) : string * [.colset](#ERMrest.ForeignKeyRef+colset) : [ColSet](#ERMrest.ColSet) * [.key](#ERMrest.ForeignKeyRef+key) : [Key](#ERMrest.Key) * [.rights](#ERMrest.ForeignKeyRef+rights) : Object @@ -246,6 +252,7 @@ to use for ERMrest JavaScript agents. * [.ignore](#ERMrest.ForeignKeyRef+ignore) : boolean * [.annotations](#ERMrest.ForeignKeyRef+annotations) : [Annotations](#ERMrest.Annotations) * [.comment](#ERMrest.ForeignKeyRef+comment) : string + * [.compressedDataSource](#ERMrest.ForeignKeyRef+compressedDataSource) * [.name](#ERMrest.ForeignKeyRef+name) : string * [.simple](#ERMrest.ForeignKeyRef+simple) : Boolean * [.toString(reverse, isLeft)](#ERMrest.ForeignKeyRef+toString) ⇒ string @@ -332,6 +339,7 @@ to use for ERMrest JavaScript agents. * [.appLink](#ERMrest.Reference+appLink) : String * [.csvDownloadLink](#ERMrest.Reference+csvDownloadLink) ⇒ String * [.defaultLogInfo](#ERMrest.Reference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.Reference+filterLogInfo) : Object * [.defaultExportTemplate](#ERMrest.Reference+defaultExportTemplate) : string * [.removeAllFacetFilters(sameFilter, sameCustomFacet, sameFacet)](#ERMrest.Reference+removeAllFacetFilters) ⇒ ERMrest.reference * [.hideFacets()](#ERMrest.Reference+hideFacets) ⇒ [Reference](#ERMrest.Reference) @@ -389,7 +397,7 @@ to use for ERMrest JavaScript agents. * [.isPseudo](#ERMrest.ReferenceColumn+isPseudo) : boolean * [.table](#ERMrest.ReferenceColumn+table) : [Table](#ERMrest.Table) * [.name](#ERMrest.ReferenceColumn+name) : string - * [.dataSource](#ERMrest.ReferenceColumn+dataSource) + * [.compressedDataSource](#ERMrest.ReferenceColumn+compressedDataSource) * [.displayname](#ERMrest.ReferenceColumn+displayname) : object * [.type](#ERMrest.ReferenceColumn+type) : [Type](#ERMrest.Type) * [.nullok](#ERMrest.ReferenceColumn+nullok) : Boolean @@ -463,6 +471,7 @@ to use for ERMrest JavaScript agents. * [.reference](#ERMrest.FacetColumn+reference) : [Reference](#ERMrest.Reference) * [.index](#ERMrest.FacetColumn+index) : int * [.dataSource](#ERMrest.FacetColumn+dataSource) : obj \| string + * [.compressedDataSource](#ERMrest.FacetColumn+compressedDataSource) : obj \| string * [.filters](#ERMrest.FacetColumn+filters) * [.isOpen](#ERMrest.FacetColumn+isOpen) : Boolean * [.hasPath](#ERMrest.FacetColumn+hasPath) : Boolean @@ -535,6 +544,7 @@ to use for ERMrest JavaScript agents. * [.unfilteredReference](#ERMrest.AttributeGroupReference+unfilteredReference) : [Reference](#ERMrest.Reference) * [.ermrestPath](#ERMrest.AttributeGroupReference+ermrestPath) : string * [.defaultLogInfo](#ERMrest.AttributeGroupReference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.AttributeGroupReference+filterLogInfo) : Object * [.read([limit], contextHeaderParams)](#ERMrest.AttributeGroupReference+read) ⇒ ERMRest.AttributeGroupPage * [.getColumnByName(name)](#ERMrest.AttributeGroupReference+getColumnByName) ⇒ ERMrest.AttributeGroupColumn * [.AttributeGroupPage](#ERMrest.AttributeGroupPage) @@ -640,6 +650,7 @@ to use for ERMrest JavaScript agents. * [.appLink](#ERMrest.Reference+appLink) : String * [.csvDownloadLink](#ERMrest.Reference+csvDownloadLink) ⇒ String * [.defaultLogInfo](#ERMrest.Reference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.Reference+filterLogInfo) : Object * [.defaultExportTemplate](#ERMrest.Reference+defaultExportTemplate) : string * [.removeAllFacetFilters(sameFilter, sameCustomFacet, sameFacet)](#ERMrest.Reference+removeAllFacetFilters) ⇒ ERMrest.reference * [.hideFacets()](#ERMrest.Reference+hideFacets) ⇒ [Reference](#ERMrest.Reference) @@ -672,6 +683,7 @@ to use for ERMrest JavaScript agents. * [.unfilteredReference](#ERMrest.AttributeGroupReference+unfilteredReference) : [Reference](#ERMrest.Reference) * [.ermrestPath](#ERMrest.AttributeGroupReference+ermrestPath) : string * [.defaultLogInfo](#ERMrest.AttributeGroupReference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.AttributeGroupReference+filterLogInfo) : Object * [.read([limit], contextHeaderParams)](#ERMrest.AttributeGroupReference+read) ⇒ ERMRest.AttributeGroupPage * [.getColumnByName(name)](#ERMrest.AttributeGroupReference+getColumnByName) ⇒ ERMrest.AttributeGroupColumn * [.AttributeGroupPage](#ERMrest.AttributeGroupPage) : object @@ -692,6 +704,7 @@ to use for ERMrest JavaScript agents. * [.getServer(uri, [contextHeaderParams])](#ERMrest.getServer) ⇒ [Server](#ERMrest.Server) * [.parse(uri, catalogObject)](#ERMrest.parse) ⇒ ERMrest.Location * [.resolve(uri, [contextHeaderParams])](#ERMrest.resolve) ⇒ Promise + * [.getElapsedTime()](#ERMrest.getElapsedTime) ⇒ integer @@ -971,7 +984,8 @@ it will throw an error * [.Schema](#ERMrest.Schema) * [new Schema(catalog, jsonSchema)](#new_ERMrest.Schema_new) * [.catalog](#ERMrest.Schema+catalog) : [Catalog](#ERMrest.Catalog) - * [.name](#ERMrest.Schema+name) + * [.name](#ERMrest.Schema+name) : string + * [.RID](#ERMrest.Schema+RID) : string * [.ignore](#ERMrest.Schema+ignore) : boolean * [.annotations](#ERMrest.Schema+annotations) : [Annotations](#ERMrest.Annotations) * [.rights](#ERMrest.Schema+rights) : Object @@ -996,7 +1010,15 @@ Constructor for the Catalog. **Kind**: instance property of [Schema](#ERMrest.Schema) -#### schema.name +#### schema.name : string +the database name of the schema + +**Kind**: instance property of [Schema](#ERMrest.Schema) + + +#### schema.RID : string +The RID of this schema (might not be defined) + **Kind**: instance property of [Schema](#ERMrest.Schema) @@ -1085,7 +1107,8 @@ get table by table name * [new Table(schema, jsonTable)](#new_ERMrest.Table_new) * _instance_ * [.schema](#ERMrest.Table+schema) : [Schema](#ERMrest.Schema) - * [.name](#ERMrest.Table+name) + * [.name](#ERMrest.Table+name) : string + * [.RID](#ERMrest.Table+RID) : string * [.entity](#ERMrest.Table+entity) : [Entity](#ERMrest.Table.Entity) * [.ignore](#ERMrest.Table+ignore) : boolean * [._baseTable](#ERMrest.Table+_baseTable) : [Table](#ERMrest.Table) @@ -1135,7 +1158,15 @@ Constructor for Table. **Kind**: instance property of [Table](#ERMrest.Table) -#### table.name +#### table.name : string +the database name of the table + +**Kind**: instance property of [Table](#ERMrest.Table) + + +#### table.RID : string +The RID of this table (might not be defined) + **Kind**: instance property of [Table](#ERMrest.Table) @@ -1599,6 +1630,7 @@ Constructor for Columns. * [.isSystemColumn](#ERMrest.Column+isSystemColumn) : Boolean * [.isImmutable](#ERMrest.Column+isImmutable) : Boolean * [.name](#ERMrest.Column+name) : string + * [.RID](#ERMrest.Column+RID) : string * [.type](#ERMrest.Column+type) : [Type](#ERMrest.Type) * [.ignore](#ERMrest.Column+ignore) : boolean * [.annotations](#ERMrest.Column+annotations) : [Annotations](#ERMrest.Annotations) @@ -1675,6 +1707,14 @@ Mentions whether this column is immutable depending on update rights #### column.name : string +The database name of this column + +**Kind**: instance property of [Column](#ERMrest.Column) + + +#### column.RID : string +The RID of this column (might not be defined) + **Kind**: instance property of [Column](#ERMrest.Column) @@ -1958,6 +1998,7 @@ get the key by the column set * [.colset](#ERMrest.Key+colset) : [ColSet](#ERMrest.ColSet) * [.annotations](#ERMrest.Key+annotations) : [Annotations](#ERMrest.Annotations) * [.comment](#ERMrest.Key+comment) : string + * [.RID](#ERMrest.Key+RID) : string * [.constraint_names](#ERMrest.Key+constraint_names) : Array * [.name](#ERMrest.Key+name) : string * [.simple](#ERMrest.Key+simple) : boolean @@ -1993,6 +2034,12 @@ Reference to the table that this Key belongs to. #### key.comment : string Documentation for this key +**Kind**: instance property of [Key](#ERMrest.Key) + + +#### key.RID : string +The RID of this key (might not be defined) + **Kind**: instance property of [Key](#ERMrest.Key) @@ -2203,6 +2250,8 @@ get the foreign key of the given column set * [.ForeignKeyRef](#ERMrest.ForeignKeyRef) * [new ForeignKeyRef(table, jsonFKR)](#new_ERMrest.ForeignKeyRef_new) + * [.table](#ERMrest.ForeignKeyRef+table) : [Table](#ERMrest.Table) + * [.RID](#ERMrest.ForeignKeyRef+RID) : string * [.colset](#ERMrest.ForeignKeyRef+colset) : [ColSet](#ERMrest.ColSet) * [.key](#ERMrest.ForeignKeyRef+key) : [Key](#ERMrest.Key) * [.rights](#ERMrest.ForeignKeyRef+rights) : Object @@ -2213,6 +2262,7 @@ get the foreign key of the given column set * [.ignore](#ERMrest.ForeignKeyRef+ignore) : boolean * [.annotations](#ERMrest.ForeignKeyRef+annotations) : [Annotations](#ERMrest.Annotations) * [.comment](#ERMrest.ForeignKeyRef+comment) : string + * [.compressedDataSource](#ERMrest.ForeignKeyRef+compressedDataSource) * [.name](#ERMrest.ForeignKeyRef+name) : string * [.simple](#ERMrest.ForeignKeyRef+simple) : Boolean * [.toString(reverse, isLeft)](#ERMrest.ForeignKeyRef+toString) ⇒ string @@ -2227,6 +2277,18 @@ get the foreign key of the given column set | table | [Table](#ERMrest.Table) | | jsonFKR | Object | + + +#### foreignKeyRef.table : [Table](#ERMrest.Table) +The table that this foreignkey is defined on (from table) + +**Kind**: instance property of [ForeignKeyRef](#ERMrest.ForeignKeyRef) + + +#### foreignKeyRef.RID : string +The RID of this column (might not be defined) + +**Kind**: instance property of [ForeignKeyRef](#ERMrest.ForeignKeyRef) #### foreignKeyRef.colset : [ColSet](#ERMrest.ColSet) @@ -2275,10 +2337,17 @@ The constraint names for this foreign key Documentation for this foreign key reference **Kind**: instance property of [ForeignKeyRef](#ERMrest.ForeignKeyRef) + + +#### foreignKeyRef.compressedDataSource +the compressed source path from the main reference to this column + +**Kind**: instance property of [ForeignKeyRef](#ERMrest.ForeignKeyRef) +**Type{object}**: #### foreignKeyRef.name : string -A unique nam that can be used for referring to this foreignkey. +A unique name that can be used for referring to this foreignkey. **Kind**: instance property of [ForeignKeyRef](#ERMrest.ForeignKeyRef) @@ -2754,6 +2823,7 @@ Constructor for a ParsedFilter. * [.appLink](#ERMrest.Reference+appLink) : String * [.csvDownloadLink](#ERMrest.Reference+csvDownloadLink) ⇒ String * [.defaultLogInfo](#ERMrest.Reference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.Reference+filterLogInfo) : Object * [.defaultExportTemplate](#ERMrest.Reference+defaultExportTemplate) : string * [.removeAllFacetFilters(sameFilter, sameCustomFacet, sameFacet)](#ERMrest.Reference+removeAllFacetFilters) ⇒ ERMrest.reference * [.hideFacets()](#ERMrest.Reference+hideFacets) ⇒ [Reference](#ERMrest.Reference) @@ -3063,7 +3133,24 @@ NOTE It will not have the same sort and paging as the reference. #### reference.defaultLogInfo : Object The default information that we want to be logged. This includes: - - catalog, schema_table, cfacet, cfacet_str, cfacet_path, facets + - catalog, schema_table +TODO Evaluate whether we even need this function + +**Kind**: instance property of [Reference](#ERMrest.Reference) + + +#### reference.filterLogInfo : Object +The object that can be logged to capture the filter state of the reference. +The return object can have: + - filters: the facet object. + - custom_filters: + - the filter strings that parser couldn't turn to facet. + - if we could turn the custom filter to facet, this will return `true` + - cfacet: if there's a cfacet it will be 1 + - cfacet_str: if cfacet=1, it will be displayname of cfacet. + - cfacet_path: if cfacet=1, it will be ermrest path of cfacet. +This function creates a new object everytime that it's called, so it +can be manipulated further. **Kind**: instance property of [Reference](#ERMrest.Reference) @@ -3857,7 +3944,7 @@ count aggregate representation * [.isPseudo](#ERMrest.ReferenceColumn+isPseudo) : boolean * [.table](#ERMrest.ReferenceColumn+table) : [Table](#ERMrest.Table) * [.name](#ERMrest.ReferenceColumn+name) : string - * [.dataSource](#ERMrest.ReferenceColumn+dataSource) + * [.compressedDataSource](#ERMrest.ReferenceColumn+compressedDataSource) * [.displayname](#ERMrest.ReferenceColumn+displayname) : object * [.type](#ERMrest.ReferenceColumn+type) : [Type](#ERMrest.Type) * [.nullok](#ERMrest.ReferenceColumn+nullok) : Boolean @@ -3905,10 +3992,10 @@ indicates that this object represents a Column. name of the column. **Kind**: instance property of [ReferenceColumn](#ERMrest.ReferenceColumn) - + -#### referenceColumn.dataSource -the source path from the main reference to this column +#### referenceColumn.compressedDataSource +the compressed source path from the main reference to this column **Kind**: instance property of [ReferenceColumn](#ERMrest.ReferenceColumn) **Type{object}**: @@ -4610,6 +4697,7 @@ Indicates that this ReferenceColumn is an inbound foreign key. * [.reference](#ERMrest.FacetColumn+reference) : [Reference](#ERMrest.Reference) * [.index](#ERMrest.FacetColumn+index) : int * [.dataSource](#ERMrest.FacetColumn+dataSource) : obj \| string + * [.compressedDataSource](#ERMrest.FacetColumn+compressedDataSource) : obj \| string * [.filters](#ERMrest.FacetColumn+filters) * [.isOpen](#ERMrest.FacetColumn+isOpen) : Boolean * [.hasPath](#ERMrest.FacetColumn+hasPath) : Boolean @@ -4693,6 +4781,12 @@ NOTE: Might not be needed A valid data-source path NOTE: we're not validating this data-source, we assume that this is valid. +**Kind**: instance property of [FacetColumn](#ERMrest.FacetColumn) + + +#### facetColumn.compressedDataSource : obj \| string +the compressed version of data source data-source path + **Kind**: instance property of [FacetColumn](#ERMrest.FacetColumn) @@ -5342,6 +5436,7 @@ parent table (not the end table). * [.unfilteredReference](#ERMrest.AttributeGroupReference+unfilteredReference) : [Reference](#ERMrest.Reference) * [.ermrestPath](#ERMrest.AttributeGroupReference+ermrestPath) : string * [.defaultLogInfo](#ERMrest.AttributeGroupReference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.AttributeGroupReference+filterLogInfo) : Object * [.read([limit], contextHeaderParams)](#ERMrest.AttributeGroupReference+read) ⇒ ERMRest.AttributeGroupPage * [.getColumnByName(name)](#ERMrest.AttributeGroupReference+getColumnByName) ⇒ ERMrest.AttributeGroupColumn @@ -5441,6 +5536,13 @@ NOTE: #### attributeGroupReference.defaultLogInfo : Object The default information that we want to be logged including catalog, schema_table, and facet (filter). +**Kind**: instance property of [AttributeGroupReference](#ERMrest.AttributeGroupReference) + + +#### attributeGroupReference.filterLogInfo : Object +The filter information that should be logged +Currently only includes the search term. + **Kind**: instance property of [AttributeGroupReference](#ERMrest.AttributeGroupReference) @@ -6269,6 +6371,7 @@ get PathColumn object by column name * [.appLink](#ERMrest.Reference+appLink) : String * [.csvDownloadLink](#ERMrest.Reference+csvDownloadLink) ⇒ String * [.defaultLogInfo](#ERMrest.Reference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.Reference+filterLogInfo) : Object * [.defaultExportTemplate](#ERMrest.Reference+defaultExportTemplate) : string * [.removeAllFacetFilters(sameFilter, sameCustomFacet, sameFacet)](#ERMrest.Reference+removeAllFacetFilters) ⇒ ERMrest.reference * [.hideFacets()](#ERMrest.Reference+hideFacets) ⇒ [Reference](#ERMrest.Reference) @@ -6578,7 +6681,24 @@ NOTE It will not have the same sort and paging as the reference. #### reference.defaultLogInfo : Object The default information that we want to be logged. This includes: - - catalog, schema_table, cfacet, cfacet_str, cfacet_path, facets + - catalog, schema_table +TODO Evaluate whether we even need this function + +**Kind**: instance property of [Reference](#ERMrest.Reference) + + +#### reference.filterLogInfo : Object +The object that can be logged to capture the filter state of the reference. +The return object can have: + - filters: the facet object. + - custom_filters: + - the filter strings that parser couldn't turn to facet. + - if we could turn the custom filter to facet, this will return `true` + - cfacet: if there's a cfacet it will be 1 + - cfacet_str: if cfacet=1, it will be displayname of cfacet. + - cfacet_path: if cfacet=1, it will be ermrest path of cfacet. +This function creates a new object everytime that it's called, so it +can be manipulated further. **Kind**: instance property of [Reference](#ERMrest.Reference) @@ -6973,6 +7093,7 @@ Check the sort object. Does not change the `this._location` object. * [.unfilteredReference](#ERMrest.AttributeGroupReference+unfilteredReference) : [Reference](#ERMrest.Reference) * [.ermrestPath](#ERMrest.AttributeGroupReference+ermrestPath) : string * [.defaultLogInfo](#ERMrest.AttributeGroupReference+defaultLogInfo) : Object + * [.filterLogInfo](#ERMrest.AttributeGroupReference+filterLogInfo) : Object * [.read([limit], contextHeaderParams)](#ERMrest.AttributeGroupReference+read) ⇒ ERMRest.AttributeGroupPage * [.getColumnByName(name)](#ERMrest.AttributeGroupReference+getColumnByName) ⇒ ERMrest.AttributeGroupColumn @@ -7072,6 +7193,13 @@ NOTE: #### attributeGroupReference.defaultLogInfo : Object The default information that we want to be logged including catalog, schema_table, and facet (filter). +**Kind**: instance property of [AttributeGroupReference](#ERMrest.AttributeGroupReference) + + +#### attributeGroupReference.filterLogInfo : Object +The filter information that should be logged +Currently only includes the search term. + **Kind**: instance property of [AttributeGroupReference](#ERMrest.AttributeGroupReference) @@ -7347,6 +7475,12 @@ ERMrest.resolve('https://example.org/catalog/42/entity/s:t/k=123').then( | uri | string | An ERMrest resource URI, such as `https://example.org/ermrest/catalog/1/entity/s:t/k=123`. | | [contextHeaderParams] | Object | An optional context header parameters object. The (key, value) pairs from the object are converted to URL `key=value` query parameters and appended to every request to the ERMrest service. | + + +### ERMrest.getElapsedTime() ⇒ integer +**Kind**: static method of [ERMrest](#ERMrest) +**Returns**: integer - A value set to determine the elapsed time +since the ermrestJS has been available (milliseconds). ## formatDate() ⇒ diff --git a/js/ag_reference.js b/js/ag_reference.js index 747799c3e..62d8e4122 100644 --- a/js/ag_reference.js +++ b/js/ag_reference.js @@ -350,9 +350,11 @@ AttributeGroupReference.prototype = { uri += "?limit=" + (limit+1); } - var currRef = this; + var currRef = this, action = "read"; if (!contextHeaderParams || !isObject(contextHeaderParams)) { - contextHeaderParams = {"action": "read"}; + contextHeaderParams = {"action": action}; + } else if (typeof contextHeaderParams.action === "string") { + action = contextHeaderParams.action; } var config = { headers: this._generateContextHeader(contextHeaderParams, limit) @@ -395,7 +397,10 @@ AttributeGroupReference.prototype = { // a new location without paging var newLocation = currRef.location.changePage(); var referenceWithoutPaging = new AttributeGroupReference(currRef._keyColumns, currRef._aggregateColumns, newLocation, currRef._catalog, currRef.table, currRef._context); - referenceWithoutPaging.read(limit).then(function rereadReference(rereadPage) { + + // remove the function and replace it with auto-reload + contextHeaderParams.action = action.substring(0,action.lastIndexOf(";")+1) + "auto-reload"; + referenceWithoutPaging.read(limit, contextHeaderParams).then(function rereadReference(rereadPage) { defer.resolve(rereadPage); }, function error(err) { throw err; @@ -533,6 +538,19 @@ AttributeGroupReference.prototype = { return obj; }, + /** + * The filter information that should be logged + * Currently only includes the search term. + * @type {Object} + */ + get filterLogInfo() { + var obj = {}; + if (isObjectAndNotNull(this.location.searchObject) && typeof this.location.searchTerm === "string" && this.location.searchTerm) { + obj.filters = _compressFacetObject({"and": [{"source": "search-box", "search": [this.location.searchTerm]}]}); + } + return obj; + }, + _generateContextHeader: function (contextHeaderParams, page_size) { if (!contextHeaderParams || !isObject(contextHeaderParams)) { contextHeaderParams = {}; diff --git a/js/column.js b/js/column.js index acbe325e9..6005e3e9f 100644 --- a/js/column.js +++ b/js/column.js @@ -150,20 +150,23 @@ ReferenceColumn.prototype = { }, /** - * the source path from the main reference to this column + * the compressed source path from the main reference to this column * @type{Object} */ - get dataSource() { - if (this._dataSource === undefined) { + get compressedDataSource() { + if (this._compressedDataSource === undefined) { + var ds; if (this.sourceObject && this.sourceObject.source) { - this._dataSource = this.sourceObject.source; + ds = this.sourceObject.source; } else if (this._simple) { - this._dataSource = this._baseCols[0].name; + ds = this._baseCols[0].name; } else { - this._dataSource = null; + ds = null; } + + this._compressedDataSource = _compressSource(ds); } - return this._dataSource; + return this._compressedDataSource; }, /** @@ -1455,6 +1458,14 @@ Object.defineProperty(PseudoColumn.prototype, "hasAggregate", { return this._hasAggregate; } }); +Object.defineProperty(PseudoColumn.prototype, "aggregateFn", { + get: function () { + if (this._aggregateFn === undefined) { + this._aggregateFn = this.hasAggregate ? this.sourceObject.aggregate : null; + } + return this._aggregateFn; + } +}); /** * Returns a reference to the current pseudo-column @@ -1541,7 +1552,7 @@ Object.defineProperty(PseudoColumn.prototype, "reference", { // make sure data-source is available on the reference // TODO this has been added to be consistent with the old related reference apis // other apis are not available, maybe we should add them as well? (origFKR, etc.) - self._reference.dataSource = self.dataSource; + self._reference.compressedDataSource = self.compressedDataSource; // make sure the refernece has the correct displayname if (self.hasPath) { @@ -1960,21 +1971,23 @@ Object.defineProperty(ForeignKeyPseudoColumn.prototype, "display", { return this._display_cached; } }); -Object.defineProperty(ForeignKeyPseudoColumn.prototype, "dataSource", { +Object.defineProperty(ForeignKeyPseudoColumn.prototype, "compressedDataSource", { get: function () { - if (this._dataSource === undefined) { + if (this._compressedDataSource === undefined) { + var ds; if (this.sourceObject && this.sourceObject.source) { - this._dataSource = this.sourceObject.source; + ds = this.sourceObject.source; } else if (this.table.shortestKey.length === 1){ - this._dataSource = [ + ds = [ {"outbound": this.foreignKey.constraint_names[0]}, this.table.shortestKey[0].name ]; } else { - this._dataSource = null; + ds = null; } + this._compressedDataSource = _compressSource(ds); } - return this._dataSource; + return this._compressedDataSource; } }); @@ -2635,18 +2648,18 @@ Object.defineProperty(InboundForeignKeyPseudoColumn.prototype, "nullok", { throw new Error("can not use this type of column in entry mode."); } }); -Object.defineProperty(InboundForeignKeyPseudoColumn.prototype, "dataSource", { +Object.defineProperty(InboundForeignKeyPseudoColumn.prototype, "compressedDataSource", { get: function () { - if (this._dataSource === undefined) { + if (this._compressedDataSource === undefined) { + var ds = null; if (this.sourceObject && this.sourceObject.source) { - this._dataSource = this.sourceObject.source; - } else if (this.reference.dataSource){ - this._dataSource = this.reference.dataSource; - } else { - this._dataSource = null; + ds = _compressSource(this.sourceObject.source); + } else if (this.reference.compressedDataSource){ + ds = this.reference.compressedDataSource; } + this._compressedDataSource = ds; } - return this._dataSource; + return this._compressedDataSource; } }); @@ -2696,6 +2709,12 @@ function FacetColumn (reference, index, column, facetObject, filters) { */ this.dataSource = facetObject.source; + /** + * the compressed version of data source data-source path + * @type {obj|string} + */ + this.compressedDataSource = _compressSource(facetObject.source); + /** * Filters that are applied to this facet. * @type{FacetFilter[]} @@ -3327,8 +3346,21 @@ FacetColumn.prototype = { * @return {Promise} A promise resolved with list of objects that have `uniqueId`, and `displayname`. */ getChoiceDisplaynames: function (contextHeaderParams) { - var defer = module._q.defer(); - var filters = []; + var defer = module._q.defer(), filters = []; + var table = this._column.table, columnName = this._column.name; + + var createRef = function (filterStrs) { + var uri = [ + table.schema.catalog.server.uri ,"catalog" , + table.schema.catalog.id, "entity", + module._fixedEncodeURIComponent(table.schema.name) + ":" + module._fixedEncodeURIComponent(table.name), + filterStrs.join(";") + ].join("/"); + + var ref = new Reference(module.parse(uri), table.schema.catalog).contextualize.compactSelect; + ref = ref.sort([{"column": columnName, "descending": false}]); + return ref; + }; // if no filter, just resolve with empty list. if (this.choiceFilters.length === 0) { @@ -3347,8 +3379,6 @@ FacetColumn.prototype = { } // otherwise generate an ermrest request to get the displaynames. else { - - var table = this._column.table, columnName = this._column.name; var filterStr = []; // list of filters that we want their displaynames. @@ -3369,18 +3399,7 @@ FacetColumn.prototype = { } // create a url - var uri = [ - table.schema.catalog.server.uri ,"catalog" , - table.schema.catalog.id, "entity", - module._fixedEncodeURIComponent(table.schema.name) + ":" + module._fixedEncodeURIComponent(table.name), - filterStr.join(";") - ].join("/"); - - var ref = new Reference(module.parse(uri), table.schema.catalog).contextualize.compactSelect; - - ref = ref.sort([{"column": columnName, "descending": false}]); - - ref.read(this.choiceFilters.length, contextHeaderParams, true).then(function (page) { + createRef(filterStr).read(this.choiceFilters.length, contextHeaderParams, true).then(function (page) { // create the response page.tuples.forEach(function (t) { filters.push({uniqueId: t.data[columnName], displayname: t.displayname, tuple: t}); diff --git a/js/core.js b/js/core.js index 9971099c0..01fa0d2d4 100644 --- a/js/core.js +++ b/js/core.js @@ -325,7 +325,7 @@ headers[module.contextHeaderName] = contextHeaderParams; } else { headers[module.contextHeaderName] = { - action: "model/snaptime", + action: ":,catalog/snaptime;load", catalog: self.id }; } @@ -357,7 +357,7 @@ self.snaptime = snaptime; var headers = {}; headers[module.contextHeaderName] = { - action: "model/schema", + action: ":,catalog/schema;load", catalog: self.id }; return self.server.http.get(self._uri + "/schema", {headers: headers}); @@ -594,10 +594,17 @@ this.catalog = catalog; /** - * + * @desc the database name of the schema + * @type {string} */ this.name = jsonSchema.schema_name; + /** + * @desc The RID of this schema (might not be defined) + * @type {?string} + */ + this.RID = jsonSchema.RID; + //this._uri = catalog._uri + "/schema/" + module._fixedEncodeURIComponent(this.name); /** @@ -807,11 +814,18 @@ this.schema = schema; /** - * + * @desc the database name of the table + * @type {string} */ this.name = jsonTable.table_name; this._jsonTable = jsonTable; + /** + * @desc The RID of this table (might not be defined) + * @type {?string} + */ + this.RID = jsonTable.RID; + this._nullValue = {}; // used to avoid recomputation of null value for different contexts. @@ -2458,10 +2472,17 @@ this.isImmutable = this.rights.update === false; /** + * The database name of this column * @type {string} */ this.name = jsonColumn.name; + /** + * @desc The RID of this column (might not be defined) + * @type {?string} + */ + this.RID = jsonColumn.RID; + /** * * @type {ERMrest.Type} @@ -3048,6 +3069,12 @@ } } + /** + * @desc The RID of this key (might not be defined) + * @type {?string} + */ + this.RID = jsonKey.RID; + /** * The exact `names` array in key definition * @type {Array} @@ -3558,8 +3585,18 @@ */ this._table = table; + /** + * @desc The table that this foreignkey is defined on (from table) + * @type {ERMrest.Table} + */ this.table = table; + /** + * @desc The RID of this column (might not be defined) + * @type {?string} + */ + this.RID = jsonFKR.RID; + var catalog = table.schema.catalog; // create ColSet for foreign key columns @@ -3678,7 +3715,25 @@ constructor: ForeignKeyRef, /** - * A unique nam that can be used for referring to this foreignkey. + * the compressed source path from the main reference to this column + * @type{Object} + */ + get compressedDataSource() { + if (this._compressedDataSource === undefined) { + var ds = null; + if (this.table.shortestKey.length === 1) { + ds = [ + {"outbound": this.constraint_names[0]}, + this.table.shortestKey[0].name + ]; + } + this._compressedDataSource = _compressSource(ds); + } + return this._compressedDataSource; + }, + + /** + * A unique name that can be used for referring to this foreignkey. * @type {string} */ get name () { diff --git a/js/export.js b/js/export.js index ab587f656..bafa31d87 100644 --- a/js/export.js +++ b/js/export.js @@ -251,11 +251,6 @@ var ERMrest = (function(module) { if (key in contextHeaderParams) continue; contextHeaderParams[key] = self.reference.defaultLogInfo[key]; } - // add the displayname and type of template - contextHeaderParams.template = { - displayname: self.template.displayname, - type: self.template.type - }; headers[module.contextHeaderName] = contextHeaderParams; self.canceled = false; diff --git a/js/http.js b/js/http.js index 0eaa670bf..ee4199c5d 100644 --- a/js/http.js +++ b/js/http.js @@ -126,7 +126,6 @@ function wrap(method, fn, scope) { scope = scope || window; var cfg_idx = _method_to_config_idx[method]; - var startTime = Date.now(); return function() { var args; @@ -171,7 +170,7 @@ * **/ if (typeof config.headers[module.contextHeaderName] === 'object') { - config.headers[module.contextHeaderName].elapsed_s = Date.now() - startTime; + config.headers[module.contextHeaderName].elapsed_ms = module.getElapsedTime(); // encode and make sure it's not very lengthy config.headers[module.contextHeaderName] = module._certifyContextHeader(config.headers[module.contextHeaderName]); } diff --git a/js/reference.js b/js/reference.js index 8a67ed589..2fbb2f9d1 100644 --- a/js/reference.js +++ b/js/reference.js @@ -1156,8 +1156,12 @@ uri += (i === 0 ? "?defaults=" : ',') + module._fixedEncodeURIComponent(defaults[i]); } + if (isObject(contextHeaderParams) && Array.isArray(contextHeaderParams.stack)) { + var stack = contextHeaderParams.stack; + stack[stack.length-1].num_created = data.length; + } // create the context header params for log - if (!contextHeaderParams || !isObject(contextHeaderParams)) { + else if (!contextHeaderParams || !isObject(contextHeaderParams)) { contextHeaderParams = {"action": "create"}; } var config = { @@ -1376,7 +1380,15 @@ var referenceWithoutPaging = _referenceCopy(ownReference); referenceWithoutPaging.location.beforeObject = null; - contextHeaderParams.action = action + "/correct-page"; + // remove the function and replace it with auto-reload + var actionVerb = action.substring(action.lastIndexOf(";")+1), + newActionVerb = "auto-reload"; + + // TODO (could be optimized) + if (["load-domain", "reload-domain"].indexOf(actionVerb) !== -1) { + newActionVerb = "auto-reload-domain"; + } + contextHeaderParams.action = action.substring(0,action.lastIndexOf(";")+1) + newActionVerb; referenceWithoutPaging.read(limit, contextHeaderParams, useEntity, true).then(function rereadReference(rereadPage) { defer.resolve(rereadPage); }, function error(response) { @@ -1662,9 +1674,28 @@ uri += module._fixedEncodeURIComponent(columnProjections[k]) + newAlias + ":=" + module._fixedEncodeURIComponent(columnProjections[k]); } - if (!contextHeaderParams || !isObject(contextHeaderParams)) { + /** + * We are going to add the following to the last element of stack in the logs: + * { + * "num_updated": "number of updated rows" + * } + */ + if (isObject(contextHeaderParams) && Array.isArray(contextHeaderParams.stack)) { + var stack = contextHeaderParams.stack, numUpdated = submissionData.length; + stack[stack.length-1].num_updated = numUpdated; + + stack[stack.length-1].updated_keys = { + cols: shortestKeyNames, + vals: allNewData.map(function (d) { + return shortestKeyNames.map(function (kname) { + return d[kname]; + }); + }) + }; + } else if (!contextHeaderParams || !isObject(contextHeaderParams)) { contextHeaderParams = {"action": "update"}; } + var config = { headers: this._generateContextHeader(contextHeaderParams, submissionData.length) }; @@ -2213,13 +2244,33 @@ /** * The default information that we want to be logged. This includes: - * - catalog, schema_table, cfacet, cfacet_str, cfacet_path, facets + * - catalog, schema_table + * TODO Evaluate whether we even need this function * @type {Object} */ get defaultLogInfo() { var obj = {}; obj.catalog = this.table.schema.catalog.id; obj.schema_table = this.table.schema.name + ":" + this.table.name; + return obj; + }, + + /** + * The object that can be logged to capture the filter state of the reference. + * The return object can have: + * - filters: the facet object. + * - custom_filters: + * - the filter strings that parser couldn't turn to facet. + * - if we could turn the custom filter to facet, this will return `true` + * - cfacet: if there's a cfacet it will be 1 + * - cfacet_str: if cfacet=1, it will be displayname of cfacet. + * - cfacet_path: if cfacet=1, it will be ermrest path of cfacet. + * This function creates a new object everytime that it's called, so it + * can be manipulated further. + * @type {Object} + */ + get filterLogInfo() { + var obj = {}; // custom facet if (this.location.customFacets) { @@ -2233,15 +2284,15 @@ } if (this.location.facets) { - obj.facet = this.location.facets.decoded; + obj.filters = _compressFacetObject(this.location.facets.decoded); } else if (this.location.filter) { if (this.location.filter.facet) { - obj.facet = this.location.filter.facet; + obj.filters = _compressFacetObject(this.location.filter.facet); + obj.custom_filters = true; } else { - obj.filter = this.location.filtersString; + obj.custom_filters = this.location.filtersString; } } - return obj; }, @@ -3318,7 +3369,7 @@ * 0.2 parentDisplayname: the displayname of parent * - logic: foriengkey's to_name or this.displayname * 0.3 mainTuple: the tuple used to generate the related references (might be undefined) - * 0.4 dataSource: the source path from the main to the related table (might be undefined) + * 0.4 compressedDataSource: the compressed source path from the main to the related table (might be undefined) * * 1. If it's pure and binary association. (current reference: T1) <-F1-(A)-F2-> (T2) * 1.1 displayname: F2.to_name or T2.displayname @@ -3412,11 +3463,6 @@ newRef._location = module.parse(this._location.compactUri + "/" + fkr.toString() + "/" + otherFK.toString(true), catalog); } - // build the filter source - filterSource.push({"inbound": otherFK.constraint_names[0]}); - - // buld the data source - dataSource.push({"outbound": otherFK.constraint_names[0]}); // additional values for sorting related references newRef._related_key_column_positions = fkr.key.colset._getColumnPositions(); @@ -3428,6 +3474,11 @@ newRef.derivedAssociationReference.origFKR = newRef.origFKR; newRef.derivedAssociationReference._secondFKR = otherFK; + // build the filter source + filterSource.push({"inbound": otherFK.constraint_names[0]}); + + // buld the data source + dataSource.push({"outbound": otherFK.constraint_names[0]}); } else { // Simple inbound Table newRef._table = fkrTable; newRef._shortestKey = newRef._table.shortestKey; @@ -3458,13 +3509,15 @@ }; } - // attach the dataSource if (sourceObject && sourceObject.source) { - newRef.dataSource = sourceObject.source; + dataSource = sourceObject.source; } else if (newRef._table.shortestKey.length === 1) { - newRef.dataSource = dataSource.concat(newRef._table.shortestKey[0].name); + dataSource = dataSource.concat(newRef._table.shortestKey[0].name); } + // attach the compressedDataSource + newRef.compressedDataSource = _compressSource(dataSource); + // complete the path filterSource.push({"outbound": fkr.constraint_names[0]}); @@ -3504,10 +3557,6 @@ contextHeaderParams[key] = this.defaultLogInfo[key]; } - if (isInteger(page_size)) { - contextHeaderParams.page_size = page_size; - } - var headers = {}; headers[module.contextHeaderName] = contextHeaderParams; return headers; diff --git a/js/setup/node.js b/js/setup/node.js index 4e3214760..7f0612331 100644 --- a/js/setup/node.js +++ b/js/setup/node.js @@ -199,3 +199,14 @@ ERMrest.onload = function() { return defer.promise; }; + + +var startTime = Date.now(); +/** + * @function + * @returns {integer} A value set to determine the elapsed time + * since the ermrestJS has been available (milliseconds). + */ +ERMrest.getElapsedTime = function () { + return Date.now() - startTime; +}; diff --git a/js/utils/constants.js b/js/utils/constants.js index 596077a4b..9d8edd505 100644 --- a/js/utils/constants.js +++ b/js/utils/constants.js @@ -10,6 +10,12 @@ } } + /** + * @desc Maximum allowed length of the context header that ermrestjs sends with each request. + * If the length of a context heeader goes over the limit, we will try to truncate it. + */ + module.CONTEXT_HEADER_LENGTH_LIMIT = 6500; + module._constraintTypes = Object.freeze({ KEY: "k", FOREIGN_KEY: "fk" @@ -278,3 +284,13 @@ module._specialSourceDefinitions = Object.freeze({ SEARCH_BOX: "search-box" }); + + module._shorterVersion = Object.freeze({ + "inbound": "i", + "outbound": "o", + "source": "src", + "sourcekey": "key", + "choices": "ch", + "ranges": "r", + "search": "s" + }); diff --git a/js/utils/helpers.js b/js/utils/helpers.js index 6bfcb4e6a..f7556a318 100644 --- a/js/utils/helpers.js +++ b/js/utils/helpers.js @@ -119,6 +119,23 @@ } }; + /** + * Given an object and two string (k1, k2), if object has k1 key, will + * rename that key to k2 instead (values that were accessible through k1 + * key name will be moved to k2 instead) + * @param {Object} obj + * @param {String} oldKey + * @param {String} newKey + */ + var renameKey = function (obj, oldKey, newKey) { + if (!isObjectAndNotNull(obj)) return; + if (oldKey === newKey) return; + if (!obj.hasOwnProperty(oldKey)) return; + + Object.defineProperty(obj, newKey, Object.getOwnPropertyDescriptor(obj, oldKey)); + delete obj[oldKey]; + }; + /** * Check if object has all the keys in the given array * @param {Object} obj the object @@ -2928,7 +2945,8 @@ module._constraintNames[catalogId][schemaName][constraintName] = { "subject": subject, "object": obj, - "code": "c" + (consIndex++) + "code": "c" + (consIndex++), + "RID": obj.RID }; }; @@ -3037,216 +3055,183 @@ }; /** - * Given a header value, will encode and truncate if its length is more than the allowed length. - * If the output has `"t":1`, then it has been truncated. - * These are the allowed and expected values in a header (in order or priority): - * - cid - * - pid - * - wid - * - schema_table: schema:table - * - catalog - * - cqp (chaise query parameter): 1, cfacet: 1, ppid, pcid - * - template - * - referrer: for related entities the main entity, for recordset facets: the main entity - * - schema_table - * - cfacet_str - * - cfacet_path - * - filter - * - facet - * - source: the source object of facet - * - column - * - cfacet_str - * - cfacet_path - * - filter - * - facet - * - * The truncation logic is as follows: - * 1. if the encoded string is not more than the limit, don't truncate. + * @private + * @desc + * Given a header object, will encode and if neccessary truncate it. + * Maximum allowed length of a header after encoding: 6500 characters. + * The logic is as follows: + * 1. If the encoded string is not lengthy, return it. * 2. otherwise, - * 2.1. create the bare minimum log object with the following attributes: - * - t: 1 - * - cid, wid, pid, action, catalog, schema_table - * - if cfacet exists: cfacet - * 2.2. Add other attributes step by step and check the limit. The following - * is the priority list: - * - referrer.schema_table, referrer.cfacet, referrer.filter, referrer.facet - * - source - * - column - * - filter, facet, cfacet + * 2.1. Return an empty object if the minimal header (defined below) goes over the limit. + * 2.2. Otherwise start truncating `stack` object by doing the following. In each step, + * if the encoded and truncated header goes below the length limit, return it. + * - replace all foreign key constraints with their RIDs (if RID is defined for all of them). + * - replace values (`choices`, `ranges`, `search`) in the filters with the number of values. + * - replace all `filters.and` with the number of filters. + * - replace all source paths with the number of path nodes. + * - use replace stack value with the number of stack nodes. + * If after performing all these steps, the header is still lengthy, return the minimal header. + * + * A minimal header will have the following attributes: + * - cid, pid, wid, action, schema_table, catalog, t:1 + * And might have these optional attributes: + * - elapsed_ms, cqp, ppid, pcid * - * @param {object} header The header content + * @param {object} header - the header context * @return {object} */ module._certifyContextHeader = function (header) { - var MAX_LENGTH = 6500; - var encode = module._encodeHeaderContent; - var res = encode(header), prevRes, facetRes; + var MAX_LENGTH = module.CONTEXT_HEADER_LENGTH_LIMIT; + + var shorter = module._shorterVersion; + var replaceConstraintWithRID = function (src, noRID) { + if (noRID) return false; + + var o = shorter.outbound, i = shorter.inbound, fk; + if (Array.isArray(srcs)) { + src.forEach(function (srcNode) { + if (noRID) return; + [shorter.outbound, shorter.inbound].forEach(function (direction) { + if (noRID) return; + + if (Array.isArray(srcNode[direction])) { + fk = module._getConstraintObject(catalog, srcNode[direction][0], srcNode[direction][1]); + if (fk && fk.RID) { + srcNode[direction] = fk.RID; + } else { + noRID = true; + return; + } + } + }); + }); + } + + return !noRID; + }; + + var encode = module._encodeHeaderContent, i; + + var res = encode(header); if (res.length < MAX_LENGTH) { return res; } - // the minimal required attributes for log - var obj = { + var catalog = header.catalog; + var minimalObj = { cid: header.cid, wid: header.wid, pid: header.pid, - action: header.action, catalog: header.catalog, schema_table: header.schema_table, - t: 1 // indicates that this request has been truncated + action: header.action, + t: 1 }; // these attributes might not be available on the header, but if they // are, we must include them in the minimal header content - ['cqp', 'cfacet', 'ppid', 'pcid'].forEach(function (attr) { + ['elapsed_ms', 'cqp', 'ppid', 'pcid'].forEach(function (attr) { if (header[attr]) { - obj[attr] = header[attr]; + minimalObj[attr] = header[attr]; } }); - prevRes = res = encode(obj); - if (res.length >= MAX_LENGTH) { + // if even minimal is bigger than the limit, don't log anything + if (encode(minimalObj).length >= MAX_LENGTH) { return {}; } - // truncate cfacet, facet, or filter - // if it's a facet that has `and` in the first level, - // it will remove only the array element that is needed. otherwise - // the whole facet/filter will be removed. - var truncateFacet = function (obj, header, key) { - var prevRes = encode(obj); - var h = key ? header[key] : header; + // truncation is based on stack, if there's no stack, just log the minimal object + if (!Array.isArray(header.stack)) { + return minimalObj; + } - var setObjAndEncode = function (attr) { - if (key) { - obj[key][attr] = h[attr]; - } else { - obj[attr] = h[attr]; - } - return encode(obj); - }; + var truncated = module._simpleDeepCopy(header); - if (h.cfacet_str) { - res = setObjAndEncode("cfacet_str"); - if (res.length >= MAX_LENGTH) { - return {truncated: true, res: prevRes}; - } - prevRes = res; - } - - if (h.cfacet_path) { - res = setObjAndEncode("cfacet_str"); - if (res.length >= MAX_LENGTH) { - return {truncated: true, res: prevRes}; - } - prevRes = res; - } + // replace all fk constraints with their RID + // if RID is not available on even one fk, we will not replacing any of RIDs + // and go to the next step. + var noRID = false; + truncated.stack.forEach(function (stackEl) { + if (noRID) return; - if (h.filter) { - // add filter - res = setObjAndEncode("filter"); - if (res.length >= MAX_LENGTH) { - // it was lengthy so just return the obj without filter - return {truncated: true, res: prevRes}; - } + // filters + if (stackEl.filters && Array.isArray(stackEl.filters.and)) { + stackEl.filters.and.forEach(function (facet) { + if (noRID) return; - // return result with filter - return {truncated: false, res: res}; + if (Array.isArray(facet[shorter.source])) { + noRID = !replaceConstraintWithRID(facet[shorter.source]); + } + }); } - // this function only expects cfacet, facet, and filter. it will ignore other variables - if (!h.facet) { - return {truncated: false, res: prevRes}; + // sources + if (stackEl.source && Array.isArray(stackEl.source)) { + noRID = !replaceConstraintWithRID(stackEl.source); } + }); - // only optimized for just one type of facet that we currently support: {and: []} - // otherwise just treat it as a object - if (!Array.isArray(h.facet.and)) { - - // add the facet - res = setObjAndEncode("filter"); - if (res.length >= MAX_LENGTH) { - return {truncated: true, res: prevRes}; - } - - // return result with facet - return {truncated: true, res: res}; - } + if (noRID) { + truncated = module._simpleDeepCopy(header); + } else { + res = encode(truncated); + if (res.length < MAX_LENGTH) { + return res; + } + } - if (key) { - obj[key].facet = {and: []}; - } else { - obj.facet = {and: []}; + // replace choices, ranges, search with number of values + truncated.stack.forEach(function (stackEl) { + if (stackEl.filters && Array.isArray(stackEl.filters.and)) { + stackEl.filters.and.forEach(function (facet) { + [shorter.choices, shorter.ranges, shorter.search].forEach(function (k) { + facet[k] = Array.isArray(facet[k]) ? facet[k].length : 1; + }); + }); } + }); - // add fitlers in the and array one by one until getting to the limit. - for (var i = 0; i < h.facet.and.length; i++) { - if (key) { - obj[key].facet.and.push(h.facet.and[i]); - } else { - obj.facet.and.push(h.facet.and[i]); - } + res = encode(truncated); + if (res.length < MAX_LENGTH) { + return res; + } - res = encode(obj); - if (res.length >= MAX_LENGTH) { - return {truncated: true, res: prevRes}; - } - prevRes = res; + // replace all filters.and with the number of filters + truncated.stack.forEach(function (stackEl) { + if (stackEl.filters && Array.isArray(stackEl.filters.and)) { + stackEl.filters.and = stackEl.filters.and.length; } + }); - // this means that the object had other attributes (apart from filter and facet) - // which is getting truncated - return {truncated: true, res: res}; - }; - - // template - if (header.template) { - obj.template = header.template; - res = encode(obj); - if (res.length >= MAX_LENGTH) { - return prevRes; - } + res = encode(truncated); + if (res.length < MAX_LENGTH) { + return res; } - // referrer: schema_table, facet (filter) - if (header.referrer) { - obj.referrer = { - schema_table: header.referrer.schema_table - }; - res = encode(obj); - if (res.length >= MAX_LENGTH) { - return prevRes; + // replace all source paths with the number of path nodes + truncated.stack.forEach(function (stackEl) { + if (stackEl.source) { + stackEl.source = Array.isArray(stackEl.source) ? stackEl.source.length : 1; } + }); - // take care of facet and fitler in referrer - facetRes = truncateFacet(obj, header, "referrer"); - if (facetRes.truncated) { - return facetRes.res; - } - prevRes = facetRes.res; + res = encode(truncated); + if (res.length < MAX_LENGTH) { + return res; } - // .source - if (header.source) { - obj.source = header.source; - res = encode(obj); - if (res.length >= MAX_LENGTH) { - return prevRes; - } - } + // replace stack with the number of elements + truncated.stack = truncated.stack.length; - // .column - if (header.column) { - obj.column = header.column; - res = encode(obj); - if (res.length >= MAX_LENGTH) { - return prevRes; - } + res = encode(truncated); + if (res.length < MAX_LENGTH) { + return res; } - // take care of facet and fitler - return truncateFacet(obj, header).res; + // if none of the truncation works, just return the minimal obj + return encode(minimalObj); }; module._isEntryContext = function(context) { diff --git a/js/utils/polyfills.js b/js/utils/polyfills.js index 72d03b59e..01d03c5f5 100644 --- a/js/utils/polyfills.js +++ b/js/utils/polyfills.js @@ -254,3 +254,10 @@ String.prototype.replaceAll = function(search, replacement) { Array.prototype.clear = function() { this.length = 0; }; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger +Number.isInteger = Number.isInteger || function(value) { + return typeof value === 'number' && + isFinite(value) && + Math.floor(value) === value; +}; diff --git a/js/utils/pseudocolumn_helpers.js b/js/utils/pseudocolumn_helpers.js index 53dc2baf9..f7efb2c4f 100644 --- a/js/utils/pseudocolumn_helpers.js +++ b/js/utils/pseudocolumn_helpers.js @@ -835,3 +835,70 @@ return returnError("Invalid column name in source"); } }; + + /** + * @private + * @param {Object} source the source array or string + * @desc + * Will compress the source that can be used for logging purposes. It will, + * - `inbound` to `i` + * - `outbound` to `o` + */ + _compressSource = function (source) { + if (!source) return source; + + var res = module._simpleDeepCopy(source); + var shorter = module._shorterVersion; + + if (!_sourceHasPath(source)) return res; + + for (var i = 0; i < res.length; i++) { + renameKey(res[i], "inbound", shorter.inbound); + renameKey(res[i], "outbound", shorter.outbound); + } + return res; + }; + + /** + * @private + * @param {Object} facet the facet object + * Given a facet will compress it so it can be used for logging purposes, it will, + * - `inbound` to `i` + * - `outbound` to `o` + * - `source` to `src` + * - `sourcekey` to `key` + * - `choices` to `ch` + * - `ranges` to `r` + * - `search` to `s` + * NOTE: This function supports combination of conjunction and disjunction. + */ + _compressFacetObject = function (facet) { + var res = module._simpleDeepCopy(facet), + and = module._FacetsLogicalOperators.AND, + or = module._FacetsLogicalOperators.OR, + shorter = module._shorterVersion; + + var shorten = function (node) { + return function (k) { + renameKey(node, k, shorter[k]); + }; + }; + + var compressRec = function (node) { + if ("source" in node) { + node.src = _compressSource(node.source); + delete node.source; + + ["choices", "ranges", "search", "sourcekey"].forEach(shorten(node)); + } else { + [and, or].forEach(function (operator) { + if (!Array.isArray(node[operator])) return; + + node[operator].forEach(compressRec); + }); + } + }; + + compressRec(res); + return res; + }; diff --git a/test/specs/column/tests/02.reference_column.js b/test/specs/column/tests/02.reference_column.js index a0e212341..3bd76090a 100644 --- a/test/specs/column/tests/02.reference_column.js +++ b/test/specs/column/tests/02.reference_column.js @@ -73,12 +73,12 @@ exports.execute = function (options) { // NOTE relies on the heuristics // needs to be adjusted if we change the heuristics - var expectedDataSources = [ + var expectedCompressedDataSources = [ "id", - [{"outbound": ["columns_schema", "outbound_fk_1"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_2"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_3"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_4"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_1"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_2"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_3"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_4"]}, "RID"], "col_3", "col_4", "col 5", @@ -91,11 +91,11 @@ exports.execute = function (options) { "RMT", "RCB", "RMB", - [{"outbound": ["columns_schema", "outbound_fk_5"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_6"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_8"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_7"]}, "RID"], - [{"outbound": ["columns_schema", "outbound_fk_9"]}, "RID"] + [{"o": ["columns_schema", "outbound_fk_5"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_6"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_8"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_7"]}, "RID"], + [{"o": ["columns_schema", "outbound_fk_9"]}, "RID"] ]; @@ -259,10 +259,10 @@ exports.execute = function (options) { }); - describe("dataSource, ", function () { + describe("compressedDataSource, ", function () { it ("should return the correct value for all the different column types.", function () { compactColumns.forEach(function (col, index) { - expect(col.dataSource).toEqual(expectedDataSources[index], "missmatch for index=" + index); + expect(col.compressedDataSource).toEqual(expectedCompressedDataSources[index], "missmatch for index=" + index); }); }); }) diff --git a/test/specs/column/tests/03.pseudo_column.js b/test/specs/column/tests/03.pseudo_column.js index 2979a5f73..aa9cde959 100644 --- a/test/specs/column/tests/03.pseudo_column.js +++ b/test/specs/column/tests/03.pseudo_column.js @@ -446,38 +446,38 @@ exports.execute = function (options) { // (ForeignKey, InboundforeignKey, etc.) to the shortestkey. expect(facetColumns.length).toBe(12, "length missmatch."); expect(facetColumns.map(function (fc) { - return fc.dataSource; + return fc.compressedDataSource; })).toEqual( [ "main_table_id_col", "col", "main_table_id_col", // the key - [{"outbound": ["pseudo_column_schema", "main_fk1"]}, "RID"], - [{"outbound": ["pseudo_column_schema", "main_fk1"]}, "id"], //entity:false + [{"o": ["pseudo_column_schema", "main_fk1"]}, "RID"], + [{"o": ["pseudo_column_schema", "main_fk1"]}, "id"], //entity:false [ - {"outbound": ["pseudo_column_schema", "main_fk1"]}, - {"outbound": ["pseudo_column_schema", "outbound_1_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk1"]}, + {"o": ["pseudo_column_schema", "outbound_1_fk1"]}, "id" ], [ - {"outbound": ["pseudo_column_schema", "main_fk1"]}, - {"outbound": ["pseudo_column_schema", "outbound_1_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk1"]}, + {"o": ["pseudo_column_schema", "outbound_1_fk1"]}, "id" ], "asset_filename", [ - {"outbound": ["pseudo_column_schema", "main_fk2"]}, - {"outbound": ["pseudo_column_schema", "outbound_2_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk2"]}, + {"o": ["pseudo_column_schema", "outbound_2_fk1"]}, "id" ], - [{"inbound": ["pseudo_column_schema", "inbound_3_outbound_1_fk1"]}, "RID"], + [{"i": ["pseudo_column_schema", "inbound_3_outbound_1_fk1"]}, "RID"], [ - {"inbound": ["pseudo_column_schema", "main_inbound_3_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_3_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_3_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_3_association_fk2"]}, "RID" ], [ - {"inbound": ["pseudo_column_schema", "main_inbound_3_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_3_association_fk2"]}, - {"outbound": ["pseudo_column_schema", "inbound_3_fk1"]}, + {"i": ["pseudo_column_schema", "main_inbound_3_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_3_association_fk2"]}, + {"o": ["pseudo_column_schema", "inbound_3_fk1"]}, "id" ] ], @@ -671,102 +671,102 @@ exports.execute = function (options) { }); }); - describe("dataSource, ", function () { - var detailedColumnDataSources = [ + describe("compressedDataSource, ", function () { + var detailedColumnCompressedDataSources = [ "main_table_id_col", //0 "col", //1 "main_table_id_col", //2 - [{"outbound": ["pseudo_column_schema", "main_fk1"]}, "id"], //3 - [{"outbound": ["pseudo_column_schema", "main_fk1"]}, "id"], //4 + [{"o": ["pseudo_column_schema", "main_fk1"]}, "id"], //3 + [{"o": ["pseudo_column_schema", "main_fk1"]}, "id"], //4 [ - {"outbound": ["pseudo_column_schema", "main_fk1"]}, - {"outbound": ["pseudo_column_schema", "outbound_1_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk1"]}, + {"o": ["pseudo_column_schema", "outbound_1_fk1"]}, "id" ], //5 [ - {"outbound": ["pseudo_column_schema", "main_fk1"]}, - {"outbound": ["pseudo_column_schema", "outbound_1_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk1"]}, + {"o": ["pseudo_column_schema", "outbound_1_fk1"]}, "id" ], //6 - [{"inbound": ["pseudo_column_schema", "inbound_1_fk1"]}, "id"], //7 + [{"i": ["pseudo_column_schema", "inbound_1_fk1"]}, "id"], //7 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //8 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, - {"outbound": ["pseudo_column_schema", "inbound_2_fk1"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"o": ["pseudo_column_schema", "inbound_2_fk1"]}, "id" ], //9 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, "id" + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, "id" ], //10 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //11 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, - {"outbound": ["pseudo_column_schema", "inbound_2_fk1"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"o": ["pseudo_column_schema", "inbound_2_fk1"]}, "id" ], //12 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, "id" + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, "id" ], //13 "col", //14 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //15 [ - { "inbound": [ "pseudo_column_schema", "main_inbound_2_association_fk1"]}, - { "outbound": [ "pseudo_column_schema", "main_inbound_2_association_fk2"]}, + { "i": [ "pseudo_column_schema", "main_inbound_2_association_fk1"]}, + { "o": [ "pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //16 [ - { "inbound": [ "pseudo_column_schema", "main_inbound_2_association_fk1"]}, - { "outbound": [ "pseudo_column_schema", "main_inbound_2_association_fk2"]}, + { "i": [ "pseudo_column_schema", "main_inbound_2_association_fk1"]}, + { "o": [ "pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //17 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "id" ], //18 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "RID" ], //19 [ - {"inbound": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, - {"outbound": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, + {"i": ["pseudo_column_schema", "main_inbound_2_association_fk1"]}, + {"o": ["pseudo_column_schema", "main_inbound_2_association_fk2"]}, "timestamp_col" ], //20 [ - {"inbound": ["pseudo_column_schema", "inbound_4_long_table_name_fk"]}, "foreign key column name to main" + {"i": ["pseudo_column_schema", "inbound_4_long_table_name_fk"]}, "foreign key column name to main" ], //21 "asset", //22 "asset_filename", //23 [ - {"outbound": ["pseudo_column_schema", "main_fk2"]}, - {"inbound": ["pseudo_column_schema", "outbound_2_inbound_1_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk2"]}, + {"i": ["pseudo_column_schema", "outbound_2_inbound_1_fk1"]}, "id" ], //24 [ - {"outbound": ["pseudo_column_schema", "main_fk2"]}, - {"outbound": ["pseudo_column_schema", "outbound_2_fk1"]}, + {"o": ["pseudo_column_schema", "main_fk2"]}, + {"o": ["pseudo_column_schema", "outbound_2_fk1"]}, "id" ] //25 ]; it ("should return the data source of the pseudo-column.", function () { detailedColsWTuple.forEach(function (col, index) { - expect(col.dataSource).toEqual(detailedColumnDataSources[index], "missmatch for index=" + index); + expect(col.compressedDataSource).toEqual(detailedColumnCompressedDataSources[index], "missmatch for index=" + index); }); }); }) diff --git a/test/specs/reference/tests/02.related_reference.js b/test/specs/reference/tests/02.related_reference.js index 778687f8e..2f26eb3ee 100644 --- a/test/specs/reference/tests/02.related_reference.js +++ b/test/specs/reference/tests/02.related_reference.js @@ -199,54 +199,54 @@ exports.execute = function(options) { expect(related[3].origFKR.toString()).toBe('(id)=(reference_schema:association%20table%20with%20id:id%20from%20ref%20table)'); }); - it ('.dataSource should have the correct value', function () { - var expectedDataSources = [ - [{"inbound": ["reference_schema", "fromname_fk_inbound_related_to_reference"]}, "RID"], - [{"inbound": ["reference_schema", "fk_inbound_related_to_reference"]}, "id"], + it ('.compressedDataSource should have the correct value', function () { + var expectedCompressedDataSources = [ + [{"i": ["reference_schema", "fromname_fk_inbound_related_to_reference"]}, "RID"], + [{"i": ["reference_schema", "fk_inbound_related_to_reference"]}, "id"], [ - {"inbound": ["reference_schema", "toname_fk_association_related_to_reference"]}, - {"outbound": ["reference_schema", "association_table_with_toname_id_from_inbound_related_table1"]}, + {"i": ["reference_schema", "toname_fk_association_related_to_reference"]}, + {"o": ["reference_schema", "association_table_with_toname_id_from_inbound_related_table1"]}, "RID" ], [ - {"inbound": ["reference_schema", "id_fk_association_related_to_reference"]}, - {"outbound": ["reference_schema", "fk_to_inbound_related_reference_table"]}, + {"i": ["reference_schema", "id_fk_association_related_to_reference"]}, + {"o": ["reference_schema", "fk_to_inbound_related_reference_table"]}, "id" ], [ - {"inbound": ["reference_schema", "system_col_fk_asscoation_related_to_reference"]}, - {"outbound": ["reference_schema", "association_table_with_system_col_fk_fk2"]}, + {"i": ["reference_schema", "system_col_fk_asscoation_related_to_reference"]}, + {"o": ["reference_schema", "association_table_with_system_col_fk_fk2"]}, "RID" ], [ - {"inbound": ["reference_schema", "extra_fk_association_related_to_reference"]}, + {"i": ["reference_schema", "extra_fk_association_related_to_reference"]}, "RID" ] ]; related.forEach(function (rel, i) { - expect(related[i].dataSource).toEqual(expectedDataSources[i], "missmatch for index=" + i); + expect(related[i].compressedDataSource).toEqual(expectedCompressedDataSources[i], "missmatch for index=" + i); }); }); describe("for related tables using source path.", function () { // what about other APIs? - it (".dataSource should have the correct value.", function () { - var expectedDataSources = [ - [{"inbound": ["reference_schema", "id_fk_association_related_to_reference"]}, "ID"], + it (".compressedDataSource should have the correct value.", function () { + var expectedCompressedDataSources = [ + [{"i": ["reference_schema", "id_fk_association_related_to_reference"]}, "ID"], [ - {"inbound": ["reference_schema", "id_fk_association_related_to_reference"]}, - {"outbound": ["reference_schema", "fk_to_inbound_related_reference_table"]}, - {"outbound": ["reference_schema", "fromname_fk_inbound_related_to_reference"]}, + {"i": ["reference_schema", "id_fk_association_related_to_reference"]}, + {"o": ["reference_schema", "fk_to_inbound_related_reference_table"]}, + {"o": ["reference_schema", "fromname_fk_inbound_related_to_reference"]}, "id" ], [ - {"outbound": ["reference_schema", "reference_table_fk1"]}, - {"inbound": ["reference_schema", "reference_outbound_1_inbound_1_fk1"]}, + {"o": ["reference_schema", "reference_table_fk1"]}, + {"i": ["reference_schema", "reference_outbound_1_inbound_1_fk1"]}, "id" ] ]; pathRelatedWithTuple.forEach(function (rel, i) { - expect(rel.dataSource).toEqual(expectedDataSources[i], "missmatch for index=" + i); + expect(rel.compressedDataSource).toEqual(expectedCompressedDataSources[i], "missmatch for index=" + i); }); }); });