doc-detective 3.0.0 → 3.0.2
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.
- package/package.json +4 -4
- package/src/utils.js +5 -3
- package/test/artifacts/httpRequest.spec.yaml +37 -0
- package/test/artifacts/test.spec.json +0 -14
- package/test/runTests.test.js +31 -0
- package/test/server/index.js +139 -0
- package/test/server/public/index.html +174 -0
- package/test/artifacts/httpRequest.spec.json +0 -55
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doc-detective",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.2",
|
|
4
4
|
"description": "Treat doc content as testable assertions to validate doc accuracy and product UX.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"doc-detective": "src/index.js"
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"homepage": "https://github.com/doc-detective/doc-detective#readme",
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
36
|
-
"doc-detective-common": "^3.0.
|
|
37
|
-
"doc-detective-core": "^3.0.
|
|
36
|
+
"doc-detective-common": "^3.0.2",
|
|
37
|
+
"doc-detective-core": "^3.0.4",
|
|
38
38
|
"yargs": "^17.7.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"chai": "^5.2.0",
|
|
42
|
-
"mocha": "^11.
|
|
42
|
+
"mocha": "^11.2.2"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/utils.js
CHANGED
|
@@ -77,12 +77,14 @@ async function setConfig(config, args, configPath) {
|
|
|
77
77
|
}
|
|
78
78
|
// Override config values
|
|
79
79
|
if (args.input) {
|
|
80
|
-
config.input = args.input;
|
|
80
|
+
config.input = path.resolve(args.input);
|
|
81
81
|
}
|
|
82
82
|
if (args.output) {
|
|
83
|
-
config.output = args.output;
|
|
83
|
+
config.output = path.resolve(args.output);
|
|
84
|
+
}
|
|
85
|
+
if (args.logLevel) {
|
|
86
|
+
config.logLevel = args.logLevel;
|
|
84
87
|
}
|
|
85
|
-
if (args.logLevel) config.logLevel = args.logLevel;
|
|
86
88
|
// Resolve paths
|
|
87
89
|
config = await resolvePaths({
|
|
88
90
|
config: config,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
tests:
|
|
2
|
+
- steps:
|
|
3
|
+
- loadVariables: env
|
|
4
|
+
- httpRequest:
|
|
5
|
+
url: http://localhost:8080/api/users
|
|
6
|
+
method: post
|
|
7
|
+
request:
|
|
8
|
+
body:
|
|
9
|
+
name: $USER
|
|
10
|
+
job: $JOB
|
|
11
|
+
response:
|
|
12
|
+
body:
|
|
13
|
+
name: John Doe
|
|
14
|
+
job: Software Engineer
|
|
15
|
+
- httpRequest:
|
|
16
|
+
url: http://localhost:8080/api/users
|
|
17
|
+
method: post
|
|
18
|
+
request:
|
|
19
|
+
body:
|
|
20
|
+
data:
|
|
21
|
+
- first_name: George
|
|
22
|
+
last_name: Bluth
|
|
23
|
+
id: 1
|
|
24
|
+
response:
|
|
25
|
+
body:
|
|
26
|
+
data:
|
|
27
|
+
- first_name: George
|
|
28
|
+
last_name: Bluth
|
|
29
|
+
variables:
|
|
30
|
+
ID: $$response.body.data[0].id
|
|
31
|
+
- httpRequest:
|
|
32
|
+
url: http://localhost:8080/api/$ID
|
|
33
|
+
method: get
|
|
34
|
+
timeout: 1000
|
|
35
|
+
savePath: response.json
|
|
36
|
+
maxVariation: 0
|
|
37
|
+
overwrite: aboveVariation
|
|
@@ -20,20 +20,6 @@
|
|
|
20
20
|
"action": "checkLink",
|
|
21
21
|
"url": "https://www.duckduckgo.com"
|
|
22
22
|
},
|
|
23
|
-
{
|
|
24
|
-
"action": "httpRequest",
|
|
25
|
-
"url": "https://reqres.in/api/users",
|
|
26
|
-
"method": "post",
|
|
27
|
-
"requestData": {
|
|
28
|
-
"name": "morpheus",
|
|
29
|
-
"job": "leader"
|
|
30
|
-
},
|
|
31
|
-
"responseData": {
|
|
32
|
-
"name": "morpheus",
|
|
33
|
-
"job": "leader"
|
|
34
|
-
},
|
|
35
|
-
"statusCodes": [200, 201]
|
|
36
|
-
},
|
|
37
23
|
{
|
|
38
24
|
"action": "goTo",
|
|
39
25
|
"url": "https://www.google.com"
|
package/test/runTests.test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { createServer } = require("./server");
|
|
1
2
|
const path = require("path");
|
|
2
3
|
const { spawnCommand } = require("../src/utils");
|
|
3
4
|
const assert = require("assert").strict;
|
|
@@ -5,6 +6,36 @@ const fs = require("fs");
|
|
|
5
6
|
const artifactPath = path.resolve(__dirname, "./artifacts");
|
|
6
7
|
const outputFile = path.resolve(`${artifactPath}/testResults.json`);
|
|
7
8
|
|
|
9
|
+
// Create a server with custom options
|
|
10
|
+
const server = createServer({
|
|
11
|
+
port: 8080,
|
|
12
|
+
staticDir: './test/server/public',
|
|
13
|
+
modifyResponse: (req, body) => {
|
|
14
|
+
// Optional modification of responses
|
|
15
|
+
return { ...body, extraField: 'added by server' };
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// Start the server before tests
|
|
20
|
+
before(async () => {
|
|
21
|
+
try {
|
|
22
|
+
await server.start();
|
|
23
|
+
} catch (error) {
|
|
24
|
+
console.error(`Failed to start test server: ${error.message}`);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Stop the server after tests
|
|
30
|
+
after(async () => {
|
|
31
|
+
try {
|
|
32
|
+
await server.stop();
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`Failed to stop test server: ${error.message}`);
|
|
35
|
+
// Don't rethrow here to avoid masking test failures
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
8
39
|
describe("Run tests successfully", function () {
|
|
9
40
|
// Set indefinite timeout
|
|
10
41
|
this.timeout(0);
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const express = require("express");
|
|
2
|
+
const bodyParser = require("body-parser");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates an echo server that can serve static content and echo back API requests
|
|
8
|
+
* @param {Object} options - Configuration options
|
|
9
|
+
* @param {number} [options.port=8080] - Port to run the server on
|
|
10
|
+
* @param {string} [options.staticDir="public"] - Directory to serve static files from
|
|
11
|
+
* @param {Function} [options.modifyResponse] - Function to modify responses before sending
|
|
12
|
+
* @returns {Object} Server object with start and stop methods
|
|
13
|
+
*/
|
|
14
|
+
function createServer(options = {}) {
|
|
15
|
+
const {
|
|
16
|
+
port = 8080,
|
|
17
|
+
staticDir = "public",
|
|
18
|
+
modifyResponse = (req, body) => body,
|
|
19
|
+
} = options;
|
|
20
|
+
|
|
21
|
+
const app = express();
|
|
22
|
+
let server = null;
|
|
23
|
+
|
|
24
|
+
// Parse JSON and urlencoded bodies
|
|
25
|
+
app.use(bodyParser.json({ limit: "10mb" }));
|
|
26
|
+
app.use(bodyParser.urlencoded({ extended: true }));
|
|
27
|
+
|
|
28
|
+
// Serve static files if a directory is provided
|
|
29
|
+
if (staticDir && fs.existsSync(staticDir)) {
|
|
30
|
+
app.use(express.static(staticDir));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Echo API endpoint that returns the request body
|
|
34
|
+
app.all("/api/:path", (req, res) => {
|
|
35
|
+
try {
|
|
36
|
+
const requestBody = req.method === "GET" ? req.query : req.body;
|
|
37
|
+
const modifiedResponse = modifyResponse(req, requestBody);
|
|
38
|
+
console.log("Request:", {
|
|
39
|
+
Method: req.method,
|
|
40
|
+
Path: req.path,
|
|
41
|
+
Query: req.query,
|
|
42
|
+
Headers: req.headers,
|
|
43
|
+
Body: req.body,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
res.set("x-server", "doc-detective-echo-server");
|
|
47
|
+
|
|
48
|
+
console.log("Response:", { Body: modifiedResponse });
|
|
49
|
+
|
|
50
|
+
res.json(modifiedResponse);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error("Error processing request:", error);
|
|
53
|
+
res.status(500).json({ error: "Internal server error" });
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
/**
|
|
59
|
+
* Start the server
|
|
60
|
+
* @returns {Promise} Promise that resolves with the server address
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
start: () => {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
try {
|
|
66
|
+
server = app.listen(port, () => {
|
|
67
|
+
const serverAddress = `http://localhost:${port}`;
|
|
68
|
+
console.log(`Echo server running at ${serverAddress}`);
|
|
69
|
+
resolve(serverAddress);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
server.on("error", (error) => {
|
|
73
|
+
console.error(`Failed to start server: ${error.message}`);
|
|
74
|
+
reject(error);
|
|
75
|
+
});
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(`Error setting up server: ${error.message}`);
|
|
78
|
+
reject(error);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Stop the server
|
|
85
|
+
* @returns {Promise} Promise that resolves when server is stopped
|
|
86
|
+
*/
|
|
87
|
+
stop: () => {
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
if (server) {
|
|
90
|
+
server.close((error) => {
|
|
91
|
+
if (error) {
|
|
92
|
+
console.error("Error stopping server:", error);
|
|
93
|
+
reject(error);
|
|
94
|
+
} else {
|
|
95
|
+
console.log("Echo server stopped");
|
|
96
|
+
server = null;
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
resolve();
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the Express app instance
|
|
108
|
+
* @returns {Object} Express app
|
|
109
|
+
*/
|
|
110
|
+
getApp: () => app,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Export the function
|
|
115
|
+
module.exports = { createServer };
|
|
116
|
+
|
|
117
|
+
// If this file is run directly, start a server
|
|
118
|
+
if (require.main === module) {
|
|
119
|
+
const server = createServer({
|
|
120
|
+
port: process.env.PORT || 8080,
|
|
121
|
+
staticDir:
|
|
122
|
+
process.env.STATIC_DIR ||
|
|
123
|
+
path.join(process.cwd(), "./test/server/public"),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
server.start();
|
|
127
|
+
|
|
128
|
+
// Handle graceful shutdown
|
|
129
|
+
const shutdown = () => {
|
|
130
|
+
console.log("Shutting down server...");
|
|
131
|
+
server
|
|
132
|
+
.stop()
|
|
133
|
+
.then(() => process.exit(0))
|
|
134
|
+
.catch(() => process.exit(1));
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
process.on("SIGINT", shutdown);
|
|
138
|
+
process.on("SIGTERM", shutdown);
|
|
139
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Basic HTML Elements Demo</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<header>
|
|
10
|
+
<h1>Common HTML Elements</h1>
|
|
11
|
+
<p>This page demonstrates common HTML elements a user might interact with.</p>
|
|
12
|
+
</header>
|
|
13
|
+
|
|
14
|
+
<nav>
|
|
15
|
+
<ul>
|
|
16
|
+
<li><a href="#text-inputs">Text Inputs</a></li>
|
|
17
|
+
<li><a href="#selection-elements">Selection Elements</a></li>
|
|
18
|
+
<li><a href="#buttons">Buttons</a></li>
|
|
19
|
+
<li><a href="#text-elements">Text Elements</a></li>
|
|
20
|
+
<li><a href="#lists">Lists</a></li>
|
|
21
|
+
<li><a href="#tables">Tables</a></li>
|
|
22
|
+
</ul>
|
|
23
|
+
</nav>
|
|
24
|
+
|
|
25
|
+
<main>
|
|
26
|
+
<section id="text-inputs">
|
|
27
|
+
<h2>Text Inputs</h2>
|
|
28
|
+
<form>
|
|
29
|
+
<div>
|
|
30
|
+
<label for="text-input">Text Input:</label>
|
|
31
|
+
<input type="text" id="text-input" name="text-input" placeholder="Enter some text">
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div>
|
|
35
|
+
<label for="password-input">Password Input:</label>
|
|
36
|
+
<input type="password" id="password-input" name="password-input" placeholder="Enter password">
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<div>
|
|
40
|
+
<label for="email-input">Email Input:</label>
|
|
41
|
+
<input type="email" id="email-input" name="email-input" placeholder="Enter your email">
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div>
|
|
45
|
+
<label for="textarea">Textarea:</label>
|
|
46
|
+
<textarea id="textarea" name="textarea" rows="4" cols="50" placeholder="Enter longer text here..."></textarea>
|
|
47
|
+
</div>
|
|
48
|
+
</form>
|
|
49
|
+
</section>
|
|
50
|
+
|
|
51
|
+
<section id="selection-elements">
|
|
52
|
+
<h2>Selection Elements</h2>
|
|
53
|
+
<form>
|
|
54
|
+
<div>
|
|
55
|
+
<p>Checkboxes:</p>
|
|
56
|
+
<input type="checkbox" id="checkbox1" name="checkbox1" value="option1">
|
|
57
|
+
<label for="checkbox1">Option 1</label><br>
|
|
58
|
+
<input type="checkbox" id="checkbox2" name="checkbox2" value="option2">
|
|
59
|
+
<label for="checkbox2">Option 2</label><br>
|
|
60
|
+
<input type="checkbox" id="checkbox3" name="checkbox3" value="option3">
|
|
61
|
+
<label for="checkbox3">Option 3</label>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
<p>Radio Buttons:</p>
|
|
66
|
+
<input type="radio" id="radio1" name="radio-group" value="option1">
|
|
67
|
+
<label for="radio1">Option 1</label><br>
|
|
68
|
+
<input type="radio" id="radio2" name="radio-group" value="option2">
|
|
69
|
+
<label for="radio2">Option 2</label><br>
|
|
70
|
+
<input type="radio" id="radio3" name="radio-group" value="option3">
|
|
71
|
+
<label for="radio3">Option 3</label>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
<div>
|
|
75
|
+
<label for="select">Dropdown Select:</label>
|
|
76
|
+
<select id="select" name="select">
|
|
77
|
+
<option value="">-- Please choose an option --</option>
|
|
78
|
+
<option value="option1">Option 1</option>
|
|
79
|
+
<option value="option2">Option 2</option>
|
|
80
|
+
<option value="option3">Option 3</option>
|
|
81
|
+
</select>
|
|
82
|
+
</div>
|
|
83
|
+
</form>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<section id="buttons">
|
|
87
|
+
<h2>Buttons</h2>
|
|
88
|
+
<button type="button">Standard Button</button>
|
|
89
|
+
<input type="button" value="Input Button">
|
|
90
|
+
<input type="submit" value="Submit Button">
|
|
91
|
+
<input type="reset" value="Reset Button">
|
|
92
|
+
</section>
|
|
93
|
+
|
|
94
|
+
<section id="text-elements">
|
|
95
|
+
<h2>Text Elements</h2>
|
|
96
|
+
<h3>This is a heading 3</h3>
|
|
97
|
+
<h4>This is a heading 4</h4>
|
|
98
|
+
<p>This is a paragraph of text. It demonstrates the paragraph element in HTML.</p>
|
|
99
|
+
<p><strong>Bold text</strong> and <em>italic text</em> are created using the strong and em elements.</p>
|
|
100
|
+
<p>This is a <a href="https://example.com">hyperlink</a> to example.com.</p>
|
|
101
|
+
<blockquote>
|
|
102
|
+
This is a blockquote. It's typically used for quoting content from another source.
|
|
103
|
+
</blockquote>
|
|
104
|
+
<pre>This is preformatted text.
|
|
105
|
+
It preserves both spaces
|
|
106
|
+
and line breaks.</pre>
|
|
107
|
+
<code>This is inline code.</code>
|
|
108
|
+
</section>
|
|
109
|
+
|
|
110
|
+
<section id="lists">
|
|
111
|
+
<h2>Lists</h2>
|
|
112
|
+
<h3>Unordered List</h3>
|
|
113
|
+
<ul>
|
|
114
|
+
<li>Item 1</li>
|
|
115
|
+
<li>Item 2</li>
|
|
116
|
+
<li>Item 3</li>
|
|
117
|
+
</ul>
|
|
118
|
+
|
|
119
|
+
<h3>Ordered List</h3>
|
|
120
|
+
<ol>
|
|
121
|
+
<li>First item</li>
|
|
122
|
+
<li>Second item</li>
|
|
123
|
+
<li>Third item</li>
|
|
124
|
+
</ol>
|
|
125
|
+
|
|
126
|
+
<h3>Definition List</h3>
|
|
127
|
+
<dl>
|
|
128
|
+
<dt>Term 1</dt>
|
|
129
|
+
<dd>Definition for Term 1</dd>
|
|
130
|
+
<dt>Term 2</dt>
|
|
131
|
+
<dd>Definition for Term 2</dd>
|
|
132
|
+
</dl>
|
|
133
|
+
</section>
|
|
134
|
+
|
|
135
|
+
<section id="tables">
|
|
136
|
+
<h2>Tables</h2>
|
|
137
|
+
<table border="1">
|
|
138
|
+
<caption>Simple Data Table</caption>
|
|
139
|
+
<thead>
|
|
140
|
+
<tr>
|
|
141
|
+
<th>Name</th>
|
|
142
|
+
<th>Age</th>
|
|
143
|
+
<th>Country</th>
|
|
144
|
+
</tr>
|
|
145
|
+
</thead>
|
|
146
|
+
<tbody>
|
|
147
|
+
<tr>
|
|
148
|
+
<td>John Doe</td>
|
|
149
|
+
<td>25</td>
|
|
150
|
+
<td>USA</td>
|
|
151
|
+
</tr>
|
|
152
|
+
<tr>
|
|
153
|
+
<td>Jane Smith</td>
|
|
154
|
+
<td>30</td>
|
|
155
|
+
<td>Canada</td>
|
|
156
|
+
</tr>
|
|
157
|
+
<tr>
|
|
158
|
+
<td>Bob Johnson</td>
|
|
159
|
+
<td>45</td>
|
|
160
|
+
<td>UK</td>
|
|
161
|
+
</tr>
|
|
162
|
+
</tbody>
|
|
163
|
+
</table>
|
|
164
|
+
</section>
|
|
165
|
+
</main>
|
|
166
|
+
|
|
167
|
+
<footer>
|
|
168
|
+
<p>© 2025 Basic HTML Elements Demo</p>
|
|
169
|
+
<address>
|
|
170
|
+
Contact: <a href="mailto:example@example.com">example@example.com</a>
|
|
171
|
+
</address>
|
|
172
|
+
</footer>
|
|
173
|
+
</body>
|
|
174
|
+
</html>
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "http tests",
|
|
3
|
-
"description": "This is a test collection",
|
|
4
|
-
"tests": [
|
|
5
|
-
{
|
|
6
|
-
"id": "env variables test",
|
|
7
|
-
"description": "These tests will show off how using env files to set variables works in Doc Detective.",
|
|
8
|
-
"steps": [
|
|
9
|
-
{
|
|
10
|
-
"action": "setVariables",
|
|
11
|
-
"path": "env"
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"action": "httpRequest",
|
|
15
|
-
"url": "https://reqres.in/api/users",
|
|
16
|
-
"method": "post",
|
|
17
|
-
"requestData": {
|
|
18
|
-
"auth": "$SECRET",
|
|
19
|
-
"name": "$USER",
|
|
20
|
-
"job": "$JOB"
|
|
21
|
-
},
|
|
22
|
-
"responseData": {
|
|
23
|
-
"name": "John Doe",
|
|
24
|
-
"job": "Software Engineer"
|
|
25
|
-
},
|
|
26
|
-
"statusCodes": [200, 201]
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"action": "httpRequest",
|
|
30
|
-
"url": "https://reqres.in/api/users",
|
|
31
|
-
"method": "get",
|
|
32
|
-
"envsFromResponseData": [
|
|
33
|
-
{
|
|
34
|
-
"name": "ID",
|
|
35
|
-
"jqFilter": ".data[0].id"
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
"statusCodes": [200, 201]
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"action": "httpRequest",
|
|
42
|
-
"url": "https://reqres.in/api/users/$ID",
|
|
43
|
-
"method": "get",
|
|
44
|
-
"responseData": {
|
|
45
|
-
"data": {
|
|
46
|
-
"first_name": "George",
|
|
47
|
-
"last_name": "Bluth"
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"statusCodes": [200, 201]
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
]
|
|
55
|
-
}
|