@8ms/helpers 1.5.6 → 1.5.9

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.
@@ -1,8 +1,9 @@
1
+ type Contains = {
2
+ haystack: any;
3
+ needle: any;
4
+ };
1
5
  /**
2
6
  * Shorthand to check if an array or string contains a needle.
3
7
  */
4
- declare const contains: ({ haystack, needle }: {
5
- needle: any;
6
- haystack: any;
7
- }) => boolean;
8
+ declare const contains: (props: Contains) => boolean;
8
9
  export default contains;
package/array/contains.js CHANGED
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
4
  * Shorthand to check if an array or string contains a needle.
5
5
  */
6
- const contains = ({ haystack, needle }) => haystack.indexOf(needle) > -1;
6
+ const contains = (props) => -1 !== props.haystack.indexOf(props.needle);
7
7
  exports.default = contains;
@@ -1,7 +1,8 @@
1
+ type GetArray = {
2
+ input: any;
3
+ };
1
4
  /**
2
5
  * Convert an array into an array or return if already an array.
3
6
  */
4
- declare const getArray: ({ input }: {
5
- input: any;
6
- }) => any[];
7
+ declare const getArray: (props: GetArray) => any[];
7
8
  export default getArray;
package/array/getArray.js CHANGED
@@ -7,5 +7,5 @@ const isArray_1 = __importDefault(require("lodash/isArray"));
7
7
  /**
8
8
  * Convert an array into an array or return if already an array.
9
9
  */
10
- const getArray = ({ input }) => (0, isArray_1.default)(input) ? input : [input];
10
+ const getArray = (props) => (0, isArray_1.default)(props.input) ? props.input : [props.input];
11
11
  exports.default = getArray;
