From ccfafbe5f508b068af551179ca1abe4a90066577 Mon Sep 17 00:00:00 2001 From: Shubhendu Madhukar Date: Fri, 4 Aug 2023 16:39:10 +0000 Subject: [PATCH 1/3] docs: cosmetic changes: highlights and formatting --- docs/code-helper.md | 62 ++++++++++++++++++++++++++++++++++++ docs/external-data-source.md | 4 +-- docs/external-helper.md | 2 +- docs/handlebars.md | 18 +++++------ docs/mocking-soap.md | 4 +-- docs/proxying.md | 2 +- docs/request-matching.md | 8 ++--- docs/response-delays.md | 2 +- docs/validation.md | 4 +-- mkdocs.yml | 4 +++ 10 files changed, 88 insertions(+), 22 deletions(-) create mode 100644 docs/code-helper.md diff --git a/docs/code-helper.md b/docs/code-helper.md new file mode 100644 index 0000000..2e3d8bb --- /dev/null +++ b/docs/code-helper.md @@ -0,0 +1,62 @@ +# Code Helper + +## Tips for better usage + +Writing long custom code for your mocks could be a painful task without IDE features like syntax highlighting, auto formatting, code completion etc. While Camouflage does not have a extensions for your IDEs, you can easily write your code in javascript files and import them in mock files, instead of writing everything in your mock files. + +Here's an example by [jsapes](https://github.com/jsapes) + +Ususally you would write your mocks like this: + +```javascript +HTTP/1.1 200 OK +Content-Type: application/json + +{{#code}} +(()=>{ + /** + * Long javascript code to create myBody + */ + return { + status: 201, + body: { ...myBody }, + }; +})(); +{{/code}} +``` + +Instead you can break this down into two files. + +* GET.mock +```javascript +HTTP/1.1 200 OK +Content-Type: application/json + +{{#code}} +(()=>{ +const path = require("path") +// Store all your custom code in a separate folder. +const myPath = path.join(process.cwd(), "mycustomcodes", "getResponseBody.js") +const { getResponseBody } = require(myPath) +return getResponseBody(request, logger) +})(); +{{/code}} +``` + +* getResponseBody.js + +```javascript +const getResponseBody = (request, logger) => { +/** + * Long javascript code to create myBody + */ +return { + status: 201, + body: { ...myBody }, +}; +}; + +module.exports = { getResponseBody }; +``` + +Reference: [discussioncomment-3769698](https://github.com/testinggospels/camouflage/discussions/22#discussioncomment-3769698) diff --git a/docs/external-data-source.md b/docs/external-data-source.md index 0ea8b77..e94b59b 100644 --- a/docs/external-data-source.md +++ b/docs/external-data-source.md @@ -24,7 +24,7 @@ Update the connection parameters with your database details. In your mock files, use `pg` helper to run queries and fetch corresponding data. A sample mock file would look similar to the following content: -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json @@ -72,7 +72,7 @@ Camouflage then gives you access to a `result` array, which you can use inside a Please note that the value you return MUST be a json object which contains a body in a string format (required), you can optionally provide status and headers as well. -``` +```javascript HTTP/1.1 200 OK X-Requested-By: Shubhendu Madhukar Content-Type: application/json diff --git a/docs/external-helper.md b/docs/external-helper.md index ee53b7f..b66db5f 100644 --- a/docs/external-helper.md +++ b/docs/external-helper.md @@ -22,7 +22,7 @@ The JSON in the file should be an array of JSON Objects containing two keys: `na This loads a custom helper, `is`, which can be used in your mock files to compare to values. Use it as shown in example: -``` +```javascript HTTP/1.1 200 OK {{#is value1=request.query.name value2='Shubhendu'}} diff --git a/docs/handlebars.md b/docs/handlebars.md index 1f73a40..baf3a5d 100644 --- a/docs/handlebars.md +++ b/docs/handlebars.md @@ -53,7 +53,7 @@ Usage: 1. **{{capture from='query' key='firstName'}}** - Pretty self-explanatory, but if your endpoint looks like /hello-world?firstName=John&lastName=Wick. And your response is {"message": "Hello Wick, John"}, you can make the response dynamic by formatting your response as -``` +```json { "message": "Hello {{capture from='query' key='lastName'}}, {{capture from='query' key='firstName'}}" } @@ -94,7 +94,7 @@ Usage: **{{file path='/location/of/the/image/or/text/or/any/file'}}**: If you want to serve a file as a response, maybe an image, or text file, a pdf document, or any type of supported files, use file helper to do so. An example is shown below: -``` +```javascript HTTP/1.1 200 OK Content-Type: application/pdf @@ -107,7 +107,7 @@ Type: Custom Helper Usage: Camouflage's implementation of Handlebars is robust enough to handle most dynamic responses i.e. capturing data from request, generating random numbers, as shown in examples above. However, if your requirement still cannot be fulfilled by Camouflage's helpers, you can write a custom code in javascript to achieve the same results. Refer to the example mock and explanation below: -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json @@ -160,7 +160,7 @@ Type: Custom Helper Usage: Another use case for custom code could be when you don't want to write a code for the entire response generation, but there are some parts of your response that need a custom code. Using `inject` helper you can use Camouflage's helpers and your custom code both together. Implementation remains similar to `code` helper, refer to the example below. -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json @@ -223,7 +223,7 @@ Type: Custom Helper Usage: Proxy Helper allows you to redirect your calls to an actual downstream selectively. You might want to redirect all calls to actual downstream or some calls based on some condition, i.e. if a specific header exists, or a query param is provided. Example mock file content: -``` +```javascript HTTP/1.1 200 OK x-additional-headers: somevalue @@ -252,7 +252,7 @@ Usage: Assign helper can be used to assign a value to a variable, by specifying Example: Using a complex combination of helpers, i.e. `assign`, `concat`, `pg` and `capture`, to create a mock that would fetch a response from postgres table for a given id passed as a query parameter. -``` +```javascript {{assign name='query' value=(concat "SELECT * FROM emp WHERE id = '" (capture from="query" key="id") "'") }} HTTP/1.1 200 OK Content-Type: application/json @@ -347,7 +347,7 @@ cy.setState('cart', [{id: 1, name: "prod1"}, {id: 2, name: "prod2"}]); Raw HTML Request: -``` +```javascript POST /users HTTP/1.1 Content-Type: application/json @@ -367,7 +367,7 @@ Content-Type: application/json Expected Raw HTML Response: -``` +```javascript HTTP/1.1 201 OK X-Requested-By: user-service Content-Type: application/json @@ -389,7 +389,7 @@ Content-Type: application/json 1. To create this service in camouflage, create a directory users under your ${MOCKS_DIR}. i.e. ${MOCKS_DIR}/users 2. Create a file POST.mock and add following content to the file -``` +```javascript HTTP/1.1 201 OK X-Requested-By: user-service Content-Type: application/json diff --git a/docs/mocking-soap.md b/docs/mocking-soap.md index e9ca711..d89cb6e 100644 --- a/docs/mocking-soap.md +++ b/docs/mocking-soap.md @@ -60,7 +60,7 @@ protocols: **Request** -``` +```bash curl -X POST \ 'http://localhost:8100/countryinfo' \ --header 'Accept: */*' \ @@ -76,7 +76,7 @@ curl -X POST \ ``` **Response** -``` +```xml diff --git a/docs/proxying.md b/docs/proxying.md index b3caae6..6de072b 100644 --- a/docs/proxying.md +++ b/docs/proxying.md @@ -10,7 +10,7 @@ ${HTTP_MOCKS_DIR}/calls-proxied/to/this-target/POST.mock Content of this mock file will use proxy block helper and specify a configuration as supported by `http-proxy`: -``` +```javascript HTTP/1.1 200 OK {{#proxy}} diff --git a/docs/request-matching.md b/docs/request-matching.md index 7d6a2cc..293dd88 100644 --- a/docs/request-matching.md +++ b/docs/request-matching.md @@ -16,7 +16,7 @@ response. This can be done in following manner: Create a GET.mock file under the directory ${MOCKS_DIR}/hello-world. And paste following content: -``` +```javascript {{#if request.query.name}} HTTP/1.1 200 OK X-Requested-By: Shubhendu Madhukar @@ -62,7 +62,7 @@ Thus if the end user makes a GET request as `/hello-world?name=John`, he'd get a To perform request matching using headers the, mocks need to follow a slightly different approach. Using `capture` helper, we need to capture a specific header value which then can be passed to other helpers like `is` or `if`. -``` +```javascript {{#if (capture from='headers' key='Authorization') }} HTTP/1.1 200 OK Content-Type: application/json @@ -82,7 +82,7 @@ Content-Type: application/json If you want to validate a given header against a specific value, the mock file would be as shown below: -``` +```javascript {{#is (capture from='headers' key='Authorization') 'Basic c2h1YmhlbmR1Om1hZGh1a2Fy' }} HTTP/1.1 200 OK Content-Type: application/json @@ -103,7 +103,7 @@ Content-Type: application/json The same validation, albeit messy, can be carried out using `code` helper, as shown below. This needs `config.injection.enable` to be set to `true` **Code** -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json diff --git a/docs/response-delays.md b/docs/response-delays.md index 6a4c031..4ef2044 100644 --- a/docs/response-delays.md +++ b/docs/response-delays.md @@ -4,7 +4,7 @@ Response delays are handled in a similar manner as it was done in mockserver, i. For example, if you'd like to simulate a delay of 2 seconds for /hello-world endpoint, contents of your .mock file would be as follows: -``` +```javascript HTTP/1.1 200 OK X-Requested-By: Shubhendu Madhukar Content-Type: application/json diff --git a/docs/validation.md b/docs/validation.md index 56238dc..83d8e1a 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -26,7 +26,7 @@ validation: Now when you have a mock for the supported endpoint `/pets` requesting it would result in a proper response. -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json @@ -156,7 +156,7 @@ Given this schema for `/pets` we see that a pet has two required properties `id` In case previously your backend api did only had a required property `id` your assumptions in the tests are false. -``` +```javascript HTTP/1.1 200 OK Content-Type: application/json diff --git a/mkdocs.yml b/mkdocs.yml index f88cba6..2b7d21c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,9 @@ theme: markdown_extensions: - admonition - attr_list + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.superfences nav: - index.md - getting-started.md @@ -40,6 +43,7 @@ nav: - mocking-thrift.md - mocking-soap.md - capture-helper.md + - code-helper.md - camouflage-ui.md - available-monitoring.md - tests.md From 4337c2252793a1b5f5afd047d7b46c8de29afc65 Mon Sep 17 00:00:00 2001 From: Shubhendu Madhukar Date: Sat, 5 Aug 2023 07:52:15 +0530 Subject: [PATCH 2/3] fix: file helper fix return statements #229 --- src/parser/HttpParser.ts | 7 ++++++- src/routes/GlobalController.ts | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/parser/HttpParser.ts b/src/parser/HttpParser.ts index 6e33c94..113d3b8 100644 --- a/src/parser/HttpParser.ts +++ b/src/parser/HttpParser.ts @@ -176,7 +176,12 @@ export class HttpParser { const fileResponse = responseBody.split(";")[1]; if (!fs.existsSync(fileResponse)) this.res.status(404); await sleep(DELAY); - this.res.sendFile(fileResponse); + return { + ...response, + body: JSON.stringify({ + "camouflage_file_helper": fileResponse + }) + }; } else { responseBody = responseBody.replace(/\s+/g, " ").trim(); responseBody = responseBody.replace(/{{{/, "{ {{"); diff --git a/src/routes/GlobalController.ts b/src/routes/GlobalController.ts index a5bb61a..436262c 100644 --- a/src/routes/GlobalController.ts +++ b/src/routes/GlobalController.ts @@ -40,6 +40,15 @@ export default class GlobalController { const parser = new HttpParser(req, res, this.mocksDir); const mockFile = parser.getMatchedDir() + `/${verb}.mock`; const response = await parser.getResponse(mockFile); + try { + const fileBody = JSON.parse(response.body) + if (fileBody.hasOwnProperty("camouflage_file_helper")) { + res.sendFile(fileBody["camouflage_file_helper"]) + return + } + } catch (error) { + //do nothing + } if (!res.headersSent) { const responseValidation = validator.validateResponse(req, response); if (responseValidation.valid) { From 29a6bb35eaec80a52b64cd0ef061f7684718348a Mon Sep 17 00:00:00 2001 From: Shubhendu Madhukar Date: Sat, 5 Aug 2023 07:59:35 +0530 Subject: [PATCH 3/3] fix: linting error --- src/routes/GlobalController.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/GlobalController.ts b/src/routes/GlobalController.ts index 436262c..6946b4f 100644 --- a/src/routes/GlobalController.ts +++ b/src/routes/GlobalController.ts @@ -42,7 +42,8 @@ export default class GlobalController { const response = await parser.getResponse(mockFile); try { const fileBody = JSON.parse(response.body) - if (fileBody.hasOwnProperty("camouflage_file_helper")) { + const hasKey = Object.prototype.hasOwnProperty.call(fileBody, 'camouflage_file_helper'); + if (hasKey) { res.sendFile(fileBody["camouflage_file_helper"]) return }