Skip to content

Commit 2ec08f2

Browse files
Add support for decorating and handling 429 errors with the saved objects client (#75664) (#75762)
* Add support for decorating 429 errors in the saved objects client * Update the docs Co-authored-by: Elastic Machine <[email protected]> Co-authored-by: Elastic Machine <[email protected]>
1 parent 57e383d commit 2ec08f2

9 files changed

+152
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [createTooManyRequestsError](./kibana-plugin-core-server.savedobjectserrorhelpers.createtoomanyrequestserror.md)
4+
5+
## SavedObjectsErrorHelpers.createTooManyRequestsError() method
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
static createTooManyRequestsError(type: string, id: string): DecoratedError;
11+
```
12+
13+
## Parameters
14+
15+
| Parameter | Type | Description |
16+
| --- | --- | --- |
17+
| type | <code>string</code> | |
18+
| id | <code>string</code> | |
19+
20+
<b>Returns:</b>
21+
22+
`DecoratedError`
23+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [decorateTooManyRequestsError](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratetoomanyrequestserror.md)
4+
5+
## SavedObjectsErrorHelpers.decorateTooManyRequestsError() method
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
static decorateTooManyRequestsError(error: Error, reason?: string): DecoratedError;
11+
```
12+
13+
## Parameters
14+
15+
| Parameter | Type | Description |
16+
| --- | --- | --- |
17+
| error | <code>Error</code> | |
18+
| reason | <code>string</code> | |
19+
20+
<b>Returns:</b>
21+
22+
`DecoratedError`
23+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
2+
3+
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [SavedObjectsErrorHelpers](./kibana-plugin-core-server.savedobjectserrorhelpers.md) &gt; [isTooManyRequestsError](./kibana-plugin-core-server.savedobjectserrorhelpers.istoomanyrequestserror.md)
4+
5+
## SavedObjectsErrorHelpers.isTooManyRequestsError() method
6+
7+
<b>Signature:</b>
8+
9+
```typescript
10+
static isTooManyRequestsError(error: Error | DecoratedError): boolean;
11+
```
12+
13+
## Parameters
14+
15+
| Parameter | Type | Description |
16+
| --- | --- | --- |
17+
| error | <code>Error &#124; DecoratedError</code> | |
18+
19+
<b>Returns:</b>
20+
21+
`boolean`
22+

docs/development/core/server/kibana-plugin-core-server.savedobjectserrorhelpers.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export declare class SavedObjectsErrorHelpers
1919
| [createConflictError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.createconflicterror.md) | <code>static</code> | |
2020
| [createGenericNotFoundError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.creategenericnotfounderror.md) | <code>static</code> | |
2121
| [createInvalidVersionError(versionInput)](./kibana-plugin-core-server.savedobjectserrorhelpers.createinvalidversionerror.md) | <code>static</code> | |
22+
| [createTooManyRequestsError(type, id)](./kibana-plugin-core-server.savedobjectserrorhelpers.createtoomanyrequestserror.md) | <code>static</code> | |
2223
| [createUnsupportedTypeError(type)](./kibana-plugin-core-server.savedobjectserrorhelpers.createunsupportedtypeerror.md) | <code>static</code> | |
2324
| [decorateBadRequestError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratebadrequesterror.md) | <code>static</code> | |
2425
| [decorateConflictError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorateconflicterror.md) | <code>static</code> | |
@@ -28,6 +29,7 @@ export declare class SavedObjectsErrorHelpers
2829
| [decorateGeneralError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decorategeneralerror.md) | <code>static</code> | |
2930
| [decorateNotAuthorizedError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratenotauthorizederror.md) | <code>static</code> | |
3031
| [decorateRequestEntityTooLargeError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoraterequestentitytoolargeerror.md) | <code>static</code> | |
32+
| [decorateTooManyRequestsError(error, reason)](./kibana-plugin-core-server.savedobjectserrorhelpers.decoratetoomanyrequestserror.md) | <code>static</code> | |
3133
| [isBadRequestError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isbadrequesterror.md) | <code>static</code> | |
3234
| [isConflictError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isconflicterror.md) | <code>static</code> | |
3335
| [isEsCannotExecuteScriptError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isescannotexecutescripterror.md) | <code>static</code> | |
@@ -38,4 +40,5 @@ export declare class SavedObjectsErrorHelpers
3840
| [isNotFoundError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isnotfounderror.md) | <code>static</code> | |
3941
| [isRequestEntityTooLargeError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.isrequestentitytoolargeerror.md) | <code>static</code> | |
4042
| [isSavedObjectsClientError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.issavedobjectsclienterror.md) | <code>static</code> | |
43+
| [isTooManyRequestsError(error)](./kibana-plugin-core-server.savedobjectserrorhelpers.istoomanyrequestserror.md) | <code>static</code> | |
4144

src/core/server/saved_objects/service/lib/decorate_es_error.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ describe('savedObjectsClient/decorateEsError', () => {
7373
expect(SavedObjectsErrorHelpers.isConflictError(error)).toBe(true);
7474
});
7575

76+
it('makes TooManyRequests a SavedObjectsClient/tooManyRequests error', () => {
77+
const error = new esErrors.ResponseError(
78+
elasticsearchClientMock.createApiResponse({ statusCode: 429 })
79+
);
80+
expect(SavedObjectsErrorHelpers.isTooManyRequestsError(error)).toBe(false);
81+
expect(decorateEsError(error)).toBe(error);
82+
expect(SavedObjectsErrorHelpers.isTooManyRequestsError(error)).toBe(true);
83+
});
84+
7685
it('makes NotAuthorized a SavedObjectsClient/NotAuthorized error', () => {
7786
const error = new esErrors.ResponseError(
7887
elasticsearchClientMock.createApiResponse({ statusCode: 401 })

src/core/server/saved_objects/service/lib/decorate_es_error.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const responseErrors = {
2828
isRequestEntityTooLarge: (statusCode: number) => statusCode === 413,
2929
isNotFound: (statusCode: number) => statusCode === 404,
3030
isBadRequest: (statusCode: number) => statusCode === 400,
31+
isTooManyRequests: (statusCode: number) => statusCode === 429,
3132
};
3233
const { ConnectionError, NoLivingConnectionsError, TimeoutError } = esErrors;
3334
const SCRIPT_CONTEXT_DISABLED_REGEX = /(?:cannot execute scripts using \[)([a-z]*)(?:\] context)/;
@@ -76,6 +77,10 @@ export function decorateEsError(error: EsErrors) {
7677
return SavedObjectsErrorHelpers.createGenericNotFoundError();
7778
}
7879

80+
if (responseErrors.isTooManyRequests(error.statusCode)) {
81+
return SavedObjectsErrorHelpers.decorateTooManyRequestsError(error, reason);
82+
}
83+
7984
if (responseErrors.isBadRequest(error.statusCode)) {
8085
if (
8186
SCRIPT_CONTEXT_DISABLED_REGEX.test(reason || '') ||

src/core/server/saved_objects/service/lib/errors.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,53 @@ describe('savedObjectsClient/errorTypes', () => {
274274
});
275275
});
276276

277+
describe('TooManyRequests error', () => {
278+
describe('decorateTooManyRequestsError', () => {
279+
it('returns original object', () => {
280+
const error = new Error();
281+
expect(SavedObjectsErrorHelpers.decorateTooManyRequestsError(error)).toBe(error);
282+
});
283+
284+
it('makes the error identifiable as a TooManyRequests error', () => {
285+
const error = new Error();
286+
expect(SavedObjectsErrorHelpers.isTooManyRequestsError(error)).toBe(false);
287+
SavedObjectsErrorHelpers.decorateTooManyRequestsError(error);
288+
expect(SavedObjectsErrorHelpers.isTooManyRequestsError(error)).toBe(true);
289+
});
290+
291+
it('adds boom properties', () => {
292+
const error = SavedObjectsErrorHelpers.decorateTooManyRequestsError(new Error());
293+
expect(error).toHaveProperty('isBoom', true);
294+
});
295+
296+
describe('error.output', () => {
297+
it('defaults to message of error', () => {
298+
const error = SavedObjectsErrorHelpers.decorateTooManyRequestsError(new Error('foobar'));
299+
expect(error.output.payload).toHaveProperty('message', 'foobar');
300+
});
301+
302+
it('prefixes message with passed reason', () => {
303+
const error = SavedObjectsErrorHelpers.decorateTooManyRequestsError(
304+
new Error('foobar'),
305+
'biz'
306+
);
307+
expect(error.output.payload).toHaveProperty('message', 'biz: foobar');
308+
});
309+
310+
it('sets statusCode to 429', () => {
311+
const error = SavedObjectsErrorHelpers.decorateTooManyRequestsError(new Error('foo'));
312+
expect(error.output).toHaveProperty('statusCode', 429);
313+
});
314+
315+
it('preserves boom properties of input', () => {
316+
const error = Boom.tooManyRequests();
317+
SavedObjectsErrorHelpers.decorateTooManyRequestsError(error);
318+
expect(error.output).toHaveProperty('statusCode', 429);
319+
});
320+
});
321+
});
322+
});
323+
277324
describe('EsCannotExecuteScript error', () => {
278325
describe('decorateEsCannotExecuteScriptError', () => {
279326
it('returns original object', () => {

src/core/server/saved_objects/service/lib/errors.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const CODE_REQUEST_ENTITY_TOO_LARGE = 'SavedObjectsClient/requestEntityTooLarge'
3333
const CODE_NOT_FOUND = 'SavedObjectsClient/notFound';
3434
// 409 - Conflict
3535
const CODE_CONFLICT = 'SavedObjectsClient/conflict';
36+
// 429 - Too Many Requests
37+
const CODE_TOO_MANY_REQUESTS = 'SavedObjectsClient/tooManyRequests';
3638
// 400 - Es Cannot Execute Script
3739
const CODE_ES_CANNOT_EXECUTE_SCRIPT = 'SavedObjectsClient/esCannotExecuteScript';
3840
// 503 - Es Unavailable
@@ -162,6 +164,18 @@ export class SavedObjectsErrorHelpers {
162164
return isSavedObjectsClientError(error) && error[code] === CODE_CONFLICT;
163165
}
164166

167+
public static decorateTooManyRequestsError(error: Error, reason?: string) {
168+
return decorate(error, CODE_TOO_MANY_REQUESTS, 429, reason);
169+
}
170+
171+
public static createTooManyRequestsError(type: string, id: string) {
172+
return SavedObjectsErrorHelpers.decorateTooManyRequestsError(Boom.tooManyRequests());
173+
}
174+
175+
public static isTooManyRequestsError(error: Error | DecoratedError) {
176+
return isSavedObjectsClientError(error) && error[code] === CODE_TOO_MANY_REQUESTS;
177+
}
178+
165179
public static decorateEsCannotExecuteScriptError(error: Error, reason?: string) {
166180
return decorate(error, CODE_ES_CANNOT_EXECUTE_SCRIPT, 400, reason);
167181
}

src/core/server/server.api.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2220,6 +2220,8 @@ export class SavedObjectsErrorHelpers {
22202220
// (undocumented)
22212221
static createInvalidVersionError(versionInput?: string): DecoratedError;
22222222
// (undocumented)
2223+
static createTooManyRequestsError(type: string, id: string): DecoratedError;
2224+
// (undocumented)
22232225
static createUnsupportedTypeError(type: string): DecoratedError;
22242226
// (undocumented)
22252227
static decorateBadRequestError(error: Error, reason?: string): DecoratedError;
@@ -2238,6 +2240,8 @@ export class SavedObjectsErrorHelpers {
22382240
// (undocumented)
22392241
static decorateRequestEntityTooLargeError(error: Error, reason?: string): DecoratedError;
22402242
// (undocumented)
2243+
static decorateTooManyRequestsError(error: Error, reason?: string): DecoratedError;
2244+
// (undocumented)
22412245
static isBadRequestError(error: Error | DecoratedError): boolean;
22422246
// (undocumented)
22432247
static isConflictError(error: Error | DecoratedError): boolean;
@@ -2259,6 +2263,8 @@ export class SavedObjectsErrorHelpers {
22592263
//
22602264
// (undocumented)
22612265
static isSavedObjectsClientError(error: any): error is DecoratedError;
2266+
// (undocumented)
2267+
static isTooManyRequestsError(error: Error | DecoratedError): boolean;
22622268
}
22632269

22642270
// @public

0 commit comments

Comments
 (0)