Skip to content

Commit d43e7de

Browse files
committed
Add JSON-RPC methods for performance tracing
1 parent 46c9eb9 commit d43e7de

File tree

9 files changed

+386
-2
lines changed

9 files changed

+386
-2
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
2+
import type { PermittedHandlerExport } from '@metamask/permission-controller';
3+
import { rpcErrors } from '@metamask/rpc-errors';
4+
import type {
5+
JsonRpcRequest,
6+
EndTraceParams,
7+
EndTraceResult,
8+
TraceRequest,
9+
} from '@metamask/snaps-sdk';
10+
import type { InferMatching, Snap } from '@metamask/snaps-utils';
11+
import {
12+
number,
13+
create,
14+
object,
15+
string,
16+
StructError,
17+
exactOptional,
18+
} from '@metamask/superstruct';
19+
import type { PendingJsonRpcResponse } from '@metamask/utils';
20+
21+
import type { MethodHooksObject } from '../utils';
22+
23+
const hookNames: MethodHooksObject<EndTraceMethodHooks> = {
24+
endTrace: true,
25+
getSnap: true,
26+
};
27+
28+
export type EndTraceMethodHooks = {
29+
/**
30+
* End a performance trace in Sentry.
31+
*
32+
* @param request - The trace request object.
33+
* @returns The performance trace context.
34+
*/
35+
endTrace: (request: TraceRequest) => string;
36+
37+
/**
38+
* Get Snap metadata.
39+
*
40+
* @param snapId - The ID of a Snap.
41+
*/
42+
getSnap: (snapId: string) => Snap | undefined;
43+
};
44+
45+
const EndTraceParametersStruct = object({
46+
id: exactOptional(string()),
47+
name: string(),
48+
timestamp: exactOptional(number()),
49+
});
50+
51+
export type EndTraceParameters = InferMatching<
52+
typeof EndTraceParametersStruct,
53+
EndTraceParams
54+
>;
55+
56+
/**
57+
* Handler for the `snap_endTrace` method.
58+
*/
59+
export const endTraceHandler: PermittedHandlerExport<
60+
EndTraceMethodHooks,
61+
EndTraceParameters,
62+
EndTraceResult
63+
> = {
64+
methodNames: ['snap_endTrace'],
65+
implementation: getEndTraceImplementation,
66+
hookNames,
67+
};
68+
69+
/**
70+
* The `snap_endTrace` method implementation. This method is used to end a
71+
* performance trace in Sentry. It is only available to preinstalled Snaps.
72+
*
73+
* @param request - The JSON-RPC request object.
74+
* @param response - The JSON-RPC response object.
75+
* @param _next - The `json-rpc-engine` "next" callback. Not used by this
76+
* function.
77+
* @param end - The `json-rpc-engine` "end" callback.
78+
* @param hooks - The RPC method hooks.
79+
* @param hooks.endTrace - The hook function to end a performance trace.
80+
* @param hooks.getSnap - The hook function to get Snap metadata.
81+
* @returns Nothing.
82+
*/
83+
function getEndTraceImplementation(
84+
request: JsonRpcRequest<EndTraceParameters>,
85+
response: PendingJsonRpcResponse,
86+
_next: unknown,
87+
end: JsonRpcEngineEndCallback,
88+
{ endTrace, getSnap }: EndTraceMethodHooks,
89+
): void {
90+
const snap = getSnap(
91+
(request as JsonRpcRequest<EndTraceParams> & { origin: string }).origin,
92+
);
93+
94+
if (!snap?.preinstalled) {
95+
return end(rpcErrors.methodNotFound());
96+
}
97+
98+
const { params } = request;
99+
100+
try {
101+
const validatedParams = getValidatedParams(params);
102+
response.result = endTrace(validatedParams);
103+
} catch (error) {
104+
return end(error);
105+
}
106+
107+
return end();
108+
}
109+
110+
/**
111+
* Validate the parameters for the `snap_endTrace` method.
112+
*
113+
* @param params - Parameters to validate.
114+
* @returns Validated parameters.
115+
* @throws Throws RPC error if validation fails.
116+
*/
117+
function getValidatedParams(params: unknown): EndTraceParameters {
118+
try {
119+
return create(params, EndTraceParametersStruct);
120+
} catch (error) {
121+
if (error instanceof StructError) {
122+
throw rpcErrors.invalidParams({
123+
message: `Invalid params: ${error.message}.`,
124+
});
125+
}
126+
127+
/* istanbul ignore next */
128+
throw rpcErrors.internal();
129+
}
130+
}

