From 8341bce6bbeb9e6605eaf569badc36cb40a41900 Mon Sep 17 00:00:00 2001 From: Rom Date: Wed, 30 Oct 2024 21:18:14 +0200 Subject: [PATCH] breaking change version - new request types --- package-lock.json | 14 ++-- packages/api/README.md | 99 +++++++++++++++++++++++-- packages/api/lib/api-client.ts | 60 +++++++++++++-- packages/api/lib/seamless-api-client.ts | 2 +- packages/api/package.json | 3 +- packages/api/tests/api-client.test.ts | 2 +- packages/setup-api/README.md | 8 +- packages/setup-api/package.json | 2 +- 8 files changed, 160 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 964dc71..c052944 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3247,10 +3247,9 @@ "dev": true }, "node_modules/graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", - "peer": true, + "version": "16.8.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.2.tgz", + "integrity": "sha512-cvVIBILwuoSyD54U4cF/UXDh5yAobhNV/tPygI4lZhgOIJQE/WLWC4waBRb4I6bDVYb3OVx3lfHbaQOEoUD5sg==", "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -5959,9 +5958,10 @@ }, "packages/api": { "name": "@mondaydotcomorg/api", - "version": "4.0.4", + "version": "6.0.0", "license": "MIT", "dependencies": { + "graphql": "16.8.2", "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6" }, @@ -5987,7 +5987,7 @@ }, "packages/api-types": { "name": "@mondaydotcomorg/api-types", - "version": "4.0.0", + "version": "5.0.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.5.12", @@ -6046,7 +6046,7 @@ }, "packages/setup-api": { "name": "@mondaydotcomorg/setup-api", - "version": "1.1.2", + "version": "1.2.2", "license": "MIT", "dependencies": { "shelljs": "^0.8.5" diff --git a/packages/api/README.md b/packages/api/README.md index 2609233..13e7169 100644 --- a/packages/api/README.md +++ b/packages/api/README.md @@ -21,7 +21,7 @@ npm install @mondaydotcomorg/api All exported types correspond to the current version of the API that existed when the NPM package was released -For the conviniecne of monday app developers, this CLI is included in the [@mondaydotcomorg/apps-cli](https://www.npmjs.com/package/@mondaycom/apps-cli). +For the convenience of monday app developers, this CLI is included in the [@mondaydotcomorg/apps-cli](https://www.npmjs.com/package/@mondaycom/apps-cli). If you want to use it on it’s own, you can install [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api). (you can find more about app development here [monday-apps-sdk](https://developer.monday.com/apps/docs/introduction-to-the-sdk)) @@ -34,7 +34,7 @@ The package exports the class `ApiClient` which is the main entry point to the S ```typescript import { ApiClient } from '@mondaydotcomorg/api'; -const client = new ApiClient(''); +const client = new ApiClient({token: ''}); // Or use the operations provided by the SDK const me = await client.operations.getMeOp(); @@ -56,10 +56,10 @@ const changeStatusColumn = await client.operations.changeColumnValueOp({ }); // Use the client to query monday's API freestyle WITHOUT TYPES -> Use @mondaydotcomorg/setup-api to setup typed project! -const boards = await client.query<{boards: [{ name: string }]}>(`query { boards(ids: some_id) { name } }`); +const boards = await client.request<{boards: [{ name: string }]}>(`query { boards(ids: some_id) { name } }`); // You can also use the types provided by the sdk -const { boards } = await client.query<{ +const { boards } = await client.request<{ boards: [Board]; }>(`query { boards(ids: some_id) { name } }`); ``` @@ -78,17 +78,102 @@ const user: User = { } ``` +### Configuration + +#### ErrorPolicy + +By default GraphQLClient will throw when an error is received. However, sometimes you still want to resolve the (partial) data you received. You can define errorPolicy in the GraphQLClient constructor. + +```typescript +const client = new ApiClient({token: '', requestConfig: { errorPolicy: 'all' }}); +``` + +**None (default)** +Allow no errors at all. If you receive a GraphQL error the client will throw. + +**Ignore** +Ignore incoming errors and resolve like no errors occurred + +**All** +Return both the errors and data, only works with the client's rawRequest call option. + +### Handling errors + +The errors are returned from the `ClientError` type. +**The example below is leveraging types using [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api).** + +```typescript +import { ApiClient, ClientError } from "@mondaydotcomorg/api"; +import { GetBoardsQuery, GetBoardsQueryVariables } from "./generated/graphql"; +import { exampleQuery } from "./queries.graphql"; + +async function getBoardDetails(): Promise { + try { + const token = ""; + const client = new ApiClient({ token }); + const queryVariables: GetBoardsQueryVariables = { ids: ["5901934630"] }; + const queryData = await client.request( + exampleQuery, + queryVariables + ); + + console.log(queryData.boards); + } catch (error) { + if (error instanceof ClientError) { + console.error(error.response.errors); + } else { + console.error(error); + } + } +} + +getBoardDetails(); +``` + +### Other request options + +If you prefer the 'old' style of response (data, errors, extensions) you can call the api using the rawRequest option +**The example below is leveraging types using [@mondaydotcomorg/setup-api](https://www.npmjs.com/package/@mondaydotcomorg/setup-api).** + +```typescript +import { ApiClient, ClientError } from "@mondaydotcomorg/api"; +import { GetBoardsQuery, GetBoardsQueryVariables } from "./generated/graphql"; +import { exampleQuery } from "./queries.graphql"; + +async function getBoardDetails(): Promise { + try { + const token = ""; + const client = new ApiClient({ token }); + const queryVariables: GetBoardsQueryVariables = { ids: ["5901934630"] }; + const queryData = await client.rawRequest( + exampleQuery, + queryVariables + ); + + console.log(queryData.data.boards); + } catch (error) { + if (error instanceof ClientError) { + console.error(error.response.errors); + } else { + console.error(error); + } + } +} + +getBoardDetails(); +``` + ## SeamlessApiClient The SeamlessApiClient class is a tool designed for making seamless API requests to Monday.com, tailored for use within the client side of applications deployed on the platform. -Basically, when you are making an api call from the client side of an app deployed on Monday.com, you dont need to specify the users token. +Basically, when you are making an api call from the client side of an app deployed on Monday.com, you don't need to specify the users token. ```typescript import { Board, } from "@mondaycom/api"; -const { boards } = await SeamlessApiClient.query<{boards: [Board];}>(`query { boards(ids: some_id) { id name } }`); +const { boards } = await SeamlessApiClient.request<{boards: [Board];}>(`query { boards(ids: some_id) { id name } }`); // or using your own types after integrating with @mondaycom/setup-api import { GetBoardsQueryVariables, GetBoardsQuery } from "./generated/graphql"; @@ -103,7 +188,7 @@ export const getBoards = gql` } `; -const data = await SeamlessApiClient.query(getBoards, variables); +const data = await SeamlessApiClient.request(getBoards, variables); ``` ### Type support diff --git a/packages/api/lib/api-client.ts b/packages/api/lib/api-client.ts index 2e4221b..f72c837 100644 --- a/packages/api/lib/api-client.ts +++ b/packages/api/lib/api-client.ts @@ -1,8 +1,17 @@ -import { GraphQLClient } from 'graphql-request'; +import { GraphQLClient, ClientError } from 'graphql-request'; import { ApiVersionType, DEFAULT_VERSION, QueryVariables } from './constants/index'; import { Sdk, getSdk } from './generated/sdk'; import pkg from '../package.json'; import { getApiEndpoint } from './shared/get-api-endpoint'; +import { GraphQLClientResponse, RequestConfig } from 'graphql-request/build/esm/types'; + +export { ClientError }; + +export interface ApiClientConfig { + token: string; + apiVersion?: string; + requestConfig?: Omit; +} /** * The `ApiClient` class provides a structured way to interact with the Monday.com API, @@ -18,17 +27,22 @@ export class ApiClient { /** * Constructs a new `ApiClient` instance, initializing the GraphQL client with - * the specified authentication token and API version. + * the specified configuration object. * - * @param {string} token - The authentication token required for making API requests to Monday.com. - * @param {string} [apiVersion=DEFAULT_VERSION] - The API version to use for requests. - * Defaults to the version corresponding to the package version release (which will be the current), but can be specified with custom string. + * @param {ApiClientConfig} config - Configuration for the API client. + * Requires `token`, and optionally includes `apiVersion` and `requestConfig`. */ - - constructor(token: string, apiVersion: string = DEFAULT_VERSION) { + constructor(config: ApiClientConfig) { + const { token, apiVersion = DEFAULT_VERSION, requestConfig = {} } = config; + if (!this.isValidApiVersion(apiVersion)) { + throw new Error( + "Invalid API version format. Expected format is 'yyyy-mm' with month as one of '01', '04', '07', or '10'.", + ); + } this.apiVersion = apiVersion; const endpoint = getApiEndpoint(); this.client = new GraphQLClient(endpoint, { + ...requestConfig, headers: { 'Content-Type': 'application/json', Authorization: token, @@ -53,8 +67,38 @@ export class ApiClient { * @returns {Promise} A promise that resolves with the result of the query or mutation. * @template T The expected type of the query or mutation result. */ - public query = async (query: string, variables?: QueryVariables): Promise => { + public request = async (query: string, variables?: QueryVariables): Promise => { const res = await this.client.request(query, variables); return res; }; + + /** + * Performs a raw GraphQL query or mutation to the Monday.com API using the configured + * GraphQL client. This method is asynchronous and returns a promise that resolves + * with the query result. + * + * The result will be in the raw format: data, errors, extensions. + * + * @param {string} query - The GraphQL query or mutation string. + * @param {QueryVariables} [variables] - An optional object containing variables for the query. + * `QueryVariables` is a type alias for `Record`, allowing specification + * of key-value pairs where the value can be any type. This parameter is used to provide + * dynamic values in the query or mutation. + * @returns {Promise} A promise that resolves with the result of the query or mutation. + * @template T The expected type of the query or mutation result. + */ + public rawRequest = async (query: string, variables?: QueryVariables): Promise> => { + const res = await this.client.rawRequest(query, variables); + return res; + }; + + /** + * Validates the API version format (yyyy-mm), restricting mm to 01, 04, 07, or 10. + * + * @param {string} version - The API version string to validate. + * @returns {boolean} - Returns true if the version matches yyyy-mm format with allowed months. + */ + private isValidApiVersion(version: string): boolean { + return /^\d{4}-(01|04|07|10)$/.test(version); + } } diff --git a/packages/api/lib/seamless-api-client.ts b/packages/api/lib/seamless-api-client.ts index 4b9b7d3..3a4c4be 100644 --- a/packages/api/lib/seamless-api-client.ts +++ b/packages/api/lib/seamless-api-client.ts @@ -44,7 +44,7 @@ export class SeamlessApiClient { * @template T The expected type of the query or mutation result. * @throws {Error} Throws an error if called from within the monday.com platform and the request failed, or if the request timed out. */ - public query( + public request( query: string, variables?: QueryVariables, version?: ApiVersionType, diff --git a/packages/api/package.json b/packages/api/package.json index 5d026ed..650692e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@mondaydotcomorg/api", - "version": "5.0.1", + "version": "6.0.0", "description": "monday.com API client", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", @@ -33,6 +33,7 @@ "url": "https://github.com/mondaycom/monday-graphql-api/tree/main/packages/api" }, "dependencies": { + "graphql": "16.8.2", "graphql-request": "^6.1.0", "graphql-tag": "^2.12.6" }, diff --git a/packages/api/tests/api-client.test.ts b/packages/api/tests/api-client.test.ts index 6389271..c617337 100644 --- a/packages/api/tests/api-client.test.ts +++ b/packages/api/tests/api-client.test.ts @@ -16,7 +16,7 @@ jest.mock('graphql-request', () => { describe('ApiClient', () => { it('should correctly initialize with default parameters', () => { const token = 'test-token'; - const apiClient = new ApiClient(token); + const apiClient = new ApiClient({ token }); expect(GraphQLClient).toHaveBeenCalledWith('https://api.monday.com/v2', { headers: { diff --git a/packages/setup-api/README.md b/packages/setup-api/README.md index 276d462..e03a426 100644 --- a/packages/setup-api/README.md +++ b/packages/setup-api/README.md @@ -94,17 +94,17 @@ When using this package, you will automatically get an example query and an exam Here is how to use them! ```typescript -const client = new ApiClient("your_api_token"); // From the monday api sdk @mondaydotcomorg/api +const client = new ApiClient({token: ''}); // From the monday api sdk @mondaydotcomorg/api const queryVariables: QueryBoardsArgs = { ids: ["your_board_id"] }; // replace with your board id -const queryData = await client.query(exampleQuery, queryVariables); +const queryData = await client.request(exampleQuery, queryVariables); const mutationVariables: CreateItemMutationVariables = { boardId: "your_board_id", // replace with your board id -groupId: "your_groyup_id", // replace with your group id +groupId: "your_group_id", // replace with your group id itemName: "Im using my own queries!", }; -const mutationData = await client.query(exampleMutation, mutationVariables); +const mutationData = await client.request(exampleMutation, mutationVariables); ``` ## Further configuration diff --git a/packages/setup-api/package.json b/packages/setup-api/package.json index 9780e52..e818278 100644 --- a/packages/setup-api/package.json +++ b/packages/setup-api/package.json @@ -1,6 +1,6 @@ { "name": "@mondaydotcomorg/setup-api", - "version": "1.2.1", + "version": "1.2.2", "description": "monday.com setup api cli", "main": "dist/cjs/index.js", "types": "dist/cjs/index.d.ts",