Skip to content

Commit

Permalink
use superagent instead of axios (#18)
Browse files Browse the repository at this point in the history
* use superagent instead of axios

* 2.13.0

* include original error for futher processing
  • Loading branch information
kbarbounakis authored Jan 23, 2024
1 parent 38f9082 commit 658491f
Show file tree
Hide file tree
Showing 6 changed files with 3,578 additions and 6,019 deletions.
165 changes: 106 additions & 59 deletions common/src/BasicDataService.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
import { ClientDataService } from '@themost/client';
import { ClientDataContextOptions, DataServiceExecuteOptions, DataServiceHeaders, ResponseError } from '@themost/client';
import { EdmSchema } from '@themost/client';
import axios, { AxiosRequestConfig } from 'axios';
import { Request, Response } from 'superagent';
import { Buffer } from 'buffer';

const ISO_DATE_REGEX = /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)$/gm;

function isDateString(value: any) {
return typeof value === 'string' && value.match(ISO_DATE_REGEX);
}

/**
* The default JSON reviver which converts an ISO date string to Date object
* @param key
* @param value
*/
function jsonReviver(key: string, value: any) {
if (isDateString(value)) {
return new Date(value);
}
return value;
}

/**
* Represents a default data service that extends the ClientDataService class.
*/
Expand All @@ -20,87 +38,116 @@ class BasicDataService extends ClientDataService {
* @returns {Promise<any>}
*/
async execute(options: DataServiceExecuteOptions): Promise<any> {
// get absolute url
const config: { method: string; url: string; headers: DataServiceHeaders; } = {
method: options.method,
url: this.resolve(options.url),
headers: Object.assign({}, this.getHeaders()) // set service headers
};
// get method
const method: string = options.method || 'GET';
// get url
const url: string = this.resolve(options.url);
// get headers
const headers: any = Object.assign({}, this.getHeaders()); // set service headers
let query: any = null;
let data: any = null;
// assign options headers if any
if (options.headers) {
Object.assign(config.headers, options.headers);
Object.assign(headers, {
'Accept': 'application/json'
}, options.headers);
}

if (options.method === 'POST' || options.method === 'PUT' || options.method === 'DELETE') {
// add body
if (options.data != null) {
Object.assign(config, {
data: options.data
});
}
if (method === 'POST' || method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
data = Object.assign({}, options.data);
} else {
// for HEAD, GET, OPTIONS, DELETE set query params
if (options.data != null) {
const queryParams = new URLSearchParams();
const data: { [key: string]: any; } = options.data;
Object.keys(data)
.filter((key) => {
return Object.prototype.hasOwnProperty.call(data, key);
})
.forEach((key) => {
queryParams.append(key, data[key]);
});
// assign url with params
Object.assign(config, {
url: config.url + '?' + queryParams.toString()
});
}
}
const reviver = this.getOptions().useJsonReviver;
if (typeof reviver === 'function') {
Object.assign(config, {
responseType: 'arraybuffer'
});
query = Object.assign({}, options.data);
}
const response = await axios(config as AxiosRequestConfig);
if (response.status === 204) {
return null;
} else if (response.status === 200) {
if (typeof reviver === 'function') {
// get response content type
const contentType: string | undefined = response.headers['content-type'];
if (contentType != null &&
(contentType.match(/^application\/json;?/) ||
contentType.match(/^application\/ld\+json;?/))) {
const buffer = Buffer.from(response.data, 'binary');
return JSON.parse(buffer.toString(), reviver);
const { useJsonReviver } = this.getOptions();
const reviver = useJsonReviver || jsonReviver;
const request = new Request(method, url).set(headers);
let response: Response;
try {
if (query) {
response = await request.query(query);
}
if (data) {
response = await request.send(data);
}
if (response.status === 204) {
return null;
} else if (response.status === 200) {
if (typeof reviver === 'function') {
// get response content type
const contentType: string | undefined = response.get('Content-Type');
if (contentType != null &&
(contentType.match(/^application\/json;?/) ||
contentType.match(/^application\/ld\+json;?/))) {
return JSON.parse(response.text, reviver);
}
}
return response.body;
}
// otherwise return response data
return response.data;
throw new ResponseError('An error occurred', response.status);
} catch (err) {
const error:{
statusText: string,
status: number,
ok: false,
body: any,
response: Response
} = err;
// get error content type
const contentType: string | undefined = error.response.get('Content-Type');
// if error content type is application/json
if (/^application\/json;?/.test(contentType)) {
// get error body
const errorBody: { message?: string } = error.response.body;
// if error body has message property
if (errorBody && errorBody.message) {
// create response error
const responseError = new ResponseError(errorBody.message, error.status);
// assign attributes
Object.assign(responseError, errorBody);
Object.defineProperty(responseError, 'originalError', {
configurable: true,
enumerable: true,
value: err
});
// and throw
throw responseError;
}
}
// otherwise throw error
throw error;
}
// otherwise throw error
throw new ResponseError(
'An error occurred while getting service metadata',
response.status
);
}

/**
* Gets the metadata of the current data service
* @returns {Promise<EdmSchema>}
*/
async getMetadata(): Promise<EdmSchema> {
const config: any = {
const config: {
method: string;
url: string;
headers: DataServiceHeaders;
} = {
method: 'GET',
url: this.resolve('$metadata'),
headers: this.getHeaders()
};
// get response
const response = await axios(config);
const response = await new Request(config.method, config.url).set(config.headers);
if (response.status === 200) {
// load schema
return EdmSchema.loadXML(response.data) as EdmSchema;
const bufferedResponse: { buffered: boolean } = response as any;
if (bufferedResponse.buffered) {
const text: string = Buffer.from(response.body, 'utf-8').toString();
return EdmSchema.loadXML(text) as EdmSchema;
}
return EdmSchema.loadXML(response.text) as EdmSchema;
}
if (response.status === 204) {
throw new ResponseError(
'Unexpected metadata content. Service metadata is empty.',
500
);
}
// otherwise throw error
throw new ResponseError(
Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ module.exports = function(config) {
require('./karma-test-api-server'),
require("karma-jasmine"),
require("karma-typescript"),
require("karma-sourcemap-loader"),
require("karma-chrome-launcher"),
require("karma-jasmine-html-reporter"),
require('karma-mocha-reporter'),
Expand Down
Loading

0 comments on commit 658491f

Please sign in to comment.