start ajax services

This commit is contained in:
Jeff Colombo 2019-01-12 11:44:02 -05:00
parent 00b06985e4
commit e72a59fe9d
5 changed files with 284 additions and 10 deletions

View File

@ -1,6 +1,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json'; import Pokemon from 'pokemongo-json-pokedex/output/pokemon.json';
import { IPokemon, IStats, League, Grade } from './src/ts/models/Pokemon'; import { ILeaguePokemon, IPokemon, IStats, League, Grade } from './src/ts/app/models/Pokemon';
interface ICpAndTotalFound { interface ICpAndTotalFound {
[ key : number ] : Array<IStats>; [ key : number ] : Array<IStats>;
@ -34,12 +34,12 @@ const getClosestCpMultiplierIndex = (value : number) => {
const familyOrder : Array<string> = []; const familyOrder : Array<string> = [];
const familyEvolutionOrder : { [ key : string ] : Array<string> } = {}; const familyEvolutionOrder : { [ key : string ] : Array<string> } = {};
const familyEncountered : { [ key : string ] : Array<IPokemon> } = {}; const familyEncountered : { [ key : string ] : Array<ILeaguePokemon> } = {};
Pokemon.forEach((mon) => { Pokemon.forEach((mon) => {
const baseAtk = mon.stats.baseAttack; const baseAtk = mon.stats.baseAttack;
const baseDef = mon.stats.baseDefense; const baseDef = mon.stats.baseDefense;
const baseHp = mon.stats.baseStamina; const baseHp = mon.stats.baseStamina;
const pokemon : IPokemon = { const pokemon : ILeaguePokemon = {
id: mon.id, id: mon.id,
name: mon.name, name: mon.name,
dex: mon.dex, dex: mon.dex,
@ -184,7 +184,7 @@ Pokemon.forEach((mon) => {
}); });
}); });
const pokemonOrder : Array<{ id : string, name : string }> = []; const pokemonOrder : Array<IPokemon> = [];
familyOrder.forEach((familyId) => { familyOrder.forEach((familyId) => {
familyEvolutionOrder[familyId].forEach((id, order) => { familyEvolutionOrder[familyId].forEach((id, order) => {
familyEncountered[familyId].some((pokemon, index) => { familyEncountered[familyId].some((pokemon, index) => {
@ -196,11 +196,9 @@ familyOrder.forEach((familyId) => {
}); });
}); });
familyEncountered[familyId].forEach((pokemon) => { familyEncountered[familyId].forEach((leaguePokemon) => {
pokemonOrder.push({ const { pvp, ...pokemon } = leaguePokemon;
id: pokemon.id, pokemonOrder.push(pokemon);
name: pokemon.name,
});
}); });
}); });

223
src/ts/api/AjaxUtils.ts Normal file
View File

@ -0,0 +1,223 @@
export interface IRestApiRejection {
message : string;
status : number;
xhr : XMLHttpRequest;
error : Error | null;
type : string | null;
content : any | null;
}
const buildQueryString = (queryObject : any) => {
const queryParameters : Array<string> = [];
let queryString = '';
Object.keys(queryObject || {}).forEach((key : string) => {
queryParameters.push(key + '=' + encodeURIComponent(queryObject[key]));
});
if (queryParameters.length > 0) {
queryString = '?' + queryParameters.join('&');
}
return queryString;
};
const handleXhrOnLoad = (resolve : (value : any) => void, reject : (reason : any) => void, xhr : XMLHttpRequest) : void => {
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 204)) {
if (xhr.responseType === '' && xhr.responseText === '') {
return resolve(undefined);
}
if (xhr.responseType === 'text') {
return resolve(xhr.response);
}
try {
const responseJSON = JSON.parse(xhr.response);
if (typeof(responseJSON.success) !== 'undefined' && !responseJSON.success) {
if (responseJSON.error === 'user_required') {
window.location.reload();
}
}
return resolve(responseJSON);
} catch (e) {
const rejection : IRestApiRejection = {
message: e.message,
status: xhr.status,
xhr,
error: e,
type: null,
content: null,
};
// tslint:disable-next-line:no-console
console.error('Failed to parse response', xhr);
return reject(rejection);
}
} else {
return handleXhrOnLoadCaughtException(reject, xhr);
}
};
const handleXhrOnLoadCaughtException = (reject : (reason : any) => void, xhr : XMLHttpRequest) : void => {
if (xhr.status === 401) {
window.location.reload();
}
if (xhr.responseType === 'text' || xhr.responseType === '') {
const rejection : IRestApiRejection = {
message: xhr.responseText || xhr.statusText,
status: xhr.status,
xhr,
error: null,
type: null,
content: null,
};
return reject(rejection);
}
try {
const responseJSON = JSON.parse(xhr.response);
const rejection : IRestApiRejection = {
message: responseJSON.message || xhr.statusText,
status: xhr.status,
xhr,
error: null,
type: responseJSON.exception_type,
content: responseJSON.content,
};
return reject(rejection);
} catch (e) {
const rejection : IRestApiRejection = {
message: e.message,
status: xhr.status,
xhr,
error: e,
type: null,
content: null,
};
// tslint:disable-next-line:no-console
console.error('Failed to parse response', xhr);
return reject(rejection);
}
};
export const AjaxUtils = {
ajaxGet(url : string, queryObject? : any) : Promise<any> {
return new Promise<any>((resolve, reject) => {
const xhr = new XMLHttpRequest();
const cacheBustingQueryObject = (queryObject || {}); // IE 11 caches GET requests
cacheBustingQueryObject._cache_busting_arg_ = +new Date(); // this parameter name must be in the backend's excluded parameter set (see api_utils.py)
xhr.open('GET', url + buildQueryString(cacheBustingQueryObject));
xhr.withCredentials = true;
xhr.onload = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
return resolve(JSON.parse(xhr.response));
} catch (e) {
const rejection : IRestApiRejection = {
message: e.message,
status: xhr.status,
xhr,
error: e,
type: null,
content: null,
};
// tslint:disable-next-line:no-console
console.error('Failure to parse response', xhr);
return reject(rejection);
}
} else {
return handleXhrOnLoadCaughtException(reject, xhr);
}
};
xhr.onerror = (e) => {
return handleXhrOnLoadCaughtException(reject, xhr);
};
xhr.send();
});
},
ajaxPost(url : string, postBody : any, queryObject? : any, type? : 'text' | 'json') : Promise<any> {
return new Promise<any>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url + buildQueryString(queryObject));
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRFToken', (window as any).CSRF_TOKEN);
xhr.onload = () => {
if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 201)) {
if (type === 'text') {
return resolve(xhr.response);
}
try {
return resolve(JSON.parse(xhr.response));
} catch (e) {
const rejection : IRestApiRejection = {
message: e.message,
status: xhr.status,
xhr,
error: e,
type: null,
content: null,
};
// tslint:disable-next-line:no-console
console.error('Failure to parse response', xhr);
return reject(rejection);
}
} else {
return handleXhrOnLoadCaughtException(reject, xhr);
}
};
xhr.onerror = (e) => {
return handleXhrOnLoadCaughtException(reject, xhr);
};
xhr.send(JSON.stringify(postBody || undefined));
});
},
ajaxPostForm(url : string, formData? : FormData, queryObject? : any) : Promise<any> {
// Lesman 4/24/2017
// NOTE: you do not set the Content-Type on this request,
// the browser will automatically encode the form data and
// set the appropriate Content-Type. The encoding is browser
// specific so you MUST allow the browser to set the header
return new Promise<any>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('POST', url + buildQueryString(queryObject));
xhr.withCredentials = true;
xhr.setRequestHeader('X-CSRFToken', (window as any).CSRF_TOKEN);
xhr.onload = () => handleXhrOnLoad(resolve, reject, xhr);
xhr.onerror = (e) => {
return handleXhrOnLoadCaughtException(reject, xhr);
};
xhr.send(formData);
});
},
ajaxPut(url : string, postBody : any, queryObject? : any) : Promise<any> {
return new Promise<any>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('PUT', url + buildQueryString(queryObject));
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRFToken', (window as any).CSRF_TOKEN);
xhr.onload = () => handleXhrOnLoad(resolve, reject, xhr);
xhr.onerror = (e) => {
return handleXhrOnLoadCaughtException(reject, xhr);
};
xhr.send(JSON.stringify(postBody || undefined));
});
},
ajaxDelete(url : string, queryObject? : any) : Promise<any> {
return new Promise<any>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('DELETE', url + buildQueryString(queryObject));
xhr.withCredentials = true;
xhr.setRequestHeader('X-CSRFToken', (window as any).CSRF_TOKEN);
xhr.onload = () => handleXhrOnLoad(resolve, reject, xhr);
xhr.onerror = (e) => {
return handleXhrOnLoadCaughtException(reject, xhr);
};
xhr.send();
});
},
};

