Skip to content
Draft
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
15 changes: 2 additions & 13 deletions packages/compiler/src/lib/paging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import type {
} from "../core/types.js";
import { createStateSymbol } from "../lib/utils.js";
import { DuplicateTracker, useStateSet } from "../utils/index.js";
import { isNumericType, isStringType } from "./decorators.js";
import { isNumericType } from "./decorators.js";

export const [
/**
Expand Down Expand Up @@ -109,18 +109,7 @@ export const [
markContinuationTokenProperty,
/** {@inheritdoc ContinuationTokenDecorator} */
continuationTokenDecorator,
] = createMarkerDecorator<ContinuationTokenDecorator>("continuationToken", (context, target) => {
if (!isStringType(context.program, target.type)) {
reportDiagnostic(context.program, {
code: "decorator-wrong-target",
messageId: "withExpected",
format: { decorator: "continuationToken", expected: "string", to: getTypeName(target.type) },
target: context.decoratorTarget,
});
return false;
}
return true;
});
] = createMarkerDecorator<ContinuationTokenDecorator>("continuationToken");

export const [
/**
Expand Down
77 changes: 77 additions & 0 deletions packages/compiler/test/decorators/paging.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,80 @@ describe("collect nested paging properties", () => {
expect(pathString).toBe("results.items");
});
});

describe("@continuationToken supports nullable and optional properties", () => {
it("accepts nullable string type (string | null)", async () => {
const diagnostics = await runner.diagnose(`
@list op list(): {
@pageItems items: string[];
@continuationToken token: string | null;
};
`);
expectDiagnosticEmpty(diagnostics);
});

it("accepts optional string type", async () => {
const diagnostics = await runner.diagnose(`
@list op list(): {
@pageItems items: string[];
@continuationToken token?: string;
};
`);
expectDiagnosticEmpty(diagnostics);
});

it("accepts nullable optional string type", async () => {
const diagnostics = await runner.diagnose(`
@list op list(): {
@pageItems items: string[];
@continuationToken token?: string | null;
};
`);
expectDiagnosticEmpty(diagnostics);
});

it("accepts non-string types", async () => {
const diagnostics = await runner.diagnose(`
@list op list(): {
@pageItems items: string[];
@continuationToken token: int32;
};
`);
expectDiagnosticEmpty(diagnostics);
});

it("collects nullable continuation token in input", async () => {
const { list, token } = (await runner.compile(`
@list @test op list(
@continuationToken @test token: string | null
): { @pageItems items: string[] };
`)) as { list: Operation; token: ModelProperty };

const paging = ignoreDiagnostics(getPagingOperation(runner.program, list));
expect(paging?.input.continuationToken?.property).toBe(token);
});

it("collects nullable continuation token in output", async () => {
const { list, token } = (await runner.compile(`
@list @test op list(): {
@pageItems items: string[];
@continuationToken @test token: string | null;
};
`)) as { list: Operation; token: ModelProperty };

const paging = ignoreDiagnostics(getPagingOperation(runner.program, list));
expect(paging?.output.continuationToken?.property).toBe(token);
});

it("collects optional continuation token", async () => {
const { list, token } = (await runner.compile(`
@list @test op list(): {
@pageItems items: string[];
@continuationToken @test token?: string;
};
`)) as { list: Operation; token: ModelProperty };

const paging = ignoreDiagnostics(getPagingOperation(runner.program, list));
expect(paging?.output.continuationToken?.property).toBe(token);
});
});
Loading