Skip to content

Commit b52bf76

Browse files
committed
refactor(twenty-partners): strictly type list-available-partners, drop as any
The CoreApiClient is codegenerated from the synced workspace schema, so the `/partners` query is fully typed. Remove the type-lossy `as any` on the query and the `as Array<{ node: Partner }>` result cast, and derive the response type from the selection itself (dropping the hand-written Partner DTO that claimed `amountMicros: number` for a BigFloat and required fields for optional ones). This also fixes the latent "Property 'edges' does not exist on type '{}'" error the cast was masking.
1 parent 701d1fb commit b52bf76

1 file changed

Lines changed: 44 additions & 60 deletions

File tree

packages/twenty-apps/internal/twenty-partners/src/logic-functions/list-available-partners.logic-function.ts

Lines changed: 44 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -4,74 +4,58 @@ import { defineLogicFunction } from 'twenty-sdk/define';
44
export const LIST_AVAILABLE_PARTNERS_LOGIC_FUNCTION_ID =
55
'0f91164f-f492-41e8-9bb0-481be5a3d5b9';
66

7-
type CurrencyValue = { amountMicros: number; currencyCode: string } | null;
8-
type LinkValue = { primaryLinkUrl: string | null } | null;
7+
// CoreApiClient is codegenerated from the synced workspace schema, so the query
8+
// selection is strictly typed. Keep the fetch in one place and derive the
9+
// response shape from it, so the HTTP contract can never drift from what we
10+
// actually ask the API for.
11+
const queryAvailablePartners = (client: CoreApiClient) =>
12+
client.query({
13+
partners: {
14+
__args: {
15+
filter: {
16+
validationStage: { eq: 'VALIDATED' },
17+
availability: { eq: 'AVAILABLE' },
18+
},
19+
orderBy: [{ name: 'AscNullsLast' }],
20+
first: 100,
21+
},
22+
edges: {
23+
node: {
24+
id: true,
25+
name: true,
26+
slug: true,
27+
introduction: true,
28+
languagesSpoken: true,
29+
deploymentExpertise: true,
30+
partnerScope: true,
31+
region: true,
32+
calendarLink: { primaryLinkUrl: true },
33+
hourlyRate: { amountMicros: true, currencyCode: true },
34+
projectBudgetMin: { amountMicros: true, currencyCode: true },
35+
projectBudgetTypical: { amountMicros: true, currencyCode: true },
36+
linkedin: { primaryLinkUrl: true },
37+
profilePicture: { primaryLinkUrl: true },
38+
skills: true,
39+
city: true,
40+
country: true,
41+
},
42+
},
43+
},
44+
});
945

10-
type Partner = {
11-
id: string;
12-
name: string | null;
13-
slug: string | null;
14-
introduction: string | null;
15-
languagesSpoken: string[] | null;
16-
deploymentExpertise: string[] | null;
17-
partnerScope: string[] | null;
18-
region: string[] | null;
19-
calendarLink: LinkValue;
20-
hourlyRate: CurrencyValue;
21-
projectBudgetMin: CurrencyValue;
22-
projectBudgetTypical: CurrencyValue;
23-
linkedin: LinkValue;
24-
profilePicture: LinkValue;
25-
skills: string[] | null;
26-
city: string | null;
27-
country: string | null;
28-
};
46+
type AvailablePartner = NonNullable<
47+
Awaited<ReturnType<typeof queryAvailablePartners>>['partners']
48+
>['edges'][number]['node'];
2949

3050
type ListAvailablePartnersResult =
31-
| { ok: true; count: number; partners: Partner[] }
51+
| { ok: true; count: number; partners: AvailablePartner[] }
3252
| { ok: false; reason: string };
3353

3454
const handler = async (): Promise<ListAvailablePartnersResult> => {
3555
try {
3656
const client = new CoreApiClient();
37-
38-
const result = await client.query({
39-
partners: {
40-
__args: {
41-
filter: {
42-
validationStage: { eq: 'VALIDATED' },
43-
availability: { eq: 'AVAILABLE' },
44-
},
45-
orderBy: [{ name: 'AscNullsLast' }],
46-
first: 100,
47-
},
48-
edges: {
49-
node: {
50-
id: true,
51-
name: true,
52-
slug: true,
53-
introduction: true,
54-
languagesSpoken: true,
55-
deploymentExpertise: true,
56-
partnerScope: true,
57-
region: true,
58-
calendarLink: { primaryLinkUrl: true },
59-
hourlyRate: { amountMicros: true, currencyCode: true },
60-
projectBudgetMin: { amountMicros: true, currencyCode: true },
61-
projectBudgetTypical: { amountMicros: true, currencyCode: true },
62-
linkedin: { primaryLinkUrl: true },
63-
profilePicture: { primaryLinkUrl: true },
64-
skills: true,
65-
city: true,
66-
country: true,
67-
},
68-
},
69-
},
70-
} as any);
71-
72-
const partners = (
73-
(result?.partners?.edges ?? []) as Array<{ node: Partner }>
74-
).map((edge) => edge.node);
57+
const result = await queryAvailablePartners(client);
58+
const partners = (result.partners?.edges ?? []).map((edge) => edge.node);
7559

7660
return { ok: true, count: partners.length, partners };
7761
} catch (err) {

0 commit comments

Comments
 (0)