View File

@ -0,0 +1,51 @@
import { AjaxUtils } from 'src/ts/api/AjaxUtils';
import { IPokemon } from 'src/ts/app/models/Pokemon';
interface IPokemonService {
}
export class PokemonService implements IPokemonService {
public getPokemonList() {
const queryParameters = {
type: 'no touch',
is_active: true,
};
return AjaxUtils.ajaxGet('/api/billing/plan', queryParameters)
.then((response : Array<object>) => {
return Promise.resolve(this.serializePokemonList(response));
});
}
// does `object` need to be `any`?
private serializePokemonList(jsonPokemonList : Array<object>) : Array<IPokemon> {
const pokemonList = jsonPokemonList.map((jsonPokemon) => {
let pokemon : IPokemon | null = null;
try {
if (typeof jsonPokemon.name !== 'string') {
throw 'pokemon missing name';
}
if (typeof jsonPokemon.id !== 'string') {
throw 'pokemon missing id';
}
if (typeof jsonPokemon.family !== 'string') {
throw 'pokemon missing family';
}
if (typeof jsonPokemon.dex !== 'number') {
throw 'pokemon missing dex';
}
if (typeof jsonPokemon.stats !== 'object') {
throw 'pokemon missing stats';
}
pokemon = { ...jsonPokemon };
} catch (e) {
console.error(jsonPokemon, e.message);
}
return pokemon;
});
// TODO should we allow `null`s?
return pokemonList;
}
}

View File

@ -13,13 +13,15 @@ export interface IBaseStats {
baseStamina : number; baseStamina : number;
} }
export type League = 'great' | 'ultra';
export interface IPokemon { export interface IPokemon {
name : string; name : string;
id : string; id : string;
family : string; family : string;
dex : number; dex : number;
stats : IBaseStats; stats : IBaseStats;
}
export type League = 'great' | 'ultra';
export interface ILeaguePokemon extends IPokemon {
pvp : { pvp : {
great : Array<IStats>; great : Array<IStats>;
ultra : Array<IStats>; ultra : Array<IStats>;