Skip to content

Commit

Permalink
Merge pull request #132 from camunda:error-annotation
Browse files Browse the repository at this point in the history
Error-annotation
  • Loading branch information
jwulf authored Apr 10, 2024
2 parents d00326d + 822e56b commit 7a8c696
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 107 deletions.
45 changes: 45 additions & 0 deletions src/__tests__/operate/operate-integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RESTError } from 'lib'
import { LosslessNumber } from 'lossless-json'

import { OperateApiClient } from '../../operate'
Expand Down Expand Up @@ -51,3 +52,47 @@ test('getJSONVariablesforProcess works', async () => {
const res = await c.getJSONVariablesforProcess(p.processInstanceKey)
expect(res.foo).toBe('bar')
})

test('test error type', async () => {
const c = new OperateApiClient()
const zeebe = new ZeebeGrpcClient()
await zeebe.deployResource({
processFilename: 'src/__tests__/testdata/Operate-StraightThrough.bpmn',
})
const p = await zeebe.createProcessInstanceWithResult({
bpmnProcessId: 'operate-straightthrough',
variables: {
foo: 'bar',
},
})
await new Promise((res) => setTimeout(() => res(null), 5000))
/**
* Here we request a process instance that doesn't exist.
* Understanding that this is the issue requires a bit of gymnastics by the consumer.
* This call may fail due to lack of permissions, a network error (including misconfiguration), or the process instance not existing.
* To rule out the other issues and focus on the process instance not existing, we need to catch the error and check the response body.
* (e.response?.body as string).includes('404') will return true if the response body contains the string '404'.
* This is a bit of a hack, but it's the best we can do without a more specific error type.
* Do we really want to expose consumers to this?
*/
const res = await c
.getProcessInstance(`${p.processInstanceKey}1`)
.catch((e: RESTError) => {
// console.log(e.code)
// `ERR_NON_2XX_3XX_RESPONSE`

// console.log(e.message)
// `Response code 404 (Not Found) (request to http://localhost:8081/v1/process-instances/22517998149629301)`
// Note: The request url has been enhanced into the message by a hook.

// console.log(e.response?.body)
// `{"status":404,"message":"No process instances found for key 22517998149629301 ","instance":"76807bf1-d877-4f8e-bd0d-6d953b1799e5","type":"Requested resource not found"}`

// console.log(typeof e.response?.body)
// `string`

expect((e.response?.body as string).includes('404')).toBe(true)
return false
})
expect(res).toBe(false)
})
45 changes: 25 additions & 20 deletions src/admin/lib/AdminApiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@ import {
RequireConfiguration,
constructOAuthProvider,
createUserAgentString,
gotBeforeErrorHook,
gotErrorHandler,
} from '../../lib'
import { IOAuthProvider } from '../../oauth'

import * as Dto from './AdminDto'

const debug = d('camunda:adminconsole')

