Skip to content

Commit ad50ecf

Browse files
Correct parameter handling in Go example generation script (#566)
1 parent 3a174a2 commit ad50ecf

2 files changed

Lines changed: 89 additions & 68 deletions

File tree

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,37 @@ import (
3232
"context"
3333
"fmt"
3434
"log"
35-
"os"
36-
3735
"github.com/massive-com/client-go/v3/rest"
3836
"github.com/massive-com/client-go/v3/rest/gen"
3937
)
4038
4139
func main() {
42-
c := rest.NewWithOptions(os.Getenv("MASSIVE_API_KEY"),
40+
41+
c := rest.NewWithOptions("YOUR_API_KEY",
4342
rest.WithTrace(false),
4443
rest.WithPagination(true),
4544
)
4645
ctx := context.Background()
4746
48-
resp, err := c.GetStocksAggregatesWithResponse(ctx,
47+
params := &gen.GetStocksAggregatesParams{
48+
Adjusted: rest.Ptr(true),
49+
Sort: "asc",
50+
Limit: rest.Ptr(120),
51+
}
52+
53+
resp, err := c.GetStocksAggregatesWithResponse(
54+
ctx,
4955
"AAPL",
5056
1,
51-
gen.GetStocksAggregatesParamsTimespan("day"),
52-
"2025-11-03",
53-
"2025-11-28",
54-
nil, // nil = use API defaults (recommended)
57+
"day",
58+
"2026-02-16",
59+
"2026-02-20",
60+
params,
5561
)
5662
if err != nil {
5763
log.Fatal(err)
5864
}
5965
60-
// prints status + full error body (e.g. "Unknown API Key")
6166
if err := rest.CheckResponse(resp); err != nil {
6267
log.Fatal(err)
6368
}

rest/scripts/generate-go-examples.js

Lines changed: 75 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if (!fs.existsSync(tokenizedDir)) fs.mkdirSync(tokenizedDir, { recursive: true }
1111
// === Helpers ===
1212
const toPascalCase = (str) => {
1313
return str
14-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2') // split camelCase
14+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
1515
.split(/[-_ ]+/)
1616
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
1717
.join('');
@@ -39,11 +39,6 @@ const toSnakeCase = (str) =>
3939
.replace(/[-\/.]/g, '_')
4040
.toLowerCase();
4141

42-
const isIntField = (param) => {
43-
const type = param.schema?.type || 'string';
44-
return type === 'integer' || type === 'number';
45-
};
46-
4742
const isPaginated = (details) => {
4843
const successResp = details.responses?.['200'] || details.responses?.default;
4944
if (!successResp?.content?.['application/json']?.schema) return false;
@@ -56,18 +51,37 @@ const isEnumParam = (param) => {
5651
return Array.isArray(param.schema.enum) || !!param.schema.$ref;
5752
};
5853

59-
// NEW helper: decides whether a TOKEN_ should be wrapped in quotes in the tokenized version
54+
const shouldUseTypedEnum = (param) => {
55+
return !!param.schema?.$ref || param.schema?.default !== undefined;
56+
};
57+
6058
const isStringParam = (param) => {
61-
if (!param?.schema) return true; // most path params + unknown fields default to string
59+
if (!param?.schema) return true;
6260
const schema = param.schema;
6361
const type = schema.type;
6462
if (type === 'string' || !type) return true;
6563
if (type === 'integer' || type === 'number' || type === 'boolean') return false;
66-
// enums / $refs / arrays-of-strings are string-like
6764
return Array.isArray(schema.enum) || !!schema.$ref ||
6865
(schema.type === 'array' && schema.items?.type === 'string');
6966
};
7067

68+
const getParamValue = (param, useTokens = false) => {
69+
if (useTokens) {
70+
const token = toPolygonToken(param.name);
71+
return isStringParam(param) ? `"${token}"` : token;
72+
}
73+
74+
let value = param.example ?? param.schema?.example ?? param.schema?.default;
75+
if (value !== undefined) {
76+
return typeof value === 'string' ? `"${value}"` : value;
77+
}
78+
79+
const schemaType = param.schema?.type || 'string';
80+
if (schemaType === 'boolean') return 'true';
81+
if (schemaType === 'integer' || schemaType === 'number') return '100';
82+
return `"${param.name}"`;
83+
};
84+
7185
Object.entries(spec.paths).forEach(([route, methods]) => {
7286
Object.entries(methods).forEach(([httpMethod, details]) => {
7387
const operationId = details.operationId;
@@ -78,8 +92,11 @@ Object.entries(spec.paths).forEach(([route, methods]) => {
7892
const paramsType = goBaseName + 'Params';
7993
const fileName = toSnakeCase(operationId);
8094

81-
// Collect full path param objects (so we have .schema for type detection)
82-
const pathParams = (details.parameters || []).filter(p => p.in === 'path');
95+
const allParams = details.parameters || [];
96+
const pathParams = allParams.filter(p => p.in === 'path');
97+
const queryParams = allParams.filter(p => p.in === 'query');
98+
99+
const hasQueryParams = queryParams.length > 0;
83100

84101
const generateSnippet = (dir, useTokens = false) => {
85102
const lines = [
@@ -89,68 +106,67 @@ Object.entries(spec.paths).forEach(([route, methods]) => {
89106
` "context"`,
90107
` "fmt"`,
91108
` "log"`,
92-
` "massive-go-poc/rest"`,
93-
` "massive-go-poc/rest/gen"`,
94-
`)`,
95-
``,
96-
`func main() {`,
97-
``,
98-
` c := rest.NewWithOptions("YOUR_API_KEY", rest.WithTrace(false), rest.WithPagination(true))`,
99-
` ctx := context.Background()`,
100-
``,
101-
//` params := &gen.${paramsType}{`,
109+
` "github.com/massive-com/client-go/v3/rest"`,
102110
];
103111

112+
if (hasQueryParams) {
113+
lines.push(` "github.com/massive-com/client-go/v3/rest/gen"`);
114+
}
115+
116+
lines.push(`)`);
117+
lines.push(``);
118+
lines.push(`func main() {`);
119+
lines.push(``);
120+
121+
lines.push(` c := rest.NewWithOptions("GLOBAL_TOKEN_API_KEY",`);
122+
lines.push(` rest.WithTrace(false),`);
123+
lines.push(` rest.WithPagination(true),`);
124+
lines.push(` )`);
125+
lines.push(` ctx := context.Background()`);
126+
lines.push(``);
104127

105-
// === Build query param lines (extra indent for inline struct) ===
106-
const paramLines = [];
107-
if (details.parameters) {
108-
details.parameters.forEach(param => {
109-
if (param.in === 'query') {
110-
const fieldName = toGoFieldName(param.name);
111-
const schemaType = param.schema?.type || 'string';
128+
if (hasQueryParams) {
129+
lines.push(` params := &gen.${paramsType}{`);
112130

113-
let value;
114-
if (useTokens) {
115-
const token = toPolygonToken(param.name);
116-
value = isStringParam(param) ? `"${token}"` : token;
117-
} else {
118-
if (schemaType === 'boolean') value = 'true';
119-
else if (schemaType === 'integer' || schemaType === 'number') value = '100';
120-
else value = param.example !== undefined
121-
? (typeof param.example === 'string' ? `"${param.example}"` : param.example)
122-
: `"${param.name}"`;
123-
}
131+
const paramLines = [];
132+
queryParams.forEach(param => {
133+
const fieldName = toGoFieldName(param.name);
134+
const value = getParamValue(param, useTokens);
124135

125-
let line;
126-
if (isEnumParam(param)) {
127-
line = ` ${fieldName}: rest.Ptr(gen.${paramsType}${fieldName}(${value})),`;
136+
let line;
137+
if (isEnumParam(param)) {
138+
if (shouldUseTypedEnum(param)) {
139+
line = ` ${fieldName}: rest.Ptr(gen.${paramsType}${fieldName}(${value})),`;
128140
} else {
129-
line = ` ${fieldName}: rest.Ptr(${value}),`;
141+
line = ` ${fieldName}: ${value},`;
130142
}
131-
paramLines.push(line);
143+
} else {
144+
line = ` ${fieldName}: rest.Ptr(${value}),`;
132145
}
146+
paramLines.push(line);
133147
});
134-
}
135148

136-
// === Multi-line call with inline params (exactly the style you wanted) ===
137-
const pathArgs = pathParams.map(param => {
138-
if (!useTokens) return `"${param.name}"`;
139-
const token = toPolygonToken(param.name);
140-
return isStringParam(param) ? `"${token}"` : token;
141-
});
149+
lines.push(paramLines.join('\n'));
150+
lines.push(` }`);
151+
lines.push(``);
152+
}
142153

143-
lines.push(` resp, err := c.${goMethod}(ctx,`);
154+
// === UNIFORM MULTILINE CALL FOR EVERY ENDPOINT (fixes the syntax error) ===
155+
// Every argument (including ctx) is now on its own line → perfect for token replacement
156+
lines.push(` resp, err := c.${goMethod}(`);
157+
lines.push(` ctx,`);
144158

145-
pathArgs.forEach(arg => {
146-
lines.push(` ${arg},`);
159+
// Path parameters (if any)
160+
pathParams.forEach(param => {
161+
const value = getParamValue(param, useTokens);
162+
lines.push(` ${value},`);
147163
});
148164

149-
lines.push(` &gen.${paramsType}{`);
150-
if (paramLines.length > 0) {
151-
lines.push(paramLines.join('\n'));
165+
// Query params struct (if any)
166+
if (hasQueryParams) {
167+
lines.push(` params,`);
152168
}
153-
lines.push(` },`);
169+
154170
lines.push(` )`);
155171

156172
lines.push(` if err != nil {`);
@@ -186,4 +202,4 @@ Object.entries(spec.paths).forEach(([route, methods]) => {
186202
});
187203
});
188204

189-
console.log('🎉 All Go examples generated with string tokens properly quoted in the tokenized version!');
205+
console.log('🎉 All Go examples generated.');

0 commit comments

Comments
 (0)