Skip to content

Commit c8016dc

Browse files
fix(rpc): handle sa schema properly with multi stack (#218)
1 parent ac37ead commit c8016dc

4 files changed

Lines changed: 115 additions & 2 deletions

File tree

packages/datasource-rpc/src/introspector.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ export function parseIntrospection(introSchema: IntrospectionSchema): RpcSchema
1212
// eslint-disable-next-line @typescript-eslint/naming-convention
1313
const { actions, fields, aggregation_capabilities, ...rest } = collection;
1414
const parsedActions = Object.entries(actions).reduce((pActions: any, [name, schema]) => {
15-
pActions[name] = cameliseKeys(schema);
15+
const camelSchema = cameliseKeys(schema) as Record<string, unknown>;
16+
camelSchema.generateFile = camelSchema.isGenerateFile;
17+
pActions[name] = camelSchema;
1618

1719
return pActions;
1820
}, {});
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { parseIntrospection } from '../src/introspector';
2+
import { IntrospectionSchema } from '../src/types';
3+
4+
function buildIntroSchema(actionSchema: Record<string, unknown>): IntrospectionSchema {
5+
return {
6+
etag: 'etag',
7+
charts: [],
8+
native_query_connections: [],
9+
rpc_relations: {},
10+
collections: [
11+
{
12+
name: 'Files',
13+
countable: false,
14+
searchable: false,
15+
charts: [],
16+
segments: [],
17+
fields: {},
18+
actions: { download: actionSchema },
19+
aggregation_capabilities: {
20+
supported_date_operations: [],
21+
support_groups: false,
22+
},
23+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
24+
} as any,
25+
],
26+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
27+
} as any;
28+
}
29+
30+
describe('parseIntrospection', () => {
31+
it('exposes is_generate_file from the wire as generateFile expected by the JS toolkit', () => {
32+
const parsed = parseIntrospection(
33+
buildIntroSchema({ scope: 'Single', is_generate_file: true, description: 'export' }),
34+
);
35+
36+
const action = parsed.collections[0].actions.download as Record<string, unknown>;
37+
expect(action.generateFile).toBe(true);
38+
expect(action.scope).toBe('Single');
39+
expect(action.description).toBe('export');
40+
});
41+
42+
it('leaves generateFile undefined when is_generate_file is absent', () => {
43+
const parsed = parseIntrospection(buildIntroSchema({ scope: 'Single' }));
44+
45+
const action = parsed.collections[0].actions.download as Record<string, unknown>;
46+
expect(action.generateFile).toBeUndefined();
47+
expect(action.scope).toBe('Single');
48+
});
49+
});

packages/rpc-agent/src/agent.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,11 @@ export default class RpcAgent<S extends TSchema = TSchema> extends Agent<S> {
9797
}, {});
9898

9999
const buildedActions = Object.entries(actions).reduce((bActions, [name, schema]) => {
100-
bActions[name] = keysToSnake(schema);
100+
const snakeSchema = keysToSnake(schema) as Record<string, unknown>;
101+
// Ruby toolkit reads the flag as `is_generate_file`; align the wire format with that
102+
// convention so Ruby main agents pick it up without an ad-hoc mapping on their side.
103+
snakeSchema.is_generate_file = snakeSchema.generate_file;
104+
bActions[name] = snakeSchema;
101105

102106
return bActions;
103107
}, {});
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import RpcAgent from '../src/agent';
2+
3+
function createAgent(): RpcAgent {
4+
return Object.create(RpcAgent.prototype) as RpcAgent;
5+
}
6+
7+
function buildCollectionFixture(actionSchema: Record<string, unknown>) {
8+
return {
9+
name: 'Files',
10+
schema: {
11+
fields: {},
12+
actions: { download: actionSchema },
13+
aggregationCapabilities: {
14+
supportedDateOperations: new Set<string>(),
15+
supportGroups: false,
16+
},
17+
countable: false,
18+
searchable: false,
19+
charts: [],
20+
segments: [],
21+
},
22+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
23+
} as any;
24+
}
25+
26+
describe('RpcAgent.buildCollection', () => {
27+
it('serialises generateFile as is_generate_file so Ruby main agents read it natively', () => {
28+
const agent = createAgent();
29+
const collection = buildCollectionFixture({
30+
scope: 'Single',
31+
generateFile: true,
32+
description: 'export',
33+
});
34+
35+
const built = agent.buildCollection(collection, {}) as { actions: Record<string, unknown> };
36+
const action = built.actions.download as Record<string, unknown>;
37+
38+
expect(action).toEqual({
39+
scope: 'Single',
40+
is_generate_file: true,
41+
description: 'export',
42+
});
43+
expect(action).not.toHaveProperty('generate_file');
44+
expect(action).not.toHaveProperty('generateFile');
45+
});
46+
47+
it('does not introduce is_generate_file when the source schema omits generateFile', () => {
48+
const agent = createAgent();
49+
const collection = buildCollectionFixture({ scope: 'Single' });
50+
51+
const built = agent.buildCollection(collection, {}) as { actions: Record<string, unknown> };
52+
const action = built.actions.download as Record<string, unknown>;
53+
54+
expect(action).toEqual({ scope: 'Single' });
55+
expect(action).not.toHaveProperty('is_generate_file');
56+
expect(action).not.toHaveProperty('generate_file');
57+
});
58+
});

0 commit comments

Comments
 (0)