diff --git a/package.json b/package.json index dd11d23..7cd63e0 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,6 @@ "homepage": "http://ftrack.com", "dependencies": { "loglevel": "^1.9.2", - "moment": "^2.30.1", "uuid": "^11.1.0" }, "peerDependencies": { diff --git a/source/session.ts b/source/session.ts index 69c769a..042951d 100644 --- a/source/session.ts +++ b/source/session.ts @@ -1,5 +1,4 @@ // :copyright: Copyright (c) 2016 ftrack -import moment from "moment"; import loglevel from "loglevel"; import { v4 as uuidV4 } from "uuid"; @@ -41,8 +40,6 @@ import getSchemaMappingFromSchemas from "./util/get_schema_mapping.js"; const logger = loglevel.getLogger("ftrack_api"); -const ENCODE_DATETIME_FORMAT = "YYYY-MM-DDTHH:mm:ss"; - /** * ftrack API session * @class Session @@ -64,7 +61,6 @@ export class Session< serverInformation?: QueryServerInformationResponse; serverVersion?: string; private ensureSerializableResponse: boolean; - private decodeDatesAsIso: boolean; private schemasPromise?: Promise[]>; private serverInformationPromise?: Promise; private serverInformationValues?: string[]; @@ -86,7 +82,6 @@ export class Session< * @param {string} [options.apiEndpoint=/api] - API endpoint. * @param {object} [options.headers] - Additional headers to send with the request * @param {object} [options.strictApi] - Turn on strict API mode - * @param {object} [options.decodeDatesAsIso] - Decode dates as ISO strings instead of moment objects * @param {object} [options.ensureSerializableResponse] - Disable normalization of response data * * @constructs Session @@ -103,7 +98,6 @@ export class Session< apiEndpoint = "/api", additionalHeaders = {}, strictApi = false, - decodeDatesAsIso = false, ensureSerializableResponse = false, }: SessionOptions = {}, ) { @@ -181,15 +175,6 @@ export class Session< this.clientToken = `ftrack-javascript-api--${uuidV4()}`; } - // Always include is_timezone_support_enabled as required by API. - // TODO: Remove this in next major. - if ( - serverInformationValues && - !serverInformationValues.includes("is_timezone_support_enabled") - ) { - serverInformationValues.push("is_timezone_support_enabled"); - } - // TODO: remove these operations from session initialization in next major const operations: [ operation.QueryServerInformationOperation, @@ -202,8 +187,6 @@ export class Session< { action: "query_schemas" }, ]; - this.decodeDatesAsIso = decodeDatesAsIso; - /** * By default the API server will return normalized responses, and we denormalize them in the client. * This might cause cyclical references in the response data, making it non-JSON serializable. @@ -292,7 +275,7 @@ export class Session< /** * Return encoded *data* as JSON string. * - * This will translate date, moment, and dayjs objects into ISO8601 string representation in UTC. + * This will translate date and dayjs objects into ISO8601 string representation in UTC. * * @private * @param {*} data The data to encode. @@ -316,23 +299,9 @@ export class Session< const date = convertToIsoString(data); if (date) { - if ( - this.serverInformation && - this.serverInformation.is_timezone_support_enabled - ) { - // Ensure that the moment object is in UTC and format - // to timezone naive string. - return { - __type__: "datetime", - value: date, - }; - } - - // Ensure that the moment object is in local time zone and format - // to timezone naive string. return { __type__: "datetime", - value: moment(date).local().format(ENCODE_DATETIME_FORMAT), + value: date, }; } @@ -375,7 +344,7 @@ export class Session< * de-duplicated in the back end and point them to a single object in * *identityMap*. * - * datetime objects will be converted to timezone-aware moment objects. + * datetime objects will be converted to timezone-aware dayjs objects. * * @private * @param {*} data The data to decode. @@ -386,33 +355,26 @@ export class Session< data: any, identityMap: Data = {}, { - decodeDatesAsIso = false, ensureSerializableResponse = false, }: { - decodeDatesAsIso?: boolean; ensureSerializableResponse?: boolean; } = {}, ): any { if (Array.isArray(data)) { return this._decodeArray(data, identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }); } if (!!data && typeof data === "object") { if (data.__entity_type__ && !ensureSerializableResponse) { return this._mergeEntity(data, identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }); } - if (data.__type__ === "datetime" && decodeDatesAsIso) { + if (data.__type__ === "datetime") { return this._decodeDateTimeAsIso(data); - } else if (data.__type__ === "datetime") { - return this._decodeDateTimeAsMoment(data); } return this._decodePlainObject(data, identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }); } @@ -422,19 +384,14 @@ export class Session< /** * Decode datetime *data* into ISO 8601 strings. * - * Translate objects with __type__ equal to 'datetime' into moment + * Translate objects with __type__ equal to 'datetime' into dayjs * datetime objects. If time zone support is enabled on the server the date - * will be assumed to be UTC and the moment will be in utc. + * will be assumed to be UTC and the dayjs will be in utc. * @private */ private _decodeDateTimeAsIso(data: any) { let dateValue = data.value; - if ( - this.serverInformation && - this.serverInformation.is_timezone_support_enabled && - !dateValue.endsWith("Z") && - !dateValue.includes("+") - ) { + if (!dateValue.endsWith("Z") && !dateValue.includes("+")) { // Server responds with timezone naive strings, add Z to indicate UTC. // If the string somehow already contains a timezone offset, do not add Z. dateValue += "Z"; @@ -443,27 +400,6 @@ export class Session< return new Date(dateValue).toISOString(); } - /** - * Decode datetime *data* into moment objects. - * - * Translate objects with __type__ equal to 'datetime' into moment - * datetime objects. If time zone support is enabled on the server the date - * will be assumed to be UTC and the moment will be in utc. - * @private - */ - private _decodeDateTimeAsMoment(data: any) { - if ( - this.serverInformation && - this.serverInformation.is_timezone_support_enabled - ) { - // Return date as moment object with UTC set to true. - return moment.utc(data.value); - } - - // Return date as local moment object. - return moment(data.value); - } - /** * Return new object where all values have been decoded. * @private @@ -472,16 +408,13 @@ export class Session< object: Data, identityMap: Data, { - decodeDatesAsIso, ensureSerializableResponse, }: { - decodeDatesAsIso?: boolean; ensureSerializableResponse?: boolean; } = {}, ) { return Object.keys(object).reduce((previous, key) => { previous[key] = this.decode(object[key], identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }); return previous; @@ -496,16 +429,13 @@ export class Session< collection: any[], identityMap: Data, { - decodeDatesAsIso = false, ensureSerializableResponse = false, }: { - decodeDatesAsIso?: boolean; ensureSerializableResponse?: boolean; } = {}, ): any[] { return collection.map((item) => this.decode(item, identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }), ); @@ -519,10 +449,8 @@ export class Session< entity: Data, identityMap: Data, { - decodeDatesAsIso, ensureSerializableResponse, }: { - decodeDatesAsIso?: boolean; ensureSerializableResponse?: boolean; } = {}, ) { @@ -547,7 +475,6 @@ export class Session< for (const key in entity) { if (Object.hasOwn(entity, key)) { mergedEntity[key] = this.decode(entity[key], identityMap, { - decodeDatesAsIso, ensureSerializableResponse, }); } @@ -630,7 +557,6 @@ export class Session< * @param {AbortSignal} options.signal - Abort signal * @param {string} options.pushToken - push token to associate with the request * @param {object} options.headers - Additional headers to send with the request - * @param {string} options.decodeDatesAsIso - Return dates as ISO strings instead of moment objects * */ async call>( @@ -640,7 +566,6 @@ export class Session< pushToken, signal, additionalHeaders = {}, - decodeDatesAsIso = this.decodeDatesAsIso, ensureSerializableResponse = this.ensureSerializableResponse, }: CallOptions = {}, ): Promise extends true ? T : T[]> { @@ -690,11 +615,7 @@ export class Session< throw this.getErrorFromResponse(response); } try { - return this.decode( - response, - {}, - { decodeDatesAsIso, ensureSerializableResponse }, - ); + return this.decode(response, {}, { ensureSerializableResponse }); } catch (reason) { logger.warn("Server reported error in unexpected format. ", reason); throw this.getErrorFromResponse({ @@ -856,7 +777,6 @@ export class Session< * @param {object} options.abortController - Deprecated in favour of options.signal * @param {object} options.signal - Abort signal user for aborting requests prematurely * @param {object} options.headers - Additional headers to send with the request - * @param {object} options.decodeDatesAsIso - Decode dates as ISO strings instead of moment objects * @param {object} options.ensureSerializableResponse - Disable normalization of response data * @return {Promise} Promise which will be resolved with an object * containing action, data and metadata @@ -885,7 +805,6 @@ export class Session< * @param {object} options.abortController - Deprecated in favour of options.signal * @param {object} options.signal - Abort signal user for aborting requests prematurely * @param {object} options.headers - Additional headers to send with the request - * @param {object} options.decodeDatesAsIso - Decode dates as ISO strings instead of moment objects * @param {object} options.ensureSerializableResponse - Disable normalization of response data * @return {Promise} Promise which will be resolved with an object * containing data and metadata @@ -931,7 +850,6 @@ export class Session< * @param {Object} options * @param {string} options.pushToken - push token to associate with the request * @param {object} options.headers - Additional headers to send with the request - * @param {object} options.decodeDatesAsIso - Decode dates as ISO strings instead of moment objects * @param {object} options.ensureSerializableResponse - Disable normalization of response data * @return {Promise} Promise which will be resolved with the response. */ @@ -957,7 +875,6 @@ export class Session< * @param {Object} options * @param {string} options.pushToken - push token to associate with the request * @param {object} options.headers - Additional headers to send with the request - * @param {object} options.decodeDatesAsIso - Decode dates as ISO strings instead of moment objects * @param {object} options.ensureSerializableResponse - Disable normalization of response data * @return {Promise} Promise resolved with the response. */ @@ -983,7 +900,6 @@ export class Session< * @param {Object} options * @param {string} options.pushToken - push token to associate with the request * @param {object} options.headers - Additional headers to send with the request - * @param {object} options.decodeDatesAsIso - Decode dates as ISO strings instead of moment objects * @return {Promise} Promise resolved with the response. */ async delete( diff --git a/source/util/convert_to_iso_string.ts b/source/util/convert_to_iso_string.ts index 81591ef..8e6e9eb 100644 --- a/source/util/convert_to_iso_string.ts +++ b/source/util/convert_to_iso_string.ts @@ -1,4 +1,3 @@ -import type { Moment } from "moment"; import type dayjs from "dayjs"; /** @@ -18,17 +17,17 @@ function isIsoDate(str: string) { /** * Converts a string or date object to ISO 6801 compatible string. - * Supports converting regular date objects, or any object that has toISOString() method such as moment or dayjs. + * Supports converting regular date objects, or any object that has toISOString() method such as dayjs. * * @param data - string or date object * @returns ISO 6801 compatible string, or null if invalid date */ export function convertToIsoString( - data: string | Date | Moment | ReturnType, + data: string | Date | ReturnType, ) { if ( data && - // if this is a date object of type moment or dayjs, or regular date object (all of them has toISOString) + // if this is a date object of type dayjs, or regular date object (all of them has toISOString) ((typeof data !== "string" && typeof data.toISOString === "function") || // if it's a ISO string already (typeof data == "string" && isIsoDate(data))) diff --git a/test/convert_to_iso_string.test.ts b/test/convert_to_iso_string.test.ts index b0bfd3d..39c5867 100644 --- a/test/convert_to_iso_string.test.ts +++ b/test/convert_to_iso_string.test.ts @@ -1,7 +1,6 @@ // :copyright: Copyright (c) 2022 ftrack import { convertToIsoString } from "../source/util/convert_to_iso_string.js"; -import moment from "moment"; import dayjs from "dayjs"; import { describe, it, expect } from "vitest"; @@ -27,13 +26,6 @@ describe("convertToIsoString", () => { expect(converted).toEqual(isoDate); }); - it("should convert moment objects to ISO strings in UTC", () => { - const tzDate = "2023-01-01T01:00:00+01:00"; - const isoDate = "2023-01-01T00:00:00.000Z"; - const converted = convertToIsoString(moment(tzDate)); - expect(converted).toEqual(isoDate); - }); - it("should convert dayjs objects to ISO strings", () => { const tzDate = "2023-01-01T01:00:00+01:00"; const isoDate = "2023-01-01T00:00:00.000Z"; diff --git a/test/session.test.ts b/test/session.test.ts index c0dfb8b..9bd2bf9 100755 --- a/test/session.test.ts +++ b/test/session.test.ts @@ -3,7 +3,7 @@ import { beforeAll, describe, it, expect } from "vitest"; import { v4 as uuidV4 } from "uuid"; import loglevel from "loglevel"; -import moment from "moment"; +import dayjs from "dayjs"; import { ServerPermissionDeniedError, ServerValidationError, @@ -39,7 +39,6 @@ beforeAll(async () => { credentials.apiKey, { autoConnectEventHub: false, - decodeDatesAsIso: false, }, ); await session.initializing; @@ -128,20 +127,9 @@ describe("Session", () => { return expect((await headers).get("ftrack-strict-api")).toEqual("true"); }); - it("Should allow querying with datetimes decoded as moment objects (default)", async () => { - const result = await session.query( - "select name, created_at from Task limit 1", - ); - expect(result.data[0].created_at).toBeInstanceOf(moment); - expect(result.data[0].created_at.toISOString()).toEqual( - "2022-10-10T10:12:09.000Z", - ); - }); - it("Should allow querying with datetimes decoded as ISO objects", async () => { const result = await session.query( "select name, created_at from Task limit 1", - { decodeDatesAsIso: true }, ); expect(result.data[0].created_at).toEqual("2022-10-10T10:12:09.000Z"); }); @@ -150,9 +138,6 @@ describe("Session", () => { credentials.serverUrl, credentials.apiUser, credentials.apiKey, - { - decodeDatesAsIso: true, - }, ); await decodeDatesAsIsoSession.initializing; const result = await decodeDatesAsIsoSession.query( @@ -160,59 +145,6 @@ describe("Session", () => { ); expect(result.data[0].created_at).toEqual("2022-10-10T10:12:09.000Z"); }); - it("Should allow overriding session decodeDatesAsIso when querying", async () => { - const decodeDatesAsIsoSession = new Session( - credentials.serverUrl, - credentials.apiUser, - credentials.apiKey, - { - decodeDatesAsIso: true, - }, - ); - await decodeDatesAsIsoSession.initializing; - const result = await decodeDatesAsIsoSession.query( - "select name, created_at from Task limit 1", - { decodeDatesAsIso: false }, - ); - expect(result.data[0].created_at).toBeInstanceOf(moment); - expect(result.data[0].created_at.toISOString()).toEqual( - "2022-10-10T10:12:09.000Z", - ); - const result2 = await session.query( - "select name, created_at from Task limit 1", - { decodeDatesAsIso: true }, - ); - expect(result2.data[0].created_at).toEqual("2022-10-10T10:12:09.000Z"); - }); - - it("Should allow querying with datetimes decoded as ISO objects with timezone support disabled", async () => { - server.use( - http.post( - "http://ftrack.test/api", - () => { - return HttpResponse.json([ - { ...queryServerInformation, is_timezone_support_enabled: false }, - querySchemas, - ]); - }, - { once: true }, - ), - ); - const timezoneDisabledSession = new Session( - credentials.serverUrl, - credentials.apiUser, - credentials.apiKey, - { - autoConnectEventHub: false, - }, - ); - await timezoneDisabledSession.initializing; - const result = await timezoneDisabledSession.query( - "select name, created_at from Task limit 1", - { decodeDatesAsIso: true }, - ); - expect(result.data[0].created_at).toEqual("2022-10-10T08:12:09.000Z"); - }); it("Should allow adding additional headers on calls", async () => { const headers = new Promise((resolve) => { @@ -513,8 +445,8 @@ describe("Session", () => { .then(done); }); - it.skip("Should support ensure with update moment object as criteria", async (done: any) => { - const now = moment(); + it.skip("Should support ensure with update dayjs object as criteria", async (done: any) => { + const now = dayjs(); const name = uuidV4(); @@ -638,9 +570,7 @@ describe("Session", () => { ); await expect(() => - session.call([{ action: "configure_totp", secret, code }], { - decodeDatesAsIso: true, - }), + session.call([{ action: "configure_totp", secret, code }]), ).rejects.toThrowError("Code must be provided to enable totp."); }); @@ -660,8 +590,8 @@ describe("Session", () => { }); describe("Encoding entities", () => { - it("Should support encoding moment dates", () => { - const now = moment(); + it("Should support encoding dayjs dates", () => { + const now = dayjs(); //@ts-ignore - Otherwise internal method used for testing purposes const output = session.encode([{ foo: now, bar: "baz" }, 12321]); @@ -677,47 +607,6 @@ describe("Encoding entities", () => { 12321, ]); }); - it("Should support encoding moment dates to local timezone if timezone support is disabled", async () => { - const now = moment(); - server.use( - http.post( - "http://ftrack.test/api", - () => { - return HttpResponse.json([ - { ...queryServerInformation, is_timezone_support_enabled: false }, - querySchemas, - ]); - }, - { once: true }, - ), - ); - const timezoneDisabledSession = new Session( - credentials.serverUrl, - credentials.apiUser, - credentials.apiKey, - { - autoConnectEventHub: false, - }, - ); - await timezoneDisabledSession.initializing; - - //@ts-ignore - Otherwise internal method used for testing purposes - const output = timezoneDisabledSession.encode([ - { foo: now, bar: "baz" }, - 12321, - ]); - - expect(output).toEqual([ - { - foo: { - __type__: "datetime", - value: now.local().format("YYYY-MM-DDTHH:mm:ss"), - }, - bar: "baz", - }, - 12321, - ]); - }); describe("Decoding entities", () => { it("Should support merging 0-level nested data", async () => { @@ -834,20 +723,6 @@ describe("Encoding entities", () => { expect(data[2].status.state.short).toEqual("DONE"); }); - it("Should support decoding datetime as moment (default)", () => { - const now = moment(); - - //@ts-ignore - Otherwise internal method used for testing purposes - const output = session.decode({ - foo: { - __type__: "datetime", - value: now.toISOString(), - }, - }); - expect(output.foo).toBeInstanceOf(moment); - expect(output.foo.toISOString()).toEqual(now.toISOString()); - }); - it("Should support decoding datetime as ISO string", () => { const now = new Date(); @@ -860,7 +735,6 @@ describe("Encoding entities", () => { }, }, {}, - { decodeDatesAsIso: true }, ); expect(output.foo).toEqual(now.toISOString()); }); diff --git a/vite.config.ts b/vite.config.ts index d99bc8f..3609e72 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -15,11 +15,10 @@ export default defineConfig({ rollupOptions: { // make sure to externalize deps that shouldn't be bundled // into your library - external: ["moment", "uuid", "loglevel", "isomorphic-ws"], + external: ["uuid", "loglevel", "isomorphic-ws"], output: { globals: { "ftrack-javascript-api": "ftrack", - moment: "moment", uuid: "uuid", loglevel: "log", "isomorphic-ws": "WebSocket", diff --git a/yarn.lock b/yarn.lock index 4fb3d91..391c997 100644 --- a/yarn.lock +++ b/yarn.lock @@ -403,7 +403,6 @@ __metadata: jsdom: "npm:^26.0.0" lint-staged: "npm:^15.4.3" loglevel: "npm:^1.9.2" - moment: "npm:^2.30.1" msw: "npm:^2.7.3" pinst: "npm:^3.0.0" prettier: "npm:^3.5.3" @@ -3356,13 +3355,6 @@ __metadata: languageName: node linkType: hard -"moment@npm:^2.30.1": - version: 2.30.1 - resolution: "moment@npm:2.30.1" - checksum: 10/ae42d876d4ec831ef66110bdc302c0657c664991e45cf2afffc4b0f6cd6d251dde11375c982a5c0564ccc0fa593fc564576ddceb8c8845e87c15f58aa6baca69 - languageName: node - linkType: hard - "ms@npm:^2.0.0, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3"