Skip to content

Commit 3e02b82

Browse files
authored
Merge pull request #545 from marle3003/develop
Develop
2 parents 7339c45 + ce493b2 commit 3e02b82

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+1499
-423
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
asyncapi: 3.0.0
2+
info:
3+
title: Petstore Stream API
4+
version: 1.0.0
5+
servers:
6+
kafkaServer:
7+
host: localhost:19093
8+
protocol: kafka
9+
defaultContentType: application/json
10+
operations:
11+
receivePetArrived:
12+
action: receive
13+
channel:
14+
$ref: '#/channels/petstore.order-event'
15+
messages:
16+
- $ref: '#/components/messages/OrderMessage'
17+
sendPetArrived:
18+
action: send
19+
channel:
20+
$ref: '#/channels/petstore.order-event'
21+
messages:
22+
- $ref: '#/components/messages/OrderMessage'
23+
channels:
24+
petstore.order-event:
25+
description: Details about a newly placed pet store order.
26+
messages:
27+
OrderMessage:
28+
$ref: '#/components/messages/OrderMessage'
29+
components:
30+
messages:
31+
OrderMessage:
32+
payload:
33+
$ref: '#/components/schemas/Order'
34+
schemas:
35+
Order:
36+
properties:
37+
id:
38+
type: integer
39+
placed:
40+
type: object
41+
properties:
42+
petId:
43+
type: integer
44+
quantity:
45+
type: integer
46+
format: int32
47+
shipDate:
48+
type: string
49+
format: date-time
50+
accepted:
51+
type: object
52+
properties:
53+
timestamp:
54+
type: string
55+
format: date-time
56+
completed:
57+
type: object
58+
properties:
59+
timestamp:
60+
type: string
61+
format: date-time

acceptance/petstore/petstore.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,13 @@ export default async function() {
3030
}
3131
return false
3232
});
33-
await kafka.produceAsync({ topic: 'petstore.order-event', messages: [{partition: 0}] })
33+
await kafka.produceAsync({
34+
topic: 'petstore.order-event',
35+
cluster: 'A sample AsyncApi Kafka streaming api',
36+
messages: [{partition: 0}]
37+
})
38+
await kafka.produceAsync({
39+
topic: 'petstore.order-event',
40+
cluster: 'Petstore Stream API',
41+
})
3442
}

acceptance/petstore_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/stretchr/testify/require"
77
"mokapi/config/static"
88
"mokapi/kafka"
9+
"mokapi/kafka/fetch"
910
"mokapi/kafka/kafkatest"
1011
"mokapi/kafka/metaData"
1112
"mokapi/kafka/produce"
@@ -278,3 +279,29 @@ func (suite *PetStoreSuite) TestKafkaEventAndMetrics() {
278279
try.BodyContains(`"messageId":"order"`),
279280
)
280281
}
282+
283+
func (suite *PetStoreSuite) TestKafka3_Consume() {
284+
// ensure scripts are executed
285+
time.Sleep(3 * time.Second)
286+
287+
c := kafkatest.NewClient("localhost:19093", "test")
288+
defer c.Close()
289+
290+
r, err := c.Fetch(12, &fetch.Request{
291+
MaxBytes: 1000,
292+
MinBytes: 1,
293+
MaxWaitMs: 5000,
294+
Topics: []fetch.Topic{
295+
{
296+
Name: "petstore.order-event",
297+
Partitions: []fetch.RequestPartition{{
298+
Index: 0,
299+
MaxBytes: 1000,
300+
}},
301+
},
302+
},
303+
})
304+
require.NoError(suite.T(), err)
305+
require.NotNil(suite.T(), r)
306+
require.Len(suite.T(), r.Topics[0].Partitions[0].RecordSet.Records, 1)
307+
}

