Skip to content

Commit

Permalink
Merge pull request #108 from aserto-dev/json_reponse
Browse files Browse the repository at this point in the history
serialize response to json before return
  • Loading branch information
gimmyxd authored Dec 19, 2024
2 parents 270d10a + addefeb commit 0653724
Show file tree
Hide file tree
Showing 9 changed files with 307 additions and 346 deletions.
50 changes: 0 additions & 50 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,60 +101,10 @@ m
});
```

## Serialization and deserialization of data

Response Messages no longer implement the magic toJSON method, which serializes a message with the Protobuf JSON format when it's passed to `JSON.stringify`. Make sure to always serializes to JSON with the toJson or toJsonString function.

```ts
import { GetObjectsResponseSchema, toJson } from '@aserto/aserto-node'

const response = await directoryClient.objects({
objectType: "user",
page: { token: "" },
});

const json = toJson(GetObjectsResponseSchema, response)
```

The same applies to the methods `equals`, `clone`, `toJson`, and `toJsonString`, and to the static methods `fromBinary`, `fromJson`, `fromJsonString`.


#### Reading object properties is now simplified, enabling direct access.
```diff
const object = await directoryClient.object({objectType: 'user', objectId: "key"});
- const owner = object?.properties?.fields?.owner?.kind?.value as string
+ const { owner } = object.result.properties
```

## Troubleshooting

#### Express.js
```ts
app.get("/api/users/:id", async (req, res) => {
const id = req.params.id;
const user = await directoryClient.object({objectType: 'user', objectId: "key"});
res.status(200).send(user);
})
```

```
express/lib/response.js:1160
: JSON.stringify(value);
^
TypeError: Do not know how to serialize a BigInt
at JSON.stringify (<anonymous>)
at stringify (express/lib/response.js:1160:12)
```

This requires [data serialization](#serialization-and-deserialization-of-data):

```ts
import { GetObjectsResponseSchema, toJson } from '@aserto/aserto-node'

