@grafana/openapi-to-k6 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/.github/workflows/tests.yaml +1 -0
  2. package/README.md +10 -4
  3. package/dist/cli.js +27 -12
  4. package/dist/constants.js +7 -3
  5. package/dist/errors.js +10 -0
  6. package/dist/generator/index.js +50 -3
  7. package/dist/generator/k6Client.js +5 -55
  8. package/dist/generator/k6ScriptBuilder.js +256 -0
  9. package/dist/helper.js +32 -0
  10. package/examples/basic_schema/single/k6-script.sample.ts +3 -2
  11. package/examples/basic_schema/single/simpleAPI.ts +1 -1
  12. package/examples/basic_schema/split/k6-script.sample.ts +3 -2
  13. package/examples/basic_schema/split/simpleAPI.schemas.ts +1 -1
  14. package/examples/basic_schema/split/simpleAPI.ts +1 -1
  15. package/examples/basic_schema/tags/default.ts +1 -1
  16. package/examples/basic_schema/tags/k6-script.sample.ts +4 -3
  17. package/examples/basic_schema/tags/simpleAPI.schemas.ts +1 -1
  18. package/examples/form_data_schema/schema.json +6 -3
  19. package/examples/form_data_schema/single/formDataAPI.ts +1 -1
  20. package/examples/form_data_schema/single/k6-script.sample.ts +10 -2
  21. package/examples/form_data_schema/split/formDataAPI.schemas.ts +1 -1
  22. package/examples/form_data_schema/split/formDataAPI.ts +1 -1
  23. package/examples/form_data_schema/split/k6-script.sample.ts +10 -2
  24. package/examples/form_data_schema/tags/default.ts +1 -1
  25. package/examples/form_data_schema/tags/formDataAPI.schemas.ts +1 -1
  26. package/examples/form_data_schema/tags/k6-script.sample.ts +11 -3
  27. package/examples/form_url_encoded_data_schema/schema.json +6 -3
  28. package/examples/form_url_encoded_data_schema/single/formURLEncodedAPI.ts +1 -1
  29. package/examples/form_url_encoded_data_schema/single/k6-script.sample.ts +11 -2
  30. package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.schemas.ts +1 -1
  31. package/examples/form_url_encoded_data_schema/split/formURLEncodedAPI.ts +1 -1
  32. package/examples/form_url_encoded_data_schema/split/k6-script.sample.ts +11 -2
  33. package/examples/form_url_encoded_data_schema/tags/default.ts +1 -1
  34. package/examples/form_url_encoded_data_schema/tags/formURLEncodedAPI.schemas.ts +1 -1
  35. package/examples/form_url_encoded_data_schema/tags/k6-script.sample.ts +12 -3
  36. package/examples/form_url_encoded_data_with_query_params_schema/schema.json +8 -4
  37. package/examples/form_url_encoded_data_with_query_params_schema/single/formURLEncodedAPIWithQueryParameters.ts +1 -1
  38. package/examples/form_url_encoded_data_with_query_params_schema/single/k6-script.sample.ts +19 -6
  39. package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.schemas.ts +1 -1
  40. package/examples/form_url_encoded_data_with_query_params_schema/split/formURLEncodedAPIWithQueryParameters.ts +1 -1
  41. package/examples/form_url_encoded_data_with_query_params_schema/split/k6-script.sample.ts +19 -6
  42. package/examples/form_url_encoded_data_with_query_params_schema/tags/default.ts +1 -1
  43. package/examples/form_url_encoded_data_with_query_params_schema/tags/formURLEncodedAPIWithQueryParameters.schemas.ts +1 -1
  44. package/examples/form_url_encoded_data_with_query_params_schema/tags/k6-script.sample.ts +14 -3
  45. package/examples/get_request_with_path_parameters_schema/schema.json +2 -1
  46. package/examples/get_request_with_path_parameters_schema/single/k6-script.sample.ts +6 -2
  47. package/examples/get_request_with_path_parameters_schema/single/simpleAPI.ts +1 -1
  48. package/examples/get_request_with_path_parameters_schema/split/k6-script.sample.ts +6 -2
  49. package/examples/get_request_with_path_parameters_schema/split/simpleAPI.schemas.ts +1 -1
  50. package/examples/get_request_with_path_parameters_schema/split/simpleAPI.ts +1 -1
  51. package/examples/get_request_with_path_parameters_schema/tags/default.ts +1 -1
  52. package/examples/get_request_with_path_parameters_schema/tags/k6-script.sample.ts +7 -3
  53. package/examples/get_request_with_path_parameters_schema/tags/simpleAPI.schemas.ts +1 -1
  54. package/examples/headers_schema/schema.json +2 -1
  55. package/examples/headers_schema/single/headerDemoAPI.ts +1 -1
  56. package/examples/headers_schema/single/k6-script.sample.ts +15 -4
  57. package/examples/headers_schema/split/headerDemoAPI.schemas.ts +1 -1
  58. package/examples/headers_schema/split/headerDemoAPI.ts +1 -1
  59. package/examples/headers_schema/split/k6-script.sample.ts +15 -4
  60. package/examples/headers_schema/tags/default.ts +1 -1
  61. package/examples/headers_schema/tags/headerDemoAPI.schemas.ts +1 -1
  62. package/examples/headers_schema/tags/k6-script.sample.ts +16 -5
  63. package/examples/no_title_schema/single/k6-script.sample.ts +3 -2
  64. package/examples/no_title_schema/single/k6Client.ts +1 -1
  65. package/examples/no_title_schema/split/k6-script.sample.ts +3 -2
  66. package/examples/no_title_schema/split/k6Client.schemas.ts +1 -1
  67. package/examples/no_title_schema/split/k6Client.ts +1 -1
  68. package/examples/no_title_schema/tags/default.ts +1 -1
  69. package/examples/no_title_schema/tags/k6-script.sample.ts +4 -3
  70. package/examples/no_title_schema/tags/k6Client.schemas.ts +1 -1
  71. package/examples/post_request_with_query_params/schema.json +14 -7
  72. package/examples/post_request_with_query_params/single/exampleAPI.ts +1 -1
  73. package/examples/post_request_with_query_params/single/k6-script.sample.ts +11 -2
  74. package/examples/post_request_with_query_params/split/exampleAPI.schemas.ts +1 -1
  75. package/examples/post_request_with_query_params/split/exampleAPI.ts +1 -1
  76. package/examples/post_request_with_query_params/split/k6-script.sample.ts +11 -2
  77. package/examples/post_request_with_query_params/tags/default.ts +1 -1
  78. package/examples/post_request_with_query_params/tags/exampleAPI.schemas.ts +1 -1
  79. package/examples/post_request_with_query_params/tags/k6-script.sample.ts +12 -3
  80. package/examples/query_params_schema/schema.json +20 -10
  81. package/examples/query_params_schema/single/exampleAPI.ts +1 -1
  82. package/examples/query_params_schema/single/k6-script.sample.ts +8 -2
  83. package/examples/query_params_schema/split/exampleAPI.schemas.ts +1 -1
  84. package/examples/query_params_schema/split/exampleAPI.ts +1 -1
  85. package/examples/query_params_schema/split/k6-script.sample.ts +8 -2
  86. package/examples/query_params_schema/tags/default.ts +1 -1
  87. package/examples/query_params_schema/tags/exampleAPI.schemas.ts +1 -1
  88. package/examples/query_params_schema/tags/k6-script.sample.ts +9 -3
  89. package/examples/simple_post_request_schema/schema.json +30 -15
  90. package/examples/simple_post_request_schema/single/exampleAPI.ts +1 -1
  91. package/examples/simple_post_request_schema/single/k6-script.sample.ts +16 -2
  92. package/examples/simple_post_request_schema/split/exampleAPI.schemas.ts +1 -1
  93. package/examples/simple_post_request_schema/split/exampleAPI.ts +1 -1
  94. package/examples/simple_post_request_schema/split/k6-script.sample.ts +16 -2
  95. package/examples/simple_post_request_schema/tags/default.ts +1 -1
  96. package/examples/simple_post_request_schema/tags/exampleAPI.schemas.ts +1 -1
  97. package/examples/simple_post_request_schema/tags/k6-script.sample.ts +17 -3
  98. package/package.json +3 -1
  99. package/src/cli.ts +32 -14
  100. package/src/constants.ts +7 -3
  101. package/src/errors.ts +6 -0
  102. package/src/generator/index.ts +67 -2
  103. package/src/generator/k6Client.ts +3 -72
  104. package/src/generator/k6ScriptBuilder.ts +328 -0
  105. package/src/helper.ts +40 -0
  106. package/src/type.d.ts +1 -0
  107. package/tests/e2e/schema.json +136 -18
  108. package/tests/e2e/single-tag-filter/k6Script.ts +50 -0
  109. package/tests/e2e/tags/k6Script.ts +3 -3
  110. package/tests/functional-tests/helper.ts +16 -0
  111. package/tests/functional-tests/{generator.test.ts → test-generator/generator.test.ts} +12 -21
  112. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_using_ref_models.json +394 -0
  113. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_with_examples.json +416 -0
  114. package/tests/functional-tests/test-sample-k6-scripts/fixtures/schema_with_no_variables.json +32 -0
  115. package/tests/functional-tests/test-sample-k6-scripts/sampleK6Scripts.test.ts +248 -0
  116. package/tests/functional-tests/test-tags-filtering/fixtures/tags_filtering.json +141 -0
  117. package/tests/functional-tests/test-tags-filtering/tagsFiltering.test.ts +166 -0
  118. package/tests/helper.test.ts +59 -0
  119. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/basic_schema.json +0 -0
  120. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_data_schema.json +0 -0
  121. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_url_encoded_data_schema.json +0 -0
  122. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/form_url_encoded_data_with_query_params_schema.json +0 -0
  123. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/get_request_with_path_parameters_schema.json +0 -0
  124. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/headers_schema.json +0 -0
  125. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/no_title_schema.json +0 -0
  126. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/post_request_with_query_params.json +0 -0
  127. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/query_params_schema.json +0 -0
  128. /package/tests/functional-tests/{fixtures/schemas → test-generator/fixtures}/simple_post_request_schema.json +0 -0
