Skip to content

Commit a45787d

Browse files
authored
Merge pull request #557 from marle3003/develop
Develop
2 parents eb90cc4 + 9611c1d commit a45787d

File tree

20 files changed

+1315
-126
lines changed

20 files changed

+1315
-126
lines changed

config/dynamic/provider/file/filetest/MockFS.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ func (m *MockFS) Stat(name string) (fs.FileInfo, error) {
7474
return nil, fmt.Errorf("not found")
7575
}
7676

77+
func (m *MockFS) Abs(name string) (string, error) {
78+
if strings.HasPrefix(name, ".") {
79+
return filepath.Join(m.WorkingDir, name), nil
80+
}
81+
return name, nil
82+
}
83+
7784
func (f *fileInfo) Name() string {
7885
return f.entry.Name
7986
}

config/dynamic/provider/file/fs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type FSReader interface {
1111
ReadFile(name string) ([]byte, error)
1212
Stat(name string) (os.FileInfo, error)
1313
GetWorkingDir() (string, error)
14+
Abs(string) (string, error)
1415
}
1516

1617
type Reader struct {
@@ -31,3 +32,5 @@ func (r *Reader) Stat(name string) (fs.FileInfo, error) {
3132
func (r *Reader) GetWorkingDir() (string, error) {
3233
return os.Getwd()
3334
}
35+
36+
func (r *Reader) Abs(path string) (string, error) { return filepath.Abs(path) }

config/dynamic/provider/npm/npm.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ func (p *Provider) getPackageDir(name string, workDir string) (string, error) {
162162
}
163163

164164
for _, folder := range p.cfg.GlobalFolders {
165+
if folder != "" {
166+
abs, err := p.reader.Abs(folder)
167+
if err == nil {
168+
folder = abs
169+
} else {
170+
log.Debugf("unable to get absolute path of global folder %v: %v", folder, err)
171+
}
172+
}
165173
dir := filepath.Join(folder, name)
166174
if _, err := p.reader.Stat(dir); err == nil {
167175
return dir, nil

config/dynamic/provider/npm/npm_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,34 @@ func TestNpmProvider(t *testing.T) {
114114
require.Equal(t, []byte("foobar"), configs["/bar/node_modules/foo/foo.txt"].Raw)
115115
},
116116
},
117+
{
118+
name: "with relative global folder",
119+
fs: &filetest.MockFS{
120+
WorkingDir: root,
121+
Entries: []*filetest.Entry{
122+
{
123+
Name: "/bar/node_modules",
124+
IsDir: true,
125+
},
126+
{
127+
Name: "/bar/node_modules/foo",
128+
IsDir: true,
129+
},
130+
{
131+
Name: "/bar/node_modules/foo/foo.txt",
132+
IsDir: false,
133+
Data: []byte("foobar"),
134+
}}},
135+
cfg: static.NpmProvider{
136+
GlobalFolders: []string{"./bar/node_modules"},
137+
Packages: []static.NpmPackage{{Name: "foo"}},
138+
},
139+
test: func(t *testing.T, configs map[string]*dynamic.Config) {
140+
require.Len(t, configs, 1)
141+
require.Contains(t, configs, "/bar/node_modules/foo/foo.txt")
142+
require.Equal(t, []byte("foobar"), configs["/bar/node_modules/foo/foo.txt"].Raw)
143+
},
144+
},
117145
{
118146
name: "node_modules in parent directory and two packages",
119147
fs: &filetest.MockFS{

docs/config.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
199199
},
200200
"items": {
201+
"Mock APIs based on OpenAPI and AsyncAPI": "resources/blogs/mock-api-based-on-openapi-asyncapi.md",
201202
"Automation Testing in Agile Development": "resources/blogs/automation-testing-agile-development.md",
202203
"Contract Testing": "resources/blogs/contract-testing.md",
203204
"End to End Testing with Mocked APIs": "resources/blogs/end-to-end-testing-mocked-apis.md",

docs/resources/blogs/mock-api-based-on-openapi-asyncapi.md

Lines changed: 165 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
---
22
title: Mock REST and Kafka APIs Using OpenAPI & AsyncAPI
33
description: Mock REST and Kafka APIs with Mokapi using OpenAPI or AsyncAPI. Simulate real world scenario using JavaScript — all in a single tool.
4+
dashboard:
5+
- title: HTTP Requests
6+
description: Incoming HTTP requests, including method, URL, headers, query parameters, and body
7+
img: /blogs/dashboard-petstore-request.png
8+
- title: Kafka Topics
9+
description: A live view of Kafka topic contents—see the messages being produced and consumed in real time.
10+
img: /blogs/dashboard-petstore-kafka-topic.png
11+
- title: Scheduled Jobs
12+
description: A list of all scheduled tasks defined in your scripts, along with their execution history, status, and any output or errors.
13+
img: /blogs/dashboard-petstore-jobs.png
414
---
515

616
# Mock REST and Kafka APIs with OpenAPI & AsyncAPI Using Mokapi
@@ -93,7 +103,7 @@ paths:
93103
mokapi pet-api.yaml
94104
```
95105

96-
### 3. Call the API
106+
### 3. Make a request
97107

98108
```shell
99109
curl http://localhost/pets
@@ -103,34 +113,46 @@ curl "http://localhost/pets?category=dog"
103113
Mokapi will generate random data for those requests. If you query with ?category=dog, Mokapi will return only dogs
104114
thanks to smart mock data generation.
105115

106-
### Adding Custom Logic
116+
### Add Custom Logic with JavaScript
107117

108-
You can add a JavaScript file to dynamically control responses:
118+
You can control responses using JavaScript:
109119

110-
```javascript
120+
```typescript
111121
import { on } from 'mokapi';
112-
import { fake } from 'mokapi/faker';
122+
123+
const pets = [
124+
{
125+
id: 1,
126+
name: 'Yoda',
127+
category: 'dog'
128+
},
129+
{
130+
id: 2,
131+
name: 'Rex',
132+
category: 'dog'
133+
},
134+
{
135+
id: 3,
136+
name: 'Nugget',
137+
category: 'canary'
138+
}
139+
];
113140

114141
export default () => {
115142
on('http', (request, response) => {
116-
if (request.query.category === 'cat') {
117-
response.data = [
118-
{
119-
id: fake({ type: 'string', format: 'uuid' }),
120-
name: 'Molly',
121-
category: 'cat'
122-
}
123-
];
124-
return true;
125-
}
126-
return false;
143+
if (request.query.category) {
144+
response.data = pets.filter(pet => pet.category === request.query.category)
145+
} else {
146+
response.data = pets
147+
}
148+
return true;
127149
});
128150
}
129151
```
130152

131-
Run Mokapi with the script:
153+
Run it like this:
132154
```shell
133-
mokapi pet-api.yaml pets.js
155+
mokapi pet-api.yaml petstore.ts
134156
```
135157

136158
## Mocking Kafka with AsyncAPI
@@ -144,56 +166,143 @@ info:
144166
version: 1.0.0
145167
servers:
146168
kafkaServer:
147-
host: localhost:8092
169+
host: localhost:9092
148170
protocol: kafka
149171
defaultContentType: application/json
150172
operations:
151173
receivePetArrived:
152174
action: receive
153175
channel:
154-
$ref: '#/channels/store.pet.arrived'
176+
$ref: '#/channels/petstore.order-event'
177+
messages:
178+
- $ref: '#/components/messages/OrderMessage'
155179
sendPetArrived:
156180
action: send
157181
channel:
158-
$ref: '#/channels/store.pet.arrived'
182+
$ref: '#/channels/petstore.order-event'
183+
messages:
184+
- $ref: '#/components/messages/OrderMessage'
159185
channels:
160-
store.pet.arrived:
161-
description: Details about a newly arrived pet.
186+
petstore.order-event:
187+
description: Details about a newly placed pet store order.
162188
messages:
163-
userSignedUp:
164-
$ref: '#/components/messages/petArrived'
189+
OrderMessage:
190+
$ref: '#/components/messages/OrderMessage'
165191
components:
166192
messages:
167-
petArrived:
193+
OrderMessage:
168194
payload:
169-
$ref: '#/components/schemas/Pet'
195+
$ref: '#/components/schemas/Order'
170196
schemas:
171-
Pet:
172-
id:
173-
type: string
174-
name:
175-
type: string
176-
category:
177-
type: string
197+
Order:
198+
properties:
199+
id:
200+
type: integer
201+
petId:
202+
type: integer
203+
status:
204+
type: string
205+
enum: [ placed, accepted, completed ]
206+
shipDate:
207+
type: string
208+
format: date-time
209+
placed:
210+
type: object
211+
properties:
212+
timestamp:
213+
type: string
214+
format: date-time
215+
required:
216+
- timestamp
217+
accepted:
218+
type: object
219+
properties:
220+
timestamp:
221+
type: string
222+
format: date-time
223+
required:
224+
- timestamp
225+
completed:
226+
type: object
227+
properties:
228+
timestamp:
229+
type: string
230+
format: date-time
231+
required:
232+
- timestamp
233+
required:
234+
- id
235+
- petId
236+
- status
237+
- placed
178238
```
179239
180-
### 2. Create a Kafka producer
240+
### 2. Create a Kafka producer script
181241
182-
```javascript
242+
```typescript
183243
import { every } from 'mokapi';
184244
import { produce } from 'mokapi/kafka';
185245

246+
const orders = [
247+
{
248+
id: 1,
249+
petId: 1,
250+
status: 'placed',
251+
placed: {
252+
timestamp: new Date().toISOString()
253+
}
254+
},
255+
{
256+
id: 2,
257+
petId: 2,
258+
status: 'accepted',
259+
shipDate: new Date(Date.now() + 26*60*60*1000).toISOString(),
260+
placed: {
261+
timestamp: new Date(Date.now() - 60*60*1000).toISOString()
262+
},
263+
accepted: {
264+
timestamp: new Date().toISOString()
265+
}
266+
},
267+
{
268+
id: 3,
269+
petId: 3,
270+
status: 'completed',
271+
shipDate: new Date(Date.now() + 24*60*60*1000).toISOString(),
272+
placed: {
273+
timestamp: new Date(Date.now() - 2.5*60*60*1000).toISOString()
274+
},
275+
accepted: {
276+
timestamp: new Date(Date.now() - 1.5*60*60*1000).toISOString()
277+
},
278+
completed: {
279+
timestamp: new Date().toISOString()
280+
}
281+
}
282+
]
283+
186284
export default () => {
187-
every('10s', () => {
188-
produce({ topic: 'store.pet.arrived' });
189-
});
285+
let index = 0;
286+
every('30s', () => {
287+
console.log('producing new random Kafka message')
288+
produce({
289+
topic: 'petstore.order-event',
290+
messages: [
291+
{
292+
key: orders[index].id,
293+
data: orders[index],
294+
}
295+
]
296+
});
297+
index++;
298+
}, { times: orders.length });
190299
}
191300
```
192301

193-
### 3. Run Mokapi with your AsyncAPI spec
302+
### 3. Run Mokapi
194303

195304
```shell
196-
mokapi pet-stream-api.yaml producer.js
305+
mokapi pet-stream-api.yaml producer.ts
197306
```
198307

199308
Mokapi will simulate Kafka messages and allow you to inspect them via the dashboard.
@@ -204,17 +313,29 @@ Mokapi will simulate Kafka messages and allow you to inspect them via the dashbo
204313
- Simulate Kafka events for microservice testing
205314
- Share mock configurations with your team/organization
206315

207-
You can use Mokapi in a GitHub action run as docker container to streamline your automation.
316+
You can use Mokapi in a GitHub action run as a docker container to streamline your automation.
208317

209318
```yaml
210319
- name: Start Mokapi
211320
run: |
212-
docker run -d --rm --name mokapi -p 80:80 -p 8080:8080 -v ${{ github.workspace }}/mocks:/mocks mokapi/mokapi:latest /mocks
321+
docker run -d --rm --name mokapi \
322+
-p 80:80 -p 8080:8080 \
323+
-v ${{ github.workspace }}/mocks:/mocks \
324+
mokapi/mokapi:latest /mocks
213325
```
214326
327+
## Dashboard
328+
329+
Mokapi’s built-in dashboard provides deep visibility into your mocked services—whether they’re REST APIs, Kafka topics,
330+
or scheduled jobs.
331+
332+
{{ carousel key="dashboard" }}
333+
334+
215335
## Conclusion
216336
217337
Mokapi helps you mock REST and Kafka services quickly using standard API specs. Whether you’re building, testing,
218338
or demoing your application, Mokapi provides a simple yet powerful way to simulate realistic API behavior.
219339
220-
Give it a try: [https://mokapi.io](https://mokapi.io)
340+
**Give it a try**: [https://mokapi.io](https://mokapi.io)\
341+
**See the full example repo**: [https://github.com/marle3003/mokapi-petstore](https://github.com/marle3003/mokapi-petstore)

docs/resources/examples/mokapi-with-custom-base-image.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ENTRYPOINT ["mokapi"]
2626
## Using Multi-stage build
2727

2828
```Dockerfile tab=Dockerfile
29-
FROM mokapi/mokapi:v0.9.25 as mokapi
29+
FROM mokapi/mokapi:v0.9.25 AS mokapi
3030

3131
FROM ubuntu:noble
3232

0 commit comments

Comments
 (0)