packages/snaps-rpc-methods/src/permitted/handlers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { cancelBackgroundEventHandler } from './cancelBackgroundEvent';
22
import { clearStateHandler } from './clearState';
33
import { closeWebSocketHandler } from './closeWebSocket';
44
import { createInterfaceHandler } from './createInterface';
5+
import { endTraceHandler } from './endTrace';
56
import { providerRequestHandler } from './experimentalProviderRequest';
67
import { getAllSnapsHandler } from './getAllSnaps';
78
import { getBackgroundEventsHandler } from './getBackgroundEvents';
@@ -22,6 +23,7 @@ import { resolveInterfaceHandler } from './resolveInterface';
2223
import { scheduleBackgroundEventHandler } from './scheduleBackgroundEvent';
2324
import { sendWebSocketMessageHandler } from './sendWebSocketMessage';
2425
import { setStateHandler } from './setState';
26+
import { startTraceHandler } from './startTrace';
2527
import { trackErrorHandler } from './trackError';
2628
import { trackEventHandler } from './trackEvent';
2729
import { updateInterfaceHandler } from './updateInterface';
@@ -55,6 +57,8 @@ export const methodHandlers = {
5557
snap_closeWebSocket: closeWebSocketHandler,
5658
snap_sendWebSocketMessage: sendWebSocketMessageHandler,
5759
snap_getWebSockets: getWebSocketsHandler,
60+
snap_startTrace: startTraceHandler,
61+
snap_endTrace: endTraceHandler,
5862
};
5963
/* eslint-enable @typescript-eslint/naming-convention */
6064

packages/snaps-rpc-methods/src/permitted/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { CancelBackgroundEventMethodHooks } from './cancelBackgroundEvent';
22
import type { ClearStateHooks } from './clearState';
33
import type { CloseWebSocketMethodHooks } from './closeWebSocket';
44
import type { CreateInterfaceMethodHooks } from './createInterface';
5+
import type { EndTraceMethodHooks } from './endTrace';
56
import type { ProviderRequestMethodHooks } from './experimentalProviderRequest';
67
import type { GetAllSnapsHooks } from './getAllSnaps';
78
import type { GetBackgroundEventsMethodHooks } from './getBackgroundEvents';
@@ -18,6 +19,7 @@ import type { ResolveInterfaceMethodHooks } from './resolveInterface';
1819
import type { ScheduleBackgroundEventMethodHooks } from './scheduleBackgroundEvent';
1920
import type { SendWebSocketMessageMethodHooks } from './sendWebSocketMessage';
2021
import type { SetStateHooks } from './setState';
22+
import type { StartTraceMethodHooks } from './startTrace';
2123
import type { TrackErrorMethodHooks } from './trackError';
2224
import type { TrackEventMethodHooks } from './trackEvent';
2325
import type { UpdateInterfaceMethodHooks } from './updateInterface';
@@ -44,7 +46,9 @@ export type PermittedRpcMethodHooks = ClearStateHooks &
4446
SendWebSocketMessageMethodHooks &
4547
GetWebSocketsMethodHooks &
4648
TrackEventMethodHooks &
47-
TrackErrorMethodHooks;
49+
TrackErrorMethodHooks &
50+
StartTraceMethodHooks &
51+
EndTraceMethodHooks;
4852

