Skip to content

New Typescript SDK with support for CRUDs, Application Metadata and more#54

Merged
marioserrano09 merged 5 commits intomainfrom
feature/typescript-basic-sdk
Mar 9, 2026
Merged

New Typescript SDK with support for CRUDs, Application Metadata and more#54
marioserrano09 merged 5 commits intomainfrom
feature/typescript-basic-sdk

Conversation

@marioserrano09
Copy link
Contributor

No description provided.

Copilot AI review requested due to automatic review settings March 9, 2026 05:28
@marioserrano09 marioserrano09 merged commit f2a9054 into main Mar 9, 2026
3 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new TypeScript SDK package for Dynamia Platform REST APIs (CRUD, metadata, actions, reports, files, SaaS, schedules), sets up a pnpm workspace for publishing, and adds an Astro-based demo client that consumes the SDK. It also includes a backend tweak to allow request filters to fall back to bean-property reflection when a view descriptor field is missing.

Changes:

  • Added @dynamia-tools/sdk with typed API clients, shared HTTP/error handling, build tooling (Vite + dts), and Vitest tests.
  • Added platform/packages pnpm workspace configuration and an NPM publish GitHub Action.
  • Added examples/demo-client-books (Astro SSR demo) and updated backend request-filter handling.

Reviewed changes

Copilot reviewed 43 out of 45 changed files in this pull request and generated 22 comments.

Show a summary per file
File Description
platform/packages/tsconfig.base.json Adds shared TS compiler defaults for packages.
platform/packages/sdk/vitest.config.ts Configures Vitest and coverage for the SDK.
platform/packages/sdk/vite.config.ts Configures Vite library build and type generation.
platform/packages/sdk/tsconfig.json SDK TS config for dev/test typechecking.
platform/packages/sdk/tsconfig.build.json SDK TS config for emitting declarations/build output.
platform/packages/sdk/test/client.test.ts Adds unit tests covering client construction, CRUD, auth headers, and error handling.
platform/packages/sdk/src/types.ts Defines exported SDK types (metadata, actions, CRUD envelopes/results, etc.).
platform/packages/sdk/src/index.ts Defines the public SDK entrypoint exports.
platform/packages/sdk/src/http.ts Implements fetch wrapper with auth headers and response parsing.
platform/packages/sdk/src/errors.ts Adds SDK error type for non-2xx responses.
platform/packages/sdk/src/client.ts Adds root DynamiaClient composing sub-APIs.
platform/packages/sdk/src/api/schedule.ts Adds schedule endpoints wrapper.
platform/packages/sdk/src/api/saas.ts Adds SaaS account endpoint wrapper.
platform/packages/sdk/src/api/reports.ts Adds reports endpoints wrapper.
platform/packages/sdk/src/api/metadata.ts Adds application metadata endpoints wrapper.
platform/packages/sdk/src/api/index.ts Re-exports API classes for advanced usage.
platform/packages/sdk/src/api/files.ts Adds file download and URL helpers.
platform/packages/sdk/src/api/crud.ts Adds navigation CRUD resource wrapper and response normalization.
platform/packages/sdk/src/api/crud-service.ts Adds class-name-based CRUD service wrapper.
platform/packages/sdk/src/api/actions.ts Adds global/entity action execution wrapper.
platform/packages/sdk/package.json Defines SDK package metadata, exports map, scripts, and dev deps.
platform/packages/sdk/README.md Adds SDK documentation and usage examples.
platform/packages/pnpm-workspace.yaml Declares pnpm workspace packages.
platform/packages/pnpm-lock.yaml Locks dependencies for the packages workspace.
platform/packages/package.json Adds workspace root package with shared tooling scripts/deps.
platform/packages/README.md Documents the packages workspace and intended package set.
platform/packages/.npmrc Sets NPM publish defaults (registry/access).
platform/core/web/src/main/java/tools/dynamia/web/navigation/RestNavigationQuerySupport.java Adds reflective fallback for request filters when descriptor field is absent.
examples/demo-client-books/tsconfig.json Configures TS for the Astro demo.
examples/demo-client-books/src/pages/index.astro Adds demo homepage using SDK-driven CRUD queries.
examples/demo-client-books/src/pages/categories/index.astro Adds categories listing page.
examples/demo-client-books/src/pages/books/index.astro Adds books listing with filters/pagination.
examples/demo-client-books/src/pages/books/[id].astro Adds book detail page and error handling.
examples/demo-client-books/src/lib/types.ts Adds demo domain types.
examples/demo-client-books/src/lib/client.ts Creates a demo DynamiaClient instance from env config.
examples/demo-client-books/src/lib/api.ts Adds demo data-access helpers on top of the SDK.
examples/demo-client-books/src/layouts/Layout.astro Adds shared layout and styling for the demo.
examples/demo-client-books/package.json Adds Astro demo package config and local SDK dependency.
examples/demo-client-books/astro.config.mjs Configures Astro SSR node adapter.
examples/demo-client-books/README.md Documents how to run and what the demo includes.
examples/demo-client-books/.gitignore Ignores build artifacts and env files.
examples/demo-client-books/.env.example Provides example backend URL env var.
.github/workflows/release.yml Adjusts release trigger type.
.github/workflows/publish-npm.yml Adds workflow to build/test/publish packages on release publish.
Files not reviewed (1)
  • platform/packages/pnpm-lock.yaml: Language not supported

