Skip to content

Commit

Permalink
Merge pull request #346 from Esri/chore/arcgis-rest-js
Browse files Browse the repository at this point in the history
Chore/arcgis rest js
  • Loading branch information
tomwayson authored Nov 30, 2017
2 parents 75a4e72 + df20d62 commit 76f24eb
Show file tree
Hide file tree
Showing 19 changed files with 685 additions and 92 deletions.
11 changes: 11 additions & 0 deletions packages/arcgis-rest-request/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions packages/arcgis-rest-request/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@esri/arcgis-rest-request",
"version": "1.0.0-alpha.2",
"description": "Common methods and utilities for @esri/arcgis-rest-* packages.",
"main": "dist/node/index.js",
"module": "dist/esm/index.js",
"js:next": "dist/esm/index.js",
"types": "dist/esm/index.d.ts",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^1.7.1"
},
"scripts": {
"prepublish": "npm run build",
"build": "npm run build:node && npm run build:esm",
"build:esm": "tsc --module es2015 --outDir ./dist/esm --declaration",
"build:node": "tsc --module commonjs --outDir ./dist/node",
"test": "npm run build"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Esri/arcgis-rest-js.git"
},
"contributors": [
{
"name": "Patrick Arlt",
"email": "[email protected]",
"url": "http://patrickarlt.com/"
}
],
"bugs": {
"url": "https://github.com/Esri/arcgis-rest-js/issues"
},
"homepage": "https://github.com/Esri/arcgis-rest-js#readme",
"devDependencies": {
"typescript": "^2.6.2"
}
}
8 changes: 8 additions & 0 deletions packages/arcgis-rest-request/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export * from "./request";
export * from "./utils/encode-form-data";
export * from "./utils/encode-query-string";
export * from "./utils/check-for-errors";
export * from "./utils/ArcGISRequestError";
export * from "./utils/ArcGISAuthError";
export * from "./utils/ErrorTypes";
export * from "./utils/process-params";
152 changes: 152 additions & 0 deletions packages/arcgis-rest-request/src/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* Copyright (c) 2017 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { checkForErrors } from "./utils/check-for-errors";
import { encodeFormData } from "./utils/encode-form-data";
import { encodeQueryString } from "./utils/encode-query-string";

export interface IAuthenticationManager {
getToken(url: string): Promise<string>;
}

/**
* HTTP methods used by the ArcGIS REST API.
*/
export type HTTPMethods = "GET" | "POST";

/**
* Valid response formats for the `f` parameter.
*/
export type ResponseFormats =
| "json"
| "geojson"
| "text"
| "html"
| "image"
| "zip";

export interface IParams {
f?: ResponseFormats;
[key: string]: any;
}

/**
* Options for the [`request()`](/api/arcgis-core/request/) method.
*/
export interface IRequestOptions {
/**
* The HTTP method to send the request with.
*/
httpMethod?: HTTPMethods;

/**
* The instance of `IAuthenticationManager` to use to authenticate this request.
*/
authentication?: IAuthenticationManager;

/**
* The implementation of `fetch` to use. Defaults to a global `fetch`
*/
fetch?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;
}

/**
* Generic method for making HTTP requests to ArcGIS REST API endpoints.
*
* ```js
* import { request } from 'arcgis-core';
*
* request('https://www.arcgis.com/sharing/rest')
* .then((response) => {
* console.log(response.currentVersion); // => 5.2
* });
* ```
*
* ```js
* import { request, HTTPMethods } from 'arcgis-core';
*
* request('https://www.arcgis.com/sharing/rest', {}, {
* httpMethod: "GET"
* }).then((response) => {
* console.log(response.currentVersion); // => 5.2
* });
* ```
*
* ```js
* import { request, HTTPMethods } from 'arcgis-core';
*
* request('https://www.arcgis.com/sharing/rest/search', {
* q: 'parks'
* }).then((response) => {
* console.log(response.total); // => 78379
* });
* ```
*
* @param url - The URL of the ArcGIS REST API endpoint.
* @param params - The parameters to pass to the endpoint.
* @param requestOptions - Options for the request.
* @returns A Promise that will resolve with the data from the request.
*/
export function request(
url: string,
requestParams: IParams = { f: "json" },
requestOptions?: IRequestOptions
): Promise<any> {
const options: IRequestOptions = {
...{ httpMethod: "POST", fetch: fetch.bind(Function("return this")()) },
...requestOptions
};

const { httpMethod, authentication } = options;

const params: IParams = {
...{ f: "json" },
...requestParams
};

const fetchOptions: RequestInit = {
method: httpMethod
};

return (authentication ? authentication.getToken(url) : Promise.resolve(""))
.then(token => {
if (token.length) {
params.token = token;
}

if (httpMethod === "GET") {
url = url + "?" + encodeQueryString(params);
}

if (httpMethod === "POST") {
fetchOptions.body = encodeFormData(params);
}

return options.fetch(url, fetchOptions);
})
.then(response => {
switch (params.f) {
case "json":
return response.json();
case "geojson":
return response.json();
case "html":
return response.text();
case "text":
return response.text();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "image":
return response.blob();
/* istanbul ignore next blob responses are difficult to make cross platform we will just have to trust the isomorphic fetch will do its job */
case "zip":
return response.blob();
}
})
.then(data => {
if (params.f === "json" || params.f === "geojson") {
return checkForErrors(data, url, params, options);
} else {
return data;
}
});
}
75 changes: 75 additions & 0 deletions packages/arcgis-rest-request/src/utils/ArcGISAuthError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Copyright (c) 2017 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import {
request,
IRequestOptions,
IParams,
IAuthenticationManager
} from "../request";
import { ArcGISRequestError } from "./ArcGISRequestError";