/**
* This class provides methods to interact with the Camunda Admin API.
* @throws {RESTError} An error that may occur during API operations.
*/
export class AdminApiClient {
private userAgentString: string
private oAuthProvider: IOAuthProvider
Expand Down Expand Up @@ -44,25 +50,9 @@ export class AdminApiClient {
https: {
certificateAuthority,
},
handlers: [
(options, next) => {
if (Object.isFrozen(options.context)) {
options.context = { ...options.context }
}
Error.captureStackTrace(options.context)
return next(options)
},
],
handlers: [gotErrorHandler],
hooks: {
beforeError: [
(error) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(error as any).source = (error as any).options.context.stack.split(
'\n'
)
return error
},
],
beforeError: [gotBeforeErrorHook],
},
})
debug('prefixUrl', `${baseUrl}/clusters`)
Expand All @@ -82,6 +72,7 @@ export class AdminApiClient {
/**
*
* @description Get an array of the current API clients for this cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetClients) for more details.
* @throws {RESTError}
* @param clusterUuid - The cluster UUID
*
*/
Expand All @@ -94,7 +85,7 @@ export class AdminApiClient {

/**
* @description Create a new API client for a cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/CreateClient) for more details.
* @returns
* @throws {RESTError}
*/
async createClient(req: {
clusterUuid: string
Expand All @@ -117,6 +108,7 @@ export class AdminApiClient {
* @description Get the details of an API client. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetClient) for more details.
* @param clusterUuid
* @param clientId
* @throws {RESTError}
* @returns
*/
async getClient(
Expand All @@ -133,6 +125,7 @@ export class AdminApiClient {
* @description See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/DeleteClient) for more details.
* @param clusterUuid
* @param clientId
* @throws {RESTError}
*/
async deleteClient(clusterUuid: string, clientId: string): Promise<null> {
const headers = await this.getHeaders()
Expand All @@ -146,6 +139,7 @@ export class AdminApiClient {
/**
*
* @description Return an array of clusters. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetClusters) for more details.
* @throws {RESTError}
*/
async getClusters(): Promise<Dto.Cluster[]> {
const headers = await this.getHeaders()
Expand All @@ -157,6 +151,7 @@ export class AdminApiClient {
/**
*
* @description Create a new cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/CreateCluster) for more details.
* @throws {RESTError}
*/
async createCluster(
createClusterRequest: Dto.CreateClusterBody
Expand All @@ -173,6 +168,7 @@ export class AdminApiClient {
/**
*
* @description Retrieve the metadata for a cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetCluster) for more details.
* @throws {RESTError}
*
*/
async getCluster(clusterUuid: string): Promise<Dto.Cluster> {
Expand All @@ -185,6 +181,7 @@ export class AdminApiClient {
/**
*
* @description Delete a cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/DeleteCluster) for more details.
* @throws {RESTError}
*
*/
async deleteCluster(clusterUuid: string): Promise<null> {
Expand All @@ -199,6 +196,7 @@ export class AdminApiClient {
/**
*
* @description Retrieve the available parameters for cluster creation. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetParameters) for more details.
* @throws {RESTError}
*/
async getParameters(): Promise<Dto.Parameters> {
const headers = await this.getHeaders()
Expand All @@ -210,6 +208,7 @@ export class AdminApiClient {
/**
*
* @description Retrieve the connector secrets. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/GetSecrets) for more details.
* @throws {RESTError}
*/
async getSecrets(clusterUuid: string): Promise<{ [key: string]: string }> {
const headers = await this.getHeaders()
Expand All @@ -221,6 +220,7 @@ export class AdminApiClient {
/**
*
* @description Create a new connector secret. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/CreateSecret) for more details.
* @throws {RESTError}
*/
async createSecret({
clusterUuid,
Expand All @@ -242,6 +242,7 @@ export class AdminApiClient {
/**
*
* @description Delete a connector secret. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/DeleteSecret) for more details.
* @throws {RESTError}
*/
async deleteSecret(clusterUuid: string, secretName: string): Promise<null> {
const headers = await this.getHeaders()
Expand All @@ -255,6 +256,7 @@ export class AdminApiClient {
/**
*
* @description Add one or more IPs to the whitelist for the cluster. See [the API Documentation](https://console.cloud.camunda.io/customer-api/openapi/docs/#/default/UpdateIpWhitelist) for more details.
* @throws {RESTError}
* @param ipwhitelist
* @returns
*/
Expand All @@ -266,7 +268,7 @@ export class AdminApiClient {
ip: string
},
]
) {
): Promise<null> {
const headers = await this.getHeaders()
return this.rest
.put(`${clusterUuid}/ipwhitelist`, {
Expand All @@ -281,6 +283,7 @@ export class AdminApiClient {
/**
*
* @description Retrieve a list of members and pending invites for your organisation. See the [API Documentation]() for more details.
* @throws {RESTError}
*/
async getUsers(): Promise<Dto.Member[]> {
const headers = await this.getHeaders()
Expand All @@ -294,6 +297,7 @@ export class AdminApiClient {
/**
*
* @description Add a member. See the [API Documentation]() for more details.
* @throws {RESTError}
*
*/
async createMember(
Expand All @@ -312,6 +316,7 @@ export class AdminApiClient {
/**
*
* @description Delete a member from your organization. See the [API Documentation]() for more details.
* @throws {RESTError}
*
*/
async deleteMember(email: string): Promise<null> {
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import * as Optimize from './optimize'
import * as Tasklist from './tasklist'
import * as Zeebe from './zeebe'

export { RESTError } from './lib'

export const Dto = { ChildDto, BigIntValue, Int64String, LosslessDto }

export { Admin, Auth, Camunda8, Modeler, Operate, Optimize, Tasklist, Zeebe }
13 changes: 13 additions & 0 deletions src/lib/GotErrors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import * as Got from 'got'

export type RESTError =
| Got.HTTPError
| Got.RequestError
| Got.ReadError
| Got.ParseError
| Got.HTTPError
| Got.TimeoutError
| Got.CancelError
| Got.CacheError
| Got.MaxRedirectsError
| Got.UnsupportedProtocolError
16 changes: 16 additions & 0 deletions src/lib/GotHooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const gotErrorHandler = (options, next) => {
if (Object.isFrozen(options.context)) {
options.context = { ...options.context }
}
Error.captureStackTrace(options.context)

return next(options)
}

export const gotBeforeErrorHook = (error) => {
const { request } = error
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(error as any).source = (error as any).options.context.stack.split('\n')
error.message += ` (request to ${request?.options.url.href})`
return error
}
2 changes: 2 additions & 0 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export * from './CreateUserAgentString'
export * from './Delay'
export * from './EnvironmentSetup'
export { packageVersion } from './GetPackageVersion'
export * from './GotErrors'
export * from './GotHooks'
export * from './LosslessJsonParser'
export { RequireConfiguration } from './RequireConfiguration'
export * from './SuppressZeebeLogging'
Expand Down
Loading

0 comments on commit 7a8c696

Please sign in to comment.