Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion source/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import type {
SessionOptions,
UpdateResponse,
} from "./types.js";
import { convertToIsoString } from "./util/convert_to_iso_string.js";
import {
convertToIsoString,
isDateOnly,
} from "./util/convert_to_iso_string.js";
import { Uploader } from "./uploader.js";
import getSchemaMappingFromSchemas from "./util/get_schema_mapping.js";

Expand Down Expand Up @@ -374,6 +377,9 @@ export class Session<
if (data.__type__ === "datetime") {
return this._decodeDateTimeAsIso(data);
}
if (data.__type__ === "date") {
return this._decodeDateAsString(data);
}
return this._decodePlainObject(data, identityMap, {
ensureSerializableResponse,
});
Expand All @@ -400,6 +406,24 @@ export class Session<
return new Date(dateValue).toISOString();
}

/**
* Decode date *data* into date-only strings (YYYY-MM-DD).
*
* Translate objects with __type__ equal to 'date' into date-only strings
* without time information. Validates the format is YYYY-MM-DD.
* @private
*/
private _decodeDateAsString(data: any) {
const dateValue = data.value;
// Validate that the server sends date-only format (YYYY-MM-DD)
if (!isDateOnly(dateValue)) {
throw new Error(
`Invalid date format: expected YYYY-MM-DD, got ${dateValue}`,
);
}
return dateValue;
}

/**
* Return new object where all values have been decoded.
* @private
Expand Down
11 changes: 11 additions & 0 deletions source/util/convert_to_iso_string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ function isIsoDate(str: string) {
);
}

/**
* Checks if string is in date-only format (YYYY-MM-DD).
* Used for __date__ type that doesn't include time information.
*
* Matches:
* - YYYY-MM-DD
*/
export function isDateOnly(str: string) {
return /^\d{4}-\d\d-\d\d$/i.test(str);
}

/**
* 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 dayjs.
Expand Down
23 changes: 22 additions & 1 deletion test/convert_to_iso_string.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// :copyright: Copyright (c) 2022 ftrack

import { convertToIsoString } from "../source/util/convert_to_iso_string.js";
import {
convertToIsoString,
isDateOnly,
} from "../source/util/convert_to_iso_string.js";
import dayjs from "dayjs";
import { describe, it, expect } from "vitest";

Expand Down Expand Up @@ -76,3 +79,21 @@ describe("convertToIsoString", () => {
expect(converted).toEqual(null);
});
});

describe("isDateOnly", () => {
it("should validate correct date-only format", () => {
expect(isDateOnly("2025-12-01")).toBe(true);
expect(isDateOnly("2023-01-15")).toBe(true);
expect(isDateOnly("1999-12-31")).toBe(true);
});

it("should reject invalid date-only formats", () => {
expect(isDateOnly("2025-12-01T00:00:00Z")).toBe(false);
expect(isDateOnly("2025-12-01T00:00:00")).toBe(false);
expect(isDateOnly("not-a-date")).toBe(false);
expect(isDateOnly("2025-12")).toBe(false);
expect(isDateOnly("2025")).toBe(false);
expect(isDateOnly("12-01-2025")).toBe(false);
expect(isDateOnly("")).toBe(false);
});
});
69 changes: 69 additions & 0 deletions test/session.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@
await expect(promise).resolves.toEqual(true);
});

it.skip("Should support ensure with create", async () => {

Check warning on line 371 in test/session.test.ts

View workflow job for this annotation

GitHub Actions / build

Disabled test - if you want to skip a test temporarily, use .todo() instead
const identifyingKeys = [
"key" as const,
"parent_id" as const,
Expand Down Expand Up @@ -398,7 +398,7 @@
expect(ensuredData.parent_type).toEqual("User");
});

it.skip("Should support ensure with update", async (done: any) => {

Check warning on line 401 in test/session.test.ts

View workflow job for this annotation

GitHub Actions / build

Disabled test - if you want to skip a test temporarily, use .todo() instead
const identifyingKeys = ["key", "parent_id", "parent_type"];
const key = uuidV4();

Expand Down Expand Up @@ -445,7 +445,7 @@
.then(done);
});

it.skip("Should support ensure with update dayjs object as criteria", async (done: any) => {

Check warning on line 448 in test/session.test.ts

View workflow job for this annotation

GitHub Actions / build

Disabled test - if you want to skip a test temporarily, use .todo() instead
const now = dayjs();

const name = uuidV4();
Expand Down Expand Up @@ -738,6 +738,75 @@
);
expect(output.foo).toEqual(now.toISOString());
});

it("Should support decoding date as date-only string", () => {
//@ts-ignore - Otherwise internal method used for testing purposes
const output = session.decode(
{
foo: {
__type__: "date",
value: "2025-12-01",
},
},
{},
);
expect(output.foo).toEqual("2025-12-01");
});

it("Should handle date type in nested objects", () => {
//@ts-ignore - Otherwise internal method used for testing purposes
const output = session.decode(
{
task: {
name: "Test Task",
start_date: {
__type__: "date",
value: "2025-12-01",
},
end_date: {
__type__: "date",
value: "2025-12-31",
},
},
},
{},
);
expect(output.task.start_date).toEqual("2025-12-01");
expect(output.task.end_date).toEqual("2025-12-31");
});

it("Should handle arrays of date types", () => {
//@ts-ignore - Otherwise internal method used for testing purposes
const output = session.decode(
[
{
__type__: "date",
value: "2025-12-01",
},
{
__type__: "date",
value: "2025-12-15",
},
],
{},
);
expect(output).toEqual(["2025-12-01", "2025-12-15"]);
});

it("Should throw error for invalid date format", () => {
expect(() => {
//@ts-ignore - Otherwise internal method used for testing purposes
session.decode(
{
foo: {
__type__: "date",
value: "2025-12-01T00:00:00Z",
},
},
{},
);
}).toThrow("Invalid date format: expected YYYY-MM-DD");
});
});

it("Should support encoding Date object dates", () => {
Expand Down
Loading