Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Passthrough original error details from nitric plugin errors. #207

Merged
merged 1 commit into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
],
"dependencies": {
"@grpc/grpc-js": "1.8.1",
"@nitric/grpc-error-status": "^0.0.2",
"@opentelemetry/api": "^1.4.1",
"@opentelemetry/exporter-trace-otlp-http": "^0.36.1",
"@opentelemetry/instrumentation": "^0.36.1",
Expand Down
2 changes: 1 addition & 1 deletion src/api/documents/v0/collection-group-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class CollectionGroupRef<T extends DocumentStructure> {
name: string
): CollectionGroupRef<T> {
if (this.depth() >= MAX_COLLECTION_DEPTH) {
throw new InvalidArgumentError(
throw new Error(
`Maximum collection depth ${MAX_COLLECTION_DEPTH} exceeded`
);
}
Expand Down
2 changes: 1 addition & 1 deletion src/api/documents/v0/document-ref.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ describe('Document Ref Tests', () => {
const testNestedAgain = () => nestedCollection.collection('nested-again');

expect(testNestedAgain).toThrow(
new InvalidArgumentError('Maximum collection depth 1 exceeded')
new Error('Maximum collection depth 1 exceeded')
);
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/api/documents/v0/document-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class DocumentRef<T extends DocumentStructure> {
name: string
): CollectionRef<T> {
if (this.depth() >= MAX_COLLECTION_DEPTH) {
throw new InvalidArgumentError(
throw new Error(
`Maximum collection depth ${MAX_COLLECTION_DEPTH} exceeded`
);
}
Expand Down
4 changes: 2 additions & 2 deletions src/api/documents/v0/query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ describe('Query Tests', () => {

q.pagingFrom('test' as any);

await expect(q.fetch()).rejects.toStrictEqual(
new InvalidArgumentError('Invalid paging token provided!')
await expect(q.fetch()).rejects.toEqual(
new Error('Invalid paging token provided!')
);
});
});
Expand Down
6 changes: 2 additions & 4 deletions src/api/documents/v0/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,7 @@ export class Query<T extends DocumentStructure> {
*/
public limit(limit: number): Query<T> {
if (typeof limit !== 'number' || limit < 0) {
throw new InvalidArgumentError(
'limit must be a positive integer or 0 for unlimited.'
);
throw new Error('limit must be a positive integer or 0 for unlimited.');
}

this.fetchLimit = limit;
Expand All @@ -160,7 +158,7 @@ export class Query<T extends DocumentStructure> {

if (this.pagingToken != null) {
if (!(this.pagingToken instanceof Map)) {
throw new InvalidArgumentError('Invalid paging token provided!');
throw new Error('Invalid paging token provided!');
}

const map = request.getPagingTokenMap();
Expand Down
9 changes: 5 additions & 4 deletions src/api/errors/aborted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';
/**
* AbortedError
*
* The operation was aborted
*/
export class AbortedError extends Error {
constructor(message: string) {
super(message);
export class AbortedError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, AbortedError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/already-exists.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* AlreadyExistsError
*
* Client attempted to illegally create an entity that already exists
*/
export class AlreadyExistsError extends Error {
constructor(message: string) {
super(message);
export class AlreadyExistsError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, AlreadyExistsError.prototype);
}
}
12 changes: 9 additions & 3 deletions src/api/errors/cancelled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {
ErrorDetails,
ErrorScope,
} from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* CancelledError
*
* Operation was cancelled (typically occurs client side)
*/
export class CancelledError extends Error {
constructor(message: string) {
super(message);
export class CancelledError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, CancelledError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/data-loss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* DataLossError
*
* Unrecoverable data loss or corruption
*/
export class DataLossError extends Error {
constructor(message: string) {
super(message);
export class DataLossError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, DataLossError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/deadline-exceeded.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* DeadlineExceededError
*
* Specified deadline was exceeded before the operation could complete
*/
export class DeadlineExceededError extends Error {
constructor(message: string) {
super(message);
export class DeadlineExceededError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, DeadlineExceededError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/failed-precondition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* FailedPreconditionError
*
* Operation was rejected due to the system being not being
* in a state required for the requested operation.
*/
export class FailedPreconditionError extends Error {
constructor(message: string) {
super(message);
export class FailedPreconditionError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, FailedPreconditionError.prototype);
}
}
23 changes: 20 additions & 3 deletions src/api/errors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,16 @@ import { UnauthenticatedError } from './unauthenticated';
import { UnavailableError } from './unavailable';
import { UnimplementedError } from './unimplemented';
import { UnknownError } from './unknown';
import { parse } from '@nitric/grpc-error-status';
import { ErrorDetails } from '@nitric/api/proto/error/v1/error_pb';

// Accept all codes except Status OK
type codes = Exclude<status, status.OK>;

const STATUS_CODE_MAP: Record<codes, new (message: string) => Error> = {
const STATUS_CODE_MAP: Record<
codes,
new (message: string, details: ErrorDetails) => Error
> = {
[status.CANCELLED]: CancelledError,
[status.UNKNOWN]: UnknownError,
[status.INVALID_ARGUMENT]: InvalidArgumentError,
Expand Down Expand Up @@ -61,11 +66,23 @@ const STATUS_CODE_MAP: Record<codes, new (message: string) => Error> = {
export const fromGrpcError = (error: ServiceError): Error => {
const construct = STATUS_CODE_MAP[error.code];

const errorStatus = parse(error);

let errorDetails: ErrorDetails | undefined = undefined;

if (errorStatus) {
const allDetails = errorStatus.parseDetails(ErrorDetails);

if (allDetails.length > 0) {
errorDetails = allDetails[0];
}
}

if (construct) {
return new construct(error.message);
return new construct(error.message, errorDetails);
}

return new UnknownError(error.message);
return new UnknownError(error.message, errorDetails);
};

// Re-export errors
Expand Down
9 changes: 6 additions & 3 deletions src/api/errors/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* InternalError
*
* Some invariant error has incurred internally
*/
export class InternalError extends Error {
constructor(message: string) {
super(message);
export class InternalError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, InternalError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/invalid-argument.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* InvalidArgumentError
*
* Invalid argument was provided by the client
*/
export class InvalidArgumentError extends Error {
constructor(message: string) {
super(message);
export class InvalidArgumentError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, InvalidArgumentError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/not-found.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* NotFoundError
*
* Requested resource was not found
*/
export class NotFoundError extends Error {
constructor(message: string) {
super(message);
export class NotFoundError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, NotFoundError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/out-of-range.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* OutOfRangeError
*
* The operation was attempted outside of valid range
* e.g. seeking past the end of a file or array, or specifying invalid offsets
*/
export class OutOfRangeError extends Error {
constructor(message: string) {
super(message);
export class OutOfRangeError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, OutOfRangeError.prototype);
}
}
9 changes: 6 additions & 3 deletions src/api/errors/permission-denied.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';
import { NitricPluginError } from './plugin-error';

/**
* PermissionDeniedError
*
* The client is authenticated but does not have permission to
* perform the requested operation
*/
export class PermissionDeniedError extends Error {
constructor(message: string) {
super(message);
export class PermissionDeniedError extends NitricPluginError {
constructor(message: string, details: ErrorDetails) {
super(message, details);
Object.setPrototypeOf(this, PermissionDeniedError.prototype);
}
}
36 changes: 36 additions & 0 deletions src/api/errors/plugin-error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2021, Nitric Technologies Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { ErrorDetails } from '@nitric/sdk/gen/proto/error/v1/error_pb';

/**
* CancelledError
*
* Operation was cancelled (typically occurs client side)
*/
export class NitricPluginError extends Error {
constructor(message: string, details?: ErrorDetails) {
let errorMessage = message;
if (details) {
errorMessage = `${message};
Nitric Plugin Error: ${details.getScope().getPlugin()}.${details
.getScope()
.getService()}
Message: ${details.getMessage()}
Caused By: ${details.getCause()}`;
}

super(errorMessage);
}
}
Loading
Loading