4953
export * from './handlers';
5054
export * from './middleware';
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
2+
import type { PermittedHandlerExport } from '@metamask/permission-controller';
3+
import { rpcErrors } from '@metamask/rpc-errors';
4+
import type {
5+
JsonRpcRequest,
6+
StartTraceParams,
7+
StartTraceResult,
8+
TraceRequest,
9+
} from '@metamask/snaps-sdk';
10+
import type { InferMatching, Snap } from '@metamask/snaps-utils';
11+
import {
12+
boolean,
13+
number,
14+
record,
15+
union,
16+
create,
17+
object,
18+
string,
19+
StructError,
20+
exactOptional,
21+
} from '@metamask/superstruct';
22+
import type { PendingJsonRpcResponse } from '@metamask/utils';
23+
import { JsonStruct } from '@metamask/utils';
24+
25+
import type { MethodHooksObject } from '../utils';
26+
27+
const hookNames: MethodHooksObject<StartTraceMethodHooks> = {
28+
startTrace: true,
29+
getSnap: true,
30+
};
31+
32+
export type StartTraceMethodHooks = {
33+
/**
34+
* Start a performance trace in Sentry.
35+
*
36+
* @param request - The trace request object.
37+
* @returns The performance trace context.
38+
*/
39+
startTrace: (request: TraceRequest) => string;
40+
41+
/**
42+
* Get Snap metadata.
43+
*
44+
* @param snapId - The ID of a Snap.
45+
*/
46+
getSnap: (snapId: string) => Snap | undefined;
47+
};
48+
49+
const StartTraceParametersStruct = object({
50+
data: exactOptional(record(string(), union([string(), number(), boolean()]))),
51+
id: exactOptional(string()),
52+
name: string(),
53+
parentContext: exactOptional(JsonStruct),
54+
startTime: exactOptional(number()),
55+
tags: exactOptional(record(string(), union([string(), number(), boolean()]))),
56+
});
57+
58+
export type StartTraceParameters = InferMatching<
59+
typeof StartTraceParametersStruct,
60+
StartTraceParams
61+
>;
62+
63+
/**
64+
* Handler for the `snap_startTrace` method.
65+
*/
66+
export const startTraceHandler: PermittedHandlerExport<
67+
StartTraceMethodHooks,
68+
StartTraceParameters,
69+
StartTraceResult
70+
> = {
71+
methodNames: ['snap_startTrace'],
72+
implementation: getStartTraceImplementation,
73+
hookNames,
74+
};
75+
76+
/**
77+
* The `snap_startTrace` method implementation. This method is used to start a
78+
* performance trace in Sentry. It is only available to preinstalled Snaps.
79+
*
80+
* @param request - The JSON-RPC request object.
81+
* @param response - The JSON-RPC response object.
82+
* @param _next - The `json-rpc-engine` "next" callback. Not used by this
83+
* function.
84+
* @param end - The `json-rpc-engine` "end" callback.
85+
* @param hooks - The RPC method hooks.
86+
* @param hooks.startTrace - The hook function to start a performance trace.
87+
* @param hooks.getSnap - The hook function to get Snap metadata.
88+
* @returns Nothing.
89+
*/
90+
function getStartTraceImplementation(
91+
request: JsonRpcRequest<StartTraceParameters>,
92+
response: PendingJsonRpcResponse,
93+
_next: unknown,
94+
end: JsonRpcEngineEndCallback,
95+
{ startTrace, getSnap }: StartTraceMethodHooks,
96+
): void {
97+
const snap = getSnap(
98+
(request as JsonRpcRequest<StartTraceParams> & { origin: string }).origin,
99+
);
100+
101+
if (!snap?.preinstalled) {
102+
return end(rpcErrors.methodNotFound());
103+
}
104+
105+
const { params } = request;
106+
107+
try {
108+
const validatedParams = getValidatedParams(params);
109+
response.result = startTrace(validatedParams);
110+
} catch (error) {
111+
return end(error);
112+
}
113+
114+
return end();
115+
}
116+
117+
/**
118+
* Validate the parameters for the `snap_startTrace` method.
119+
*
120+
* @param params - Parameters to validate.
121+
* @returns Validated parameters.
122+
* @throws Throws RPC error if validation fails.
123+
*/
124+
function getValidatedParams(params: unknown): StartTraceParameters {
125+
try {
126+
return create(params, StartTraceParametersStruct);
127+
} catch (error) {
128+
if (error instanceof StructError) {
129+
throw rpcErrors.invalidParams({
130+
message: `Invalid params: ${error.message}.`,
131+
});
132+
}
133+
134+
/* istanbul ignore next */
135+
throw rpcErrors.internal();
136+
}
137+
}

packages/snaps-rpc-methods/src/permitted/trackError.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function getTrackErrorImplementation(
117117
}
118118

119119
/**
120-
* Validates the parameters for the snap_trackEvent method.
120+
* Validate the parameters for the `snap_trackError` method.
121121
*
122122
* @param params - Parameters to validate.
123123
* @returns Validated parameters.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import type { TraceName } from './start-trace';
2+
3+
/**
4+
* A request to end a pending trace.
5+
*/
6+
export type EndTraceRequest = {
7+
/**
8+
* The unique identifier of the trace.
9+
* Defaults to 'default' if not provided.
10+
*/
11+
id?: string;
12+
13+
/**
14+
* The name of the trace.
15+
*/
16+
name: TraceName;
17+
18+
/**
19+
* Override the end time of the trace.
20+
*/
21+
timestamp?: number;
22+
};
23+
24+
/**
25+
* The request parameters for the `snap_endTrace` method. This method is used
26+
* to end a performance trace in Sentry.
27+
*
28+
* Note that this method is only available to preinstalled Snaps.
29+
*/
30+
export type EndTraceParams = EndTraceRequest;
31+
32+
/**
33+
* The result returned by the `snap_endTrace` method.
34+
*/
35+
export type EndTraceResult = null;

packages/snaps-sdk/src/types/methods/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export type * from './clear-state';
22
export type * from './create-interface';
33
export * from './dialog';
4+
export type * from './end-trace';
45
export type * from './get-bip32-entropy';
56
export type * from './get-bip32-public-key';
67
export type * from './get-bip44-entropy';
@@ -23,6 +24,7 @@ export type * from './methods';
2324
export * from './notify';
2425
export type * from './provider-request';
2526
export type * from './request-snaps';
27+
export type * from './start-trace';
2628
export type * from './update-interface';
2729
export type * from './resolve-interface';
2830
export type * from './schedule-background-event';

0 commit comments

Comments
 (0)