From da6320a102c9230f76b70067533df3477e8f2951 Mon Sep 17 00:00:00 2001 From: Lawrence Forooghian Date: Tue, 15 Aug 2023 11:45:07 -0300 Subject: [PATCH] Remove Message-related static things in tree-shakable library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the static BaseClient.Message property and the static Message.{fromEncoded, fromEncodedArray} methods. The motivation, and the approach taken for the tree-shakable and non tree-shakable versions of the library, are as in 4323846. Note that instead of fromEncoded and fromEncodedArray, the standalone functions are named decodeMessage and decodeMessages — after some discussion we decided that this naming was more consistent with the kind of naming used for standalone functions in JavaScript libraries. --- scripts/moduleReport.js | 2 +- src/common/lib/client/baseclient.ts | 2 - src/common/lib/client/defaultrealtime.ts | 3 + src/common/lib/client/defaultrest.ts | 3 + src/common/lib/types/defaultmessage.ts | 16 +++++ src/common/lib/types/message.ts | 8 --- src/platform/web/modules.ts | 1 + src/platform/web/modules/message.ts | 13 ++++ test/browser/modules.test.js | 83 +++++++++++++++++++++++- 9 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 src/common/lib/types/defaultmessage.ts create mode 100644 src/platform/web/modules/message.ts diff --git a/scripts/moduleReport.js b/scripts/moduleReport.js index 8c71c3a406..54c50af390 100644 --- a/scripts/moduleReport.js +++ b/scripts/moduleReport.js @@ -4,7 +4,7 @@ const esbuild = require('esbuild'); const moduleNames = ['Rest']; // List of all free-standing functions exported by the library -const functionNames = ['generateRandomKey', 'getDefaultCryptoParams']; +const functionNames = ['generateRandomKey', 'getDefaultCryptoParams', 'decodeMessage', 'decodeMessages']; function formatBytes(bytes) { const kibibytes = bytes / 1024; diff --git a/src/common/lib/client/baseclient.ts b/src/common/lib/client/baseclient.ts index 65e1f92588..b0ebe30787 100644 --- a/src/common/lib/client/baseclient.ts +++ b/src/common/lib/client/baseclient.ts @@ -9,7 +9,6 @@ import { IHttp, RequestParams } from '../../types/http'; import ClientOptions, { NormalisedClientOptions } from '../../types/ClientOptions'; import Platform from '../../platform'; -import Message from '../types/message'; import PresenceMessage from '../types/presencemessage'; import { ModulesMap } from './modulesmap'; import { Rest } from './rest'; @@ -127,7 +126,6 @@ class BaseClient { } static Platform = Platform; - static Message = Message; static PresenceMessage = PresenceMessage; } diff --git a/src/common/lib/client/defaultrealtime.ts b/src/common/lib/client/defaultrealtime.ts index 8c58501b04..1cfbab3ab7 100644 --- a/src/common/lib/client/defaultrealtime.ts +++ b/src/common/lib/client/defaultrealtime.ts @@ -5,6 +5,7 @@ import * as Utils from '../util/utils'; import ConnectionManager from '../transport/connectionmanager'; import ProtocolMessage from '../types/protocolmessage'; import Platform from 'common/platform'; +import { DefaultMessage } from '../types/defaultmessage'; /** `DefaultRealtime` is the class that the non tree-shakable version of the SDK exports as `Realtime`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version. @@ -29,4 +30,6 @@ export class DefaultRealtime extends BaseRealtime { static set Crypto(newValue: typeof Platform.Crypto) { this._Crypto = newValue; } + + static Message = DefaultMessage; } diff --git a/src/common/lib/client/defaultrest.ts b/src/common/lib/client/defaultrest.ts index 4e46527a63..324f6d8756 100644 --- a/src/common/lib/client/defaultrest.ts +++ b/src/common/lib/client/defaultrest.ts @@ -2,6 +2,7 @@ import { BaseRest } from './baserest'; import ClientOptions from '../../types/ClientOptions'; import { allCommonModules } from './modulesmap'; import Platform from 'common/platform'; +import { DefaultMessage } from '../types/defaultmessage'; /** `DefaultRest` is the class that the non tree-shakable version of the SDK exports as `Rest`. It ensures that this version of the SDK includes all of the functionality which is optionally available in the tree-shakable version. @@ -22,4 +23,6 @@ export class DefaultRest extends BaseRest { static set Crypto(newValue: typeof Platform.Crypto) { this._Crypto = newValue; } + + static Message = DefaultMessage; } diff --git a/src/common/lib/types/defaultmessage.ts b/src/common/lib/types/defaultmessage.ts new file mode 100644 index 0000000000..3ef7dab056 --- /dev/null +++ b/src/common/lib/types/defaultmessage.ts @@ -0,0 +1,16 @@ +import Message, { fromEncoded, fromEncodedArray } from './message'; +import * as API from '../../../../ably'; +import Platform from 'common/platform'; + +/** + `DefaultMessage` is the class returned by `DefaultRest` and `DefaultRealtime`’s `Message` static property. It introduces the static methods described in the `MessageStatic` interface of the public API of the non tree-shakable version of the library. + */ +export class DefaultMessage extends Message { + static async fromEncoded(encoded: unknown, inputOptions?: API.Types.ChannelOptions): Promise { + return fromEncoded(Platform.Crypto, encoded, inputOptions); + } + + static async fromEncodedArray(encodedArray: Array, options?: API.Types.ChannelOptions): Promise { + return fromEncodedArray(Platform.Crypto, encodedArray, options); + } +} diff --git a/src/common/lib/types/message.ts b/src/common/lib/types/message.ts index c78c0c401f..35697038b9 100644 --- a/src/common/lib/types/message.ts +++ b/src/common/lib/types/message.ts @@ -363,14 +363,6 @@ class Message { return result; } - static async fromEncoded(encoded: unknown, inputOptions?: API.Types.ChannelOptions): Promise { - return fromEncoded(Platform.Crypto, encoded, inputOptions); - } - - static async fromEncodedArray(encodedArray: Array, options?: API.Types.ChannelOptions): Promise { - return fromEncodedArray(Platform.Crypto, encodedArray, options); - } - /* This should be called on encode()d (and encrypt()d) Messages (as it * assumes the data is a string or buffer) */ static getMessagesSize(messages: Message[]): number { diff --git a/src/platform/web/modules.ts b/src/platform/web/modules.ts index a1865a1fa7..113417ea17 100644 --- a/src/platform/web/modules.ts +++ b/src/platform/web/modules.ts @@ -43,5 +43,6 @@ if (Platform.Config.noUpgrade) { } export * from './modules/crypto'; +export * from './modules/message'; export { Rest } from '../../common/lib/client/rest'; export { BaseRest, BaseRealtime }; diff --git a/src/platform/web/modules/message.ts b/src/platform/web/modules/message.ts new file mode 100644 index 0000000000..b908dce825 --- /dev/null +++ b/src/platform/web/modules/message.ts @@ -0,0 +1,13 @@ +import * as API from '../../../../ably'; +import Platform from 'common/platform'; +import { fromEncoded, fromEncodedArray } from '../../../common/lib/types/message'; + +// The type assertions for the decode* functions below are due to https://github.com/ably/ably-js/issues/1421 + +export const decodeMessage = ((obj, options) => { + return fromEncoded(Platform.Crypto, obj, options); +}) as API.Types.MessageStatic['fromEncoded']; + +export const decodeMessages = ((obj, options) => { + return fromEncodedArray(Platform.Crypto, obj, options); +}) as API.Types.MessageStatic['fromEncodedArray']; diff --git a/test/browser/modules.test.js b/test/browser/modules.test.js index 0a3fd0ad9d..e619bbdf6a 100644 --- a/test/browser/modules.test.js +++ b/test/browser/modules.test.js @@ -1,12 +1,33 @@ -import { BaseRest, BaseRealtime, Rest, generateRandomKey, getDefaultCryptoParams } from '../../build/modules/index.js'; +import { + BaseRest, + BaseRealtime, + Rest, + generateRandomKey, + getDefaultCryptoParams, + decodeMessage, + decodeMessages, +} from '../../build/modules/index.js'; describe('browser/modules', function () { this.timeout(10 * 1000); const expect = chai.expect; + const BufferUtils = BaseRest.Platform.BufferUtils; let ablyClientOptions; + let testResourcesPath; + let loadTestData; + let testMessageEquality; before((done) => { ablyClientOptions = window.ablyHelpers.ablyClientOptions; + testResourcesPath = window.ablyHelpers.testResourcesPath; + testMessageEquality = window.ablyHelpers.testMessageEquality; + + loadTestData = async (dataPath) => { + return new Promise((resolve, reject) => { + window.ablyHelpers.loadTestData(dataPath, (err, testData) => (err ? reject(err) : resolve(testData))); + }); + }; + window.ablyHelpers.setupApp(done); }); @@ -57,4 +78,64 @@ describe('browser/modules', function () { expect(params).to.be.an('object'); }); }); + + describe('Message standalone functions', () => { + describe('decodeMessage', () => { + it('decodes a message’s data', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const item = testData.items[1]; + const decoded = await decodeMessage(item.encoded); + + expect(decoded.data).to.be.an('ArrayBuffer'); + }); + + it('decrypts a message', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + for (const item of testData.items) { + const [decodedFromEncoded, decodedFromEncrypted] = await Promise.all([ + decodeMessage(item.encoded), + decodeMessage(item.encrypted, { cipher: { key, iv } }), + ]); + + testMessageEquality(decodedFromEncoded, decodedFromEncrypted); + } + }); + }); + + describe('decodeMessages', () => { + it('decodes messages’ data', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const items = [testData.items[1], testData.items[3]]; + const decoded = await decodeMessages(items.map((item) => item.encoded)); + + expect(decoded[0].data).to.be.an('ArrayBuffer'); + expect(decoded[1].data).to.be.an('array'); + }); + + it('decrypts messages', async () => { + const testData = await loadTestData(testResourcesPath + 'crypto-data-128.json'); + + const key = BufferUtils.base64Decode(testData.key); + const iv = BufferUtils.base64Decode(testData.iv); + + const [decodedFromEncoded, decodedFromEncrypted] = await Promise.all([ + decodeMessages(testData.items.map((item) => item.encoded)), + decodeMessages( + testData.items.map((item) => item.encrypted), + { cipher: { key, iv } } + ), + ]); + + for (let i = 0; i < decodedFromEncoded.length; i++) { + testMessageEquality(decodedFromEncoded[i], decodedFromEncrypted[i]); + } + }); + }); + }); });