Skip to content

Commit

Permalink
Implement flattened stats API
Browse files Browse the repository at this point in the history
This implements the API changes of spec commit a731d12, and the protocol
version bump from spec commit dfe9476. Documentation taken from
sdk-api-reference commit 570728b.

Resolves #1269.
  • Loading branch information
lawrence-forooghian committed Dec 8, 2023
1 parent 4b3a1c6 commit 4ccce5a
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 463 deletions.
300 changes: 10 additions & 290 deletions src/common/lib/types/stats.ts
Original file line number Diff line number Diff line change
@@ -1,304 +1,24 @@
import * as Utils from '../util/utils';

type MessageValues = {
count?: number;
data?: number;
uncompressedData?: number;
failed?: number;
refused?: number;
category?: Record<string, MessageValues>;
};

type ResourceValues = {
peak?: number;
min?: number;
mean?: number;
opened?: number;
refused?: number;
};

type RequestValues = {
succeeded?: number;
failed?: number;
refused?: number;
};

type ConnectionTypesValues = {
plain?: ResourceValues;
tls?: ResourceValues;
all?: ResourceValues;
};

type MessageTypesValues = {
messages?: MessageValues;
presence?: MessageValues;
all?: MessageValues;
};

type MessageTrafficValues = {
realtime?: MessageTypesValues;
rest?: MessageTypesValues;
webhook?: MessageTypesValues;
sharedQueue?: MessageTypesValues;
externalQueue?: MessageTypesValues;
httpEvent?: MessageTypesValues;
push?: MessageTypesValues;
all?: MessageTypesValues;
};

type MessageDirectionsValues = {
all?: MessageTypesValues;
inbound?: MessageTrafficValues;
outbound?: MessageTrafficValues;
};

type XchgMessagesValues = {
all?: MessageTypesValues;
producerPaid?: MessageDirectionsValues;
consumerPaid?: MessageDirectionsValues;
};

type NotificationsValues = {
invalid?: number;
attempted?: number;
successful?: number;
failed?: number;
};

type PushValues = {
messages?: number;
notifications?: NotificationsValues;
directPublishes?: number;
};

type ProcessedCountValues = {
succeeded?: number;
skipped?: number;
failed?: number;
};

type ProcessedMessagesValues = {
delta?: Record<string, ProcessedCountValues>;
};

type StatsValues = {
all?: MessageTypesValues;
inbound?: MessageTrafficValues;
outbound?: MessageTrafficValues;
persisted?: MessageTypesValues;
connections?: ConnectionTypesValues;
channels?: ResourceValues;
apiRequests?: RequestValues;
tokenRequests?: RequestValues;
xchgProducer?: XchgMessagesValues;
xchgConsumer?: XchgMessagesValues;
pushStats?: PushValues;
processed?: ProcessedMessagesValues;
entries?: Partial<Record<string, number>>;
schema?: string;
appId?: string;
inProgress?: never;
unit?: never;
intervalId?: never;
};

class MessageCount {
count?: number;
data?: number;
uncompressedData?: number;
failed?: number;
refused?: number;

constructor(values?: MessageValues) {
this.count = (values && values.count) || 0;
this.data = (values && values.data) || 0;
this.uncompressedData = (values && values.uncompressedData) || 0;
this.failed = (values && values.failed) || 0;
this.refused = (values && values.refused) || 0;
}
}

class MessageCategory extends MessageCount {
category?: Record<string, MessageCount>;
constructor(values?: MessageValues) {
super(values);
if (values && values.category) {
this.category = {};
Utils.forInOwnNonNullProperties(values.category, (prop: string) => {
(this.category as Record<string, MessageCount>)[prop] = new MessageCount(
(values.category as Record<string, MessageCount>)[prop]
);
});
}
}
}

class ResourceCount {
peak?: number;
min?: number;
mean?: number;
opened?: number;
refused?: number;

constructor(values?: ResourceValues) {
this.peak = (values && values.peak) || 0;
this.min = (values && values.min) || 0;
this.mean = (values && values.mean) || 0;
this.opened = (values && values.opened) || 0;
this.refused = (values && values.refused) || 0;
}
}

