Skip to content

Commit

Permalink
feat: added ability to cancel requests, refactor (async/await in tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikBjare committed Mar 4, 2022
1 parent 84915ed commit c232696
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 62 deletions.
4 changes: 2 additions & 2 deletions Makefile
@@ -1,9 +1,9 @@
.PHONY: build install test lint

build: install
build:
npm run compile

install:
install: package-lock.json
npm install

test:
Expand Down
55 changes: 41 additions & 14 deletions src/aw-client.ts
Expand Up @@ -18,6 +18,12 @@ export interface IAppEditorEvent extends IEvent {
};
}

export interface AWReqOptions {
controller?: AbortController,
testing?: boolean,
baseURL?: string
}

export interface IBucket {
id: string;
name: string;
Expand Down Expand Up @@ -47,14 +53,16 @@ export class AWClient {
public testing: boolean;
public req: AxiosInstance;

public controller: AbortController;

private heartbeatQueues: {
[bucketId: string]: {
isProcessing: boolean;
data: IHeartbeatQueueItem[];
},
} = {};

constructor(clientname: string, options: {testing?: boolean, baseURL?: string} = {}) {
constructor(clientname: string, options: AWReqOptions = {}) {
this.clientname = clientname;
this.testing = options.testing || false;
if (typeof options.baseURL === "undefined") {
Expand All @@ -65,20 +73,39 @@ export class AWClient {
} else {
this.baseURL = options.baseURL;
}
this.controller = options.controller || new AbortController();

this.req = axios.create({
baseURL: this.baseURL + "/api",
timeout: 30000,
});
}

private async _get(endpoint: string, params: object = {}) {
return this.req.get(endpoint, {...params, signal: this.controller.signal}).then(res => (res && res.data) || res);
}

private async _post(endpoint: string, data: object = {}) {
return this.req.post(endpoint, data, {signal: this.controller.signal}).then(res => (res && res.data) || res);
}

private async _delete(endpoint: string) {
return this.req.delete(endpoint, {signal: this.controller.signal});
}

public async getInfo(): Promise<IInfo> {
return this.req.get("/0/info").then(res => res.data);
return this._get("/0/info");
}

public async abort(msg?: string) {
console.info(msg || 'Requests cancelled');
this.controller.abort();
this.controller = new AbortController();
}

public async ensureBucket(bucketId: string, type: string, hostname: string): Promise<{ alreadyExist: boolean }> {
try {
await this.req.post(`/0/buckets/${bucketId}`, {
await this._post(`/0/buckets/${bucketId}`, {
client: this.clientname,
type,
hostname,
Expand All @@ -94,7 +121,7 @@ export class AWClient {
}

public async createBucket(bucketId: string, type: string, hostname: string): Promise<undefined> {
await this.req.post(`/0/buckets/${bucketId}`, {
await this._post(`/0/buckets/${bucketId}`, {
client: this.clientname,
type,
hostname,
Expand All @@ -103,12 +130,12 @@ export class AWClient {
}

public async deleteBucket(bucketId: string): Promise<undefined> {
await this.req.delete(`/0/buckets/${bucketId}?force=1`);
await this._delete(`/0/buckets/${bucketId}?force=1`);
return undefined;
}

public async getBuckets(): Promise<{[bucketId: string]: IBucket}> {
const buckets = (await this.req.get("/0/buckets/")).data;
const buckets = await this._get("/0/buckets/");
Object.keys(buckets).forEach(bucket => {
buckets[bucket].created = new Date(buckets[bucket].created);
if (buckets[bucket].last_updated) {
Expand All @@ -119,20 +146,20 @@ export class AWClient {
}

public async getBucketInfo(bucketId: string): Promise<IBucket> {
const bucket = (await this.req.get(`/0/buckets/${bucketId}`)).data;
const bucket = await this._get(`/0/buckets/${bucketId}`);
bucket.created = new Date(bucket.created);
return bucket;
}

public async getEvent(bucketId: string, eventId: number): Promise<IEvent> {
// Get a single event by ID
const event = (await this.req.get("/0/buckets/" + bucketId + "/events/" + eventId)).data;
const event = await this._get("/0/buckets/" + bucketId + "/events/" + eventId);
event.timestamp = new Date(event.timestamp);
return event;
}

public async getEvents(bucketId: string, params: { [k: string]: any }): Promise<IEvent[]> {
const events = (await this.req.get("/0/buckets/" + bucketId + "/events", { params })).data;
const events = await this._get("/0/buckets/" + bucketId + "/events", { params });
events.forEach((event: IEvent) => {
event.timestamp = new Date(event.timestamp);
});
Expand All @@ -144,15 +171,15 @@ export class AWClient {
starttime: startTime ? startTime.toISOString() : null,
endtime: endTime ? endTime.toISOString() : null,
};
return this.req.get("/0/buckets/" + bucketId + "/events/count", { params });
return this._get("/0/buckets/" + bucketId + "/events/count", { params });
}

public async insertEvent(bucketId: string, event: IEvent): Promise<void> {
await this.insertEvents(bucketId, [event]);
}

public async insertEvents(bucketId: string, events: IEvent[]): Promise<void> {
await this.req.post("/0/buckets/" + bucketId + "/events", events);
await this._post("/0/buckets/" + bucketId + "/events", events);
}

// Just an alias for insertEvent requiring the event to have an ID assigned
Expand All @@ -164,7 +191,7 @@ export class AWClient {
}

public async deleteEvent(bucketId: string, eventId: number): Promise<void> {
await this.req.delete("/0/buckets/" + bucketId + "/events/" + eventId);
await this._delete("/0/buckets/" + bucketId + "/events/" + eventId);
}

/**
Expand Down Expand Up @@ -203,12 +230,12 @@ export class AWClient {
return typeof tp !== "string" ? `${tp.start.toISOString()}/${tp.end.toISOString()}` : tp;
}),
};
return (await this.req.post("/0/query/", data)).data;
return await this._post("/0/query/", data);
}

private async send_heartbeat(bucketId: string, pulsetime: number, data: IEvent): Promise<IEvent> {
const url = "/0/buckets/" + bucketId + "/heartbeat?pulsetime=" + pulsetime;
const heartbeat = (await this.req.post(url, data)).data;
const heartbeat = await this._post(url, data);
heartbeat.timestamp = new Date(heartbeat.timestamp);
return heartbeat;
}
Expand Down
97 changes: 51 additions & 46 deletions src/test/test.ts
Expand Up @@ -5,12 +5,7 @@ import { AWClient, IEvent } from "../aw-client";
const bucketId = "aw-client-js-test";
const eventType = "test";
const hostname = "unknown";

// Create client
const clientName = "aw-client-js-unittest";
const awc = new AWClient(clientName, {
testing: true,
});

const testevent: IEvent = {
timestamp: new Date(),
Expand All @@ -20,7 +15,12 @@ const testevent: IEvent = {
},
};

describe("aw-client interface", () => {
describe("Basic API usage", () => {
// Create client
const awc = new AWClient(clientName, {
testing: true,
});

before("Delete test bucket", () => {
// Delete bucket if it exists
return awc.deleteBucket(bucketId)
Expand All @@ -45,49 +45,41 @@ describe("aw-client interface", () => {
});

// NOTE: This test will fail in CI until v0.12 is released (with support for 'get event by ID')
it("Post event, get event and assert", () => {
return awc.insertEvent(bucketId, testevent).then((resp) => {
console.log("insertEvent", resp);
return awc.getEvents(bucketId, { limit: 1 });
})
.then((resp) => {
console.log("result from getEvents", resp);
assert.equal(resp.length, 1);
const event: IEvent = resp[0];
console.log("getEvent", event);
return awc.getEvent(bucketId, event.id!);
})
.then((resp) => {
console.log("result from getEvent", resp);
assert.equal(testevent.timestamp.toISOString(), resp.timestamp.toISOString());
assert.equal(testevent.data.label, resp.data.label);
});
it("Post event, get event and assert", async () => {
const eventInserted = await awc.insertEvent(bucketId, testevent);
console.log("insertEvent", eventInserted);

const events = await awc.getEvents(bucketId, { limit: 1 });
console.log("result from getEvents", events);

assert.equal(events.length, 1);
let event: IEvent = events[0];
console.log("getEvent", event);

event = await awc.getEvent(bucketId, event.id!);
console.log("result from getEvent", event);

assert.equal(testevent.timestamp.toISOString(), event.timestamp.toISOString());
assert.equal(testevent.data.label, event.data.label);
});

it("Create, delete and get buckets", () => {
it("Create, delete and get buckets", async () => {
/* Create -> getBucketInfo and verify -> delete -> getBuckets and verify */
return awc.ensureBucket(bucketId, eventType, hostname)
.then(() => awc.getBuckets())
.then((resp) => {
console.log("getBuckets", resp);
assert.equal(true, bucketId in resp);
})
.then(() => {
return awc.getBucketInfo(bucketId);
})
.then((resp) => {
console.log("getBucketInfo", resp);
assert.equal(resp.created instanceof Date, true);
assert.equal(clientName, resp.client);
return awc.deleteBucket(bucketId);
})
.then(() => {
return awc.getBuckets();
})
.then((resp) => {
console.log("getBuckets", resp);
assert.equal(false, bucketId in resp);
});
await awc.ensureBucket(bucketId, eventType, hostname);
let buckets = await awc.getBuckets();

console.log("getBuckets", buckets);
assert.equal(true, bucketId in buckets);
const bucketInfo = await awc.getBucketInfo(bucketId);

console.log("getBucketInfo", bucketInfo);
assert.equal(bucketInfo.created instanceof Date, true);
assert.equal(clientName, bucketInfo.client);

await awc.deleteBucket(bucketId);
buckets = await awc.getBuckets();
console.log("getBuckets", buckets);
assert.equal(false, bucketId in buckets);
});

it("Heartbeat", () => {
Expand Down Expand Up @@ -131,3 +123,16 @@ describe("aw-client interface", () => {
assert.equal(e2.data.label, resp[0][0].data.label);
});
});

describe("API config behavior", () => {
it("can abort requests", () => {
const awc = new AWClient(clientName, {
testing: true,
});
let caught = new Promise((resolve, reject) => {
awc.getInfo().catch(resolve).then(reject);
});
awc.abort();
return caught;
});
});

0 comments on commit c232696

Please sign in to comment.