In this case I have zero control over the REST endpoint for retrieving data. Hence, I am taking a stab at creating an adaptor. The challenge arises with the findAll
method, since the maximum number of features exceeds the maximum number of features the REST endpoint will return. However, the REST endpoint does support pagnation. As a result, I am trying to sort out how to navigate the world of promises to get all the records returned…and apparently not doing a very good job. Here is the code I have thus far. Thank you in advance for any assistance or insight you may be able to provide.
./app/adapters/reach.js
import DS from 'ember-data';`
import request from '../utils/request';
import ENV from '../config/environment';
import Ember from 'ember';
const urlFeatureLayer = ENV.APP.ARCGIS.POINTS.URL;
const urlQuery = `${urlFeatureLayer}/query`;
const uidField = 'reachId'; // not the same one ArcGIS uses
// for quickly making a get request to the ArcGIS REST endpoint
const makeQueryRequest = (opts) => {
opts.f = 'json'; // just ensuring this is taken care of
// providing a default if none is provided
if (!opts.where){
opts.where = '1=1';
}
// using Ember's built in jQuery to parameterize inputs for request
opts = Ember.$.param(opts);
// combine params onto url, and make request
let url = `${urlQuery}?${opts}`;
return request(url, {method: 'GET'});
}
// pulling together the steps to side step the maximum record response limit
let retrieveAll = () => {
// retrieve the maximum feature count
request(`${urlFeatureLayer}?f=json`, {method: 'GET'})
.then((respProp) => {
Ember.debug(`Max Record Count: ${respProp.maxRecordCount}`);
// get the feature count
makeQueryRequest({returnCountOnly: true})
.then((respCnt) => {
Ember.debug(`Feature Count: ${respCnt.count}`);
// calculate the number of pulls required to get all the data
let pullCount = Math.ceil(respCnt.count / respProp.maxRecordCount);
Ember.debug(`Pull Count: ${pullCount}`);
// iteratively, in chunks, retrieve all the features
let allFeatures = [];
for (let i = 0; i < pullCount; i++){
let offset = i * respProp.maxRecordCount;
let recordCount = respProp.maxRecordCount;
allFeatures.push(makeQueryRequest({
resultOffset: offset,
resultRecordCount: recordCount
}));
}
// catcher to get all the promises and send back the results
Promise.all(allFeatures).then((allFeatures) => {
return allFeatures;
});
});
});
}
export default DS.JSONAPIAdapter.extend({
findAll: function(store) {
return retrieveAll();
// let opts = Ember.$.param({
// where: '1=1',
// f: 'json',
// outFields: '*'
// });
// let url = `${urlQuery}?${opts}`;
// return request(url, {method: 'GET'});
},
findRecord (store, type, id) {
let opts = Ember.$.param({
where: `${uidField} = ${id}`,
f: 'json',
outFields: '*'
});
let url = `${urlQuery}?${opts}`;
return request(url, {method: 'GET'});
}
});
The referenced request
is really little more than a wrapper for fetch, but here it is for reference…
./utils/request.js
import Ember from 'ember';
import fetch from 'fetch';
import addToken from './add-token';
import encodeForm from './encode-form';
/**
* Fetch based request method
*/
export default function request (url, opts = {}) {
// if we are POSTing, we need to manually set the content-type because AGO
// actually does care about this header
if (opts.method && opts.method === 'POST') {
if (!opts.headers) {
opts.headers = {
'Accept': 'application/json, application/xml, multipart/form-data, text/plain, text/html, *.*',
'Content-Type': 'multipart/form-data'
};
}
// if we have a data, prep it to send
if (opts.data) {
opts.body = encodeForm(opts.data);
}
}
opts.redirect = 'follow';
opts.mode = 'cors';
// add a token if provided
url = addToken(url, opts.token);
Ember.debug('Making request to ' + url);
return fetch(url, opts).then(checkStatusAndParseJson);
}
/**
* Fetch does not reject on non-200 responses, so we need to check this manually
*/
function checkStatusAndParseJson (response) {
let error;
Ember.debug('Fetch request status: ' + response.status);
// check if this is one of those groovy 200-but-a-400 things
if (response.status >= 200 && response.status < 300) {
return response.json().then(json => {
// cook an error
if (json.error) {
error = new Error(json.error.message);
error.code = json.error.code || 404;
error.response = response;
Ember.debug('Error in response: ' + json.error.message);
throw error;
} else {
return json;
}
});
} else {
// Response has non 200 http code
error = new Error(response.statusText);
error.response = response;
throw error;
}
}