export type IRetryAuthError = (
url: string,
params: IParams,
options: IRequestOptions
) => Promise<IAuthenticationManager>;

export class ArcGISAuthError extends ArcGISRequestError {
/**
* Create a new `ArcGISAuthError` object.
*
* @param message - The error message from the API
* @param code - The error code from the API
* @param response - The original response from the API that caused the error
* @param url - The original url of the request
* @param params - The original params of the request
* @param options - The original options of the request
*/
constructor(
message = "AUTHENTICATION_ERROR",
code: string | number = "AUTHENTICATION_ERROR_CODE",
response?: any,
url?: string,
params?: IParams,
options?: IRequestOptions
) {
super(message, code, response, url, params, options);
this.name = "ArcGISAuthError";
this.message =
code === "AUTHENTICATION_ERROR_CODE" ? message : `${code}: ${message}`;
}

retry(getSession: IRetryAuthError, retryLimit = 3) {
let tries = 0;

const retryRequest = (resolve: any, reject: any) => {
getSession(this.url, this.params, this.options)
.then(session => {
const newOptions = {
...this.options,
...{ authentication: session }
};

tries = tries + 1;
return request(this.url, this.params, newOptions);
})
.then(response => {
resolve(response);
})
.catch(e => {
if (e.name === "ArcGISAuthError" && tries < retryLimit) {
retryRequest(resolve, reject);
} else if (e.name === "ArcGISAuthError" && tries >= retryLimit) {
reject(this);
} else {
reject(e);
}
});
};

return new Promise((resolve, reject) => {
retryRequest(resolve, reject);
});
}
}
81 changes: 81 additions & 0 deletions packages/arcgis-rest-request/src/utils/ArcGISRequestError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/* Copyright (c) 2017 Environmental Systems Research Institute, Inc.
* Apache-2.0 */

import { IRequestOptions, IParams } from "../request";

// TypeScript 2.1 no longer allows you to extend built in types. See https://github.com/Microsoft/TypeScript/issues/12790#issuecomment-265981442
// and https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
//
// This code is from MDN https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Custom_Error_Types.
export class ArcGISRequestError {
/**
* The name of this error. Will always be `"ArcGISRequestError"` to conform with the [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) class.
*/
name: string;

/**
* Formatted error message. See the [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) class for more details.
*/
message: string;

/**
* The errror message return from the request.
*/
originalMessage: string;

/**
* The error code returned from the request.
*/
code: string | number;

/**
* The original JSON response the caused the error.
*/
response: any;

/**
* The URL of the original request that caused the error
*/
url: string;

/**
* The parameters of the original request that caused the error
*/
params: IParams;

/**
* The options of the original request that caused the error
*/
options: IRequestOptions;

/**
* Create a new `ArcGISRequestError` object.
*
* @param message - The error message from the API
* @param code - The error code from the API
* @param response - The original response from the API that caused the error
* @param url - The original url of the request
* @param params - The original params of the request
* @param options - The original options of the request
*/
constructor(
message = "UNKNOWN_ERROR",
code: string | number = "UNKNOWN_ERROR_CODE",
response?: any,
url?: string,
params?: IParams,
options?: IRequestOptions
) {
this.name = "ArcGISRequestError";
this.message =
code === "UNKNOWN_ERROR_CODE" ? message : `${code}: ${message}`;
this.originalMessage = message;
this.code = code;
this.response = response;
this.url = url;
this.params = params;
this.options = options;
}
}
ArcGISRequestError.prototype = Object.create(Error.prototype);
ArcGISRequestError.prototype.constructor = ArcGISRequestError;
Loading

0 comments on commit 76f24eb

Please sign in to comment.