@@ -8,5 +8,5 @@ type InitClient = {
8
8
  /**
9
9
  * Initialise the database using manually provided credentials.
10
10
  */
11
- declare const initClient: ({ config, database, s3Bucket, s3Key }: InitClient) => void;
11
+ declare const initClient: (props: InitClient) => void;
12
12
  export default initClient;
@@ -8,17 +8,20 @@ global.awsAthenaExpressClient = null;
8
8
  /**
9
9
  * Initialise the database using manually provided credentials.
10
10
  */
11
- const initClient = ({ config, database, s3Bucket, s3Key }) => {
11
+ const initClient = (props) => {
12
12
  if (!global.awsAthenaExpressClient) {
13
13
  const athenaExpress = require('athena-express');
14
14
  const awsSdk = require('aws-sdk');
15
- const formattedConfig = (0, getConfig_1.default)({ config, isAthenaExpress: true });
15
+ const formattedConfig = (0, getConfig_1.default)({
16
+ config: props.config,
17
+ isAthenaExpress: true
18
+ });
16
19
  // Set the aws authentication
17
20
  awsSdk.config.update(formattedConfig);
18
21
  global.awsAthenaExpressClient = new athenaExpress.AthenaExpress({
19
22
  aws: awsSdk,
20
- s3: `s3://${s3Bucket}/${s3Key}`,
21
- db: database,
23
+ s3: `s3://${props.s3Bucket}/${props.s3Key}`,
24
+ db: props.database,
22
25
  formatJson: true,
23
26
  retry: 200,
24
27
  getStats: false,
@@ -4,5 +4,5 @@ type Query = {
4
4
  /**
5
5
  * Build the query and return the data.
6
6
  */
7
- declare const query: ({ query }: Query) => Promise<any>;
7
+ declare const query: (props: Query) => Promise<any>;
8
8
  export default query;
@@ -3,8 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  /**
4
4
  * Build the query and return the data.
5
5
  */
6
- const query = async ({ query }) => {
7
- return await global.awsAthenaExpressClient.query(query)
8
- .then(data => undefined === data['Items'] ? [] : data['Items']);
6
+ const query = async (props) => {
7
+ return await global.awsAthenaExpressClient.query(props.query)
8
+ .then(data => {
9
+ let subResponse = [];
10
+ if (undefined !== data['Items']) {
11
+ subResponse = data['Items'];
12
+ }
13
+ return subResponse;
14
+ });
9
15
  };
10
16
  exports.default = query;
@@ -6,5 +6,5 @@ type InitClient = {
6
6
  * Shorthand function to get the Glue Client.
7
7
  * Library: @aws-sdk/client-glue
8
8
  */
9
- declare const initClient: ({ config }: InitClient) => void;
9
+ declare const initClient: (props: InitClient) => void;
10
10
  export default initClient;
@@ -9,10 +9,12 @@ global.awsGlueClient = null;
9
9
  * Shorthand function to get the Glue Client.
10
10
  * Library: @aws-sdk/client-glue
11
11
  */
12
- const initClient = ({ config }) => {
12
+ const initClient = (props) => {
13
13
  if (!global.awsGlueClient) {
14
14
  const { GlueClient } = require('@aws-sdk/client-glue');
15
- const formattedConfig = (0, getConfig_1.default)({ config });
15
+ const formattedConfig = (0, getConfig_1.default)({
16
+ config: props.config
17
+ });
16
18
  global.awsGlueClient = new GlueClient(formattedConfig);
17
19
  }
18
20
  };
@@ -5,5 +5,5 @@ type Invoke = {
5
5
  * Invoke an AWS Glue Crawler by passing in the name of the Crawler.
6
6
  * Library: @aws-sdk/client-glue
7
7
  */
8
- declare const invoke: ({ crawler }: Invoke) => Promise<boolean>;
8
+ declare const invoke: (props: Invoke) => Promise<boolean>;
9
9
  export default invoke;
@@ -8,14 +8,16 @@ const isResponse200_1 = __importDefault(require("../isResponse200"));
8
8
  * Invoke an AWS Glue Crawler by passing in the name of the Crawler.
9
9
  * Library: @aws-sdk/client-glue
10
10
  */
11
- const invoke = async ({ crawler }) => {
11
+ const invoke = async (props) => {
12
12
  const { StartCrawlerCommand } = require('@aws-sdk/client-glue');
13
13
  const command = new StartCrawlerCommand({
14
- Name: crawler,
14
+ Name: props.crawler,
15
15
  });
16
16
  // Fire the event but don't wait for it to complete
17
17
  const apiResponse = await global.awsGlueClient.send(command);
18
18
  // Success
19
- return (0, isResponse200_1.default)({ apiResponse });
19
+ return (0, isResponse200_1.default)({
20
+ apiResponse
21
+ });
20
22
  };
21
23
  exports.default = invoke;
@@ -1,5 +1,5 @@
1
1
  type GetHandlerPath = {
2
2
  input: string;
3
3
  };
4
- declare const getHandlerPath: ({ input }: GetHandlerPath) => string;
4
+ declare const getHandlerPath: (props: GetHandlerPath) => string;
5
5
  export default getHandlerPath;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const getHandlerPath = ({ input }) => {
4
- return `${input.split(process.cwd())[1].substring(1)
3
+ const getHandlerPath = (props) => {
4
+ return `${props.input.split(process.cwd())[1].substring(1)
5
5
  .replace(/\\/g, '/')}`;
6
6
  };
7
7
  exports.default = getHandlerPath;
@@ -6,5 +6,5 @@ type InitClient = {
6
6
  * Shorthand function to get the Lambda Client.
7
7
  * Library: @aws-sdk/client-lambda
8
8
  */
9
- declare const initClient: ({ config }: InitClient) => void;
9
+ declare const initClient: (props: InitClient) => void;
10
10
  export default initClient;
@@ -9,10 +9,12 @@ global.awsLambdaClient = null;
9
9
  * Shorthand function to get the Lambda Client.
10
10
  * Library: @aws-sdk/client-lambda
11
11
  */
12
- const initClient = ({ config }) => {
12
+ const initClient = (props) => {
13
13
  if (!global.awsLambdaClient) {
14
14
  const { LambdaClient } = require('@aws-sdk/client-lambda');
15
- const formattedConfig = (0, getConfig_1.default)({ config });
15
+ const formattedConfig = (0, getConfig_1.default)({
16
+ config: props.config
17
+ });
16
18
  global.awsLambdaClient = new LambdaClient(formattedConfig);
17
19
  }
18
20
  };
@@ -9,5 +9,5 @@ type Invoke = {
9
9
  * Runs asynchronously, doesn't wait for function to fully end.
10
10
  * Library: @aws-sdk/client-lambda
11
11
  */
12
- declare const invoke: ({ awaitResponse, functionName, isJson, payload }: Invoke) => Promise<any>;
12
+ declare const invoke: (props: Invoke) => Promise<any>;
13
13
  export default invoke;
@@ -10,15 +10,15 @@ const ApiResponse_1 = __importDefault(require("../../api/ApiResponse"));
10
10
  * Runs asynchronously, doesn't wait for function to fully end.
11
11
  * Library: @aws-sdk/client-lambda
12
12
  */
13
- const invoke = async ({ awaitResponse, functionName, isJson, payload }) => {
13
+ const invoke = async (props) => {
14
14
  let response = false;
15
15
  const params = {
16
- FunctionName: functionName,
17
- InvocationType: true === awaitResponse ? 'RequestResponse' : 'Event',
16
+ FunctionName: props.functionName,
17
+ InvocationType: true === props.awaitResponse ? 'RequestResponse' : 'Event',
18
18
  };
19
19
  // Payload is defined add to parameters
20
- if (undefined !== payload) {
21
- params['Payload'] = new Uint8Array(Buffer.from(JSON.stringify(payload)));
20
+ if (undefined !== props.payload) {
21
+ params['Payload'] = new Uint8Array(Buffer.from(JSON.stringify(props.payload)));
22
22
  }
23
23
  // Create the Lambda Command
24
24
  const { InvokeCommand } = require('@aws-sdk/client-lambda');
@@ -28,11 +28,11 @@ const invoke = async ({ awaitResponse, functionName, isJson, payload }) => {
28
28
  // Success
29
29
  if ((0, isResponse200_1.default)({ apiResponse })) {
30
30
  // Return the data
31
- if (awaitResponse && undefined !== apiResponse.Payload) {
31
+ if (props.awaitResponse && undefined !== apiResponse.Payload) {
32
32
  const asciiDecoder = new TextDecoder('ascii');
33
33
  response = asciiDecoder.decode(apiResponse.Payload);
34
34
  // Decode string as JSON object
35
- if (true === isJson) {
35
+ if (true === props.isJson) {
36
36
  response = JSON.parse(response);
37
37
  // If it's still a string, double decode it
38
38
  if ('string' === typeof response) {
@@ -0,0 +1,11 @@
1
+ import { ReadFileResponse } from "./readFile";
2
+ type ReadBrotli = {
3
+ bucket: string;
4
+ key: string;
5
+ isJson?: boolean;
6
+ };
7
+ /**
8
+ * Read a file from S3 and return either null, the content or JSON decoded.
9
+ */
10
+ declare const readGzip: ({ bucket, key, isJson }: ReadBrotli) => Promise<ReadFileResponse>;
11
+ export default readGzip;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const readFile_1 = __importDefault(require("./readFile"));
7
+ const getGzipDecompressed_1 = __importDefault(require("../../util/getGzipDecompressed"));
8
+ /**
9
+ * Read a file from S3 and return either null, the content or JSON decoded.
10
+ */
11
+ const readGzip = async ({ bucket, key, isJson }) => {
12
+ let response = await (0, readFile_1.default)({
13
+ bucket,
14
+ key,
15
+ isJson: false
16
+ });
17
+ // Cache exists and is still within our caching timeframe
18
+ if (undefined !== response.body) {
19
+ response.body = await (0, getGzipDecompressed_1.default)({
20
+ input: response.body,
21
+ });
22
+ if (false !== isJson) {
23
+ response.body = JSON.parse(response.body);
24
+ }
25
+ }
26
+ return response;
27
+ };
28
+ exports.default = readGzip;
@@ -0,0 +1,12 @@
1
+ type WriteBrotli = {
2
+ bucket: string;
3
+ data: any;
4
+ key: string;
5
+ isJson?: boolean;
6
+ [options: string]: any;
7
+ };
8
+ /**
9
+ * Write a gzip file to S3.
10
+ */
11
+ declare const writeBrotli: ({ bucket, data, key, isJson, ...options }: WriteBrotli) => Promise<any>;
12
+ export default writeBrotli;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const getGzipCompressed_1 = __importDefault(require("../../util/getGzipCompressed"));
7
+ const writeFile_1 = __importDefault(require("./writeFile"));
8
+ /**
9
+ * Write a gzip file to S3.
10
+ */
11
+ const writeBrotli = async ({ bucket, data, key, isJson, ...options }) => {
12
+ let finalData = data;
13
+ if (false !== isJson) {
14
+ finalData = JSON.stringify(data);
15
+ }
16
+ // Compress the data
17
+ const brotliData = await (0, getGzipCompressed_1.default)({
18
+ input: finalData,
19
+ });
20
+ const apiResponse = await (0, writeFile_1.default)({
21
+ bucket,
22
+ data: brotliData,
23
+ key,
24
+ ...options,
25
+ });
26
+ return apiResponse;
27
+ };
28
+ exports.default = writeBrotli;
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const durations_1 = __importDefault(require("../../date/durations"));
7
7
  const getKey_1 = __importDefault(require("./getKey"));
8
- const readBrotli_1 = __importDefault(require("../s3/readBrotli"));
8
+ const readGzip_1 = __importDefault(require("../s3/readGzip"));
9
9
  /**
10
10
  * Get the stored cache if it exists.
11
11
  */
@@ -17,7 +17,7 @@ const getCache = async ({ bucket, cache, fileName, folder }) => {
17
17
  if (cache) {
18
18
  // Try to get the cache
19
19
  try {
20
- const cached = await (0, readBrotli_1.default)({
20
+ const cached = await (0, readGzip_1.default)({
21
21
  bucket,
22
22
  key: s3CacheKey,
23
23
  isJson: true,
@@ -13,6 +13,6 @@ const getEnvironment_1 = __importDefault(require("../../environment/getEnvironme
13
13
  const getKey = ({ fileName, folder }) => {
14
14
  const fileNameString = (0, getSha256_1.default)({ input: fileName });
15
15
  const folderPath = (0, trimStart_1.default)('' === folder ? '' : `${folder}/`, '/');
16
- return `cache/${(0, getEnvironment_1.default)()}/${folderPath}${fileNameString}.json.br`;
16
+ return `cache/${(0, getEnvironment_1.default)()}/${folderPath}${fileNameString}.json.gz`;
17
17
  };
18
18
  exports.default = getKey;
@@ -3,12 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const writeBrotli_1 = __importDefault(require("../s3/writeBrotli"));
6
+ const writeGzip_1 = __importDefault(require("../s3/writeGzip"));
7
7
  /**
8
8
  * Save the cache.
9
9
  */
10
10
  const saveCache = async ({ bucket, data, key }) => {
11
- return await (0, writeBrotli_1.default)({
11
+ return await (0, writeGzip_1.default)({
12
12
  bucket,
13
13
  data,
14
14
  key,
@@ -1,5 +1,15 @@
1
- type Query = {
2
- input: string;
1
+ /**
2
+ * https://cloud.google.com/bigquery/docs/parameterized-queries
3
+ */
4
+ export type QueryInput = string | {
5
+ query: string;
6
+ location?: string;
7
+ params: {
8
+ [key: string]: any;
9
+ };
10
+ };
11
+ export type Query = {
12
+ input: QueryInput;
3
13
  projectId?: string;
4
14
  };
5
15
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@8ms/helpers",
3
3
  "license": "UNLICENSED",
4
- "version": "1.5.6",
4
+ "version": "1.5.9",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/8millionstories-organisation/8ms-helpers-ts.git"
@@ -14,27 +14,27 @@
14
14
  },
15
15
  "dependencies": {
16
16
  "axios": "1.5.1",
17
- "crypto-js": "4.1.1",
17
+ "crypto-js": "4.2.0",
18
18
  "date-fns": "2.30.0",
19
19
  "date-fns-tz": "2.0.0",
20
20
  "lodash": "4.17.21",
21
21
  "zod": "3.22.4"
22
22
  },
23
23
  "devDependencies": {
24
- "@aws-sdk/client-s3": "3.425.0",
25
- "@aws-sdk/client-ses": "3.425.0",
26
- "@aws-sdk/client-sqs": "3.425.0",
27
- "@aws-sdk/client-ssm": "3.425.0",
28
- "@aws-sdk/s3-request-presigner": "3.425.0",
29
- "@babel/preset-env": "7.22.20",
24
+ "@aws-sdk/client-s3": "3.436.0",
25
+ "@aws-sdk/client-ses": "3.436.0",
26
+ "@aws-sdk/client-sqs": "3.436.0",
27
+ "@aws-sdk/client-ssm": "3.436.0",
28
+ "@aws-sdk/s3-request-presigner": "3.436.0",
29
+ "@babel/preset-env": "7.23.2",
30
30
  "@babel/preset-flow": "7.22.15",
31
- "@babel/preset-typescript": "7.23.0",
31
+ "@babel/preset-typescript": "7.23.2",
32
32
  "@google-cloud/bigquery": "7.3.0",
33
- "@google-cloud/storage": "7.2.0",
34
- "@prisma/client": "5.4.1",
35
- "@types/jest": "29.5.5",
36
- "@types/lodash": "4.14.199",
37
- "@types/node": "20.8.2",
33
+ "@google-cloud/storage": "7.4.0",
34
+ "@prisma/client": "5.5.2",
35
+ "@types/jest": "29.5.6",
36
+ "@types/lodash": "4.14.200",
37
+ "@types/node": "20.8.9",
38
38
  "babel-jest": "29.7.0",
39
39
  "jest": "29.7.0",
40
40
  "node-fetch": "3.3.2",
@@ -6,5 +6,5 @@ type DefaultTo = {
6
6
  /**
7
7
  * Function to default to a value, whilst filtering through an instance
8
8
  */
9
- declare const defaultTo: ({ defaultValue, instance, keys }: DefaultTo) => any;
9
+ declare const defaultTo: (props: DefaultTo) => any;
10
10
  export default defaultTo;
package/util/defaultTo.js CHANGED
@@ -8,12 +8,14 @@ const getArray_1 = __importDefault(require("../array/getArray"));
8
8
  /**
9
9
  * Function to default to a value, whilst filtering through an instance
10
10
  */
11
- const defaultTo = ({ defaultValue, instance, keys }) => {
12
- const keysArray = (0, getArray_1.default)({ input: keys });
11
+ const defaultTo = (props) => {
12
+ const keysArray = (0, getArray_1.default)({
13
+ input: props.keys
14
+ });
13
15
  let found = true;
14
- let pointer = instance;
16
+ let pointer = props.instance;
15
17
  // Instance is undefined or is null
16
- if (undefined === instance || null === pointer) {
18
+ if (undefined === props.instance || null === pointer) {
17
19
  found = false;
18
20
  }
19
21
  // Instance exists
@@ -36,6 +38,6 @@ const defaultTo = ({ defaultValue, instance, keys }) => {
36
38
  }
37
39
  });
38
40
  }
39
- return found ? pointer : defaultValue;
41
+ return found ? pointer : props.defaultValue;
40
42
  };
41
43
  exports.default = defaultTo;
@@ -0,0 +1,9 @@
1
+ type GetGzipCompressed = {
2
+ input?: any;
3
+ options?: object;
4
+ };
5
+ /**
6
+ * Compress a given input using Gzip.
7
+ */
8
+ declare const getGzipCompressed: ({ input, options }?: GetGzipCompressed) => Promise<unknown>;
9
+ export default getGzipCompressed;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Compress a given input using Gzip.
5
+ */
6
+ const getGzipCompressed = async ({ input, options } = {}) => {
7
+ return new Promise(async (resolve, reject) => {
8
+ const zlib = require('zlib');
9
+ zlib.gzip(input || '', options || {}, (err, data) => {
10
+ if (err) {
11
+ reject(`Error compressing - ${err.code}`);
12
+ }
13
+ else {
14
+ resolve(data.toString('base64'));
15
+ }
16
+ });
17
+ });
18
+ };
19
+ exports.default = getGzipCompressed;
@@ -0,0 +1,9 @@
1
+ type GetBrotliDecompressed = {
2
+ input?: any;
3
+ options?: object;
4
+ };
5
+ /**
6
+ * Decompress a given Gzip input into a string.
7
+ */
8
+ declare const getGzipDecompressed: ({ input, options }?: GetBrotliDecompressed) => Promise<unknown>;
9
+ export default getGzipDecompressed;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Decompress a given Gzip input into a string.
5
+ */
6
+ const getGzipDecompressed = async ({ input, options } = {}) => {
7
+ return new Promise(async (resolve, reject) => {
8
+ const zlib = require('zlib');
9
+ const compressedData = Buffer.from(input || '', "base64");
10
+ zlib.gunzip(compressedData, options || {}, (err, data) => {
11
+ if (err) {
12
+ reject(`Error decompressing - ${err.code}`);
13
+ }
14
+ else {
15
+ resolve(data.toString('utf8'));
16
+ }
17
+ });
18
+ });
19
+ };
20
+ exports.default = getGzipDecompressed;
package/util/sleep.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- declare const sleep: ({ seconds }: {
1
+ type Sleep = {
2
2
  seconds: number;
3
- }) => Promise<unknown>;
3
+ };
4
+ declare const sleep: (props: Sleep) => Promise<void>;
4
5
  export default sleep;
package/util/sleep.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const sleep = ({ seconds }) => {
4
- return new Promise((resolve, reject) => setTimeout(resolve, seconds * 1000));
3
+ const sleep = (props) => {
4
+ return new Promise(resolve => setTimeout(resolve, props.seconds * 1000));
5
5
  };
6
6
  exports.default = sleep;
package/xml/getXml.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- declare const getXml: ({ url }: {
2
- url: any;
3
- }) => Promise<string>;
1
+ type GetXml = {
2
+ url: string;
3
+ };
4
+ declare const getXml: (props: GetXml) => Promise<string>;
4
5
  export default getXml;
package/xml/getXml.js CHANGED
@@ -4,13 +4,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const get_1 = __importDefault(require("../axios/get"));
7
- const getXml = async ({ url }) => {
7
+ const getXml = async (props) => {
8
8
  let response = '';
9
9
  const apiResponse = await (0, get_1.default)({
10
- url
10
+ url: props.url,
11
11
  });
12
12
  if (apiResponse.isSuccess()) {
13
- response = apiResponse.getBody().toString();
13
+ response = apiResponse.getBody()
14
+ .toString();
14
15
  // Trim the contents
15
16
  response = response.trim();
16
17
  // Remove tabs, new lines, double spaces