@@ -0,0 +1,141 @@
1
+ {
2
+ "openapi": "3.0.0",
3
+ "info": {
4
+ "title": "Sample API",
5
+ "version": "1.0.0"
6
+ },
7
+ "paths": {
8
+ "/users": {
9
+ "get": {
10
+ "tags": ["users"],
11
+ "responses": {
12
+ "200": {
13
+ "description": "Success"
14
+ }
15
+ }
16
+ }
17
+ },
18
+ "/user-profiles": {
19
+ "get": {
20
+ "tags": ["userProfiles"],
21
+ "responses": {
22
+ "200": {
23
+ "description": "Success"
24
+ }
25
+ }
26
+ }
27
+ },
28
+ "/pets": {
29
+ "get": {
30
+ "tags": ["pets"],
31
+ "responses": {
32
+ "200": {
33
+ "description": "Success"
34
+ }
35
+ }
36
+ }
37
+ },
38
+ "/auth": {
39
+ "post": {
40
+ "tags": ["auth"],
41
+ "requestBody": {
42
+ "content": {
43
+ "application/json": {
44
+ "schema": {
45
+ "$ref": "#/components/schemas/AuthRequest"
46
+ }
47
+ }
48
+ }
49
+ },
50
+ "responses": {
51
+ "200": {
52
+ "description": "Success"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ },
58
+ "components": {
59
+ "schemas": {
60
+ "AuthRequest": {
61
+ "type": "object",
62
+ "properties": {
63
+ "username": {
64
+ "type": "string",
65
+ "example": "user123"
66
+ },
67
+ "password": {
68
+ "type": "string",
69
+ "format": "password",
70
+ "example": "mypassword"
71
+ }
72
+ },
73
+ "required": ["username", "password"]
74
+ },
75
+ "User": {
76
+ "type": "object",
77
+ "properties": {
78
+ "id": {
79
+ "type": "string",
80
+ "example": "abc123"
81
+ },
82
+ "name": {
83
+ "type": "string",
84
+ "example": "John Doe"
85
+ },
86
+ "email": {
87
+ "type": "string",
88
+ "format": "email",
89
+ "example": "john.doe@example.com"
90
+ }
91
+ },
92
+ "required": ["id", "name", "email"]
93
+ },
94
+ "UserProfile": {
95
+ "type": "object",
96
+ "properties": {
97
+ "id": {
98
+ "type": "string",
99
+ "example": "profile123"
100
+ },
101
+ "userId": {
102
+ "type": "string",
103
+ "example": "abc123"
104
+ },
105
+ "bio": {
106
+ "type": "string",
107
+ "example": "Software developer with 10 years of experience"
108
+ },
109
+ "avatarUrl": {
110
+ "type": "string",
111
+ "format": "uri",
112
+ "example": "https://example.com/avatar.jpg"
113
+ }
114
+ },
115
+ "required": ["id", "userId", "bio"]
116
+ },
117
+ "Pet": {
118
+ "type": "object",
119
+ "properties": {
120
+ "id": {
121
+ "type": "string",
122
+ "example": "pet123"
123
+ },
124
+ "name": {
125
+ "type": "string",
126
+ "example": "Buddy"
127
+ },
128
+ "species": {
129
+ "type": "string",
130
+ "example": "Dog"
131
+ },
132
+ "age": {
133
+ "type": "integer",
134
+ "example": 3
135
+ }
136
+ },
137
+ "required": ["id", "name", "species"]
138
+ }
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,166 @@
1
+ import fs from 'fs'
2
+ import os from 'os'
3
+ import path from 'path'
4
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest'
5
+ import { Mode } from '../../../src/constants'
6
+ import { NoFilesGeneratedError } from '../../../src/errors'
7
+ import generator from '../../../src/generator'
8
+
9
+ import { loadFixture, mkdtemp, readFile, rmdir, writeFile } from '../helper'
10
+
11
+ describe('validate tags filtering', () => {
12
+ let tempDir: string, openApiPath: string
13
+ const tagsFilteringSchema = loadFixture(
14
+ path.join(__dirname, 'fixtures', 'tags_filtering.json')
15
+ )
16
+
17
+ beforeAll(async () => {
18
+ tempDir = await mkdtemp(path.join(os.tmpdir(), 'tags-filtering-test-'))
19
+ openApiPath = path.join(tempDir, 'openapi-schema.json')
20
+ await writeFile(openApiPath, JSON.stringify(tagsFilteringSchema))
21
+ })
22
+
23
+ afterAll(async () => {
24
+ await rmdir(tempDir, { recursive: true })
25
+ })
26
+
27
+ const generateAndTest = async (
28
+ mode: Mode,
29
+ tags: string[],
30
+ expectedFiles: string[],
31
+ expectedContents: string[],
32
+ unexpectedContents: string[]
33
+ ) => {
34
+ const generatedSchemaPath = path.join(
35
+ tempDir,
36
+ `generated-schema-${mode}-${tags.join('-')}`
37
+ )
38
+ await generator({
39
+ openApiPath,
40
+ outputDir: generatedSchemaPath,
41
+ tags,
42
+ mode,
43
+ })
44
+
45
+ const generatedFiles = fs.readdirSync(generatedSchemaPath)
46
+ expect(generatedFiles).to.have.members(expectedFiles)
47
+
48
+ for (const file of expectedFiles) {
49
+ if (file.includes('.schemas.ts')) {
50
+ // Skip schemas file
51
+ continue
52
+ }
53
+ const generatedFilePath = path.join(generatedSchemaPath, file)
54
+ const generatedContent = await readFile(generatedFilePath, 'utf-8')
55
+
56
+ for (const content of expectedContents) {
57
+ expect(generatedContent).to.contain(content)
58
+ }
59
+
60
+ for (const content of unexpectedContents) {
61
+ expect(generatedContent).not.to.contain(content)
62
+ }
63
+ }
64
+ }
65
+
66
+ it('should generate only endpoints with specified tags in single mode', async () => {
67
+ await generateAndTest(
68
+ Mode.SINGLE,
69
+ ['users'],
70
+ ['sampleAPI.ts'],
71
+ ['/users'],
72
+ ['/user-profiles', '/pets', '/auth']
73
+ )
74
+
75
+ await generateAndTest(
76
+ Mode.SINGLE,
77
+ ['users', 'userProfiles'],
78
+ ['sampleAPI.ts'],
79
+ ['/users', '/user-profiles'],
80
+ ['/pets', '/auth']
81
+ )
82
+
83
+ // Invalid tag should not generate any endpoints
84
+ await generateAndTest(
85
+ Mode.SINGLE,
86
+ ['invalid-tag'],
87
+ ['sampleAPI.ts'],
88
+ [],
89
+ ['/users', '/user-profiles', '/pets', '/auth']
90
+ )
91
+
92
+ // Empty tags should generate all endpoints
93
+ await generateAndTest(
94
+ Mode.SINGLE,
95
+ [],
96
+ ['sampleAPI.ts'],
97
+ ['/users', '/user-profiles', '/pets', '/auth'],
98
+ []
99
+ )
100
+ })
101
+
102
+ it('should generate only endpoints with specified tags in split mode', async () => {
103
+ await generateAndTest(
104
+ Mode.SPLIT,
105
+ ['users'],
106
+ ['sampleAPI.ts', 'sampleAPI.schemas.ts'],
107
+ ['/users'],
108
+ ['/user-profiles', '/pets', '/auth']
109
+ )
110
+
111
+ // Test empty tags
112
+ await generateAndTest(
113
+ Mode.SPLIT,
114
+ [],
115
+ ['sampleAPI.ts', 'sampleAPI.schemas.ts'],
116
+ ['/users', '/user-profiles', '/pets', '/auth'],
117
+ []
118
+ )
119
+
120
+ // Invalid tag should not generate any endpoints
121
+ // In the single mode it will create the client file which will have only the schemas
122
+ await expect(
123
+ generateAndTest(
124
+ Mode.SPLIT,
125
+ ['invalid-tag'],
126
+ [],
127
+ [],
128
+ ['/users', '/user-profiles', '/pets', '/auth']
129
+ )
130
+ ).rejects.toThrow(NoFilesGeneratedError)
131
+
132
+ // Empty tags should generate all endpoints
133
+ await generateAndTest(
134
+ Mode.SPLIT,
135
+ [],
136
+ ['sampleAPI.ts', 'sampleAPI.schemas.ts'],
137
+ ['/users', '/user-profiles', '/pets', '/auth'],
138
+ []
139
+ )
140
+ })
141
+
142
+ it('should generate only endpoints with specified tags in tags mode', async () => {
143
+ await generateAndTest(
144
+ Mode.TAGS,
145
+ ['users'],
146
+ ['users.ts', 'sampleAPI.schemas.ts'],
147
+ ['/users'],
148
+ ['/user-profiles', '/pets', '/auth']
149
+ )
150
+
151
+ // Empty tags should generate all endpoints
152
+ await generateAndTest(
153
+ Mode.TAGS,
154
+ [],
155
+ [
156
+ 'users.ts',
157
+ 'sampleAPI.schemas.ts',
158
+ 'auth.ts',
159
+ 'pets.ts',
160
+ 'user-profiles.ts',
161
+ ],
162
+ [], // Skipping test for content
163
+ []
164
+ )
165
+ })
166
+ })
@@ -157,3 +157,62 @@ describe('getDirectoryForPath', () => {
157
157
  expect(directory).toEqual(expectedDirectory)
158
158
  })
159
159
  })
160
+
161
+ describe('hasOnlyComments', () => {
162
+ it('should return true if the file has only comments', () => {
163
+ const expectedResultsAndData = [
164
+ {
165
+ fileContent: '// This is a comment',
166
+ expectedResult: true,
167
+ },
168
+ {
169
+ fileContent: '/* This is a comment */',
170
+ expectedResult: true,
171
+ },
172
+ {
173
+ fileContent: '// This is a comment\nconst x = 1',
174
+ expectedResult: false,
175
+ },
176
+ {
177
+ fileContent: '/* This is a comment */\nconst x = 1',
178
+ expectedResult: false,
179
+ },
180
+ {
181
+ fileContent: 'const x = 1',
182
+ expectedResult: false,
183
+ },
184
+ {
185
+ fileContent: '',
186
+ expectedResult: true,
187
+ },
188
+ {
189
+ fileContent: '// This is a comment\n// This is another comment',
190
+ expectedResult: true,
191
+ },
192
+ {
193
+ fileContent: '/* This is a comment */\n/* This is another comment */',
194
+ expectedResult: true,
195
+ },
196
+ {
197
+ fileContent: '// This is a comment\n/* This is another comment */',
198
+ expectedResult: true,
199
+ },
200
+ {
201
+ fileContent: `/**
202
+ * This is a comment
203
+ */
204
+ `,
205
+ expectedResult: true,
206
+ },
207
+ ]
208
+ for (const { fileContent, expectedResult } of expectedResultsAndData) {
209
+ const result = helper.hasOnlyComments(fileContent)
210
+ try {
211
+ expect(result).toBe(expectedResult)
212
+ } catch (error) {
213
+ console.log(`Expected ${expectedResult} for content:\n${fileContent}`)
214
+ throw error
215
+ }
216
+ }
217
+ })
218
+ })