Comment on lines 118 to +129
Field field = descriptor.getField(paramName);
if (field == null) {
return;
try {
var property = ObjectOperations.getPropertyInfo(descriptor.getBeanClass(), paramName);
if (property != null) {
field = new Field(paramName, property.getType());
} else {
return;
}
}catch (Exception e) {
return;
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes request filters apply to any bean property (via reflection) even when the field is not present in the ViewDescriptor. That’s a behavior change from the documented/previous contract (“fields not present in the descriptor are ignored”) and may allow filtering on properties that weren’t intended to be queryable. Consider keeping the descriptor as the allowlist (or gating the reflective fallback behind an explicit opt-in), and update the Javadoc accordingly.

Copilot uses AI. Check for mistakes.
Comment on lines +157 to +166
export interface CrudRawResponse<T = unknown> {
/** The page records */
data: T[];
/**
* Pagination metadata. `null` when the response is not paginated
* (`@JsonInclude(JsonInclude.Include.NON_NULL)` in Java — field may be absent from JSON).
*/
pageable: CrudPageable | null;
/** Status string, typically `"OK"` */
response: string;
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CrudRawResponse.pageable is documented as potentially absent from JSON (due to @JsonInclude(NON_NULL)), but the type requires the property to be present (pageable: CrudPageable | null). To match real payloads and avoid forcing consumers to fabricate pageable, consider making it optional (pageable?: CrudPageable | null).

Copilot uses AI. Check for mistakes.
Comment on lines +25 to +30
return this.http.get<T>(`${this.basePath}/${id}`);
}

/** DELETE /crud-service/{className}/{id} — Delete by ID */
delete(id: string | number): Promise<void> {
return this.http.delete<void>(`${this.basePath}/${id}`);
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findById() interpolates id into the URL without encoding. If IDs can be strings (as your signature allows), reserved URL characters can break routing. Encode the id segment (e.g. with encodeURIComponent).

Suggested change
return this.http.get<T>(`${this.basePath}/${id}`);
}
/** DELETE /crud-service/{className}/{id} — Delete by ID */
delete(id: string | number): Promise<void> {
return this.http.delete<void>(`${this.basePath}/${id}`);
return this.http.get<T>(`${this.basePath}/${encodeURIComponent(String(id))}`);
}
/** DELETE /crud-service/{className}/{id} — Delete by ID */
delete(id: string | number): Promise<void> {
return this.http.delete<void>(`${this.basePath}/${encodeURIComponent(String(id))}`);

Copilot uses AI. Check for mistakes.
const PAGE_SIZE = 6;

const [booksResult, categoriesResult] = await Promise.all([
getBooks({ page, pageSize: PAGE_SIZE, ...(categoryId ? { 'category.id': categoryId } : {}) }),
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The REST navigation API expects the pagination parameter to be size (see RestNavigationReadOperation), but this page passes pageSize. As a result pagination won't be applied server-side. Use size: PAGE_SIZE instead of pageSize: PAGE_SIZE when calling getBooks.

Suggested change
getBooks({ page, pageSize: PAGE_SIZE, ...(categoryId ? { 'category.id': categoryId } : {}) }),
getBooks({ page, size: PAGE_SIZE, ...(categoryId ? { 'category.id': categoryId } : {}) }),

Copilot uses AI. Check for mistakes.

export async function getBookReviews(bookId: number | string): Promise<BookReview[]> {
const result = await client.crud<BookReview>('library/books')
.findAll({ 'book.id': bookId, pageSize: 50 })
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findAll({ 'book.id': bookId, pageSize: 50 }) uses pageSize, but the backend expects the pagination param to be size. With pageSize, the server will likely ignore the requested size and use its default. Use size: 50 here.

Suggested change
.findAll({ 'book.id': bookId, pageSize: 50 })
.findAll({ 'book.id': bookId, size: 50 })

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +45
/** PUT /api/{path}/{id} — Full update */
update(id: string | number, entity: Partial<T>): Promise<T> {
return this.http.put<T>(`${this.basePath}/${id}`, entity);
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update() interpolates id into the URL without encoding, which can break requests for string IDs containing reserved URL characters. Encode the id path segment before concatenating.

Copilot uses AI. Check for mistakes.
Comment on lines +47 to +50
/** DELETE /api/{path}/{id} — Delete */
delete(id: string | number): Promise<void> {
return this.http.delete<void>(`${this.basePath}/${id}`);
}
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete() interpolates id into the URL without encoding. For non-numeric/string IDs this can produce an invalid path or unintended routing. Encode the id path segment when constructing the URL.

Copilot uses AI. Check for mistakes.
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exports.require.types points to ./dist/index.d.cts, but the build (vite-plugin-dts/tsc) typically emits index.d.ts and may not generate .d.cts. If index.d.cts isn't produced, CommonJS consumers will fail to resolve types. Either generate the .d.cts file during build or point both import and require types to ./dist/index.d.ts.

Suggested change
"types": "./dist/index.d.cts",
"types": "./dist/index.d.ts",

Copilot uses AI. Check for mistakes.
const [booksResult, categoriesResult] = await Promise.all([
getBooks({
page,
pageSize: PAGE_SIZE,
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The REST navigation API uses page + size for pagination. This page sends pageSize, so the backend will fall back to its default page size and ignore your intended PAGE_SIZE. Switch to size: PAGE_SIZE in the getBooks(...) call.

Suggested change
pageSize: PAGE_SIZE,
size: PAGE_SIZE,

Copilot uses AI. Check for mistakes.
node-version: '24'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: framework/platform/packages/pnpm-lock.yaml
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache-dependency-path points to framework/platform/packages/pnpm-lock.yaml, but this repo's lockfile is at platform/packages/pnpm-lock.yaml (no framework/ directory). This will disable pnpm caching and may slow down the workflow; update the path to the correct lockfile location.

Suggested change
cache-dependency-path: framework/platform/packages/pnpm-lock.yaml
cache-dependency-path: platform/packages/pnpm-lock.yaml

Copilot uses AI. Check for mistakes.
@marioserrano09 marioserrano09 deleted the feature/typescript-basic-sdk branch March 12, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants