@esri/hub-common 9.1.3 → 9.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/es2017/content.js +127 -19
  2. package/dist/es2017/content.js.map +1 -1
  3. package/dist/es2017/search/_searchContent.js +62 -0
  4. package/dist/es2017/search/_searchContent.js.map +1 -0
  5. package/dist/es2017/search/content-utils.js +422 -0
  6. package/dist/es2017/search/content-utils.js.map +1 -0
  7. package/dist/es2017/search/index.js +4 -0
  8. package/dist/es2017/search/index.js.map +1 -1
  9. package/dist/es2017/search/types.js +1 -0
  10. package/dist/es2017/search/types.js.map +1 -0
  11. package/dist/es2017/search/utils.js +344 -0
  12. package/dist/es2017/search/utils.js.map +1 -0
  13. package/dist/esm/content.js +129 -19
  14. package/dist/esm/content.js.map +1 -1
  15. package/dist/esm/search/_searchContent.js +70 -0
  16. package/dist/esm/search/_searchContent.js.map +1 -0
  17. package/dist/esm/search/content-utils.js +426 -0
  18. package/dist/esm/search/content-utils.js.map +1 -0
  19. package/dist/esm/search/index.js +4 -0
  20. package/dist/esm/search/index.js.map +1 -1
  21. package/dist/esm/search/types.js +1 -0
  22. package/dist/esm/search/types.js.map +1 -0
  23. package/dist/esm/search/utils.js +341 -0
  24. package/dist/esm/search/utils.js.map +1 -0
  25. package/dist/node/content.js +128 -20
  26. package/dist/node/content.js.map +1 -1
  27. package/dist/node/search/_searchContent.js +66 -0
  28. package/dist/node/search/_searchContent.js.map +1 -0
  29. package/dist/node/search/content-utils.js +432 -0
  30. package/dist/node/search/content-utils.js.map +1 -0
  31. package/dist/node/search/index.js +4 -0
  32. package/dist/node/search/index.js.map +1 -1
  33. package/dist/node/search/types.js +3 -0
  34. package/dist/node/search/types.js.map +1 -0
  35. package/dist/node/search/utils.js +357 -0
  36. package/dist/node/search/utils.js.map +1 -0
  37. package/dist/types/content.d.ts +23 -0
  38. package/dist/types/search/_searchContent.d.ts +7 -0
  39. package/dist/types/search/content-utils.d.ts +70 -0
  40. package/dist/types/search/index.d.ts +4 -0
  41. package/dist/types/search/types.d.ts +294 -0
  42. package/dist/types/search/utils.d.ts +92 -0
  43. package/dist/umd/common.umd.js +992 -25
  44. package/dist/umd/common.umd.js.map +1 -1
  45. package/dist/umd/common.umd.min.js +1 -1
  46. package/dist/umd/common.umd.min.js.map +1 -1
  47. package/package.json +3 -3
@@ -1,5 +1,5 @@
1
1
  /* @preserve
2
- * @esri/hub-common - v9.1.2 - Fri Oct 15 2021 14:28:36 GMT-0400 (Eastern Daylight Time)
2
+ * @esri/hub-common - v9.3.0 - Thu Oct 28 2021 14:17:26 GMT-0600 (Mountain Daylight Time)
3
3
  * Copyright (c) 2021 Environmental Systems Research Institute, Inc.
4
4
  * Apache-2.0
5
5
  */
@@ -2637,14 +2637,27 @@
2637
2637
  options = {
2638
2638
  httpMethod: "GET",
2639
2639
  params: {
2640
- q: search
2641
- }
2640
+ q: search,
2641
+ },
2642
2642
  };
2643
2643
  }
