-
Notifications
You must be signed in to change notification settings - Fork 48
RSDK-10408 Add world-state-store service #621
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
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
431057c
Add world-state-store service
DTCurrie 6df9360
cleanup, include transform with uuid string
DTCurrie fcad09a
uuid to string for list rpc
DTCurrie 79772b8
accept string for get transform
DTCurrie 77f251f
Merge branch 'main' of github.com:viamrobotics/viam-typescript-sdk in…
DTCurrie 35cf81f
fix tests
DTCurrie b8b706f
Add service to client
DTCurrie e8fb2f5
fix types
DTCurrie 866db00
export TransformChangeStream
DTCurrie a8df906
add stream iterator type
DTCurrie 5cb0c40
Merge branch 'main' of github.com:viamrobotics/viam-typescript-sdk in…
DTCurrie a826132
fix test
DTCurrie 1832d9e
some type clenaup post e2e testing
DTCurrie File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// @vitest-environment happy-dom | ||
|
||
import { createClient, createRouterTransport } from '@connectrpc/connect'; | ||
import { Struct } from '@bufbuild/protobuf'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { WorldStateStoreService } from '../../gen/service/worldstatestore/v1/world_state_store_connect'; | ||
import { | ||
GetTransformResponse, | ||
ListUUIDsResponse, | ||
StreamTransformChangesResponse, | ||
} from '../../gen/service/worldstatestore/v1/world_state_store_pb'; | ||
import { RobotClient } from '../../robot'; | ||
import { WorldStateStoreClient } from './client'; | ||
import { TransformChangeType } from '../../gen/service/worldstatestore/v1/world_state_store_pb'; | ||
import { Transform, PoseInFrame, Pose } from '../../gen/common/v1/common_pb'; | ||
import { transformWithUUID, uuidToString } from './world-state-store'; | ||
|
||
vi.mock('../../robot'); | ||
|
||
const worldStateStoreClientName = 'test-world-state-store'; | ||
|
||
let worldStateStore: WorldStateStoreClient; | ||
|
||
const mockUuids = [new Uint8Array([1, 2, 3, 4]), new Uint8Array([5, 6, 7, 8])]; | ||
const mockTransform = new Transform({ | ||
referenceFrame: 'test-frame', | ||
poseInObserverFrame: new PoseInFrame({ | ||
referenceFrame: 'observer-frame', | ||
pose: new Pose({ | ||
x: 10, | ||
y: 20, | ||
z: 30, | ||
oX: 0, | ||
oY: 0, | ||
oZ: 1, | ||
theta: 90, | ||
}), | ||
}), | ||
uuid: mockUuids[0], | ||
}); | ||
|
||
describe('WorldStateStoreClient Tests', () => { | ||
beforeEach(() => { | ||
const mockTransport = createRouterTransport(({ service }) => { | ||
service(WorldStateStoreService, { | ||
listUUIDs: () => new ListUUIDsResponse({ uuids: mockUuids }), | ||
getTransform: () => | ||
new GetTransformResponse({ transform: mockTransform }), | ||
streamTransformChanges: async function* mockStream() { | ||
// Add await to satisfy linter | ||
await Promise.resolve(); | ||
yield new StreamTransformChangesResponse({ | ||
changeType: TransformChangeType.ADDED, | ||
transform: mockTransform, | ||
}); | ||
yield new StreamTransformChangesResponse({ | ||
changeType: TransformChangeType.UPDATED, | ||
transform: mockTransform, | ||
updatedFields: { paths: ['pose_in_observer_frame'] }, | ||
}); | ||
}, | ||
doCommand: () => ({ result: Struct.fromJson({ success: true }) }), | ||
}); | ||
}); | ||
|
||
RobotClient.prototype.createServiceClient = vi | ||
.fn() | ||
.mockImplementation(() => | ||
createClient(WorldStateStoreService, mockTransport) | ||
); | ||
worldStateStore = new WorldStateStoreClient( | ||
new RobotClient('host'), | ||
worldStateStoreClientName | ||
); | ||
}); | ||
|
||
describe('listUUIDs', () => { | ||
it('returns all transform UUIDs', async () => { | ||
const expected = mockUuids.map((uuid) => uuidToString(uuid)); | ||
|
||
await expect(worldStateStore.listUUIDs()).resolves.toStrictEqual( | ||
expected | ||
); | ||
}); | ||
}); | ||
|
||
describe('getTransform', () => { | ||
it('returns a transform by UUID', async () => { | ||
const uuid = '01020304'; | ||
const expected = mockTransform; | ||
|
||
await expect(worldStateStore.getTransform(uuid)).resolves.toStrictEqual({ | ||
...expected, | ||
uuidString: uuid, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('streamTransformChanges', () => { | ||
it('streams transform changes', async () => { | ||
const stream = worldStateStore.streamTransformChanges(); | ||
const results = []; | ||
|
||
for await (const result of stream) { | ||
results.push(result); | ||
} | ||
|
||
expect(results).toHaveLength(2); | ||
expect(results[0]).toEqual({ | ||
changeType: TransformChangeType.ADDED, | ||
transform: transformWithUUID(mockTransform), | ||
updatedFields: undefined, | ||
}); | ||
expect(results[1]).toEqual({ | ||
changeType: TransformChangeType.UPDATED, | ||
transform: transformWithUUID(mockTransform), | ||
updatedFields: { paths: ['pose_in_observer_frame'] }, | ||
}); | ||
}); | ||
}); | ||
|
||
describe('doCommand', () => { | ||
it('executes arbitrary commands', async () => { | ||
const command = Struct.fromJson({ test: 'value' }); | ||
const expected = { success: true }; | ||
|
||
await expect(worldStateStore.doCommand(command)).resolves.toStrictEqual( | ||
expected | ||
); | ||
}); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { Struct, type JsonValue } from '@bufbuild/protobuf'; | ||
import type { CallOptions, Client } from '@connectrpc/connect'; | ||
import { WorldStateStoreService } from '../../gen/service/worldstatestore/v1/world_state_store_connect'; | ||
import { | ||
GetTransformRequest, | ||
ListUUIDsRequest, | ||
StreamTransformChangesRequest, | ||
} from '../../gen/service/worldstatestore/v1/world_state_store_pb'; | ||
import type { RobotClient } from '../../robot'; | ||
import type { Options } from '../../types'; | ||
import { doCommandFromClient } from '../../utils'; | ||
import type { WorldStateStore } from './world-state-store'; | ||
import { | ||
transformWithUUID, | ||
uuidFromString, | ||
uuidToString, | ||
} from './world-state-store'; | ||
import type { TransformChangeEvent } from './types'; | ||
|
||
/** | ||
* A gRPC-web client for a WorldStateStore service. | ||
* | ||
* @group Clients | ||
*/ | ||
export class WorldStateStoreClient implements WorldStateStore { | ||
private client: Client<typeof WorldStateStoreService>; | ||
public readonly name: string; | ||
private readonly options: Options; | ||
public callOptions: CallOptions = { headers: {} as Record<string, string> }; | ||
|
||
constructor(client: RobotClient, name: string, options: Options = {}) { | ||
this.client = client.createServiceClient(WorldStateStoreService); | ||
this.name = name; | ||
this.options = options; | ||
} | ||
|
||
async listUUIDs(extra = {}, callOptions = this.callOptions) { | ||
const request = new ListUUIDsRequest({ | ||
name: this.name, | ||
extra: Struct.fromJson(extra), | ||
}); | ||
|
||
this.options.requestLogger?.(request); | ||
|
||
const response = await this.client.listUUIDs(request, callOptions); | ||
return response.uuids.map((uuid) => uuidToString(uuid)); | ||
} | ||
|
||
async getTransform(uuid: string, extra = {}, callOptions = this.callOptions) { | ||
const request = new GetTransformRequest({ | ||
name: this.name, | ||
uuid: uuidFromString(uuid), | ||
extra: Struct.fromJson(extra), | ||
}); | ||
|
||
this.options.requestLogger?.(request); | ||
|
||
const response = await this.client.getTransform(request, callOptions); | ||
if (!response.transform) { | ||
throw new Error('No transform returned from server'); | ||
} | ||
|
||
return transformWithUUID(response.transform); | ||
} | ||
|
||
async *streamTransformChanges( | ||
extra = {}, | ||
callOptions = this.callOptions | ||
): AsyncGenerator<TransformChangeEvent, void> { | ||
const request = new StreamTransformChangesRequest({ | ||
name: this.name, | ||
extra: Struct.fromJson(extra), | ||
}); | ||
|
||
this.options.requestLogger?.(request); | ||
|
||
const stream = this.client.streamTransformChanges(request, callOptions); | ||
|
||
for await (const response of stream) { | ||
if (!response.transform) { | ||
continue; | ||
} | ||
|
||
yield { | ||
...response, | ||
transform: transformWithUUID(response.transform), | ||
}; | ||
} | ||
} | ||
|
||
async doCommand( | ||
command: Struct, | ||
callOptions = this.callOptions | ||
): Promise<JsonValue> { | ||
return doCommandFromClient( | ||
this.client.doCommand, | ||
this.name, | ||
command, | ||
this.options, | ||
callOptions | ||
); | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
export { WorldStateStoreClient } from './client'; | ||
export type { WorldStateStore } from './world-state-store'; | ||
export { transformWithUUID } from './world-state-store'; | ||
export type { TransformChangeEvent, TransformWithUUID } from './types'; | ||
|
||
export { TransformChangeType } from '../../gen/service/worldstatestore/v1/world_state_store_pb'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import type { PlainMessage } from '@bufbuild/protobuf'; | ||
import type { Transform } from '../../gen/common/v1/common_pb'; | ||
import type { StreamTransformChangesResponse } from '../../gen/service/worldstatestore/v1/world_state_store_pb'; | ||
|
||
export interface TransformWithUUID extends PlainMessage<Transform> { | ||
uuidString: string; | ||
} | ||
|
||
export type TransformChangeEvent = Omit< | ||
PlainMessage<StreamTransformChangesResponse>, | ||
'transform' | ||
> & { | ||
transform: TransformWithUUID | undefined; | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.