diff --git a/docs/user-docs/annotation.md b/docs/user-docs/annotation.md
index 02550357..3c8842af 100644
--- a/docs/user-docs/annotation.md
+++ b/docs/user-docs/annotation.md
@@ -99,6 +99,7 @@ Supported JSON payload patterns:
- `{`... `"show_key_link":` `{` _context_ `:` _keylink_ `,` ... `}`: Whether default display of keys (sel link) should include link to the row.
- `{`... `"show_foreign_key_link":` `{` _context_ `:` _fklink_ `,` ... `}`: Whether default display of foreign keys should include link to the row.
- `{`... `"hide_row_count":` `{` _context_ `:` _rowcount_ `,` ... `}`: Whether we should display the total row count. Since the request to fetch total row count is expensive, you can use this to signal to client to skip the request (and therefore do not display it to users.)
+- `{`... `"max_facet_depth":` `{` _context_ `:` _maxfacetdepth_ `,` ...`}`: How many levels of facet popups we should allow.
- `{`... `"show_saved_query":` _savedquery_ ...`}`: Whether we want to display the saved query UI features or not. By default, this feature is turned off (set to false).
Supported JSON _ccomment_ patterns:
@@ -157,6 +158,13 @@ Supported JSON _savedquery_ patterns:
- `true`: Display the saved query UI features.
- `false`: Don't display the saved query UI features.
+Supported JSON _maxfacetdepth_ patterns:
+
+- `0`: Disable the faceting feature.
+- `1`: Facet panel is only displayed for one level. The facet panel is not displayed as part of the facet popups.
+- `2`: Facet panel is displayed for two levels. Both once the main table is opened and when the facet popup is opened for any of the facets.
+- Any number bigger than 2 will be treated the same as defining `2`.
+
Supported JSON _context_ patterns:
- See [Context Names](#context-names) section for the list of supported JSON _context_ patterns.
diff --git a/docs/user-docs/export.md b/docs/user-docs/export.md
index 26953949..dc597968 100644
--- a/docs/user-docs/export.md
+++ b/docs/user-docs/export.md
@@ -1,6 +1,6 @@
# Export Annotation
-Using the [export annotation](annotation.md#tag-2019-export) you can define export templates that will be used for deriva-py export service integration with the client tools. To make the process of writing export annotation simpler and modular, you can use [export fragment annotation](annotation.md#tag-2021-export-fragment-annotations). In this document, we will explain how you can write these two annotations and how export integration works in ERMrestJS/Chaise.
+Using the [export annotation](annotation.md#tag-2019-export) you can define export templates that will be used for deriva-py export service integration with the client tools. To make the process of writing export annotation simpler and modular, you can use [export fragment annotation](annotation.md#tag-2021-export-fragment-definitions). In this document, we will explain how you can write these two annotations and how export integration works in ERMrestJS/Chaise.
If you just want to see the overall structure of export annotation go [here](#overall-structure) or you can look at some examples in [here](#examples).
@@ -22,7 +22,9 @@ If you just want to see the overall structure of export annotation go [here](#ov
## Export Templates
-Export of data from Chaise is configured through the use of *export templates*. An export template is a JSON object that is used in an ERMrest table/schema/catalog annotation payload. The annotation is specified using `tag:isrd.isi.edu,2016:export` key.
+Export of data from Chaise is configured through the use of *export templates*. An export template is a JSON object that is used in an ERMrest table/schema/catalog annotation payload. The annotation is specified using `tag:isrd.isi.edu,2019:export` key.
+
+> ⚠️ The first iteration of export used the [`tag:isrd.isi.edu,2016:export`](annotation-deprecated.md#tag-2016-export) tag (notice the 2016 instead of 2019). But this old tag doesn't support context and all the new features added to the new key. So please make sure you're using the new tag.
The annotation payload is a JSON object containing a single array of `template` objects. One or more templates can be specified for a given table entity. Templates specify a format name and type, followed by a set of output descriptor objects. A template output descriptor maps one or more source table queries to one or more output file destinations.
@@ -269,7 +271,7 @@ The type of `source.api` that is used does not matter, as long as the result dat
## How it works
-For processing export, we have to consult [export annotation](annotation.md#tag-2019-export) and [export fragment annotation](annotation.md#tag-2021-export-fragment-annotations). The following is how ERMrestJS looks at these two annotations:
+For processing export, we have to consult [export annotation](annotation.md#tag-2019-export) and [export fragment annotation](annotation.md#tag-2021-export-fragment-definitions). The following is how ERMrestJS looks at these two annotations:
1. We start by creating a fragment object that can be used while writing export annotation. To do so,
diff --git a/js/column.js b/js/column.js
index 8c615276..bb5d0a4e 100644
--- a/js/column.js
+++ b/js/column.js
@@ -3476,7 +3476,7 @@ FacetColumn.prototype = {
}
// G3
- var othersHaveNull = self.reference.facetColumns.some(function (fc, index) {
+ var othersHaveNull = self.reference.location.isUsingRightJoin || self.reference.facetColumns.some(function (fc, index) {
return index !== self.index && fc.hasNullFilter && fc.hasPath;
});
diff --git a/js/parser.js b/js/parser.js
index 2cd83b58..efb9e560 100644
--- a/js/parser.js
+++ b/js/parser.js
@@ -268,14 +268,13 @@
}
// pathParts:
- var aliasJoinRegExp = /\$.*/,
- joinRegExp = /(?:left|right|full|^)\((.*)\)=\((.*:.*:.*)\)/,
+ var joinRegExp = /(?:left|right|full|^)\((.*)\)=\((.*:.*:.*)\)/,
facetsRegExp = /\*::facets::(.+)/,
customFacetsRegExp = /\*::cfacets::(.+)/;
- var schemaTable = parts[0], table = this._rootTableName, schema = this._rootSchemaName;
- var self = this, pathParts = [], alias, match, prevJoin = false;
- var facets, cfacets, filter, filtersString, searchTerm, join, joins = [];
+ var table = this._rootTableName, schema = this._rootSchemaName;
+ var self = this, pathParts = [], alias, match, prevJoin = false, startWithT1 = false, aliasNumber;
+ var facets, cfacets, filter, filtersString, join, joins = [];
// go through each of the parts
parts.forEach(function (part, index) {
@@ -291,9 +290,23 @@
// there wasn't any join before, so this is the start of new path,
// so we should create an object for the previous one.
if (!prevJoin && index !== 1) {
- // we're creating this for the previous section, so we should use the previous index
- // Alias will be T, T1, T2, ... (notice we don't have T0)
- alias = module._parserAliases.JOIN_TABLE_PREFIX + (pathParts.length > 0 ? pathParts.length : "");
+ /**
+ * we're creating this alias for the previous section, so we should use the previous index
+ * Alias will be T, T1, T2, ... (notice we don't have T0)
+ */
+ aliasNumber = pathParts.length;
+
+ /**
+ * T is always preserved for the initial schema:table. if the first pathPart doesn't have any joins
+ * and has facet/filters, then it's for the schema:table and so we should start with T.
+ * but if has join, then it means that the schema:table didn't have any filter/facets. so we have to start from T1,
+ * and the rest of the parts should just add one more
+ */
+ if (pathParts.length === 0 && joins.length > 0) {
+ startWithT1 = true;
+ }
+ if (startWithT1) aliasNumber++;
+ alias = module._parserAliases.JOIN_TABLE_PREFIX + (aliasNumber > 0 ? aliasNumber : "");
pathParts.push(new PathPart(alias, joins, schema, table, facets, cfacets, filter, filtersString));
filter = undefined; filtersString = undefined; cfacets = undefined; facets = undefined; join = undefined; joins = [];
}
@@ -485,6 +498,7 @@
* @returns {Object} an object wit the following properties:
* - `path`: Path without modifiers or queries for ermrest
* - `pathPrefixAliasMapping`: alias mapping that are used in the url
+ * - `isUsingRightJoin`: whether we've used right outer join for this path.
*/
computeERMrestCompactPath: function (usedSourceObjects) {
var self = this;
@@ -671,7 +685,7 @@
};
});
- // no rightJoin: s:t/
+ // no rightJoin: s:t/
if (rightJoinIndex === -1) {
uri = self.rootTableAlias + ":=";
if (self.rootSchemaName) {
@@ -692,14 +706,14 @@
if (self.pathParts[i].joins.length > 0) {
// since we're reversing, we have to make sure we're using the
// alias of the previous pathpart
- alias = i > 0 ? self.pathPart[i-1].alias : self.rootTableAlias;
+ alias = i > 0 ? self.pathParts[i-1].alias : self.rootTableAlias;
uri += "/" + joinsStr(self.pathParts[i], alias, i, true);
}
}
// if there was pathParts before facet with null, change back to the facet with null
if (self.pathParts[rightJoinIndex].joins.length > 0) {
- uri += "/$" + self.pathParts[i].alias;
+ uri += "/$" + self.pathParts[rightJoinIndex].alias;
}
}
@@ -723,7 +737,8 @@
return {
path: uri,
// TODO could be replaced with the function
- pathPrefixAliasMapping: lastPathPartAliasMapping
+ pathPrefixAliasMapping: lastPathPartAliasMapping,
+ isUsingRightJoin: rightJoinIndex !== -1,
};
},
@@ -732,7 +747,7 @@
var res = this.computeERMrestCompactPath();
this._ermrestCompactPath = res.path;
this._pathPrefixAliasMapping = res.pathPrefixAliasMapping;
-
+ this._isUsingRightJoin = res.isUsingRightJoin;
}
return this._ermrestCompactPath;
},
@@ -757,6 +772,18 @@
return this._pathPrefixAliasMapping;
},
+ /**
+ * whether we're using right outer join for parsing the location
+ * @type {boolean}
+ */
+ get isUsingRightJoin() {
+ if (this._isUsingRightJoin === undefined) {
+ // this API will populate this
+ var dummy = this.ermrestCompactPath;
+ }
+ return this._isUsingRightJoin;
+ },
+
/**
* Array of path parts
* @type {PathPart[]}
@@ -1381,8 +1408,31 @@
var loc = clone ? this._clone() : this;
// change alias of the previous part
var lastPart = loc.lastPathPart;
+
+ /**
+ * since we're adding a path part, we have to change the alias of previous part.
+ * this alias is for the projected table. if there are joins, it will be added to the last
+ * join and if there aren't, it will be attached to the schema:table.
+ *
+ * when there aren't any joins, this could be the schema:table, so we have to start with T.
+ * but if there are joins, we can start with T1, as T alias is always the first schema:table.
+ *
+ * this is how we're using the alias property:
+ * - if it doesn't have any joins, it's the alias of the previous path or root (so facet/)
+ * - if it has join, it's the alias that we're using to name the projected table that the join represents.
+ */
if (lastPart) {
- var alias = module._parserAliases.JOIN_TABLE_PREFIX + (loc.pathParts.length > 1 ? (loc.pathParts.length-1) : "");
+ var aliasNumber = "";
+ if (lastPart.joins && lastPart.joins.length > 0) {
+ if (loc.pathParts.length > 0) {
+ aliasNumber = loc.pathParts.length;
+ }
+ }
+ else if (loc.pathParts.length > 1) {
+ aliasNumber = loc.pathParts.length - 1;
+ }
+
+ var alias = module._parserAliases.JOIN_TABLE_PREFIX + aliasNumber;
lastPart.alias = alias;
}
@@ -1416,6 +1466,8 @@
* Container for the path part, It will have the following attributes:
* - joins: an array of ParsedJoin objects.
* - alias: The alias that the facets should refer to
+ * - if there aren't any joins, it's the alias of the previous path or root.
+ * - otherwise, it's the alias that we're using to name the projected table that the join represents.
* - schema: The schema that the join ends up with
* - table: the table that the joins end up with
* - facets: the facets object
@@ -1684,7 +1736,7 @@
* @returns {ParsedJoin}
*/
function _createParsedJoinFromStr (linking, table, schema) {
- var fromSchemaTable = schema ? [table,schema].join(":") : table;
+ var fromSchemaTable = schema ? [schema,table].join(":") : table;
var fromCols = linking[1].split(",");
var toParts = linking[2].match(/([^:]*):([^:]*):([^\)]*)/);
var toCols = toParts[3].split(",");
diff --git a/js/reference.js b/js/reference.js
index 7b8d22f6..7f5ff65f 100644
--- a/js/reference.js
+++ b/js/reference.js
@@ -2303,11 +2303,14 @@
/**
* An object which contains row display properties for this reference.
- * It is determined based on the `table-display` annotation. It has the
+ * It is determined based on the `table-display`, `display`, and 'chaise-config' annotations. It has the
* following properties:
*
* - `rowOrder`: `[{ column: '`_column object_`', descending:` {`true` | `false` } `}`...`]` or `undefined`,
* - `type`: {`'table'` | `'markdown'` | `'module'`} (default: `'table'`)
+ * - `showFaceting`: A boolean indicating whether we should show the faceting feature or not.
+ * - `maxFacetDepth`: A number indicating the facet depth.
+ * - `facetPanelOpen`: Whether the facet panel should be opened by default or not.
*
* If type is `'markdown'`, the object will also these additional
* properties:
@@ -2445,7 +2448,7 @@
}
// if facetpanel won't be used or the _clientConfig.facetPanelDisplay doesn't include the current context or a parent context, set to null
- var fpo = null;
+ var fpo = null, maxFacetDepth = null;
// NOTE: clientConfig should always be defined if used by a client, some ermrestJS tests don't always set it, so don't calculate this for those tests
if (this._context && module._clientConfig) {
// _clientConfig.facetPanelDisplay will be defined from configuration or based on chaise defaults
@@ -2454,6 +2457,20 @@
// facet panel is not available in COMPACT_BRIEF and it's subcontexts, and other non-compact contexts
if (context.startsWith(module._contexts.COMPACT) && !context.startsWith(module._contexts.COMPACT_BRIEF)) {
+
+ // get it from the display annotation
+ maxFacetDepth = module._getHierarchicalDisplayAnnotationValue(this.table, context, "max_facet_depth", true);
+ // missing from the annotation, so get it from the chaise-config
+ if (maxFacetDepth === -1 && typeof ccFacetDisplay.maxFacetDepth === 'number') {
+ maxFacetDepth = ccFacetDisplay.maxFacetDepth;
+ }
+ // validate the value
+ if (!isInteger(maxFacetDepth) || maxFacetDepth < 0) {
+ maxFacetDepth = 1
+ } else if (maxFacetDepth > 2) {
+ maxFacetDepth = 2;
+ }
+
if (ccFacetDisplay.closed && ccFacetDisplay.closed.includes("*")) fpo = false;
if (ccFacetDisplay.open && ccFacetDisplay.open.includes("*")) fpo = true;
// check inheritence
@@ -2472,6 +2489,12 @@
}
}
}
+ // for other contexts that don't allow facet
+ maxFacetDepth = maxFacetDepth === null ? 0 : maxFacetDepth;
+
+ this._display.maxFacetDepth = maxFacetDepth;
+ this._display.showFaceting = maxFacetDepth > 0;
+
this._display.facetPanelOpen = fpo;
/**
@@ -3950,7 +3973,11 @@
this._googleDatasetMetadata = null;
} else {
var metadataAnnotation = module._getRecursiveAnnotationValue(this._context, this._table.annotations.get(module._annotations.GOOGLE_DATASET_METADATA).content);
- this._googleDatasetMetadata = new GoogleDatasetMetadata(this, metadataAnnotation);
+ if (!isObjectAndNotNull(metadataAnnotation) || !isObjectAndNotNull(metadataAnnotation.dataset)) {
+ this._googleDatasetMetadata = null;
+ } else {
+ this._googleDatasetMetadata = new GoogleDatasetMetadata(this, metadataAnnotation);
+ }
}
}
return this._googleDatasetMetadata;
diff --git a/js/utils/helpers.js b/js/utils/helpers.js
index 6ed0bf08..40d9a60f 100644
--- a/js/utils/helpers.js
+++ b/js/utils/helpers.js
@@ -631,6 +631,8 @@
};
/**
+ * retun the value that should be used for the display setting. If missing, it will return "-1".
+ *
* @param {ERMrest.Table|ERMrest.Column|ERMrest.ForeignKeyRef} obj either table object, or an object that has `.table`
* @param {String} context the context string
* @param {String} annotKey the annotation key that you want the annotation value for
diff --git a/test/specs/annotation/conf/table_display/schema.json b/test/specs/annotation/conf/table_display/schema.json
index 1d88eb52..da0e7517 100644
--- a/test/specs/annotation/conf/table_display/schema.json
+++ b/test/specs/annotation/conf/table_display/schema.json
@@ -671,6 +671,37 @@
}
}
}
+ },
+ "table_w_max_facet_depth": {
+ "table_name": "table_w_max_facet_depth",
+ "schema_name": "schema_table_display",
+ "kind": "table",
+ "keys": [
+ {"unique_columns": ["id"]}
+ ],
+ "foreign_keys": [],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ },
+ {
+ "name": "text_col",
+ "type": {
+ "typename": "text"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:misd.isi.edu,2015:display": {
+ "max_facet_depth": {
+ "compact/select/association/link": 0
+ }
+ }
+ }
}
},
"schema_name": "schema_table_display",
@@ -679,6 +710,9 @@
"hide_row_count": {
"compact": true,
"compact/select": false
+ },
+ "max_facet_depth": {
+ "compact/select/association/unlink": 1
}
}
}
diff --git a/test/specs/annotation/tests/03.table_display.js b/test/specs/annotation/tests/03.table_display.js
index 87062b5f..af91fdbc 100644
--- a/test/specs/annotation/tests/03.table_display.js
+++ b/test/specs/annotation/tests/03.table_display.js
@@ -20,7 +20,8 @@ exports.execute = function (options) {
tableName11 = "table_w_table_display_annotation_w_title",
tableNameWoAnnot = "table_wo_annotation",
tableNameCatalogAnnot = "table_w_rowname_catalog_snapshot",
- tableNameCompactOptions = "table_w_compact_options";
+ tableNameCompactOptions = "table_w_compact_options",
+ tableNameWMaxFacetDepth = "table_w_max_facet_depth";
var table1EntityUri = options.url + "/catalog/" + catalog_id + "/entity/" +
schemaName + ":" + tableName1 + "/@sort(id)";
@@ -68,6 +69,9 @@ exports.execute = function (options) {
var tableCompactOptionsEntityUri = options.url + "/catalog/" + catalog_id + "/entity/" + schemaName + ":" +
tableNameCompactOptions;
+ var tableWMaxFacetDepthUri = options.url + "/catalog/" + catalog_id + "/entity/" + schemaName + ":" +
+ tableNameWMaxFacetDepth;
+
var chaiseURL = "https://example.org/chaise";
var recordURL = chaiseURL + "/record";
var record2URL = chaiseURL + "/record-two";
@@ -75,7 +79,7 @@ exports.execute = function (options) {
var searchURL = chaiseURL + "/search";
var recordsetURL = chaiseURL + "/recordset";
- var appLinkFn = function (tag, location) {
+ const appLinkFn = function (tag, location) {
var url;
switch (tag) {
case "tag:isrd.isi.edu,2016:chaise:record":
@@ -845,6 +849,57 @@ exports.execute = function (options) {
});
});
+ describe('display.showFaceting and display.maxFacetDepth', () => {
+ let refTableWMaxFacetDepth;
+
+ beforeAll((done) => {
+ utils.setCatalogAnnotations(options, {
+ "max_facet_depth": {
+ "compact/select/foreign_key": 2
+ }
+ }).then(() => {
+ options.ermRest.setClientConfig({
+ facetPanelDisplay: { maxFacetDepth: 3 }
+ });
+ return options.ermRest.resolve(tableWMaxFacetDepthUri, {cid: 'test'});
+ }).then((ref) => {
+ refTableWMaxFacetDepth = ref;
+ done();
+ }).catch((err) => done.fail(err));
+ });
+
+ it ('should get it from the display annotation on the table', () => {
+ let ref = refTableWMaxFacetDepth.contextualize.compactSelectAssociationLink;
+ expect(ref.display.showFaceting).toBe(false);
+ expect(ref.display.maxFacetDepth).toBe(0);
+ });
+
+ it ('otherwise should get it from display annotation on schema', () => {
+ let ref = refTableWMaxFacetDepth.contextualize.compactSelectAssociationUnlink;
+ expect(ref.display.showFaceting).toBe(true);
+ expect(ref.display.maxFacetDepth).toBe(1);
+ });
+
+ it ('otherwise should get it from the display annotation on catalog', () => {
+ let ref = refTableWMaxFacetDepth.contextualize.compactSelectForeignKey;
+ expect(ref.display.showFaceting).toBe(true);
+ expect(ref.display.maxFacetDepth).toBe(2);
+ });
+
+ it ('otherwise should get it from the client config.', () => {
+ let ref = refTableWMaxFacetDepth.contextualize.compactSelectSavedQueries;
+ expect(ref.display.showFaceting).toBe(true);
+ expect(ref.display.maxFacetDepth).toBe(2); // max that we allow is 2
+ });
+
+ afterAll((done) => {
+ // removed the catalog annotation
+ utils.setCatalogAnnotations(options, {}).then(() => {
+ done();
+ }).catch((err) => done.fail(err));
+ });
+ });
+
});
};
diff --git a/test/specs/json_ld/conf/google_metadata/schema.json b/test/specs/json_ld/conf/google_metadata/schema.json
index db7be0f9..9cb63abb 100644
--- a/test/specs/json_ld/conf/google_metadata/schema.json
+++ b/test/specs/json_ld/conf/google_metadata/schema.json
@@ -98,6 +98,157 @@
}
}
}
+ },
+ "missing_google_metadata": {
+ "kind": "table",
+ "table_name": "missing_google_metadata",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ]
+ },
+ "invalid_google_metadata_1": {
+ "kind": "table",
+ "table_name": "invalid_google_metadata_1",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:isrd.isi.edu,2021:google-dataset": null
+ }
+ },
+ "invalid_google_metadata_2": {
+ "kind": "table",
+ "table_name": "invalid_google_metadata_2",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:isrd.isi.edu,2021:google-dataset": {}
+ }
+ },
+ "invalid_google_metadata_3": {
+ "kind": "table",
+ "table_name": "invalid_google_metadata_3",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:isrd.isi.edu,2021:google-dataset": {
+ "detailed": null
+ }
+ }
+ },
+ "invalid_google_metadata_4": {
+ "kind": "table",
+ "table_name": "invalid_google_metadata_4",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:isrd.isi.edu,2021:google-dataset": {
+ "detailed": {
+ "some_invalid_prop": "test"
+ }
+ }
+ }
+ },
+ "invalid_google_metadata_5": {
+ "kind": "table",
+ "table_name": "invalid_google_metadata_5",
+ "schema_name": "google_metadata_schema",
+ "keys": [
+ {
+ "unique_columns": [
+ "id"
+ ]
+ }
+ ],
+ "column_definitions": [
+ {
+ "name": "id",
+ "nullok": false,
+ "type": {
+ "typename": "integer"
+ }
+ }
+ ],
+ "annotations": {
+ "tag:isrd.isi.edu,2021:google-dataset": {
+ "detailed": {
+ "dataset": "test"
+ }
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/test/specs/json_ld/tests/01.reference_google_dataset.js b/test/specs/json_ld/tests/01.reference_google_dataset.js
index 9d615b19..3f2f0423 100644
--- a/test/specs/json_ld/tests/01.reference_google_dataset.js
+++ b/test/specs/json_ld/tests/01.reference_google_dataset.js
@@ -9,8 +9,6 @@ exports.execute = function (options) {
schemaName = "google_metadata_schema",
tableName = "google_metadata_w_handlebars";
- var handlebarsGoogleMetadataUri = options.url + "/catalog/" + catalog_id + "/entity/" + schemaName + ":" + tableName + "/@sort(id)";
-
var chaiseURL = "https://example.org/chaise";
var recordURL = chaiseURL + "/record";
var record2URL = chaiseURL + "/record-two";
@@ -46,17 +44,39 @@ exports.execute = function (options) {
return url;
};
+ const getURL = (tName) => {
+ return options.url + "/catalog/" + catalog_id + "/entity/" + schemaName + ":" + tName + "/@sort(id)";
+ }
+
+ const resolveReference = async (tName) => {
+ return options.ermRest.resolve(getURL(tName), { cid: "test" });
+ }
+
beforeAll(function () {
options.ermRest.appLinkFn(appLinkFn);
});
+ it('googleDatasetMetadata.compute should handle tables without any google-dataset annotation or invalid ones', async () => {
+ const expectNull = async (tName) => {
+ const ref = await resolveReference(tName);
+ expect(ref.contextualize.detailed.googleDatasetMetadata).toBeNull();
+ };
+
+ expectNull('missing_google_metadata');
+ expectNull('invalid_google_metadata_1');
+ expectNull('invalid_google_metadata_2');
+ expectNull('invalid_google_metadata_3');
+ expectNull('invalid_google_metadata_4');
+ expectNull('invalid_google_metadata_5');
+ });
+
describe("with handlebar templating", function () {
var tuples, tuple, reference;
beforeAll(function (done) {
- options.ermRest.resolve(handlebarsGoogleMetadataUri, { cid: "test" }).then(function (response) {
+ resolveReference(tableName).then(function (response) {
reference = response.contextualize.detailed;
- return response.read(2);
+ return reference.read(2);
}).then(function (response) {
tuples = response.tuples;
done();
diff --git a/test/specs/json_ld/tests/02.json_ld_validator.js b/test/specs/json_ld/tests/02.json_ld_validator.js
index fc679532..0ce98e89 100644
--- a/test/specs/json_ld/tests/02.json_ld_validator.js
+++ b/test/specs/json_ld/tests/02.json_ld_validator.js
@@ -3,7 +3,7 @@ exports.execute = function (options) {
var module = options.includes.ermRest;
describe("validation of structured data", function () {
it("should ignore attributes that are not part of the vocabulary", function (done) {
- var jsonInput = {"@type": "Dataset","@context": "http://schema.org","name": "dummy",
+ var jsonInput = {"@type": "Dataset","@context": "http://schema.org","name": "dummy",
"description": "lorem ipsum", "verson": "4"};
expect(jsonInput.verson).toEqual("4");
@@ -16,7 +16,7 @@ exports.execute = function (options) {
});
it("should return false if missing @context", function (done) {
- var jsonInput = {"@type": "Dataset","name": "dummy",
+ var jsonInput = {"@type": "Dataset","name": "dummy",
"description": "lorem ipsum"};
expect(jsonInput["@context"]).toEqual(undefined);
@@ -29,7 +29,7 @@ exports.execute = function (options) {
});
it("should return false if incorrect @type", function (done) {
- var jsonInput = {"@type": "Datass","@context": "http://schema.org","name": "dummy",
+ var jsonInput = {"@type": "Datass","@context": "http://schema.org","name": "dummy",
"description": "lorem ipsum"};
expect(jsonInput["@type"]).toEqual("Datass");
@@ -52,11 +52,11 @@ exports.execute = function (options) {
}
};
expect(jsonInput["creator"]["name"]).toEqual(undefined);
- expect(jsonInput["creator"]).toBeObject();
+ expect(jsonInput["creator"]).toBeTruthy();
var result = module.validateJSONLD(jsonInput);
expect(result.isValid).toEqual(true);
- expect(result.modifiedJsonLd["creator"]).not.toBeObject();
+ expect(result.modifiedJsonLd["creator"]).not.toBeTruthy();
delete jsonInput["creator"];
expect(result.modifiedJsonLd).toEqual(jsonInput);
done();
@@ -72,7 +72,7 @@ exports.execute = function (options) {
});
it("should ignore attribute if datatype not followed", function (done) {
- var jsonInput = {"@type": "Dataset","@context": "http://schema.org","name": "dummy",
+ var jsonInput = {"@type": "Dataset","@context": "http://schema.org","name": "dummy",
"description": "lorem ipsum", "url": 77};
expect(jsonInput["url"]).toEqual(77);
@@ -87,7 +87,7 @@ exports.execute = function (options) {
it("The google_metadata with incorrect element(incorrect data type) inside array returned with that element excluded", function (done) {
var jsonInput = {
"@type": "Dataset", "@context": "http://schema.org", "name": "dummy",
- "description": "lorem ipsum",
+ "description": "lorem ipsum",
"keywords": ["sales",
"ice cream",
"ice cream brands",
@@ -118,7 +118,7 @@ exports.execute = function (options) {
it("The google_metadata with incorrect element(null) inside array returned with that element excluded", function (done) {
var jsonInput = {
"@type": "Dataset", "@context": "http://schema.org", "name": "dummy",
- "description": "lorem ipsum",
+ "description": "lorem ipsum",
"keywords": ["sales",
"ice cream",
"ice cream brands",
@@ -149,7 +149,7 @@ exports.execute = function (options) {
it("The google_metadata with all incorrect element(null) inside array returned without array", function (done) {
var jsonInput = {
"@type": "Dataset", "@context": "http://schema.org", "name": "dummy",
- "description": "lorem ipsum",
+ "description": "lorem ipsum",
"keywords": [
null,
""
diff --git a/test/specs/parser/tests/01.parser.js b/test/specs/parser/tests/01.parser.js
index 59ed911d..7fd85386 100644
--- a/test/specs/parser/tests/01.parser.js
+++ b/test/specs/parser/tests/01.parser.js
@@ -691,11 +691,24 @@ exports.execute = function(options) {
"ermrestCompactPath missmatch"
);
});
+
+ it ("parser should handle not having any filter on the first layer.", function () {
+ uri = baseUri + `/(id)=(s:table2:id)/!(RID=%3A%3Anull%3A%3A&RCB=%3A%3Anull%3A%3A)/(id3)=(s:table3:id)/*::facets::${validBlob2}/(id4)=(s:table4:id)`;
+ location = options.ermRest.parse(uri);
+ expect(location).toBeDefined("location is not defined");
+ expect(location.uri).toEqual(uri, "uri missmatch");
+ expect(location.ermrestCompactPath).toEqual(
+ "T:=parse_schema:parse_table/T1:=(id)=(s:table2:id)/!(RID=%3A%3Anull%3A%3A&RCB=%3A%3Anull%3A%3A)/T2:=(id3)=(s:table3:id)/accession=2/$T2/some-other-column::ciregexp::test2/$T2/M:=(id4)=(s:table4:id)",
+ "ermrestCompactPath missmatch"
+ );
+ });
});
- // relies on the previous test
describe("for changing facets in location, ", function () {
it("Location.facets setter should be able to change the facet and update other APIs.", function() {
+ uri = baseUri + "/*::facets::" + validBlob + "/some_col=v1/(id)=(s:otherTable:id)/(id2)=(s:otherTable2:id)" +
+ "/*::facets::" + validBlob2 + "/some_col2=v2/(id)=(s:yetAnotherTable:id)" + "/*::facets::" + validBlob3;
+ location = options.ermRest.parse(uri);
location.facets = facetObj;
uri = baseUri + "/*::facets::" + validBlob + "/some_col=v1/(id)=(s:otherTable:id)/(id2)=(s:otherTable2:id)" +
@@ -1536,6 +1549,7 @@ exports.execute = function(options) {
// {"and": [ {"source": "column"} ]}
expectError("N4IghgdgJiBcDaoDOB7ArgJwMYFM4ixQBs0BbCEAXwF1Kg");
});
+
});
});