class RequestCount {
succeeded?: number;
failed?: number;
refused?: number;

constructor(values?: RequestValues) {
this.succeeded = (values && values.succeeded) || 0;
this.failed = (values && values.failed) || 0;
this.refused = (values && values.refused) || 0;
}
}

class ConnectionTypes {
plain?: ResourceCount;
tls?: ResourceCount;
all?: ResourceCount;

constructor(values?: ConnectionTypesValues) {
this.plain = new ResourceCount(values && values.plain);
this.tls = new ResourceCount(values && values.tls);
this.all = new ResourceCount(values && values.all);
}
}

class MessageTypes {
messages?: MessageCategory;
presence?: MessageCategory;
all?: MessageCategory;

constructor(values?: MessageTypesValues) {
this.messages = new MessageCategory(values && values.messages);
this.presence = new MessageCategory(values && values.presence);
this.all = new MessageCategory(values && values.all);
}
}

class MessageTraffic {
realtime?: MessageTypes;
rest?: MessageTypes;
webhook?: MessageTypes;
sharedQueue?: MessageTypes;
externalQueue?: MessageTypes;
httpEvent?: MessageTypes;
push?: MessageTypes;
all?: MessageTypes;

constructor(values?: MessageTrafficValues) {
this.realtime = new MessageTypes(values && values.realtime);
this.rest = new MessageTypes(values && values.rest);
this.webhook = new MessageTypes(values && values.webhook);
this.sharedQueue = new MessageTypes(values && values.sharedQueue);
this.externalQueue = new MessageTypes(values && values.externalQueue);
this.httpEvent = new MessageTypes(values && values.httpEvent);
this.push = new MessageTypes(values && values.push);
this.all = new MessageTypes(values && values.all);
}
}

class MessageDirections {
all?: MessageTypes;
inbound?: MessageTraffic;
outbound?: MessageTraffic;

constructor(values?: MessageDirectionsValues) {
this.all = new MessageTypes(values && values.all);
this.inbound = new MessageTraffic(values && values.inbound);
this.outbound = new MessageTraffic(values && values.outbound);
}
}

class XchgMessages {
all?: MessageTypes;
producerPaid?: MessageDirections;
consumerPaid?: MessageDirections;

constructor(values?: XchgMessagesValues) {
this.all = new MessageTypes(values && values.all);
this.producerPaid = new MessageDirections(values && values.producerPaid);
this.consumerPaid = new MessageDirections(values && values.consumerPaid);
}
}

class PushStats {
messages?: number;
notifications?: NotificationsValues;
directPublishes?: number;

constructor(values?: PushValues) {
this.messages = (values && values.messages) || 0;
const notifications = values && values.notifications;
this.notifications = {
invalid: (notifications && notifications.invalid) || 0,
attempted: (notifications && notifications.attempted) || 0,
successful: (notifications && notifications.successful) || 0,
failed: (notifications && notifications.failed) || 0,
};
this.directPublishes = (values && values.directPublishes) || 0;
}
}

class ProcessedCount {
succeeded?: number;
skipped?: number;
failed?: number;

constructor(values: ProcessedCountValues) {
this.succeeded = (values && values.succeeded) || 0;
this.skipped = (values && values.skipped) || 0;
this.failed = (values && values.failed) || 0;
}
}

class ProcessedMessages {
delta?: Record<string, ProcessedCount>;

constructor(values?: ProcessedMessagesValues) {
this.delta = undefined;
if (values && values.delta) {
this.delta = {};
Utils.forInOwnNonNullProperties(values.delta, (prop: string) => {
(this.delta as Record<string, ProcessedCount>)[prop] = new ProcessedCount(
(values.delta as Record<string, ProcessedCountValues>)[prop]
);
});
}
}
}

