Skip to content

Commit

Permalink
add custom errors
Browse files Browse the repository at this point in the history
  • Loading branch information
gimmyxd committed Nov 28, 2023
1 parent 894faf4 commit 7f08946
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 29 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"codecov",
"connectrpc",
"displaystatemap",
"instanceof",
"keyof",
"morty",
"njwt",
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,23 @@ Get an object instance with the type `type-name` and the id `object-id`. For exa

```typescript
const user = await directoryClient.object({ objectType: 'user', objectId: '[email protected]' });

// Handle a not found object
import { NotFoundError } from "@aserto/aserto-node"

try {
directoryClient.object({
objectType: "user",
objectId: "[email protected]",
});
} catch (error) {
if (error instanceof NotFoundError) {
// pass trough
}

// throw back the original error
throw error;
}
```

#### 'relation' function
Expand Down
4 changes: 2 additions & 2 deletions __tests__/directory/v3/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ describe("DirectoryV3", () => {
it("handles ConnectError", async () => {
const mockCheckPermission = jest
.spyOn(directory.ReaderClient, "checkPermission")
.mockRejectedValue(new ConnectError("connect error", 5));
.mockRejectedValue(new ConnectError("connect error", 1));

const params = {
subjectId: "[email protected]",
Expand All @@ -244,7 +244,7 @@ describe("DirectoryV3", () => {
objectId: "admin",
};
await expect(directory.checkPermission(params)).rejects.toThrow(
'"checkPermission" failed with code: 5, message: [not_found] connect error'
'"checkPermission" failed with code: 1, message: [canceled] connect error'
);

mockCheckPermission.mockReset();
Expand Down
51 changes: 27 additions & 24 deletions __tests__/integration/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
createAsyncIterable,
DirectoryServiceV3,
DirectoryV3,
EtagMismatchError,
getSSLCredentials,
NotFoundError,
policyInstance,
readAsyncIterable,
} from "../../lib";
Expand Down Expand Up @@ -94,6 +96,19 @@ types:
).resolves.not.toThrow();
});

xit("throws EtagMismatchError when setting the same object without Etag", async () => {
await expect(
directoryClient.setObject({
object: {
type: "user",
id: "test-user",
displayName: "updated",
etag: "updated",
},
})
).rejects.toThrow(EtagMismatchError);
});

it("sets a another object", async () => {
await expect(
directoryClient.setObject({
Expand Down Expand Up @@ -255,7 +270,7 @@ types:
).resolves.not.toThrow();
});

it("throws error when getting a delete relation", async () => {
it("throws NotFoundError when getting a delete relation", async () => {
await expect(
directoryClient.relation({
subjectId: "test-user",
Expand All @@ -264,9 +279,7 @@ types:
objectId: "test-group",
objectType: "group",
})
).rejects.toThrow(
'"relation" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});

it("list user objects", async () => {
Expand Down Expand Up @@ -313,20 +326,16 @@ types:
).resolves.not.toThrow();
});

it("throws error when getting a deleted user object", async () => {
it("throws NotFoundError when getting a deleted user object", async () => {
await expect(
directoryClient.object({ objectType: "user", objectId: "test-user" })
).rejects.toThrow(
'"object" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});

it("throws error when getting a deleted group object", async () => {
it("throws NotFoundError when getting a deleted group object", async () => {
await expect(
directoryClient.object({ objectType: "group", objectId: "test-group" })
).rejects.toThrow(
'"object" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});

it("returns [] when there are no objects", async () => {
Expand Down Expand Up @@ -432,26 +441,22 @@ types:
).resolves.not.toThrow();
});

it("throws error when getting a deleted user object", async () => {
it("throws NotFoundError when getting a deleted user object", async () => {
await expect(
directoryClient.object({ objectType: "user", objectId: "import-user" })
).rejects.toThrow(
'"object" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});

it("throws error when getting a deleted group object", async () => {
it("throws NotFoundError when getting a deleted group object", async () => {
await expect(
directoryClient.object({
objectType: "group",
objectId: "import-group",
})
).rejects.toThrow(
'"object" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});

it("throws error when getting a delete relation", async () => {
it("throws NotFoundError when getting a delete relation", async () => {
await expect(
directoryClient.relation({
subjectId: "import-user",
Expand All @@ -460,9 +465,7 @@ types:
objectId: "import-group",
objectType: "group",
})
).rejects.toThrow(
'"relation" failed with code: 5, message: [not_found] E20051 key not found'
);
).rejects.toThrow(NotFoundError);
});
});

Expand Down
33 changes: 33 additions & 0 deletions lib/directory/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* An Error return when a directory Object or Relation is not found.
* Extends the built-in Error class.
*
* @class NotFoundError
* @extends Error
*/
export class NotFoundError extends Error {}
/**
* "Invalid Argument" error.
* Extends the built-in Error class.
*
* @class InvalidArgumentError
* @extends Error
*/
export class InvalidArgumentError extends Error {}
/**
* "Etag Mismatch" error.
* Extends the built-in Error class.
*
* @class EtagMismatchError
* @extends Error
*/
export class EtagMismatchError extends Error {}
/**
* "Unauthenticated" error.
* Extends the built-in Error class.
*
* @class EtagMismatchError
* @extends Error
*/
export class UnauthenticatedError extends Error {}
export class ServiceError extends Error {}
32 changes: 29 additions & 3 deletions lib/directory/v3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
Struct,
} from "@bufbuild/protobuf";
import {
Code,
ConnectError,
createPromiseClient,
Interceptor,
Expand All @@ -33,6 +34,13 @@ import {
} from "@connectrpc/connect";
import { createGrpcTransport } from "@connectrpc/connect-node";

import {
EtagMismatchError,
InvalidArgumentError,
NotFoundError,
ServiceError,
UnauthenticatedError,
} from "../errors";
import {
CheckPermissionRequest,
CheckRelationRequest,
Expand Down Expand Up @@ -465,9 +473,27 @@ export async function* createAsyncIterable<T>(items: T[]): AsyncIterable<T> {

function handleError(error: unknown, method: string) {
if (error instanceof ConnectError) {
throw new Error(
`"${method}" failed with code: ${error.code}, message: ${error.message}`
);
switch (error.code) {
case Code.Unauthenticated: {
throw new UnauthenticatedError(
`Authentication failed: ${error.message}`
);
}
case Code.NotFound: {
throw new NotFoundError(`${method} not found`);
}
case Code.InvalidArgument: {
throw new InvalidArgumentError(`${method}: ${error.message}`);
}
case Code.FailedPrecondition: {
throw new EtagMismatchError(`invalid etag in ${method} request`);
}
default: {
throw new ServiceError(
`"${method}" failed with code: ${error.code}, message: ${error.message}`
);
}
}
} else {
throw error;
}
Expand Down
2 changes: 2 additions & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,5 @@ export {
SubIdentityMapper,
DirectoryConfig as ServiceConfig,
};

export * from "./directory/errors";

0 comments on commit 7f08946

Please sign in to comment.