app.get("/api/users/:id", async (req, res) => {
const id = req.params.id;
const user = await directoryClient.object({objectType: 'user', objectId: "key"});
const data = toJson(GetObjectResponseSchema, user)
res.status(200).send(data);
})
```
86 changes: 26 additions & 60 deletions __tests__/directory/v3/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,9 +441,7 @@ describe("DirectoryV3", () => {
it("calls check with valid params", async () => {
const mockCheck = jest
.spyOn(directory.ReaderClient, "check")
.mockResolvedValue(
create(CheckResponseSchema, { check: true, trace: [] }),
);
.mockResolvedValue(create(CheckResponseSchema, { check: true }));

const params = {
subjectId: "[email protected]",
Expand All @@ -463,10 +461,8 @@ describe("DirectoryV3", () => {
undefined,
);
expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.CheckResponse",
check: true,
trace: [],
toJSON: expect.any(Function),
});

mockCheck.mockReset();
Expand Down Expand Up @@ -500,6 +496,7 @@ describe("DirectoryV3", () => {
create(GetObjectResponseSchema, {
result: {
id: "123",
type: "user",
},
}),
);
Expand All @@ -508,16 +505,13 @@ describe("DirectoryV3", () => {
const result = await directory.object(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectResponse",
relations: [],
result: {
$typeName: "aserto.directory.common.v3.Object",
id: "123",
type: "user",
displayName: "",
etag: "",
id: "123",
type: "",
},
toJSON: expect.any(Function),
});

mockGetObject.mockReset();
Expand Down Expand Up @@ -575,11 +569,7 @@ describe("DirectoryV3", () => {
);
const result = await directory.objects(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectsResponse",
results: [],
toJSON: expect.any(Function),
});
expect(result).toEqual({ results: [] });

mockGetObjects.mockReset();
});
Expand Down Expand Up @@ -612,11 +602,7 @@ describe("DirectoryV3", () => {
);
const result = await directory.objects(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectsResponse",
results: [],
toJSON: expect.any(Function),
});
expect(result).toEqual({ results: [] });

mockGetObjects.mockReset();
});
Expand Down Expand Up @@ -649,11 +635,7 @@ describe("DirectoryV3", () => {
);
const result = await directory.objects(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectsResponse",
results: [],
toJSON: expect.any(Function),
});
expect(result).toEqual({ results: [] });

mockGetObjects.mockReset();
});
Expand Down Expand Up @@ -687,11 +669,7 @@ describe("DirectoryV3", () => {
);
const result = await directory.objects(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectsResponse",
results: [],
toJSON: expect.any(Function),
});
expect(result).toEqual({ results: [] });

mockGetObjects.mockReset();
});
Expand Down Expand Up @@ -831,15 +809,17 @@ describe("DirectoryV3", () => {
it("returns the expected object data when calling objectMany with valid params", async () => {
const mockGetObjectMany = jest
.spyOn(directory.ReaderClient, "getObjectMany")
.mockResolvedValue(create(GetObjectManyResponseSchema, {}));
.mockResolvedValue(
create(GetObjectManyResponseSchema, {
results: [{ type: "user", id: "123" }],
}),
);

const params = { param: [{ objectType: "user", objectId: "123" }] };
const result = await directory.objectMany(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetObjectManyResponse",
results: [],
toJSON: expect.any(Function),
results: [{ type: "user", id: "123", displayName: "", etag: "" }],
});

mockGetObjectMany.mockReset();
Expand All @@ -863,7 +843,11 @@ describe("DirectoryV3", () => {
it("calls graph with valid params and return expected response", async () => {
const mockGetGraph = jest
.spyOn(directory.ReaderClient, "getGraph")
.mockResolvedValue(create(GetGraphResponseSchema, {}));
.mockResolvedValue(
create(GetGraphResponseSchema, {
results: [{ objectId: "1234", objectType: "user" }],
}),
);

const params = {
objectId: "1234",
Expand All @@ -885,10 +869,8 @@ describe("DirectoryV3", () => {
undefined,
);
expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetGraphResponse",
results: [],
results: [{ objectId: "1234", objectType: "user" }],
trace: [],
toJSON: expect.any(Function),
});

mockGetGraph.mockReset();
Expand Down Expand Up @@ -973,19 +955,16 @@ describe("DirectoryV3", () => {
const result = await directory.relation(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetRelationResponse",
objects: {},
result: {
$typeName: "aserto.directory.common.v3.Relation",
etag: "",
subjectType: "user",
subjectId: "123",
objectType: "identity",
objectId: "identity",
relation: "identifier",
subjectRelation: "",
etag: "",
},
objects: {},
toJSON: expect.any(Function),
});

mockGetRelation.mockReset();
Expand Down Expand Up @@ -1038,12 +1017,7 @@ describe("DirectoryV3", () => {
);
const result = await directory.relations(params);

expect(result).toEqual({
$typeName: "aserto.directory.reader.v3.GetRelationsResponse",
results: [],
objects: {},
toJSON: expect.any(Function),
});
expect(result).toEqual({ objects: {}, results: [] });

mockGetRelations.mockReset();
});
Expand Down Expand Up @@ -1153,9 +1127,7 @@ describe("DirectoryV3", () => {
undefined,
);
expect(result).toEqual({
$typeName: "aserto.directory.writer.v3.DeleteRelationResponse",
result: { $typeName: "google.protobuf.Empty" },
toJSON: expect.any(Function),
result: {},
});

mockDeleteRelation.mockReset();
Expand Down Expand Up @@ -1209,7 +1181,6 @@ describe("DirectoryV3", () => {
body: "test",
etag: "",
updatedAt: undefined,
toJSON: expect.any(Function),
});

getManifestMock.mockReset();
Expand Down Expand Up @@ -1238,9 +1209,7 @@ describe("DirectoryV3", () => {

const result = await directory.setManifest({ body: `a:\n b` });
expect(result).toEqual({
$typeName: "aserto.directory.model.v3.SetManifestResponse",
result: { $typeName: "google.protobuf.Empty" },
toJSON: expect.any(Function),
result: {},
});

mockSetManifest.mockReset();
Expand All @@ -1266,10 +1235,7 @@ describe("DirectoryV3", () => {
.mockResolvedValue(create(DeleteManifestResponseSchema, {}));

const result = await directory.deleteManifest();
expect(result).toEqual({
$typeName: "aserto.directory.model.v3.DeleteManifestResponse",
toJSON: expect.any(Function),
});
expect(result).toEqual({});

mockDeleteManifest.mockReset();
});
Expand Down
77 changes: 77 additions & 0 deletions __tests__/directory/v3/serializeResponse.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Unit tests for: serializeResponse

import { file_aserto_directory_common_v3_common } from "@aserto/node-directory/src/gen/cjs/aserto/directory/common/v3/common_pb";
import { file_aserto_directory_exporter_v3_exporter } from "@aserto/node-directory/src/gen/cjs/aserto/directory/exporter/v3/exporter_pb";
import { file_aserto_directory_importer_v3_importer } from "@aserto/node-directory/src/gen/cjs/aserto/directory/importer/v3/importer_pb";
import { file_aserto_directory_model_v3_model } from "@aserto/node-directory/src/gen/cjs/aserto/directory/model/v3/model_pb";
import {
file_aserto_directory_reader_v3_reader,
GetObjectResponse,
} from "@aserto/node-directory/src/gen/cjs/aserto/directory/reader/v3/reader_pb";
import { file_aserto_directory_writer_v3_writer } from "@aserto/node-directory/src/gen/cjs/aserto/directory/writer/v3/writer_pb";
import { createRegistry, Registry } from "@bufbuild/protobuf";
import { file_google_protobuf_timestamp } from "@bufbuild/protobuf/wkt";

import { InvalidSchemaError } from "../../../lib";
import { serializeResponse } from "../../../lib/directory/v3/serializer";

type MockGenMessage = {
$typeName: string;
[key: string]: unknown;
};

const mockRegistry: Registry = createRegistry(
file_aserto_directory_common_v3_common,
file_aserto_directory_reader_v3_reader,
file_aserto_directory_writer_v3_writer,
file_aserto_directory_exporter_v3_exporter,
file_aserto_directory_importer_v3_importer,
file_aserto_directory_model_v3_model,
file_google_protobuf_timestamp,
);

describe("serializeResponse() serializeResponse method", () => {
beforeEach(() => {
jest.clearAllMocks();
});

it("should serialize a valid response object", () => {
const mockResponse: GetObjectResponse = {
$typeName: "aserto.directory.reader.v3.GetObjectResponse",
result: {
$typeName: "aserto.directory.common.v3.Object",
type: "user",
id: "123",
displayName: "",
etag: "1234",
},
relations: [],
};

const result = serializeResponse(mockResponse);

expect(result).toEqual({
relations: [],
result: {
id: "123",
type: "user",
displayName: "",
etag: "1234",
},
});
});
});

it("should throw InvalidSchemaError if schema is not found", () => {
const mockResponse: MockGenMessage = {
$typeName: "invalid.type.name",
};

jest.spyOn(mockRegistry, "getMessage").mockReturnValue(undefined);

expect(() => serializeResponse(mockResponse)).toThrow(
new InvalidSchemaError(
"schema not registered for type: [invalid.type.name]",
),
);
});
Loading

0 comments on commit 0653724

Please sign in to comment.