class Stats extends MessageDirections {
persisted?: MessageTypes;
connections?: ConnectionTypes;
channels?: ResourceCount;
apiRequests?: RequestCount;
tokenRequests?: RequestCount;
xchgProducer?: XchgMessages;
xchgConsumer?: XchgMessages;
push?: PushStats;
processed?: ProcessedMessages;
class Stats {
entries?: Partial<Record<string, number>>;
schema?: string;
appId?: string;
inProgress?: never;
unit?: never;
intervalId?: never;

constructor(values?: StatsValues) {
super(values as MessageDirectionsValues);
this.persisted = new MessageTypes(values && values.persisted);
this.connections = new ConnectionTypes(values && values.connections);
this.channels = new ResourceCount(values && values.channels);
this.apiRequests = new RequestCount(values && values.apiRequests);
this.tokenRequests = new RequestCount(values && values.tokenRequests);
this.xchgProducer = new XchgMessages(values && values.xchgProducer);
this.xchgConsumer = new XchgMessages(values && values.xchgConsumer);
this.push = new PushStats(values && values.pushStats);
this.processed = new ProcessedMessages(values && values.processed);
this.entries = (values && values.entries) || undefined;
this.schema = (values && values.schema) || undefined;
this.appId = (values && values.appId) || undefined;
this.inProgress = (values && values.inProgress) || undefined;
this.unit = (values && values.unit) || undefined;
this.intervalId = (values && values.intervalId) || undefined;
Expand Down
2 changes: 1 addition & 1 deletion src/common/lib/util/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const Defaults = {
maxMessageSize: 65536,

version,
protocolVersion: 2,
protocolVersion: 3,
agent,
getHost,
getPort,
Expand Down
2 changes: 1 addition & 1 deletion test/realtime/init.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) {
var transport = realtime.connection.connectionManager.activeProtocol.transport;
var connectUri = helper.isWebsocket(transport) ? transport.uri : transport.recvRequest.uri;
try {
expect(connectUri.indexOf('v=2') > -1, 'Check uri includes v=2').to.be.ok;
expect(connectUri.indexOf('v=3') > -1, 'Check uri includes v=3').to.be.ok;
} catch (err) {
closeAndFinish(done, realtime, err);
return;
Expand Down
2 changes: 1 addition & 1 deletion test/rest/http.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ define(['ably', 'shared_helper', 'chai'], function (Ably, helper, chai) {

// This test should not directly validate version against Defaults.version, as
// ultimately the version header has been derived from that value.
expect(headers['X-Ably-Version']).to.equal('2', 'Verify current version number');
expect(headers['X-Ably-Version']).to.equal('3', 'Verify current version number');
expect(headers['Ably-Agent'].indexOf('ably-js/' + Defaults.version) > -1, 'Verify agent').to.be.ok;
expect(headers['Ably-Agent'].indexOf('custom-agent/0.1.2') > -1, 'Verify custom agent').to.be.ok;

Expand Down
4 changes: 2 additions & 2 deletions test/rest/request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async
restTestOnJsonMsgpack('request_batch_api_success', async function (rest, name) {
var body = { channels: [name + '1', name + '2'], messages: { data: 'foo' } };

const res = await rest.request('POST', '/messages', Defaults.protocolVersion, {}, body, {});
const res = await rest.request('POST', '/messages', 2, {}, body, {});
expect(res.success).to.equal(true, 'Check res.success is true for a success');
expect(res.statusCode).to.equal(201, 'Check res.statusCode is 201 for a success');
expect(res.errorCode).to.equal(null, 'Check res.errorCode is null for a success');
Expand All @@ -145,7 +145,7 @@ define(['ably', 'shared_helper', 'async', 'chai'], function (Ably, helper, async
restTestOnJsonMsgpack.skip('request_batch_api_partial_success', async function (rest, name) {
var body = { channels: [name, '[invalid', ''], messages: { data: 'foo' } };

var res = await rest.request('POST', '/messages', Defaults.protocolVersion, {}, body, {});
var res = await rest.request('POST', '/messages', 2, {}, body, {});
expect(res.success).to.equal(false, 'Check res.success is false for a partial failure');
expect(res.statusCode).to.equal(400, 'Check HPR.statusCode is 400 for a partial failure');
expect(res.errorCode).to.equal(40020, 'Check HPR.errorCode is 40020 for a partial failure');
Expand Down
Loading

0 comments on commit 4ccce5a

Please sign in to comment.