Skip to content

Commit

Permalink
Support pathing (#153)
Browse files Browse the repository at this point in the history
* can use id to match paths

* full path in header

* validation

* make redis errors clearer

* rotate log url structure

* support empty path

* rotate paths

* fix

* remove unused parsers

* validate

* remove 400s

* remove validation
  • Loading branch information
jackkav authored Jan 9, 2024
1 parent 4ff28c6 commit 14b87d2
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 68 deletions.
25 changes: 12 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,8 @@ Mockbin is used internally and maintained by [Kong](https://github.com/Kong), wh

## Installation

```shell
git clone https://github.com/Kong/mockbin.git ./mockbin
cd mockbin
cp .env.sample .env
brew install fnm
fnm use
npm install
```

Note: nvm, n or volta can be used instead of fnm.

### Requirements

other than the dependencies listed in [package.json](package.json) The following are required:

- [Redis](http://redis.io/)

```shell
Expand All @@ -49,6 +36,18 @@ brew services start redis
```

Redis should be now running on localhost:6379
Mockbin will start without redis but you wont be able to set or get response bins.

```shell
git clone https://github.com/Kong/mockbin.git ./mockbin
cd mockbin
cp .env.sample .env
brew install fnm
fnm use
npm install
```

Note: nvm, n or volta can be used instead of fnm.

### Running with Node

Expand Down
3 changes: 2 additions & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"rules": {
"recommended": true,
"complexity": {
"noForEach": "off"
"noForEach": "off",
"useArrowFunction": "off"
}
}
}
Expand Down
35 changes: 18 additions & 17 deletions docs/api/bins.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,16 @@ Responds with a `Location` header with the newly created **Bin**, e.g. `Location

#### Update Bin

> ##### `PUT /bin/:id`
> ##### `PUT /bin/:id/a/b/c`
Updates a new **Bin** with a mock HTTP response as described by a [HAR Response Object](http://www.softwareishard.com/blog/har-12-spec/#response) body.
Creates or updates a **Bin** with a mock HTTP response as described by a [HAR Response Object](http://www.softwareishard.com/blog/har-12-spec/#response) body. /a/b/c represeent any following paths than will be combined with the id for response matching.

Responds with a `Location` header with the updated **Bin**, e.g. `Location: /bin/3c149e20-bc9c-4c68-8614-048e6023a108` *(the Bin ID is also repeated in the body)*
Responds with a `Location` header with the updated **Bin**, e.g. `Location: /bin/3c149e20-bc9c-4c68-8614-048e6023a108/a/b/c` *(the Bin ID is also repeated in the body)*

###### Request

> ```http
> PUT /bin/3c149e20-bc9c-4c68-8614-048e6023a108 HTTP/1.1
> PUT /bin/3c149e20-bc9c-4c68-8614-048e6023a108/a/b/c HTTP/1.1
> Host: mockbin.org
> Content-Type: application/json
> Accept: application/json
Expand Down Expand Up @@ -162,7 +162,7 @@ Responds with a `Location` header with the updated **Bin**, e.g. `Location: /bin

> ```http
> HTTP/1.1 200 OK
> Location: /bin/3c149e20-bc9c-4c68-8614-048e6023a108
> Location: /bin/3c149e20-bc9c-4c68-8614-048e6023a108/a/b/c
> Content-Type: application/json; charset=utf-8
> Content-Length: 38
>
Expand All @@ -173,14 +173,14 @@ Responds with a `Location` header with the updated **Bin**, e.g. `Location: /bin

#### Inspect Bin

> ##### `GET /bin/:id/view`
> ##### `GET /bin/view/:id`
Respondes with the [HAR Response Object](http://www.softwareishard.com/blog/har-12-spec/#response) sent at time of [creation](#create-bin).

###### Request

> ```http
> GET /bin/3c149e20-bc9c-4c68-8614-048e6023a108/view HTTP/1.1
> GET /bin/view/3c149e20-bc9c-4c68-8614-048e6023a108 HTTP/1.1
> Host: mockbin.org
> Accept: application/json
> ```
Expand Down Expand Up @@ -252,16 +252,17 @@ The [HAR Response Object](http://www.softwareishard.com/blog/har-12-spec/#respon
Each call to this endpoint will be [logged](#bin-log) *(max of 100 requests)*.

You can request this endpoint with *any* combination of the following:
- HTTP methods *(e.g. `POST`, `XXPUT`)*
- HTTP headers *(e.g. `X-My-Header-Name: Value`)*
- body content *(max of 100mb)*
- query string *(e.g. `?foo=bar`)*
- path arguments *(e.g. `/bin/3c149e20-bc9c-4c68-8614-048e6023a108/any/extra/path/`)*

- HTTP methods *(e.g. `POST`, `XXPUT`)*
- HTTP headers *(e.g. `X-My-Header-Name: Value`)*
- body content *(max of 100mb)*
- query string *(e.g. `?foo=bar`)*
- path arguments *(e.g. `/bin/3c149e20-bc9c-4c68-8614-048e6023a108/any/extra/path/`)*

###### Request

> ```http
> GET /bin/3c149e20-bc9c-4c68-8614-048e6023a108/view HTTP/1.1
> GET /bin/view/3c149e20-bc9c-4c68-8614-048e6023a108 HTTP/1.1
> Host: mockbin.org
> Accept: application/json
> ```
Expand All @@ -284,14 +285,14 @@ You can request this endpoint with *any* combination of the following:
#### Bin Access Log

> ##### `GET /bin/:id/log`
> ##### `GET /bin/log/:id`
List all requests made to this Bin, using [HAR](http://www.softwareishard.com/blog/har-12-spec/) log format.

###### Request

> ```http
> GET /bin/3c149e20-bc9c-4c68-8614-048e6023a108/log HTTP/1.1
> GET /bin/log/3c149e20-bc9c-4c68-8614-048e6023a108 HTTP/1.1
> Host: mockbin.org
> Accept: application/json
> ```
Expand All @@ -316,14 +317,14 @@ List all requests made to this Bin, using [HAR](http://www.softwareishard.com/bl
#### Delete Bin

> ##### `DELETE /bin/:id/delete`
> ##### `DELETE /bin/delete/:id`
Deletes the bin and all of its logs

###### Request

> ```http
> GET /bin/3c149e20-bc9c-4c68-8614-048e6023a108/view HTTP/1.1
> GET /bin/view/3c149e20-bc9c-4c68-8614-048e6023a108 HTTP/1.1
> ```
###### Response
Expand Down
12 changes: 6 additions & 6 deletions lib/routes/bins.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = function bins(dsnStr) {
}

this.client.on("error", (err) => {
debug("redis error:", err);
console.log("redis error:", err);
});

const router = express.Router();
Expand All @@ -42,15 +42,15 @@ module.exports = function bins(dsnStr) {
const endpoints = [
{ action: "get", path: "/create", route: routes.form.bind(this) },
{ action: "post", path: "/create", route: routes.create.bind(this) },
{ action: "get", path: "/:uuid/view", route: routes.view.bind(this) },
{ action: "get", path: "/:uuid/sample", route: routes.sample.bind(this) },
{ action: "get", path: "/:uuid/log", route: routes.log.bind(this) },
{ action: "get", path: "/view/:uuid*", route: routes.view.bind(this) },
{ action: "get", path: "/sample/:uuid*", route: routes.sample.bind(this) },
{ action: "get", path: "/log/:uuid*", route: routes.log.bind(this) },
{
action: "delete",
path: "/:uuid/delete",
path: "/delete/:uuid*",
route: routes.delete.bind(this),
},
{ action: "put", path: "/:uuid", route: routes.update.bind(this) },
{ action: "put", path: "/:uuid*", route: routes.update.bind(this) },
{ action: "all", path: "/:uuid*", route: routes.run.bind(this) },
];

Expand Down
2 changes: 1 addition & 1 deletion lib/routes/bins/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ module.exports = async function (req, res, next) {

.catch((err) => {
res.body = {
errors: err.errors,
errors: err.message,
};
})

Expand Down
4 changes: 2 additions & 2 deletions lib/routes/bins/log.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ const pkg = require("../../../package.json");

module.exports = function (req, res, next) {
res.view = "bin/log";

this.client.lrange(`log:${req.params.uuid}`, 0, -1, (err, history) => {
const compoundId = req.params.uuid + req.params[0];
this.client.lrange(`log:${compoundId}`, 0, -1, (err, history) => {
if (err) {
debug(err);

Expand Down
8 changes: 5 additions & 3 deletions lib/routes/bins/run.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const debug = require("debug")("mockbin");

module.exports = function (req, res, next) {
// compoundId allows us to provide paths in the id to resolve to a specific bin
const compoundId = req.params.uuid + req.params[0];
this.client.get(
`bin:${req.params.uuid}`,
`bin:${compoundId}`,
function (err, value) {
if (err) {
debug(err);
Expand All @@ -15,10 +17,10 @@ module.exports = function (req, res, next) {

// log interaction & send the appropriate response based on HAR
this.client.rpush(
`log:${req.params.uuid}`,
`log:${compoundId}`,
JSON.stringify(req.har.log.entries[0]),
);
this.client.ltrim(`log:${req.params.uuid}`, 0, 100);
this.client.ltrim(`log:${compoundId}`, 0, 100);

// headers
har.headers.map((header) => {
Expand Down
34 changes: 14 additions & 20 deletions lib/routes/bins/update.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
const debug = require("debug")("mockbin");
const util = require("util");
const validate = require("har-validator");
const path = require("path");

module.exports = function (req, res, next) {
const id = req.params.uuid;
let mock = req.jsonBody;

// check for full HAR
if (req.jsonBody?.response) {
mock = req.jsonBody.response;
}
const path = req.params[0];
const compoundId = id + path;

// exception for the web Form
// TODO eliminate this and rely on req.simple.postData.text
if (req.simple.postData.params?.response) {
try {
mock = JSON.parse(req.simple.postData.params.response);
} catch (e) {
debug(e);
}
}
let mock = req.jsonBody;

// overritten by application/x-www-form-urlencoded or multipart/form-data
if (req.simple.postData.text) {
Expand All @@ -29,6 +17,13 @@ module.exports = function (req, res, next) {
debug(e);
}
}
if (!mock) {
res.body = {
errors: "Response HAR is required",
};
next();
return;
}

// provide optional values before validation
mock.redirectURL = "";
Expand All @@ -45,16 +40,15 @@ module.exports = function (req, res, next) {
.response(mock)
.then(
function () {
this.client.set(`bin:${id}`, JSON.stringify(mock));
this.client.set(`bin:${compoundId}`, JSON.stringify(mock));

res.view = "redirect";
res.status(200).location(util.format("/bin/%s", id)).body = id;
res.status(200).location(`/bin/${compoundId}`).body = id;
}.bind(this),
)

.catch((err) => {
res.body = {
errors: err.errors,
errors: err.message,
};
})

Expand Down
4 changes: 2 additions & 2 deletions src/views/bin/log.pug
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ mixin method (method)
block content
div(data-page="bin/log").container
div.btn-group.pull-right.hidden-xs
a(href= '/bin/' + req.params.uuid + '/view').btn.btn-primary View Details
a(href= '/bin/view/' + req.params.uuid).btn.btn-primary View Details

h3 Bin History: #[code= req.params.uuid]

div.visible-xs
a(href= '/bin/' + req.params.uuid + '/view').btn.btn-block.btn-primary View Details
a(href= '/bin/view' + req.params.uuid).btn.btn-block.btn-primary View Details

hr

Expand Down
2 changes: 1 addition & 1 deletion src/views/bin/view.pug
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ block content

a(href= '#apiembed').btn.btn-block.btn-primary #[span.badge 1]   Send Some Requests
a(href= '/bin/' + req.params.uuid).btn.btn-block.btn-primary #[span.badge 2]   Visit in Browser
a(href= '/bin/' + req.params.uuid + '/log').btn.btn-block.btn-primary #[span.badge 3]   View History
a(href= '/bin/log/' + req.params.uuid).btn.btn-block.btn-primary #[span.badge 3]   View History

br

Expand Down
4 changes: 2 additions & 2 deletions src/views/redirect.pug
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
doctype html
html
head
meta(http-equiv='refresh', content=`0; url=${res.getHeaders().location}/view`)
meta(http-equiv='refresh', content=`0; url=${res.getHeaders().location.replace('/bin','/bin/view')}`)

body
p Redirecting to #[a(href=res.getHeaders().location)= res.getHeaders().location + '/view']
p Redirecting to #[a(href=res.getHeaders().location)= res.getHeaders().location.replace('/bin','/bin/view')]

0 comments on commit 14b87d2

Please sign in to comment.