diff --git a/.changeset/calm-berries-check.md b/.changeset/calm-berries-check.md new file mode 100644 index 0000000..5966980 --- /dev/null +++ b/.changeset/calm-berries-check.md @@ -0,0 +1,5 @@ +--- +'@mermaidchart/sdk': patch +--- + +Add a suggestPrSummary method to the SDK that generates PR details based on Mermaid diagram changes, including the branch name, title, commit message, and description. diff --git a/packages/sdk/src/index.e2e.test.ts b/packages/sdk/src/index.e2e.test.ts index 65c64c7..99592e1 100644 --- a/packages/sdk/src/index.e2e.test.ts +++ b/packages/sdk/src/index.e2e.test.ts @@ -242,3 +242,28 @@ describe('repairDiagram', () => { } }, 60000); // 60 seconds timeout for AI repair operations }); + +describe('suggestPrSummary', () => { + it('should generate PR summary from diagram differences', async () => { + const originalDiagram = `flowchart TD\n A[Start] --> B[Process]\n B --> C[End]`; + const editedDiagram = `flowchart TD\n A[Start] --> B[Process]\n B --> D[Validate]\n D --> C[End]`; + + try { + const result = await client.suggestPrSummary({ + originalDiagram, + editedDiagram, + }); + + // Verify response structure + expect(result).toHaveProperty('title'); + expect(result).toHaveProperty('description'); + expect(result).toHaveProperty('branchName'); + expect(result).toHaveProperty('commitMessage'); + } catch (error) { + if (error instanceof AICreditsLimitExceededError) { + return; // Credits exceeded is acceptable for E2E test + } + throw error; + } + }, 60000); // 60 seconds timeout for AI operations +}); diff --git a/packages/sdk/src/index.test.ts b/packages/sdk/src/index.test.ts index fc89cb8..a3d9b3b 100644 --- a/packages/sdk/src/index.test.ts +++ b/packages/sdk/src/index.test.ts @@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { MermaidChart } from './index.js'; import { AICreditsLimitExceededError } from './errors.js'; import type { AuthorizationData } from './types.js'; +import { URLS } from './urls.js'; import { OAuth2Client } from '@badgateway/oauth2-client'; @@ -171,4 +172,52 @@ describe('MermaidChart', () => { ).rejects.toThrow(AICreditsLimitExceededError); }); }); + + describe('#suggestPrSummary', () => { + beforeEach(async () => { + await client.setAccessToken('test-access-token'); + }); + + it('should POST to the pr-summary endpoint with the request body and return response.data', async () => { + const jsonResponse = { + title: 'Add validation step to flowchart', + description: '## What changed\n- Added node C', + branchName: 'feature/flowchart-validation', + commitMessage: 'Add validation node C', + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const postSpy = vi.spyOn((client as any).axios, 'post').mockResolvedValue({ + data: jsonResponse, + }); + + const requestBody = { + originalDiagram: 'flowchart TD\n A --> B', + editedDiagram: 'flowchart TD\n A --> B\n B --> C[Validate]', + }; + + const result = await client.suggestPrSummary(requestBody); + + expect(postSpy).toHaveBeenCalledWith(URLS.rest.openai.prSummary, requestBody); + expect(result).toEqual(jsonResponse); + }); + + it('should throw AICreditsLimitExceededError on 402', async () => { + // Mock the underlying axios call so the error mapping in suggestPrSummary is exercised. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + vi.spyOn((client as any).axios, 'post').mockRejectedValue({ + response: { + status: 402, + data: 'AI credits limit exceeded', + }, + }); + + await expect( + client.suggestPrSummary({ + originalDiagram: 'flowchart TD\n A --> B', + editedDiagram: 'flowchart TD\n A --> B\n B --> C', + }), + ).rejects.toThrow(AICreditsLimitExceededError); + }); + }); }); diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index aeb4802..4ffe783 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -19,6 +19,8 @@ import type { MCUser, RepairDiagramRequest, RepairDiagramResponse, + PrSummaryRequest, + PrSummaryResponse, AICreditsUsage, } from './types.js'; import { URLS } from './urls.js'; @@ -330,6 +332,25 @@ export class MermaidChart { } } + /** + * Suggests a pull request title and body (markdown) from a before/after diagram diff (Mermaid AI). + * The response also includes `branchName` and `commitMessage`. + * + * @param request - `originalDiagram` and `editedDiagram` only + * @throws {@link AICreditsLimitExceededError} if credits limit exceeded (HTTP 402) + */ + public async suggestPrSummary(request: PrSummaryRequest): Promise { + try { + const response = await this.axios.post( + URLS.rest.openai.prSummary, + request, + ); + return response.data; + } catch (error: unknown) { + throwIfAICreditsExceeded(error); + } + } + /** * Chat with Mermaid AI about a diagram. * diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 8cf930f..029733b 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -96,6 +96,24 @@ export interface RepairDiagramRequest { userID?: string; } +/** + * Only the two diagram versions are sent; the server derives user plan and usage from the auth context provided by the access token. + */ +export interface PrSummaryRequest { + originalDiagram: string; + editedDiagram: string; +} + +/** + * Public response: suggested PR title and markdown description, branch name, and commit message. + */ +export interface PrSummaryResponse { + title: string; + description: string; + branchName: string; + commitMessage: string; +} + /** * Request parameters for chatting with the Mermaid AI about a diagram. */ diff --git a/packages/sdk/src/urls.ts b/packages/sdk/src/urls.ts index a074a18..54d53f3 100644 --- a/packages/sdk/src/urls.ts +++ b/packages/sdk/src/urls.ts @@ -41,6 +41,7 @@ export const URLS = { }, openai: { repair: `/rest-api/openai/repair`, + prSummary: `/rest-api/openai/pr-summary`, chat: `/rest-api/openai/chat`, }, },