Skip to content

Commit 3fdea7d

Browse files
committed
feat(code-samples): add instance configuration support
Add support for injecting instance configuration into generated code samples across multiple languages (Python, TypeScript, Go, Java) with language-specific transformation patterns.
1 parent 85f889a commit 3fdea7d

File tree

2 files changed

+241
-9
lines changed

2 files changed

+241
-9
lines changed

src/code-sample-transformer.js

Lines changed: 83 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import yaml from 'js-yaml';
22

3+
/**
4+
* Generator that yields language and code sample pairs from pathSpec
5+
* @param {Object} pathSpec The OpenAPI spec object for the specific path being processed
6+
* @yields {[string, Object]} A tuple containing the language and code sample object
7+
*/
8+
export function* codeSnippets(pathSpec) {
9+
for (const [_, methodSpec] of Object.entries(pathSpec)) {
10+
if (methodSpec && methodSpec['x-codeSamples']) {
11+
const codeSamples = methodSpec['x-codeSamples'];
12+
for (const sample of codeSamples) {
13+
yield [sample.lang, sample];
14+
}
15+
}
16+
}
17+
}
18+
319
/**
420
* Extracts code snippet for a specific language from pathSpec
521
* @param {Object} pathSpec The OpenAPI spec object for the specific path being processed
622
* @param {string} language The language to extract code snippets for
723
* @returns {string} The source code snippet for the specified language
824
*/
925
export function extractCodeSnippet(pathSpec, language) {
10-
for (const [httpMethod, methodSpec] of Object.entries(pathSpec)) {
11-
if (methodSpec && methodSpec['x-codeSamples']) {
12-
const codeSamples = methodSpec['x-codeSamples'];
13-
14-
const matchingSample = codeSamples.find(
15-
(sample) => sample.lang === language,
16-
);
17-
18-
return matchingSample;
26+
for (const [lang, sample] of codeSnippets(pathSpec)) {
27+
if (lang === language) {
28+
return sample;
1929
}
2030
}
2131

@@ -66,6 +76,69 @@ export function transformPythonCodeSamplesToPython(spec) {
6676
return spec;
6777
}
6878

79+
/**
80+
* Transforms code samples in an OpenAPI specification to add instance configuration
81+
* for various programming languages. This function adds the necessary instance/server
82+
* configuration to existing code samples that only contain API token configuration.
83+
*
84+
* Supported transformations:
85+
* - Python: Adds `instance=os.getenv("GLEAN_INSTANCE", "")` after `api_token`
86+
* - TypeScript: Adds `instance: process.env["GLEAN_INSTANCE"] ?? ""` after `apiToken`
87+
* - Go: Adds `apiclientgo.WithInstance("<value>")` before `WithSecurity`
88+
* - Java: Adds `.instance("<YOUR_GLEAN_INSTANCE_HERE>")` after `.apiToken()`
89+
*
90+
* @param {Object} spec The OpenAPI specification object containing code samples
91+
* @returns {Object} The modified OpenAPI specification with updated code samples
92+
*/
93+
export function addInstanceToCodeSamples(spec) {
94+
const transformationsByLang = {
95+
python: [
96+
[
97+
/([\s]*)(api_token=os\.getenv\("GLEAN_API_TOKEN", ""\),)/,
98+
'$1$2$1instance=os.getenv("GLEAN_INSTANCE", ""),',
99+
],
100+
],
101+
typescript: [
102+
[
103+
/([\s]*)(apiToken: process\.env\["GLEAN_API_TOKEN"\] \?\? "",)/,
104+
'$1$2$1instance: process.env["GLEAN_INSTANCE"] ?? "",',
105+
],
106+
],
107+
go: [
108+
[
109+
/([\s]*)(apiclientgo\.WithSecurity\(os\.Getenv\("GLEAN_API_TOKEN"\)\),)/,
110+
'$1$2$1apiclientgo.WithInstance(os.Getenv("GLEAN_INSTANCE")),',
111+
],
112+
],
113+
java: [
114+
[
115+
/([\s]*)(\.apiToken\("<YOUR_BEARER_TOKEN_HERE>"\))/,
116+
'$1$2$1.instance("<YOUR_GLEAN_INSTANCE_HERE>")',
117+
],
118+
],
119+
};
120+
121+
for (const [_, pathSpec] of path(spec)) {
122+
for (const [lang, codeSample] of codeSnippets(pathSpec)) {
123+
const transformations = transformationsByLang[lang];
124+
125+
if (!transformations) {
126+
continue;
127+
}
128+
129+
let codeSampleSource = codeSample.source;
130+
131+
for (const [pattern, replacement] of transformations) {
132+
codeSampleSource = codeSampleSource.replace(pattern, replacement);
133+
}
134+
135+
codeSample.source = codeSampleSource;
136+
}
137+
}
138+
139+
return spec;
140+
}
141+
69142
/**
70143
* Transforms OpenAPI YAML by adjusting server URLs and paths
71144
* @param {string} content The OpenAPI YAML content
@@ -76,6 +149,7 @@ export function transform(content, _filename) {
76149
const spec = yaml.load(content);
77150

78151
transformPythonCodeSamplesToPython(spec);
152+
addInstanceToCodeSamples(spec);
79153

80154
return yaml.dump(spec, {
81155
lineWidth: -1, // Preserve line breaks

tests/code-sample-transformer.test.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,162 @@ paths:
132132
`);
133133
});
134134
});
135+
136+
describe('addInstanceToCodeSamples', () => {
137+
test('updates chat code sample to use namespace', () => {
138+
const spec = yaml.load(fixtureContent);
139+
140+
const updatedSpec = codeSampleTransformer.addInstanceToCodeSamples(spec);
141+
const chatSpec = updatedSpec.paths['/rest/api/v1/chat'];
142+
143+
expect(
144+
codeSampleTransformer.extractCodeSnippet(chatSpec, 'python'),
145+
).toMatchInlineSnapshot(`
146+
{
147+
"label": "Python (API Client)",
148+
"lang": "python",
149+
"source": "from glean import Glean, models
150+
import os
151+
152+
153+
with Glean(
154+
api_token=os.getenv("GLEAN_API_TOKEN", ""),
155+
instance=os.getenv("GLEAN_INSTANCE", ""),
156+
) as g_client:
157+
158+
res = g_client.client.chat.create(messages=[
159+
{
160+
"fragments": [
161+
models.ChatMessageFragment(
162+
text="What are the company holidays this year?",
163+
),
164+
],
165+
},
166+
], timeout_millis=30000)
167+
168+
# Handle response
169+
print(res)",
170+
}
171+
`);
172+
expect(
173+
codeSampleTransformer.extractCodeSnippet(chatSpec, 'typescript'),
174+
).toMatchInlineSnapshot(`
175+
{
176+
"label": "Typescript (API Client)",
177+
"lang": "typescript",
178+
"source": "import { Glean } from "@gleanwork/api-client";
179+
180+
const glean = new Glean({
181+
apiToken: process.env["GLEAN_API_TOKEN"] ?? "",
182+
instance: process.env["GLEAN_INSTANCE"] ?? "",
183+
});
184+
185+
async function run() {
186+
const result = await glean.client.chat.create({
187+
messages: [
188+
{
189+
fragments: [
190+
{
191+
text: "What are the company holidays this year?",
192+
},
193+
],
194+
},
195+
],
196+
});
197+
198+
// Handle the result
199+
console.log(result);
200+
}
201+
202+
run();",
203+
}
204+
`);
205+
expect(
206+
codeSampleTransformer.extractCodeSnippet(chatSpec, 'go'),
207+
).toMatchInlineSnapshot(`
208+
{
209+
"label": "Go (API Client)",
210+
"lang": "go",
211+
"source": "package main
212+
213+
import(
214+
"context"
215+
"os"
216+
apiclientgo "github.com/gleanwork/api-client-go"
217+
"github.com/gleanwork/api-client-go/models/components"
218+
"log"
219+
)
220+
221+
func main() {
222+
ctx := context.Background()
223+
224+
s := apiclientgo.New(
225+
apiclientgo.WithSecurity(os.Getenv("GLEAN_API_TOKEN")),
226+
apiclientgo.WithInstance(os.Getenv("GLEAN_INSTANCE")),
227+
)
228+
229+
res, err := s.Client.Chat.Create(ctx, components.ChatRequest{
230+
Messages: []components.ChatMessage{
231+
components.ChatMessage{
232+
Fragments: []components.ChatMessageFragment{
233+
components.ChatMessageFragment{
234+
Text: apiclientgo.String("What are the company holidays this year?"),
235+
},
236+
},
237+
},
238+
},
239+
}, nil)
240+
if err != nil {
241+
log.Fatal(err)
242+
}
243+
if res.ChatResponse != nil {
244+
// handle response
245+
}
246+
}",
247+
}
248+
`);
249+
expect(
250+
codeSampleTransformer.extractCodeSnippet(chatSpec, 'java'),
251+
).toMatchInlineSnapshot(`
252+
{
253+
"label": "Java (API Client)",
254+
"lang": "java",
255+
"source": "package hello.world;
256+
257+
import com.glean.api_client.glean_api_client.Glean;
258+
import com.glean.api_client.glean_api_client.models.components.*;
259+
import com.glean.api_client.glean_api_client.models.operations.ChatResponse;
260+
import java.lang.Exception;
261+
import java.util.List;
262+
263+
public class Application {
264+
265+
public static void main(String[] args) throws Exception {
266+
267+
Glean sdk = Glean.builder()
268+
.apiToken("<YOUR_BEARER_TOKEN_HERE>")
269+
.instance("<YOUR_GLEAN_INSTANCE_HERE>")
270+
.build();
271+
272+
ChatResponse res = sdk.client().chat().create()
273+
.chatRequest(ChatRequest.builder()
274+
.messages(List.of(
275+
ChatMessage.builder()
276+
.fragments(List.of(
277+
ChatMessageFragment.builder()
278+
.text("What are the company holidays this year?")
279+
.build()))
280+
.build()))
281+
.build())
282+
.call();
283+
284+
if (res.chatResponse().isPresent()) {
285+
// handle response
286+
}
287+
}
288+
}",
289+
}
290+
`);
291+
});
292+
});
135293
});

0 commit comments

Comments
 (0)