2644
2644
  else {
2645
2645
  // searchUserAccess has one (knonw) valid value: "groupMember"
2646
- options = appendCustomParams(search, ["q", "num", "start", "sortField", "sortOrder", "searchUserAccess", "searchUserName"], {
2647
- httpMethod: "GET"
2646
+ options = appendCustomParams(search, [
2647
+ "q",
2648
+ "num",
2649
+ "start",
2650
+ "sortField",
2651
+ "sortOrder",
2652
+ "searchUserAccess",
2653
+ "searchUserName",
2654
+ "filter",
2655
+ "countFields",
2656
+ "countSize",
2657
+ "categories",
2658
+ "categoryFilters",
2659
+ ], {
2660
+ httpMethod: "GET",
2648
2661
  });
2649
2662
  }
2650
2663
  var path;
@@ -2682,7 +2695,7 @@
2682
2695
  search instanceof SearchQueryBuilder) {
2683
2696
  newOptions = {
2684
2697
  q: search,
2685
- start: r.nextStart
2698
+ start: r.nextStart,
2686
2699
  };
2687
2700
  }
2688
2701
  else {
@@ -5611,14 +5624,11 @@
5611
5624
  // site data instead. Because this one is the original one that was used
5612
5625
  // to create the page url (not mutable once created) and the slug (below)
5613
5626
  // generated by the hub-indexer could simply change with page name.
5614
- // TODO: we check both "Hub Page" and "Site Page" because we don't know whether the site is a portal or not
5615
- // In the future, the site object will have a site.isPortal() function that we can leverage
5616
- // instead of hardcoding the types here.
5617
- if (includes(["Hub Page", "Site Page"], content.type)) {
5627
+ if (isPageContent(content)) {
5618
5628
  // check if the page is linked to the current site
5619
5629
  var pages = getProp(site, "data.values.pages") || [];
5620
- var page = pages.find(function (p) { return p.id === content.id; });
5621
5630
  // if so, return the page slug otherwise the page id
5631
+ var page = pages.find(function (p) { return p.id === content.id; });
5622
5632
  return page ? page.slug : content.id;
5623
5633
  }
5624
5634
  // If a slug is present, always return it
@@ -5781,8 +5791,8 @@
5781
5791
  var createdDate = new Date(item.created);
5782
5792
  var createdDateSource = "item.created";
5783
5793
  var properties = item.properties;
5784
- var normalizedType = normalizeItemType(item);
5785
5794
  var content = Object.assign({}, item, {
5795
+ identifier: item.id,
5786
5796
  // no server errors when fetching the item directly
5787
5797
  errors: [],
5788
5798
  // store a reference to the item
@@ -5792,10 +5802,8 @@
5792
5802
  // presumably there to use as the default file name when downloading
5793
5803
  // we don't store item.name in the Hub API and we use name for title
5794
5804
  name: item.title,
5795
- family: getFamily(normalizedType),
5796
5805
  // TODO: hubType is no longer used, remove it at next breaking change
5797
5806
  hubType: getItemHubType(item),
5798
- normalizedType: normalizedType,
5799
5807
  categories: parseItemCategories(item.categories),
5800
5808
  itemCategories: item.categories,
5801
5809
  // can we strip HTML from description, and do we need to trim it to a X chars?
@@ -5825,7 +5833,7 @@
5825
5833
  updatedDateSource: "item.modified",
5826
5834
  structuredLicense: getStructuredLicense(item.licenseInfo),
5827
5835
  });
5828
- return content;
5836
+ return setContentType(content, item.type);
5829
5837
  }
5830
5838
  /**
5831
5839
  * Convert a Hub API dataset resource to Hub Content
@@ -5835,17 +5843,14 @@
5835
5843
  * @export
5836
5844
  */
5837
5845
  function datasetToContent(dataset) {
5838
- // extract item from dataset, create content, & store a reference to the item
5846
+ // extract item from dataset, create content from the item
5839
5847
  var item = datasetToItem(dataset);
5840
5848
  var content = itemToContent(item);
5841
- content.item = item;
5842
5849
  // We remove these because the indexer doesn't actually
5843
5850
  // preserve the original item categories so this attribute is invalid
5844
5851
  delete content.itemCategories;
5845
- // overwrite hubId
5846
- content.hubId = dataset.id;
5847
5852
  // overwrite or add enrichments from Hub API
5848
- var attributes = dataset.attributes;
5853
+ var hubId = dataset.id, attributes = dataset.attributes;
5849
5854
  var
5850
5855
  // common enrichments
5851
5856
  errors = attributes.errors, boundary = attributes.boundary, extent = attributes.extent, metadata = attributes.metadata, modified = attributes.modified, modifiedProvenance = attributes.modifiedProvenance, slug = attributes.slug, searchDescription = attributes.searchDescription, groupIds = attributes.groupIds,
@@ -5866,10 +5871,6 @@
5866
5871
  // NOTE: for layers and tables the Hub API returns the layer type
5867
5872
  // ("Feature Layer", "Table", "Raster Layer") instead of the item type
5868
5873
  type = attributes.type;
5869
- // use the type returned by the API (i.e. possibly layer.type)
5870
- content.type = type;
5871
- content.normalizedType = normalizeItemType(__assign(__assign({}, content.item), { type: type }));
5872
- content.family = getFamily(type);
5873
5874
  // NOTE: we could throw or return if there are errors
5874
5875
  // to prevent type errors trying to read properties below
5875
5876
  content.errors = errors;
@@ -5913,7 +5914,9 @@
5913
5914
  extent: orgExtent,
5914
5915
  };
5915
5916
  }
5916
- return content;
5917
+ // return a content with updated hubId and type
5918
+ // along w/ related properties like identifier, family, etc
5919
+ return setContentType(setContentHubId(content, hubId), type);
5917
5920
  }
5918
5921
  /**
5919
5922
  * Convert a Hub API dataset resource to a portal item
@@ -6020,6 +6023,126 @@
6020
6023
  scoreCompleteness: scoreCompleteness,
6021
6024
  };
6022
6025
  }
6026
+ /**
6027
+ * retunrs a new content that has the specified type and
6028
+ * and updated related properties like normalizedType, family, etc
6029
+ * @param content orignal content
6030
+ * @param type new type
6031
+ * @returns new content
6032
+ */
6033
+ var setContentType = function (content, type) {
6034
+ // get family and normalized type based on new type
6035
+ var family = getFamily(type);
6036
+ var normalizedType = normalizeItemType(__assign(__assign({}, content.item), { type: type }));
6037
+ var updated = __assign(__assign({}, content), { type: type, family: family, normalizedType: normalizedType });
6038
+ // update the relative URL to the content
6039
+ // which is based on type and family
6040
+ return appendContentUrls(updated, {
6041
+ relative: getContentRelativeUrl(updated),
6042
+ });
6043
+ };
6044
+ /**
6045
+ * retunrs a new content that has the specified hubId and updated identifier
6046
+ * @param content orignal content
6047
+ * @param hubId new hubId
6048
+ * @returns new content
6049
+ */
6050
+ var setContentHubId = function (content, hubId) {
6051
+ var id = content.id, slug = content.slug;
6052
+ // get the identifier which is based on hubId
6053
+ var identifier = slug || hubId || id;
6054
+ var updated = __assign(__assign({}, content), { hubId: hubId, identifier: identifier });
6055
+ // update the relative URL to the content
6056
+ // which is based on identifier
6057
+ return appendContentUrls(updated, {
6058
+ relative: getContentRelativeUrl(updated),
6059
+ });
6060
+ };
6061
+ /**
6062
+ * append the absolute URL to the content on the site
6063
+ * also updates the relative URL in case the
6064
+ * @param content
6065
+ * @param siteModel
6066
+ * @returns
6067
+ */
6068
+ var setContentSiteUrls = function (content, siteModel) {
6069
+ // recompute relative URL using a site specific identifier
6070
+ var siteIdentifier = getContentIdentifier(content, siteModel);
6071
+ var relative = getContentRelativeUrl(content, siteIdentifier);
6072
+ // get the absolute URL to this content on the site
6073
+ var siteUrl = getProp(siteModel, "item.url").replace(/\/$/, "");
6074
+ var site = "" + siteUrl + relative;
6075
+ // append the updated URLs to a new content
6076
+ return appendContentUrls(content, {
6077
+ relative: relative,
6078
+ site: site,
6079
+ });
6080
+ };
6081
+ // URL helpers
6082
+ var appendContentUrls = function (content, newUrls) {
6083
+ // merge new urls into existing ones and return a new content
6084
+ var urls = __assign(__assign({}, content.urls), newUrls);
6085
+ return __assign(__assign({}, content), { urls: urls });
6086
+ };
6087
+ var getContentRelativeUrl = function (content, siteIdentifier) {
6088
+ var family = content.family;
6089
+ // prefer site specific identifier if passed in
6090
+ var identifier = siteIdentifier || content.identifier;
6091
+ // solution types have their own logic
6092
+ var contentUrl = getSolutionUrl(content);
6093
+ if (!contentUrl) {
6094
+ var pluralizedFamilies = [
6095
+ "app",
6096
+ "dataset",
6097
+ "document",
6098
+ "map",
6099
+ "template",
6100
+ ];
6101
+ // default to the catchall content route
6102
+ var path = "/content";
6103
+ if (content.family === "feedback") {
6104
+ // exception
6105
+ path = "/feedback/surveys";
6106
+ }
6107
+ else if (isPageContent(content)) {
6108
+ // pages are in the document family,
6109
+ // but instead of showing the page's metadata on /documents/about
6110
+ // but we render the page on the pages route
6111
+ path = "/pages";
6112
+ }
6113
+ else if (pluralizedFamilies.indexOf(family) > -1) {
6114
+ // the rule
6115
+ path = "/" + family + "s";
6116
+ }
6117
+ contentUrl = path + "/" + identifier;
6118
+ }
6119
+ return contentUrl;
6120
+ };
6121
+ var getSolutionUrl = function (content) {
6122
+ var _a;
6123
+ var hubUrl;
6124
+ var normalizedType = content.normalizedType, identifier = content.identifier;
6125
+ if (normalizedType === "Solution") {
6126
+ // NOTE: as per the above spreadsheet,
6127
+ // solution types are now in the Template family
6128
+ // but we don't send them to the route for initiative templates
6129
+ if (((_a = content.typeKeywords) === null || _a === void 0 ? void 0 : _a.indexOf("Deployed")) > 0) {
6130
+ // deployed solutions go to the generic content route
6131
+ hubUrl = "/content/" + identifier;
6132
+ }
6133
+ // others go to the solution about route
6134
+ hubUrl = "/templates/" + identifier + "/about";
6135
+ }
6136
+ return hubUrl;
6137
+ };
6138
+ // page helpers
6139
+ // TODO: we check both "Hub Page" and "Site Page" because we don't know whether the site is a portal or not
6140
+ // In the future, the site object will have a site.isPortal() function that we can leverage
6141
+ // instead of hardcoding the types here.
6142
+ // TODO: should this be based on normalizedType instead?
6143
+ var isPageContent = function (content) {
6144
+ return includes(["Hub Page", "Site Page"], content.type);
6145
+ };
6023
6146
 