api/handler_metrics_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestHandler_Metrics(t *testing.T) {
3030
h,
3131
try.HasStatusCode(200),
3232
try.HasHeader("Content-Type", "application/json"),
33-
try.HasBody(fmt.Sprintf(`[{"name":"app_start_timestamp","value":%v},{"name":"app_memory_usage_bytes","value":0}]`, int64(app.Monitor.StartTime.Value()))))
33+
try.HasBody(fmt.Sprintf(`[{"name":"app_start_timestamp","value":%v},{"name":"app_memory_usage_bytes","value":0},{"name":"app_job_run_total","value":0}]`, int64(app.Monitor.StartTime.Value()))))
3434
},
3535
},
3636
{

cmd/mokapi/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ func main() {
100100
func createServer(cfg *static.Config) (*server.Server, error) {
101101
events.SetStore(int(cfg.Event.Store["default"].Size), events.NewTraits().WithNamespace("http"))
102102
events.SetStore(int(cfg.Event.Store["default"].Size), events.NewTraits().WithNamespace("kafka"))
103+
events.SetStore(int(cfg.Event.Store["default"].Size), events.NewTraits().WithNamespace("job"))
103104

104105
pool := safe.NewPool(context.Background())
105106
app := runtime.New(cfg)

config/dynamic/asyncApi/convert.go

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (c *Config) Convert() (*asyncapi3.Config, error) {
4444

4545
if c.Components != nil {
4646
var err error
47-
target.Components, err = convertComponents(c.Components)
47+
target.Components, err = convertComponents(c.Components, target)
4848
if err != nil {
4949
return nil, err
5050
}
@@ -69,7 +69,7 @@ func convertChannels(cfg *asyncapi3.Config, channels map[string]*ChannelRef) err
6969
cfg.Channels[name] = &asyncapi3.ChannelRef{Reference: dynamic.Reference{Ref: orig.Ref}}
7070
}
7171
if orig.Value != nil {
72-
ch, err := convertChannel(name, orig.Value)
72+
ch, err := convertChannel(name, orig.Value, cfg)
7373
if err != nil {
7474
return err
7575
}
@@ -80,14 +80,15 @@ func convertChannels(cfg *asyncapi3.Config, channels map[string]*ChannelRef) err
8080
return nil
8181
}
8282

83-
func convertChannel(name string, c *Channel) (*asyncapi3.ChannelRef, error) {
83+
func convertChannel(name string, c *Channel, config *asyncapi3.Config) (*asyncapi3.ChannelRef, error) {
8484
target := &asyncapi3.Channel{
8585
Address: name,
8686
Summary: "",
8787
Description: c.Description,
8888
Messages: map[string]*asyncapi3.MessageRef{},
8989
Bindings: convertChannelBinding(c.Bindings),
9090
}
91+
ref := &asyncapi3.ChannelRef{Value: target}
9192

9293
for _, server := range c.Servers {
9394
target.Servers = append(
@@ -104,23 +105,57 @@ func convertChannel(name string, c *Channel) (*asyncapi3.ChannelRef, error) {
104105
if c.Publish != nil && c.Subscribe != nil && c.Publish.Message != nil && c.Subscribe.Message != nil {
105106
if c.Publish.Message.Ref == c.Subscribe.Message.Ref {
106107
msg := convertMessage(c.Publish.Message)
107-
addMessage(target, msg, "", "", c.Publish.Message.Ref)
108+
msgName := addMessage(target, msg, "", "", c.Publish.Message.Ref)
109+
if msgName != "" {
110+
addOperation(msgName, "send", c.Publish, ref, msg, config)
111+
addOperation(msgName, "receive", c.Subscribe, ref, msg, config)
112+
}
108113
}
109114
} else {
110115
if c.Publish != nil {
111-
addMessage(target, convertMessage(c.Publish.Message), c.Publish.OperationId, c.Publish.Message.Ref, "publish")
116+
msg := convertMessage(c.Publish.Message)
117+
msgName := addMessage(target, msg, c.Publish.OperationId, c.Publish.Message.Ref, "publish")
118+
if msgName != "" {
119+
addOperation(msgName, "send", c.Publish, ref, msg, config)
120+
}
112121
}
113122
if c.Subscribe != nil {
114-
addMessage(target, convertMessage(c.Subscribe.Message), c.Subscribe.OperationId, c.Subscribe.Message.Ref, "subscribe")
123+
msg := convertMessage(c.Subscribe.Message)
124+
msgName := addMessage(target, msg, c.Subscribe.OperationId, c.Subscribe.Message.Ref, "subscribe")
125+
if msgName != "" {
126+
addOperation(msgName, "receive", c.Subscribe, ref, msg, config)
127+
}
115128
}
116129
}
117130

118131
return &asyncapi3.ChannelRef{Value: target}, nil
119132
}
120133

121-
func addMessage(target *asyncapi3.Channel, msg *asyncapi3.MessageRef, opId, ref, opName string) {
134+
func addOperation(msgName, action string, op *Operation, ch *asyncapi3.ChannelRef, msg *asyncapi3.MessageRef, config *asyncapi3.Config) {
135+
name := fmt.Sprintf("%s_%s", action, msgName)
136+
if len(op.OperationId) > 0 {
137+
name = op.OperationId
138+
}
139+
140+
result := &asyncapi3.Operation{
141+
Action: action,
142+
Channel: *ch,
143+
Summary: op.Summary,
144+
Description: op.Description,
145+
Bindings: convertOperationBinding(op.Bindings),
146+
Messages: []*asyncapi3.MessageRef{msg},
147+
}
148+
149+
if config.Operations == nil {
150+
config.Operations = map[string]*asyncapi3.OperationRef{}
151+
}
152+
153+
config.Operations[name] = &asyncapi3.OperationRef{Value: result}
154+
}
155+
156+
func addMessage(target *asyncapi3.Channel, msg *asyncapi3.MessageRef, opId, ref, opName string) string {
122157
if msg == nil {
123-
return
158+
return ""
124159
}
125160

126161
var msgId string
@@ -135,6 +170,7 @@ func addMessage(target *asyncapi3.Channel, msg *asyncapi3.MessageRef, opId, ref,
135170
}
136171

137172
target.Messages[msgId] = msg
173+
return msgId
138174
}
139175

140176
func convertMessage(msg *MessageRef) *asyncapi3.MessageRef {
@@ -294,12 +330,12 @@ func convertServer(s *Server) *asyncapi3.ServerRef {
294330
Bindings: convertServerBinding(s.Bindings),
295331
}
296332

297-
protocol, host, path := resolveServerUrl(s.Url)
333+
protocol, host, p := resolveServerUrl(s.Url)
298334
if target.Protocol == "" {
299335
target.Protocol = protocol
300336
}
301337
target.Host = host
302-
target.Pathname = path
338+
target.Pathname = p
303339

304340
return &asyncapi3.ServerRef{Value: target}
305341
}
@@ -352,7 +388,16 @@ func convertChannelBinding(b ChannelBindings) asyncapi3.ChannelBindings {
352388
}}
353389
}
354390

355-
func convertComponents(c *Components) (*asyncapi3.Components, error) {
391+
func convertOperationBinding(b OperationBindings) asyncapi3.OperationBindings {
392+
return asyncapi3.OperationBindings{
393+
Kafka: asyncapi3.KafkaOperationBinding{
394+
GroupId: b.Kafka.GroupId,
395+
ClientId: b.Kafka.ClientId,
396+
},
397+
}
398+
}
399+
400+
func convertComponents(c *Components, config *asyncapi3.Config) (*asyncapi3.Components, error) {
356401
target := &asyncapi3.Components{}
357402

358403
for name, server := range c.Servers {
@@ -366,7 +411,7 @@ func convertComponents(c *Components) (*asyncapi3.Components, error) {
366411
if target.Servers == nil {
367412
target.Channels = map[string]*asyncapi3.ChannelRef{}
368413
}
369-
ch, err := convertChannel(name, orig)
414+
ch, err := convertChannel(name, orig, config)
370415
if err != nil {
371416
return nil, err
372417
}

config/dynamic/asyncApi/convert_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,11 @@ func TestConfig_Convert(t *testing.T) {
6666
require.IsType(t, &schema.Schema{}, payload.Schema)
6767
s := payload.Schema.(*schema.Schema)
6868
require.Equal(t, "object", s.Type[0])
69+
70+
// operations
71+
require.Len(t, cfg3.Operations, 4)
72+
op := cfg3.Operations["receiveLightMeasurement"].Value
73+
require.Equal(t, "send", op.Action)
74+
require.Equal(t, channel, op.Channel.Value)
75+
require.Equal(t, "Inform about environmental lighting conditions of a particular streetlight.", op.Summary)
6976
}

docs/config.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,13 @@
102102
},
103103
"mokapi/http": {
104104
"items": {
105-
"delete": "javascript-api/mokapi-http/delete.md",
106105
"get": "javascript-api/mokapi-http/get.md",
106+
"post": "javascript-api/mokapi-http/post.md",
107+
"put": "javascript-api/mokapi-http/put.md",
107108
"head": "javascript-api/mokapi-http/head.md",
109+
"delete": "javascript-api/mokapi-http/delete.md",
108110
"options": "javascript-api/mokapi-http/options.md",
109111
"patch": "javascript-api/mokapi-http/patch.md",
110-
"post": "javascript-api/mokapi-http/post.md",
111-
"put": "javascript-api/mokapi-http/put.md",
112112
"HttpResponse": "javascript-api/mokapi-http/httpresponse.md",
113113
"Args": "javascript-api/mokapi-http/args.md"
114114
}
@@ -147,7 +147,7 @@
147147
"component": "examples",
148148
"hideNavigation": true,
149149
"hideInNavigation": true,
150-
"canonical": "/docs/resources",
150+
"canonical": "https://mokapi.io/docs/resources",
151151
"title": "Explore Mokapi Resources: Tutorials, Examples, and Blog Articles",
152152
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
153153
},
@@ -157,7 +157,7 @@
157157
"component": "examples",
158158
"hideNavigation": true,
159159
"hideInNavigation": true,
160-
"canonical": "/docs/resources",
160+
"canonical": "https://mokapi.io/docs/resources",
161161
"title": "Explore Mokapi Resources: Tutorials, Examples, and Blog Articles",
162162
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
163163
},
@@ -179,7 +179,7 @@
179179
"component": "examples",
180180
"hideNavigation": true,
181181
"hideInNavigation": true,
182-
"canonical": "/docs/resources",
182+
"canonical": "https://mokapi.io/docs/resources",
183183
"title": "Explore Mokapi Resources: Tutorials, Examples, and Blog Articles",
184184
"description": "Explore Mokapi's resources including tutorials, examples, and blog articles. Learn to mock APIs, validate schemas, and streamline your development."
185185
},
@@ -193,14 +193,15 @@
193193
"component": "examples",
194194
"hideNavigation": true,
195195
"hideInNavigation": true,
196-
"canonical": "/docs/resources",
196+
"canonical": "https://mokapi.io/docs/resources",
197197
"title": "Explore Mokapi Resources: Tutorials, Examples, and Blog Articles",
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": {
201201
"Automation Testing in Agile Development": "resources/blogs/automation-testing-agile-development.md",
202202
"Contract Testing": "resources/blogs/contract-testing.md",
203203
"End to End Testing with Mocked APIs": "resources/blogs/end-to-end-testing-mocked-apis.md",
204+
"Bring Your Mock APIs to Life with Mokapi and JavaScript": "resources/blogs/dynamic-mocks-with-javascript.md",
204205
"Debugging Mokapi Scripts": "resources/blogs/debugging-mokapi-scripts.md"
205206
}
206207
}

docs/guides/get-started/installation.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Install Mokapi
3-
description: Mokapi has packages for Linux, Mac and Windows. You can also use Mokapi inside a Docker container, a standalone binary or using NPM package
2+
title: "Install Mokapi: Quick & Easy Setup Guide"
3+
description: Learn how to install Mokapi effortlessly across Windows, macOS, and Linux. Follow the step-by-step guide for a smooth setup experience.
44
---
55
# Install Mokapi
66

@@ -56,5 +56,5 @@ You can download Mokapi's binary file from [GitHub Releases page](https://github
5656

5757
## Next steps
5858

59-
- Go to [Running Mokapi](/docs/guides/get-started/running.md) to learn how to mock your first HTTP API.
60-
- Configure Intellisense for Mokapi Scripts by installing [Mokapi types](https://www.npmjs.com/package/@types/mokapi)
59+
- [Create your first Mock](/docs/guides/get-started/running.md)
60+
- [Install @types/mokapi](https://www.npmjs.com/package/@types/mokapi)

docs/guides/get-started/welcome.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: "Getting Started with Mokapi | Mock APIs & Validate with OpenAPI and AsyncAPI"
2+
title: "Getting Started with Mokapi"
33
description: Learn how to set up Mokapi to mock APIs and validate requests using OpenAPI or AsyncAPI. No account needed—free, open-source, and easy to use.
44
cards:
55
items:

0 commit comments

Comments
 (0)