@esri/telemetry-amazon 5.1.2 → 5.1.4
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.
|
@@ -538,6 +538,168 @@
|
|
|
538
538
|
}
|
|
539
539
|
}
|
|
540
540
|
|
|
541
|
+
/* istanbul ignore file */
|
|
542
|
+
// Note: currently this is all internal to the package, and we are not exposing
|
|
543
|
+
// anything that a user can set... but we need all this to be able to ensure
|
|
544
|
+
// that multiple instances of the package can share the same config.
|
|
545
|
+
/**
|
|
546
|
+
* The default config for the request module. This is used to store
|
|
547
|
+
* the no-cors domains and pending requests.
|
|
548
|
+
*/
|
|
549
|
+
const DEFAULT_ARCGIS_REQUEST_CONFIG = {
|
|
550
|
+
noCorsDomains: [],
|
|
551
|
+
crossOriginNoCorsDomains: {},
|
|
552
|
+
pendingNoCorsRequests: {}
|
|
553
|
+
};
|
|
554
|
+
const GLOBAL_VARIABLE_NAME = "ARCGIS_REST_JS_NO_CORS";
|
|
555
|
+
// Set the global variable to the default config if it is not aleady defined
|
|
556
|
+
// This is done to ensure that all instances of rest-request work with a single
|
|
557
|
+
// instance of the config
|
|
558
|
+
if (!globalThis[GLOBAL_VARIABLE_NAME]) {
|
|
559
|
+
globalThis[GLOBAL_VARIABLE_NAME] = Object.assign({}, DEFAULT_ARCGIS_REQUEST_CONFIG);
|
|
560
|
+
}
|
|
561
|
+
// export the settings as immutable consts that read from the global config
|
|
562
|
+
const requestConfig = globalThis[GLOBAL_VARIABLE_NAME];
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Send a no-cors request to the passed uri. This is used to pick up
|
|
566
|
+
* a cookie from a 3rd party server to meet a requirement of some authentication
|
|
567
|
+
* flows.
|
|
568
|
+
* @param url
|
|
569
|
+
* @returns
|
|
570
|
+
*/
|
|
571
|
+
function sendNoCorsRequest(url) {
|
|
572
|
+
// drop any query params, other than f=json
|
|
573
|
+
const urlObj = new URL(url);
|
|
574
|
+
url = urlObj.origin + urlObj.pathname;
|
|
575
|
+
if (urlObj.search.includes("f=json")) {
|
|
576
|
+
url += "?f=json";
|
|
577
|
+
}
|
|
578
|
+
const origin = urlObj.origin;
|
|
579
|
+
// If we have already sent a no-cors request to this url, return the promise
|
|
580
|
+
// so we don't send multiple requests
|
|
581
|
+
if (requestConfig.pendingNoCorsRequests[origin]) {
|
|
582
|
+
return requestConfig.pendingNoCorsRequests[origin];
|
|
583
|
+
}
|
|
584
|
+
// Make the request and add to the cache
|
|
585
|
+
requestConfig.pendingNoCorsRequests[origin] = fetch(url, {
|
|
586
|
+
mode: "no-cors",
|
|
587
|
+
credentials: "include",
|
|
588
|
+
cache: "no-store"
|
|
589
|
+
})
|
|
590
|
+
.then((response) => {
|
|
591
|
+
// Add to the list of cross-origin no-cors domains
|
|
592
|
+
// if the domain is not already in the list
|
|
593
|
+
if (requestConfig.noCorsDomains.indexOf(origin) === -1) {
|
|
594
|
+
requestConfig.noCorsDomains.push(origin);
|
|
595
|
+
}
|
|
596
|
+
// Hold the timestamp of this request so we can decide when to
|
|
597
|
+
// send another request to this domain
|
|
598
|
+
requestConfig.crossOriginNoCorsDomains[origin.toLowerCase()] = Date.now();
|
|
599
|
+
// Remove the pending request from the cache
|
|
600
|
+
delete requestConfig.pendingNoCorsRequests[origin];
|
|
601
|
+
// Due to limitations of fetchMock at the version of the tooling
|
|
602
|
+
// in this project, we can't mock the response type of a no-cors request
|
|
603
|
+
// and thus we can't test this. So we are going to comment this out
|
|
604
|
+
// and leave it in place for now. If we need to test this, we can
|
|
605
|
+
// update the tooling to a version that supports this. Also
|
|
606
|
+
// JS SDK does not do this check, so we are going to leave it out for now.
|
|
607
|
+
// ================================================================
|
|
608
|
+
// no-cors requests are opaque to javascript
|
|
609
|
+
// and thus will always return a response with a type of "opaque"
|
|
610
|
+
// if (response.type === "opaque") {
|
|
611
|
+
// return Promise.resolve();
|
|
612
|
+
// } else {
|
|
613
|
+
// // Not sure if this is possible, but since we have a check above
|
|
614
|
+
// // lets handle the else case
|
|
615
|
+
// return Promise.reject(
|
|
616
|
+
// new Error(`no-cors request to ${origin} not opaque`)
|
|
617
|
+
// );
|
|
618
|
+
// }
|
|
619
|
+
// ================================================================
|
|
620
|
+
})
|
|
621
|
+
.catch((e) => {
|
|
622
|
+
// Not sure this is necessary, but if the request fails
|
|
623
|
+
// we should remove it from the pending requests
|
|
624
|
+
// and return a rejected promise with some information
|
|
625
|
+
delete requestConfig.pendingNoCorsRequests[origin];
|
|
626
|
+
return Promise.reject(new Error(`no-cors request to ${origin} failed`));
|
|
627
|
+
});
|
|
628
|
+
// return the promise
|
|
629
|
+
return requestConfig.pendingNoCorsRequests[origin];
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Register the domains that are allowed to be used in no-cors requests
|
|
633
|
+
* This is called by `request` when the portal/self response is intercepted
|
|
634
|
+
* and the `.authorizedCrossOriginNoCorsDomains` property is set.
|
|
635
|
+
* @param authorizedCrossOriginNoCorsDomains
|
|
636
|
+
*/
|
|
637
|
+
function registerNoCorsDomains(authorizedCrossOriginNoCorsDomains) {
|
|
638
|
+
// register the domains
|
|
639
|
+
authorizedCrossOriginNoCorsDomains.forEach((domain) => {
|
|
640
|
+
// ensure domain is lower case and ensure protocol is included
|
|
641
|
+
domain = domain.toLowerCase();
|
|
642
|
+
if (/^https?:\/\//.test(domain)) {
|
|
643
|
+
addNoCorsDomain(domain);
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
// no protocol present, so add http and https
|
|
647
|
+
addNoCorsDomain("http://" + domain);
|
|
648
|
+
addNoCorsDomain("https://" + domain);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Ensure we don't get duplicate domains in the no-cors domains list
|
|
654
|
+
* @param domain
|
|
655
|
+
*/
|
|
656
|
+
function addNoCorsDomain(url) {
|
|
657
|
+
// Since the caller of this always ensures a protocol is present
|
|
658
|
+
// we can safely use the URL constructor to get the origin
|
|
659
|
+
// and add it to the no-cors domains list
|
|
660
|
+
const uri = new URL(url);
|
|
661
|
+
const domain = uri.origin;
|
|
662
|
+
if (requestConfig.noCorsDomains.indexOf(domain) === -1) {
|
|
663
|
+
requestConfig.noCorsDomains.push(domain);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Is the origin of the passed url in the no-cors domains list?
|
|
668
|
+
* @param url
|
|
669
|
+
* @returns
|
|
670
|
+
*/
|
|
671
|
+
function isNoCorsDomain(url) {
|
|
672
|
+
let result = false;
|
|
673
|
+
if (requestConfig.noCorsDomains.length) {
|
|
674
|
+
// is the current url in the no-cors domains?
|
|
675
|
+
const origin = new URL(url).origin.toLowerCase();
|
|
676
|
+
result = requestConfig.noCorsDomains.some((domain) => {
|
|
677
|
+
return origin.includes(domain);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
return result;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Is the origin of the passed url in the no-cors domains list
|
|
684
|
+
* and do we need to send a no-cors request?
|
|
685
|
+
*
|
|
686
|
+
* @param url
|
|
687
|
+
* @returns
|
|
688
|
+
*/
|
|
689
|
+
function isNoCorsRequestRequired(url) {
|
|
690
|
+
let result = false;
|
|
691
|
+
// is the current origin in the no-cors domains?
|
|
692
|
+
if (isNoCorsDomain(url)) {
|
|
693
|
+
const origin = new URL(url).origin.toLowerCase();
|
|
694
|
+
// check if we have sent a no-cors request to this domain in the last hour
|
|
695
|
+
const lastRequest = requestConfig.crossOriginNoCorsDomains[origin] || 0;
|
|
696
|
+
if (Date.now() - 60 * 60000 > lastRequest) {
|
|
697
|
+
result = true;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return result;
|
|
701
|
+
}
|
|
702
|
+
|
|
541
703
|
/* Copyright (c) 2017-2018 Environmental Systems Research Institute, Inc.
|
|
542
704
|
* Apache-2.0 */
|
|
543
705
|
/**
|
|
@@ -558,6 +720,28 @@
|
|
|
558
720
|
});
|
|
559
721
|
}
|
|
560
722
|
|
|
723
|
+
/**
|
|
724
|
+
* Is the given URL the same origin as the current window?
|
|
725
|
+
* Used to determine if we need to do any additional cross-origin
|
|
726
|
+
* handling for the request.
|
|
727
|
+
* @param url
|
|
728
|
+
* @param win - optional window object to use for origin comparison
|
|
729
|
+
* (useful for testing)
|
|
730
|
+
* @returns
|
|
731
|
+
*/
|
|
732
|
+
function isSameOrigin(url, win) {
|
|
733
|
+
var _a;
|
|
734
|
+
/* istanbul ignore next */
|
|
735
|
+
if ((!win && !window) || !url) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
win = win || window;
|
|
740
|
+
const origin = (_a = win.location) === null || _a === void 0 ? void 0 : _a.origin;
|
|
741
|
+
return url.startsWith(origin);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
561
745
|
/* Copyright (c) 2017-2018 Environmental Systems Research Institute, Inc.
|
|
562
746
|
* Apache-2.0 */
|
|
563
747
|
const NODEJS_DEFAULT_REFERER_HEADER = `@esri/arcgis-rest-js`;
|
|
@@ -713,6 +897,10 @@
|
|
|
713
897
|
needed to support sending IWA cookies */
|
|
714
898
|
credentials: options.credentials || "same-origin"
|
|
715
899
|
};
|
|
900
|
+
// Is this a no-cors domain? if so we need to set credentials to include
|
|
901
|
+
if (isNoCorsDomain(url)) {
|
|
902
|
+
fetchOptions.credentials = "include";
|
|
903
|
+
}
|
|
716
904
|
// the /oauth2/platformSelf route will add X-Esri-Auth-Client-Id header
|
|
717
905
|
// and that request needs to send cookies cross domain
|
|
718
906
|
// so we need to set the credentials to "include"
|
|
@@ -748,7 +936,31 @@
|
|
|
748
936
|
// for errors in GET requests we want the URL passed to the error to be the URL before
|
|
749
937
|
// query params are applied.
|
|
750
938
|
const originalUrl = url;
|
|
751
|
-
|
|
939
|
+
// default to false, for nodejs
|
|
940
|
+
let sameOrigin = false;
|
|
941
|
+
// if we are in a browser, check if the url is same origin
|
|
942
|
+
/* istanbul ignore else */
|
|
943
|
+
if (typeof window !== "undefined") {
|
|
944
|
+
sameOrigin = isSameOrigin(url);
|
|
945
|
+
}
|
|
946
|
+
const requiresNoCors = !sameOrigin && isNoCorsRequestRequired(url);
|
|
947
|
+
// the /oauth2/platformSelf route will add X-Esri-Auth-Client-Id header
|
|
948
|
+
// and that request needs to send cookies cross domain
|
|
949
|
+
// so we need to set the credentials to "include"
|
|
950
|
+
if (options.headers &&
|
|
951
|
+
options.headers["X-Esri-Auth-Client-Id"] &&
|
|
952
|
+
url.indexOf("/oauth2/platformSelf") > -1) {
|
|
953
|
+
fetchOptions.credentials = "include";
|
|
954
|
+
}
|
|
955
|
+
// Simple first promise that we may turn into the no-cors request
|
|
956
|
+
let firstPromise = Promise.resolve();
|
|
957
|
+
if (requiresNoCors) {
|
|
958
|
+
// ensure we send cookies on the request after
|
|
959
|
+
fetchOptions.credentials = "include";
|
|
960
|
+
firstPromise = sendNoCorsRequest(url);
|
|
961
|
+
}
|
|
962
|
+
return firstPromise
|
|
963
|
+
.then(() => authentication
|
|
752
964
|
? authentication.getToken(url).catch((err) => {
|
|
753
965
|
/**
|
|
754
966
|
* append original request url and requestOptions
|
|
@@ -791,7 +1003,8 @@
|
|
|
791
1003
|
const urlWithQueryString = queryParams === "" ? url : url + "?" + encodeQueryString(params);
|
|
792
1004
|
if (
|
|
793
1005
|
// This would exceed the maximum length for URLs by 2000 as default or as specified by the consumer and requires POST
|
|
794
|
-
(options.maxUrlLength &&
|
|
1006
|
+
(options.maxUrlLength &&
|
|
1007
|
+
urlWithQueryString.length > options.maxUrlLength) ||
|
|
795
1008
|
(!options.maxUrlLength && urlWithQueryString.length > 2000) ||
|
|
796
1009
|
// Or if the customer requires the token to be hidden and it has not already been hidden in the header (for browsers)
|
|
797
1010
|
(params.token && options.hideToken)) {
|
|
@@ -889,6 +1102,14 @@
|
|
|
889
1102
|
// Most ArcGIS Server services will return a successful status code but include an error in the response body.
|
|
890
1103
|
if ((params.f === "json" || params.f === "geojson") && !rawResponse) {
|
|
891
1104
|
const response = checkForErrors(data, originalUrl, params, options, originalAuthError);
|
|
1105
|
+
// If this was a portal/self call, and we got authorizedNoCorsDomains back
|
|
1106
|
+
// register them
|
|
1107
|
+
if (data && /\/sharing\/rest\/(accounts|portals)\/self/i.test(url)) {
|
|
1108
|
+
// if we have a list of no-cors domains, register them
|
|
1109
|
+
if (Array.isArray(data.authorizedCrossOriginNoCorsDomains)) {
|
|
1110
|
+
registerNoCorsDomains(data.authorizedCrossOriginNoCorsDomains);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
892
1113
|
if (originalAuthError) {
|
|
893
1114
|
/* If the request was made to an unfederated service that
|
|
894
1115
|
didn't require authentication, add the base url and a dummy token
|