6024
6147
  /**
6025
6148
  * Implementation of atob() according to the HTML and Infra specs, except that
@@ -7856,6 +7979,828 @@
7856
7979
  return hubApiRequest("/search", __assign(__assign({}, defaults), requestOptions));
7857
7980
  }
7858
7981
 
7982
+ /* Copyright (c) 2018-2021 Environmental Systems Research Institute, Inc.
7983
+ * Apache-2.0 */
7984
+ /**
7985
+ * Well known APIs
7986
+ * Short-forms for specifying common APIs
7987
+ */
7988
+ var SEARCH_APIS = {
7989
+ arcgis: {
7990
+ label: "ArcGIS Online",
7991
+ url: "https://www.arcgis.com",
7992
+ type: "arcgis",
7993
+ },
7994
+ arcgisQA: {
7995
+ label: "ArcGIS Online QAEXT",
7996
+ url: "https://qaext.arcgis.com",
7997
+ type: "arcgis",
7998
+ },
7999
+ arcgisDEV: {
8000
+ label: "ArcGIS Online DEVEXT",
8001
+ url: "https://devext.arcgis.com",
8002
+ type: "arcgis",
8003
+ },
8004
+ hub: {
8005
+ label: "ArcGIS Hub",
8006
+ url: "https://hub.arcgis.com/api",
8007
+ type: "arcgis-hub",
8008
+ },
8009
+ hubDEV: {
8010
+ label: "ArcGIS Hub DEV",
8011
+ url: "https://hubdev.arcgis.com/api",
8012
+ type: "arcgis-hub",
8013
+ },
8014
+ hubQA: {
8015
+ label: "ArcGIS Hub QA",
8016
+ url: "https://hubqa.arcgis.com/api",
8017
+ type: "arcgis-hub",
8018
+ },
8019
+ };
8020
+ /**
8021
+ * @private
8022
+ * Convert api "names" into full ApiDefinitions
8023
+ * @param apis
8024
+ * @returns
8025
+ */
8026
+ function expandApis(apis) {
8027
+ return apis.map(function (api) {
8028
+ if (typeof api === "string" && api in SEARCH_APIS) {
8029
+ return SEARCH_APIS[api];
8030
+ }
8031
+ else {
8032
+ // it's an object, so we trust that it's well formed
8033
+ return api;
8034
+ }
8035
+ });
8036
+ }
8037
+ /**
8038
+ * @private
8039
+ * Merge multiple search responses
8040
+ *
8041
+ * Naieve implementation that just merges arrays
8042
+ * @param responses
8043
+ * @returns
8044
+ */
8045
+ function mergeSearchResults(responses) {
8046
+ // Merge content
8047
+ var content = responses.reduce(function (acc, response) {
8048
+ if (response.content) {
8049
+ acc = acc.concat(response.content);
8050
+ }
8051
+ return acc;
8052
+ }, []);
8053
+ // Merge Facets
8054
+ var facets = responses.reduce(function (acc, response) {
8055
+ if (response.facets) {
8056
+ acc = acc.concat(response.facets);
8057
+ }
8058
+ return acc;
8059
+ }, []);
8060
+ return { content: content, facets: facets };
8061
+ }
8062
+ /**
8063
+ * @private
8064
+ * Merge two date ranges by taking the longest span
8065
+ * @param dr1
8066
+ * @param dr2
8067
+ * @returns
8068
+ */
8069
+ function mergeDateRange(dr1, dr2) {
8070
+ var result = cloneObject$1(dr1);
8071
+ // feels like there is a more concise way to do this...
8072
+ if (dr2.from < dr1.from) {
8073
+ result.from = dr2.from;
8074
+ }
8075
+ if (dr2.to > dr1.to) {
8076
+ result.to = dr2.to;
8077
+ }
8078
+ return result;
8079
+ }
8080
+ /**
8081
+ * @private
8082
+ * Merge two [`MatchOptions`](../MatchOptions)
8083
+ *
8084
+ * Currently a naieve implementation where the arrays are simply merged
8085
+ *
8086
+ * @param mo1
8087
+ * @param mo2
8088
+ * @returns
8089
+ */
8090
+ function mergeMatchOptions(mo1, mo2) {
8091
+ var result = {};
8092
+ // None of these props are required, so we can't just
8093
+ // use Object.keys/.entries
8094
+ var props = ["any", "all", "not", "exact"];
8095
+ props.forEach(function (prop) {
8096
+ var key = prop;
8097
+ var merged = __spreadArrays(getMatchValue(mo1, key), getMatchValue(mo2, key));
8098
+ if (merged.length) {
8099
+ // remove any dupes and set on the return
8100
+ result[key] = merged.filter(unique);
8101
+ }
8102
+ });
8103
+ return result;
8104
+ }
8105
+ /**
8106
+ * Get the value of a property on an IMatchOptions
8107
+ *
8108
+ * This is complex b/c all the props are optional, and
8109
+ * they could be a simple string, or an array of strings.
8110
+ *
8111
+ * This function normalizes all that and returns an array,
8112
+ * which may or may not be empty
8113
+ * @param option
8114
+ * @param key
8115
+ * @returns
8116
+ */
8117
+ function getMatchValue(option, key) {
8118
+ var matchValue = [];
8119
+ if (option[key]) {
8120
+ var val = option[key];
8121
+ if (Array.isArray(val)) {
8122
+ matchValue = val;
8123
+ }
8124
+ else {
8125
+ matchValue = [val];
8126
+ }
8127
+ }
8128
+ return matchValue;
8129
+ }
8130
+ /**
8131
+ * @private
8132
+ * Convert a field value into a MatchOptions if it's not already one
8133
+ * @param value
8134
+ * @returns
8135
+ */
8136
+ function valueToMatchOptions(value) {
8137
+ var result = {};
8138
+ if (Array.isArray(value)) {
8139
+ result = {
8140
+ any: value,
8141
+ };
8142
+ }
8143
+ else {
8144
+ if (typeof value === "string") {
8145
+ result = {
8146
+ any: [value],
8147
+ };
8148
+ }
8149
+ if (typeof value === "object") {
8150
+ result = value;
8151
+ }
8152
+ }
8153
+ return result;
8154
+ }
8155
+ /**
8156
+ * @private
8157
+ * Convert a RelativeDate to a DateRange<number>
8158
+ * @param relative
8159
+ * @returns
8160
+ */
8161
+ function relativeDateToDateRange(relative) {
8162
+ // hash of offsets
8163
+ var offsetMs = {
8164
+ min: 1000 * 60,
8165
+ hours: 1000 * 60 * 60,
8166
+ days: 1000 * 60 * 60 * 24,
8167
+ weeks: 1000 * 60 * 60 * 24 * 7,
8168
+ };
8169
+ var now = new Date();
8170
+ // default
8171
+ var result = {
8172
+ type: "date-range",
8173
+ from: now.getTime(),
8174
+ to: now.getTime(),
8175
+ };
8176
+ //
8177
+ switch (relative.unit) {
8178
+ case "hours":
8179
+ case "days":
8180
+ case "weeks":
8181
+ result.from = result.to - offsetMs[relative.unit] * relative.num;
8182
+ break;
8183
+ case "months":
8184
+ // get the current month and subtract num
8185
+ now.setMonth(now.getMonth() - relative.num);
8186
+ result.from = now.getTime();
8187
+ break;
8188
+ case "years":
8189
+ now.setFullYear(now.getFullYear() - relative.num);
8190
+ result.from = now.getTime();
8191
+ break;
8192
+ }
8193
+ return result;
8194
+ }
8195
+ /**
8196
+ * @private
8197
+ * As a final `ISearchOptions` object gets created, many such objects are created, and
8198
+ * need to be systematically "merged" so as to return consistently structured `q` and `filter`
8199
+ * values.
8200
+ * @param so1
8201
+ * @param so2
8202
+ * @param join
8203
+ * @returns
8204
+ */
8205
+ function mergeSearchOptions(so1, so2, join) {
8206
+ var result = cloneObject$1(so1);
8207
+ var q = so2.q, filter = so2.filter;
8208
+ if (q) {
8209
+ result.q = (result.q ? " (" + result.q + " " + join + " " + q + ")" : q).trim();
8210
+ }
8211
+ if (filter) {
8212
+ result.filter = (result.filter ? "(" + result.filter + " " + join + " " + filter + ")" : filter).trim();
8213
+ }
8214
+ return result;
8215
+ }
8216
+ /**
8217
+ * @private
8218
+ * Serialize a `MatchOptions` into `q` or `filter` on an `ISearchOptions`
8219
+ * @param key
8220
+ * @param opts
8221
+ * @returns
8222
+ */
8223
+ function serializeMatchOptions(key, opts) {
8224
+ var result = {
8225
+ q: "",
8226
+ filter: "",
8227
+ };
8228
+ if (opts.exact) {
8229
+ // defined separately for refactoring later
8230
+ var userFilterableFields = [
8231
+ "username",
8232
+ "firstname",
8233
+ "lastname",
8234
+ "fullname",
8235
+ "email",
8236
+ ];
8237
+ var itemFilterableFields = [
8238
+ "title",
8239
+ "tags",
8240
+ "typekeywords",
8241
+ "type",
8242
+ "name",
8243
+ "owner",
8244
+ ];
8245
+ var groupFilterableFields = ["title", "typekeywords", "owner"];
8246
+ var filterableFields = __spreadArrays(userFilterableFields, itemFilterableFields, groupFilterableFields);
8247
+ if (filterableFields.includes(key)) {
8248
+ result.filter = serializeStringOrArray("AND", key, opts.exact);
8249
+ }
8250
+ else {
8251
+ // Treat it the same as .all
8252
+ if (typeof opts.exact === "string") {
8253
+ if (!opts.all) {
8254
+ opts.all = [];
8255
+ }
8256
+ if (typeof opts.all === "string") {
8257
+ opts.all = [opts.all];
8258
+ }
8259
+ opts.all.push(opts.exact);
8260
+ }
8261
+ if (Array.isArray(opts.exact)) {
8262
+ if (!opts.all) {
8263
+ opts.all = [];
8264
+ }
8265
+ if (typeof opts.all === "string") {
8266
+ opts.all = [opts.all];
8267
+ }
8268
+ opts.all = opts.all.concat(opts.exact);
8269
+ }
8270
+ }
8271
+ }
8272
+ // Handle the other props
8273
+ if (opts.any) {
8274
+ result.q = serializeStringOrArray("OR", key, opts.any);
8275
+ }
8276
+ if (opts.all) {
8277
+ result.q =
8278
+ (result.q ? result.q + " AND " : "") +
8279
+ serializeStringOrArray("AND", key, opts.all);
8280
+ }
8281
+ if (opts.not) {
8282
+ // negate the entries if they are not
8283
+ result.q =
8284
+ (result.q ? result.q + " AND " : "") +
8285
+ serializeStringOrArray("OR", "-" + key, opts.not);
8286
+ }
8287
+ return result;
8288
+ }
8289
+ /**
8290
+ * @private
8291
+ * Serialize a DateRange<number> into a Portal Query string
8292
+ * @param key
8293
+ * @param range
8294
+ * @returns
8295
+ */
8296
+ function serializeDateRange(key, range) {
8297
+ return {
8298
+ q: key + ":[" + range.from + " TO " + range.to + "]",
8299
+ filter: "",
8300
+ };
8301
+ }
8302
+ /**
8303
+ * @private
8304
+ * Serialize a `string` or `string[]` into a string
8305
+ * @param join
8306
+ * @param key
8307
+ * @param value
8308
+ * @returns
8309
+ */
8310
+ function serializeStringOrArray(join, key, value) {
8311
+ var q = "";
8312
+ if (Array.isArray(value)) {
8313
+ q = "(" + key + ":\"" + value.join("\" " + join + " " + key + ":\"") + "\")";
8314
+ }
8315
+ else {
8316
+ q = key + ":\"" + value + "\"";
8317
+ }
8318
+ return q;
8319
+ }
8320
+
8321
+ // TODO: Implement $dataset
8322
+ var ContentFilterExpansions = {
8323
+ $apps: [
8324
+ {
8325
+ type: {
8326
+ any: [
8327
+ "Code Sample",
8328
+ "Web Mapping Application",
8329
+ "Mobile Application",
8330
+ "Application",
8331
+ "Desktop Application Template",
8332
+ "Desktop Application",
8333
+ "Operation View",
8334
+ "Dashboard",
8335
+ "Operations Dashboard Extension",
8336
+ "Workforce Project",
8337
+ "Insights Workbook",
8338
+ "Insights Page",
8339
+ "Insights Model",
8340
+ "Hub Page",
8341
+ "Hub Initiative",
8342
+ "Hub Site Application",
8343
+ "StoryMap",
8344
+ "Web Experience",
8345
+ "Web Experience Template",
8346
+ "Form",
8347
+ ],
8348
+ not: [
8349
+ "Code Attachment",
8350
+ "Featured Items",
8351
+ "Symbol Set",
8352
+ "Color Set",
8353
+ "Windows Viewer Add In",
8354
+ "Windows Viewer Configuration",
8355
+ "Map Area",
8356
+ "Indoors Map Configuration",
8357
+ ],
8358
+ },
8359
+ typekeywords: {
8360
+ not: ["MapAreaPackage", "SMX"],
8361
+ },
8362
+ },
8363
+ ],
8364
+ $storymap: [
8365
+ {
8366
+ type: "StoryMap",
8367
+ },
8368
+ {
8369
+ type: "Web Mapping Application",
8370
+ typekeywords: ["Story Map"],
8371
+ },
8372
+ ],
8373
+ $dashboard: [
8374
+ {
8375
+ type: "Dashboard",
8376
+ typekeywords: {
8377
+ any: ["Dashboard"],
8378
+ not: ["ArcGIS Operation View", "Add In", "Extension"],
8379
+ },
8380
+ },
8381
+ ],
8382
+ $dataset: [],
8383
+ $experience: [
8384
+ {
8385
+ type: {
8386
+ any: ["Web Experience"],
8387
+ not: ["Web Experience Template"],
8388
+ },
8389
+ },
8390
+ ],
8391
+ $site: [
8392
+ {
8393
+ type: ["Hub Site Application", "Site Application"],
8394
+ },
8395
+ {
8396
+ type: ["Web Mapping Application"],
8397
+ typekeywords: ["hubSite"],
8398
+ },
8399
+ ],
8400
+ $initiative: [
8401
+ {
8402
+ type: {
8403
+ any: "Hub Initiative",
8404
+ not: "Hub Initiative Template",
8405
+ },
8406
+ },
8407
+ ],
8408
+ $document: [
8409
+ {
8410
+ typekeywords: {
8411
+ any: "Document",
8412
+ not: ["MapAreaPackage", "SMX"],
8413
+ },
8414
+ type: {
8415
+ any: [
8416
+ "Image",
8417
+ "Layout",
8418
+ "Desktop Style",
8419
+ "Project Template",
8420
+ "Report Template",
8421
+ "Pro Report",
8422
+ "Statistical Data Collection",
8423
+ "360 VR Experience",
8424
+ "netCDF",
8425
+ "PDF",
8426
+ "CSV",
8427
+ "Administrative Report",
8428
+ "Raster function template",
8429
+ ],
8430
+ not: [
8431
+ "Image Service",
8432
+ "Explorer Document",
8433
+ "Explorer Map",
8434
+ "Globe Document",
8435
+ "Scene Document",
8436
+ "Code Attachment",
8437
+ "Featured Items",
8438
+ "Symbol Set",
8439
+ "ColorSet",
8440
+ "Windows Viewer Add In",
8441
+ "Windows Viewer Configuration",
8442
+ "Map Area",
8443
+ "Indoors Map Configuration",
8444
+ ],
8445
+ },
8446
+ },
8447
+ ],
8448
+ };
8449
+ /**
8450
+ * @private
8451
+ * Convert portal search response to items
8452
+ * @param response
8453
+ * @returns
8454
+ */
8455
+ function convertPortalResponseToFacets(response) {
8456
+ var _a;
8457
+ var result = [];
8458
+ if ((_a = response.aggregations) === null || _a === void 0 ? void 0 : _a.counts) {
8459
+ response.aggregations.counts.forEach(function (entry) {
8460
+ var facet = {
8461
+ label: entry.fieldName,
8462
+ attribute: entry.fieldName,
8463
+ type: "multi-select",
8464
+ };
8465
+ var options = [];
8466
+ entry.fieldValues.forEach(function (fv) {
8467
+ var filter = {
8468
+ filterType: "content",
8469
+ };
8470
+ filter[entry.fieldName] = fv.value;
8471
+ var fo = {
8472
+ label: fv.value,
8473
+ value: fv.value,
8474
+ count: fv.count,
8475
+ selected: false,
8476
+ filter: filter,
8477
+ };
8478
+ options.push(fo);
8479
+ });
8480
+ facet.options = options;
8481
+ result.push(facet);
8482
+ });
8483
+ }
8484
+ return result;
8485
+ }
8486
+ /**
8487
+ * @private
8488
+ * Merge `Filter<"content">` objects
8489
+ * @param filters
8490
+ * @returns
8491
+ */
8492
+ function mergeContentFilter(filters) {
8493
+ // expand all the filters so all prop types are consistent
8494
+ var expanded = filters.map(expandContentFilter);
8495
+ // now we can merge based on fields
8496
+ var dateFields = ["created", "modified"];
8497
+ var specialFields = __spreadArrays(["filterType", "subFilters"], dateFields);
8498
+ var result = expanded.reduce(function (acc, entry) {
8499
+ // process fields
8500
+ Object.entries(entry).forEach(function (_a) {
8501
+ var key = _a[0], value = _a[1];
8502
+ // MatchOption fields
8503
+ if (!specialFields.includes(key)) {
8504
+ if (acc[key]) {
8505
+ acc[key] = mergeMatchOptions(acc[key], value);
8506
+ }
8507
+ else {
8508
+ acc[key] = cloneObject$1(value);
8509
+ }
8510
+ }
8511
+ // Dates
8512
+ if (dateFields.includes(key)) {
8513
+ if (acc[key]) {
8514
+ acc[key] = mergeDateRange(acc[key], value);
8515
+ }
8516
+ else {
8517
+ acc[key] = cloneObject$1(value);
8518
+ }
8519
+ }
8520
+ // SubFilters
8521
+ if (key === "subFilters" && Array.isArray(value)) {
8522
+ if (acc.subFilters) {
8523
+ acc.subFilters = mergeSubFilters(acc.subFilters, value);
8524
+ }
8525
+ else {
8526
+ acc.subFilters = cloneObject$1(value);
8527
+ }
8528
+ }
8529
+ });
8530
+ return acc;
8531
+ }, {});
8532
+ result.filterType = "content";
8533
+ return result;
8534
+ }
8535
+ function mergeSubFilters(sf1, sf2) {
8536
+ // Simplistic implementation: we just merge the arrays
8537
+ // in the future we may try to de-dupe things as a safeguard
8538
+ return __spreadArrays(sf1, sf2);
8539
+ }
8540
+ /**
8541
+ * Prior to serialization into the query syntax for the backing APIs, we first expand [Filters](../Filter)
8542
+ *
8543
+ * Filter's can express their intent in a very terse form, but to ensure consistent
8544
+ * into their more verbose form.
8545
+ *
8546
+ * i.e. `title: "Water"` expands into `title: { any: ["Water"]}`
8547
+ *
8548
+ * - "well known" type values are expanded
8549
+ * - i.e. `type: "$storymap"` expands into two `subFilter` entries
8550
+ * - Fields defined as `string | string[] | MatchOptions` will be converted to a `MatchOptions`
8551
+ * - RelativeDate fields are converted to DateRange<number>
8552
+ *
8553
+ * @param filter
8554
+ * @returns
8555
+ */
8556
+ function expandContentFilter(filter) {
8557
+ // Expand filter.type first
8558
+ var expandedTypeFilter = expandTypeField(filter);
8559
+ // Expand subfilters
8560
+ // Guard - JS Clients could send in anything...
8561
+ if (Array.isArray(filter.subFilters)) {
8562
+ // Convert any strings into the coresponding ContentFilterDefinition
8563
+ expandedTypeFilter.subFilters = expandedTypeFilter.subFilters.reduce(function (acc, entry) {
8564
+ if (typeof entry === "string") {
8565
+ // if the entry is not a key of ContentFilterExpansions
8566
+ // we just skip over it
8567
+ if (ContentFilterExpansions[entry]) {
8568
+ acc = acc.concat(ContentFilterExpansions[entry]);
8569
+ }
8570
+ }
8571
+ else {
8572
+ acc.push(entry); // should be a ContentFilterDefinition
8573
+ }
8574
+ return acc;
8575
+ }, []);
8576
+ }
8577
+ // Convert all props into MatchOptions/DateRanges
8578
+ return convertContentDefinitionToFilter(expandedTypeFilter);
8579
+ }
8580
+ /**
8581
+ * @private
8582
+ * Expand from a well-known "type" into it's longer form
8583
+ *
8584
+ * i.e. `type: "$storymap"` expands into two subFilters entries
8585
+ *
8586
+ * @param filter
8587
+ * @returns
8588
+ */
8589
+ function expandTypeField(filter) {
8590
+ var clone = cloneObject$1(filter);
8591
+ // ensure subFilters is defined as an array
8592
+ clone.subFilters = clone.subFilters || [];
8593
+ if (clone.type) {
8594
+ // if .type is an Array...
8595
+ if (Array.isArray(clone.type)) {
8596
+ // remove any well-known-keys and move their expansions into subfilters
8597
+ clone.type = clone.type.reduce(function (acc, entry) {
8598
+ if (isWellKnownType(entry)) {
8599
+ // working with dynamic objects in typescript requires some assertions
8600
+ var key = entry;
8601
+ clone.subFilters = clone.subFilters.concat(lookupTypeFilters(key));
8602
+ }
8603
+ else {
8604
+ acc.push(entry);
8605
+ }
8606
+ return acc;
8607
+ }, []);
8608
+ }
8609
+ else if (isWellKnownType(clone.type)) {
8610
+ var key = clone.type;
8611
+ clone.subFilters = clone.subFilters.concat(lookupTypeFilters(key));
8612
+ delete clone.type;
8613
+ }
8614
+ else ;
8615
+ }
8616
+ return clone;
8617
+ }
8618
+ /**
8619
+ * Is the argument a well-known type "key"
8620
+ *
8621
+ * Accepts `string`, `string[]` or `IMatchOptions`
8622
+ * but only string values can possibly be properties
8623
+ * on `ContentFilterExpansions`
8624
+ * @param key
8625
+ * @returns
8626
+ */
8627
+ function isWellKnownType(key) {
8628
+ var result = false;
8629
+ if (typeof key === "string") {
8630
+ result = key in ContentFilterExpansions;
8631
+ }
8632
+ return result;
8633
+ }
8634
+ function lookupTypeFilters(key) {
8635
+ return ContentFilterExpansions[key];
8636
+ }
8637
+ /**
8638
+ * @private
8639
+ * Convert a `ContentFilterDefinition` to a `ContentFilter`
8640
+ *
8641
+ * ContentFilter is a narrower version of ContentFilterDefinition, without
8642
+ * the union types. Using a ContentFilter makes working with the structure
8643
+ * in typescript much easier.
8644
+ *
8645
+ * @param filter
8646
+ * @returns
8647
+ */
8648
+ function convertContentDefinitionToFilter(filter) {
8649
+ var result = {};
8650
+ if (filter.term) {
8651
+ result.term = filter.term;
8652
+ }
8653
+ var dateProps = ["created", "modified"];
8654
+ // Some properties should not get converted to MatchOptions
8655
+ var specialProps = __spreadArrays(["filterType", "subFilters", "term"], dateProps);
8656
+ // Do the conversion
8657
+ Object.entries(filter).forEach(function (_a) {
8658
+ var key = _a[0], value = _a[1];
8659
+ if (!specialProps.includes(key)) {
8660
+ result[key] = valueToMatchOptions(value);
8661
+ }
8662
+ });
8663
+ // Special Cases
8664
+ // subFilters; Array of ContentFilterDefinitions
8665
+ if (filter.subFilters && Array.isArray(filter.subFilters)) {
8666
+ // downcast - would be nice to skip this or use some other constuct
8667
+ var sf = filter.subFilters;
8668
+ result.subFilters = sf.map(convertContentDefinitionToFilter);
8669
+ }
8670
+ // Dates; Ensure they are all DateRange<number>
8671
+ dateProps.forEach(function (fld) {
8672
+ if (filter[fld]) {
8673
+ if (filter[fld].type === "relative-date") {
8674
+ result[fld] = relativeDateToDateRange(filter[fld]);
8675
+ }
8676
+ else {
8677
+ result[fld] = cloneObject$1(filter[fld]);
8678
+ }
8679
+ }
8680
+ });
8681
+ return result;
8682
+ }
8683
+ /**
8684
+ * @private
8685
+ * Serialize a `ContentFilter` into an `ISearchOptions` for use with `searchItems`
8686
+ * @param filter
8687
+ * @returns
8688
+ */
8689
+ function serializeContentFilterForPortal(filter) {
8690
+ var searchOptions = convertContentFilterToSearchOptions(filter);
8691
+ if (filter.subFilters) {
8692
+ var subFilterOptions = filter.subFilters.reduce(function (acc, entry) {
8693
+ // Next guard is present b/c this can be used from javascript
8694
+ // but our tests are written in typescript which prevents us
8695
+ // from hitting the else
8696
+ /* istanbul ignore else */
8697
+ if (typeof entry === "object") {
8698
+ acc = mergeSearchOptions(acc, convertContentFilterToSearchOptions(entry), "OR");
8699
+ }
8700
+ return acc;
8701
+ }, { q: "", filter: "" });
8702
+ // merge with searchOptions using AND
8703
+ searchOptions = mergeSearchOptions(searchOptions, subFilterOptions, "AND");
8704
+ }
8705
+ // term is always last, and pre-pended on searchOptions.q
8706
+ if (filter.term) {
8707
+ searchOptions.q = (filter.term + " " + searchOptions.q).trim();
8708
+ }
8709
+ return searchOptions;
8710
+ }
8711
+ /**
8712
+ * @private
8713
+ * Convert a ContentFilter to a SearchOptions
8714
+ *
8715
+ * @param filter
8716
+ * @returns
8717
+ */
8718
+ function convertContentFilterToSearchOptions(filter) {
8719
+ var result = {
8720
+ q: "",
8721
+ filter: "",
8722
+ };
8723
+ var dateProps = ["created", "modified"];
8724
+ var specialProps = __spreadArrays(["filterType", "subFilters", "term"], dateProps);
8725
+ Object.entries(filter).forEach(function (_a) {
8726
+ var key = _a[0], value = _a[1];
8727
+ // MatchOptions may go into either q or filter
8728
+ if (!specialProps.includes(key)) {
8729
+ result = mergeSearchOptions(result, serializeMatchOptions(key, value), "AND");
8730
+ }
8731
+ // Dates only go into q
8732
+ if (dateProps.includes(key)) {
8733
+ result = mergeSearchOptions(result, serializeDateRange(key, value), "AND");
8734
+ }
8735
+ });
8736
+ return result;
8737
+ }
8738
+
8739
+ /**
8740
+ * Search for content via the Portal or Hub API
8741
+ * @param filter
8742
+ * @param options
8743
+ */
8744
+ function _searchContent(filter, options) {
8745
+ return __awaiter(this, void 0, void 0, function () {
8746
+ var expanded, apis, searchPromises, results;
8747
+ return __generator(this, function (_a) {
8748
+ switch (_a.label) {
8749
+ case 0:
8750
+ expanded = expandContentFilter(filter);
8751
+ // APIs
8752
+ if (!options.apis) {
8753
+ // default to AGO PROD
8754
+ options.apis = ["arcgis"];
8755
+ }
8756
+ apis = expandApis(options.apis);
8757
+ searchPromises = apis.map(function (api) {
8758
+ var _a;
8759
+ // Portal Search
8760
+ if (api.type === "arcgis") {
8761
+ // serialize for portal
8762
+ var so = serializeContentFilterForPortal(expanded);
8763
+ // pass auth forward
8764
+ if (options.authentication) {
8765
+ so.authentication = options.authentication;
8766
+ }
8767
+ // Aggregations
8768
+ if ((_a = options.aggregations) === null || _a === void 0 ? void 0 : _a.length) {
8769
+ so.countFields = options.aggregations.join(",");
8770
+ so.countSize = 200;
8771
+ }
8772
+ if (options.num) {
8773
+ so.num = options.num;
8774
+ }
8775
+ return searchPortal(so);
8776
+ }
8777
+ else {
8778
+ // Hub API Search
8779
+ // TODO: Implement hub api content search
8780
+ return Promise.resolve({
8781
+ content: [],
8782
+ facets: [],
8783
+ });
8784
+ }
8785
+ });
8786
+ return [4 /*yield*/, Promise.all(searchPromises)];
8787
+ case 1:
8788
+ results = _a.sent();
8789
+ // merge and return
8790
+ return [2 /*return*/, mergeSearchResults(results)];
8791
+ }
8792
+ });
8793
+ });
8794
+ }
8795
+ function searchPortal(searchOptions) {
8796
+ return searchItems(searchOptions).then(function (resp) {
8797
+ var content = resp.results.map(itemToContent);
8798
+ // convert aggregations into facets
8799
+ var facets = convertPortalResponseToFacets(resp);
8800
+ return { content: content, facets: facets };
8801
+ });
8802
+ }
8803
+
7859
8804
  /**
7860
8805
  * Parse a response object, and throw if it contains an error.
7861
8806
  * Just a wrapper to hide some platform idiosyncracies
@@ -9065,6 +10010,7 @@
9065
10010
  exports.PORTAL_EXPORT_TYPES = PORTAL_EXPORT_TYPES;
9066
10011
  exports.REQUIRED_PRIVS = REQUIRED_PRIVS;
9067
10012
  exports.RemoteServerError = RemoteServerError;
10013
+ exports.SEARCH_APIS = SEARCH_APIS;
9068
10014
  exports.SITE_SCHEMA_VERSION = SITE_SCHEMA_VERSION;
9069
10015
  exports.STANDARD_LICENSES = STANDARD_LICENSES;
9070
10016
  exports.WGS84_WKID = WGS84_WKID;
@@ -9095,6 +10041,7 @@
9095
10041
  exports._processInvite = _processInvite;
9096
10042
  exports._processPrimaryEmail = _processPrimaryEmail;
9097
10043
  exports._processSecondaryEmail = _processSecondaryEmail;
10044
+ exports._searchContent = _searchContent;
9098
10045
  exports._unprotectAndRemoveGroup = _unprotectAndRemoveGroup;
9099
10046
  exports._unprotectAndRemoveItem = _unprotectAndRemoveItem;
9100
10047
  exports.addContextToSlug = addContextToSlug;
@@ -9122,6 +10069,9 @@
9122
10069
  exports.cloneObject = cloneObject$1;
9123
10070
  exports.completeOAuth2 = completeOAuth2;
9124
10071
  exports.compose = compose;
10072
+ exports.convertContentDefinitionToFilter = convertContentDefinitionToFilter;
10073
+ exports.convertContentFilterToSearchOptions = convertContentFilterToSearchOptions;
10074
+ exports.convertPortalResponseToFacets = convertPortalResponseToFacets;
9125
10075
  exports.convertSolutionTemplateResourcesToAssets = convertSolutionTemplateResourcesToAssets;
9126
10076
  exports.convertToWellKnownLocale = convertToWellKnownLocale;
9127
10077
  exports.createExtent = createExtent;
@@ -9139,6 +10089,9 @@
9139
10089
  exports.ensureProp = ensureProp;
9140
10090
  exports.ensureUniqueDomainName = ensureUniqueDomainName;
9141
10091
  exports.ensureUniqueString = ensureUniqueString;
10092
+ exports.expandApis = expandApis;
10093
+ exports.expandContentFilter = expandContentFilter;
10094
+ exports.expandTypeField = expandTypeField;
9142
10095
  exports.extend = extend;
9143
10096
  exports.extentToBBox = extentToBBox;
9144
10097
  exports.failSafe = failSafe;
@@ -9226,7 +10179,12 @@
9226
10179
  exports.mapBy = mapBy;
9227
10180
  exports.maybeAdd = maybeAdd;
9228
10181
  exports.maybePush = maybePush;
10182
+ exports.mergeContentFilter = mergeContentFilter;
10183
+ exports.mergeDateRange = mergeDateRange;
10184
+ exports.mergeMatchOptions = mergeMatchOptions;
9229
10185
  exports.mergeObjects = mergeObjects;
10186
+ exports.mergeSearchOptions = mergeSearchOptions;
10187
+ exports.mergeSearchResults = mergeSearchResults;
9230
10188
  exports.normalizeItemType = normalizeItemType;
9231
10189
  exports.normalizeSolutionTemplateItem = normalizeSolutionTemplateItem;
9232
10190
  exports.objectToArray = objectToArray;
@@ -9235,13 +10193,21 @@
9235
10193
  exports.parseItemCategories = parseItemCategories;
9236
10194
  exports.processRevertableTasks = processRevertableTasks;
9237
10195
  exports.propifyString = propifyString;
10196
+ exports.relativeDateToDateRange = relativeDateToDateRange;
9238
10197
  exports.removeContextFromSlug = removeContextFromSlug;
9239
10198
  exports.removeDomain = removeDomain;
9240
10199
  exports.removeEmptyProps = removeEmptyProps;
9241
10200
  exports.replaceItemId = replaceItemId;
9242
10201
  exports.runRevertableTask = runRevertableTask;
10202
+ exports.serializeContentFilterForPortal = serializeContentFilterForPortal;
10203
+ exports.serializeDateRange = serializeDateRange;
10204
+ exports.serializeMatchOptions = serializeMatchOptions;
9243
10205
  exports.serializeModel = serializeModel;
9244
10206
  exports.serializeSpatialReference = serializeSpatialReference;
10207
+ exports.serializeStringOrArray = serializeStringOrArray;
10208
+ exports.setContentHubId = setContentHubId;
10209
+ exports.setContentSiteUrls = setContentSiteUrls;
10210
+ exports.setContentType = setContentType;
9245
10211
  exports.setProp = setProp;
9246
10212
  exports.shareItemToGroups = shareItemToGroups;
9247
10213
  exports.slugify = slugify;
@@ -9254,6 +10220,7 @@
9254
10220
  exports.updateDomain = updateDomain;
9255
10221
  exports.upgradeSiteSchema = upgradeSiteSchema;
9256
10222
  exports.uploadResourcesFromUrl = uploadResourcesFromUrl;
10223
+ exports.valueToMatchOptions = valueToMatchOptions;
9257
10224
  exports.without = without;
9258
10225
  exports.withoutByProp = withoutByProp;
9259
10226