Giter Site home page Giter Site logo

tshield's Introduction

TShield

Build Status Coverage Status Gem Version TShield Publish

API mocks for development and testing

TShield is an open source proxy for mocks API responses.

  • REST
  • SOAP
  • Session manager to separate multiple scenarios (success, error, sucess variation, ...)
  • gRPC [EXPERIMENTAL]
  • Lightweight
  • MIT license

Table of Contents

Basic Usage

Install

gem install tshield

Using

To run server execute this command

tshield

Default port is 4567

Command Line Options

  • -port: Overwrite default port (4567)
  • -help: Show all command line options

Config example

Before run tshield command is necessary to create config file. This is an example of config/tshield.yml

request:
  # wait time for real service
  timeout: 8

# list of domains that will be used
domains:
  # Base URI of service
  'https://service.com':
    # name to identify the domain in the generated files
    name: 'service'

    # paths list of all services that will be called
    paths:
      - /users

Windows Compatibility: If you need to use Tshield in Windows SO, change the config file and set the windows_compatibility to true.

Eg:

windows_compatibility: true
request:
  # wait time for real service
  timeout: 8
...

Config options for Pattern Matching

An example of file to create a stub:

All files should be in matching directory. Each file should be a valid JSON array of objects and each object must contain at least the following attributes:

  • method: a http method.
  • path: url path. This attribute accept regex, see example in regex.json
  • response: object with response data. Into session can be used an array of objects to return different responses like vcr mode. See example: multiples_response.json. External file can be used as body content, see example in file.json.

Response must be contain the following attributes:

  • headers: key value object with expected headers to match. In the evaluation process this stub will be returned if all headers are in request.
  • status: integer used http status respose.
  • body: content to be returned.

Optional request attributes:

  • headers: key value object with expected headers to match. In the evaluation process this stub will be returned if all headers are in request.
  • query: works like headers but use query params.

Optional response attributes:

  • delay: integer that represents time in seconds that the response will be delayed to return

Important: If VCR config conflicts with Matching config Matching will be used. Matching config have priority.

Session Configuration

To register stub into a session create an object with following attributes:

  • session: name of session.
  • stubs: an array with objects described above.

Example of HTTP matching configuration

[
  {
    "method": "GET",
    "path": "/matching/example",
    "query": {
      "user": 123
    },
    "response": {
      "body": "matching-example-response-with-query",
      "headers": {},
      "status": 200
    }
  },
  {
    "method": "GET",
    "path": "/matching/example",
    "response": {
      "body": "matching-example-response",
      "headers": {},
      "status": 200
    }
  },
  {
    "method": "POST",
    "path": "/matching/example",
    "headers": {
      "user": "123"
    },
    "response": {
      "body": "matching-example-response-with-headers",
      "headers": {},
      "status": 200
    }
  },
  {
    "method": "POST",
    "path": "/matching/example",
    "response": {
      "body": "matching-example-response-with-post",
      "headers": {},
      "status": 200
    }
  },
  {
    "session": "example-session",
    "stubs": [{
      "method": "GET",
      "path": "/matching/example",
      "response": {
        "delay": 5,
        "body": "matching-example-response-in-session",
        "headers": {},
        "status": 200
      }
    }]
  }
]

Config options for HTTP VCR

request:
  timeout: 8
  verify_ssl: <<value>>
domains:
  'http://my-soap-service:80':
    name: 'my-soap-service'
    headers:
      HTTP_AUTHORIZATION: Authorization
      HTTP_COOKIE: Cookie
    send_header_content_type: true  
    not_save_headers:
      - transfer-encoding
    cache_request: <<value>>
    filters: 
      - <<value>>
    excluded_headers:
      - <<value>>
    paths:
      - /Operation

  'http://localhost:9090':
    name: 'my-service'
    headers:
      HTTP_AUTHORIZATION: Authorization
      HTTP_COOKIE: Cookie
      HTTP_DOCUMENTID: DocumentId
    not_save_headers:
      - transfer-encoding
    paths:
      - /secure
    delay:
      '/secure': 10

  'http://localhost:9092':
    name: 'my-other-service'
    headers:
      HTTP_AUTHORIZATION: Authorization
      HTTP_COOKIE: Cookie
    not_save_headers:
      - transfer-encoding
    paths:
      - /users
    delay:
      '/users': 5

request

  • timeout: wait time for real service in seconds
  • verify_ssl: ignores invalid ssl if false

domain

  • Define Base URI of service
  • name: Name to identify the domain in the generated files
  • headers: github-issue #17
  • send_header_content_type: Boolean domain config to send header 'Content-Type' when requesting this domain
  • not_save_headers: List of headers that should be ignored in generated file
  • skip_query_params: List of query params that should be ignored in generated file
  • cache_request: <<some_description>>
  • filters: Implementation of before or after filters used in domain requests
  • excluded_headers: <<some_description>>
  • paths: Paths list of all services that will be called. Used to filter what domain will "receive the request"
  • delay: List of times in seconds that the response will be delayed to return for an specific path defined above

Manage Sessions

You can use TShield sessions to separate multiple scenarios for your mocks

By default TShield save request/response into

requests/<<domain_name>>/<<resource_with_param>>/<<http_verb>>/<<index_based.content and json>>

If you start a session a folder with de session_name will be placed between "requests/" and "<<domain_name>>"

Start TShield session

Start new or existing session

POST to http://localhost:4567/sessions?name=<<same_name>>

curl -X POST \
  'http://localhost:4567/sessions?name=my_valid'

Stop TShield session

Stop current session

DELETE to http://localhost:4567/sessions

curl -X DELETE \
  http://localhost:4567/sessions

Append secondary TShield session

Append session. Secondary sessions will used only for read content in VCR mode, writes will be do in the main session. Append only works if exists a current session setted.

POST to http://localhost:4567/sessions?name=<<same_name>>

curl -X POST \
  'http://localhost:4567/sessions/append?name=my_valid'

[Experimental] Config options for gRPC

grpc:
  port: 5678
  proto_dir: 'proto'
  services:
    'helloworld_services_pb': 
      module: 'Helloworld::Greeter'
      hostname: '0.0.0.0:50051'

Not Implemented Yet

  • Matching

Configuration

First, generate ruby files from proto files. Use grpc_tools_ruby_protoc present in the gem grpc-tools. Example:

grpc_tools_ruby_protoc -I proto --ruby_out=proto --grpc_out=proto proto/<INPUT>.proto

Call example in component_tests using grpcurl:

grpcurl -plaintext -import-path component_tests/proto -proto helloworld.proto -d '{"name": "teste"}' localhost:5678 helloworld.Greeter/SayHello

Using in VCR mode

Custom controllers

All custom controller should be created in controllers directory.

Example of controller file called controllers/foo_controller.rb

require 'json'
require 'tshield/controller'

module FooController
  include TShield::Controller
  action :tracking, methods: [:post], path: '/foo'

  module Actions
    def tracking(params, request)
      status 201
      headers 'Content-Type' => 'application/json'
      {message: 'foo'}.to_json
    end
  end
end

Features

Description of some tshield features can be found in the features directory. This features files are used as base for the component tests.

Examples

Basic example for a client app requesting an API

examples/client-api-nodejs

Basic example for component/acceptance test using tshield sessions

examples/component-test

Contributing

Hacking or Contributing to TShield

tshield's People

Contributors

dependabot[bot] avatar diegorubin avatar diegorubin-cit avatar eduardongarcia avatar flaviomeira avatar flisbao avatar fmeiracit avatar fmendonca-cit avatar gitter-badger avatar guilhermerbcit avatar igorsoares-ciandt avatar jerielverissimo avatar matheusmd-cit avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tshield's Issues

Allow matching feature to match exact query params

When a session with 2 objects is activated, and this 2 objects have different params, Tshield always returns the first object.

I'd like to get the object which has more matches. In the following example Tshield always returns the first object of the matching session, while the second object has more params to match.

Scenario: Return correct answer for same path with different params
    Given a file to describe "/path/1" path
    And in session "multiples-responses-with-different-params"
    When this api is accessed throught tshield with params
    And this api is accessed throught tshield with params
      | key | value |
      | param1 | param1_value |
      | param2 | param2_value |
      | param3 | param3_value |
    Then response should be equal "request called with 3 params"

The 'multiples-responses-with-different-params' session has 2 objects:

[
  {
    "session": "multiples-responses-with-different-params",
    "stubs": [
      {
        "method": "GET",
        "path": "/path/1",
        "query": {
          "param1": "param1_value",
          "param2": "param2_value"
        },

        "response": {
          "status": 200,
          "headers": {
            "content-type": "application/json;charset=UTF-8"
          },
          "body": "request called with 2 params"
        }
      },


      {
        "method": "GET",
        "path": "/path/1",
        "query": {
          "param1": "param1_value",
          "param2": "param2_value",
          "param3": "param3_value"
        },

        "response": {
          "status": 200,
          "headers": {
            "content-type": "application/json;charset=UTF-8"
          },
          "body": "request called with 3 params"
        }
      }
    ]}
]

Request fails during preflight (OPTIONS) when informed custom headers and method 'DELETE'

Describe the bug
When the client performs a preflight request (HTTP/ OPTIONS), the tshield sends back the headers 'Access-Control-Allow-Headers' and 'Access-Control-Allow-Methods', that results in error of 'Cross-Origin Resource Sharing (CORS)', once ther first restrict the possibile allowed headers in: Authorization, Content-Type, Accept, X-User-Email, X-Auth-Token; and the second does not inform the allowed methods supported.

To Reproduce
Perform a preflight request (HTTP/ OPTIONS), for a DELETE endpoint, that sends a header not supported.

Expected behavior
Allow the preflight request return status OK (200)

Unable to restore stubs with multiple 'set-cookie' headers values

Describe the bug
When the end service returns multiple 'set-cookie' headers, the tshiled is able to save them, however, when they're restored and sent to back, the browser recognizes as a unique cookie.

To Reproduce
Perform a stub record/ save from a endpoint that returns multiple 'set-cookie' headers.

Expected behavior
Restore and send back from stub all the cookies sent by original service.

Multiple sessions

Is your feature request related to a problem? Please describe.
No.

Describe the solution you'd like
Maybe could send to tshield session multiples session names OR not clear session before.

So, I want a user with permission to menu A and can to download something.
Today, I want to have one session with both. But, the it could be two session with self-responsibility.

Add support to save gRPC error response

Is your feature request related to a problem? Please describe.
When using gRPC with VCR and occurs an error, the error is not saved.

Describe the solution you'd like
Save a error file with gRPC error code and description, and return the error to client.

Mapping a similar path to yml config will match wrong domains

Describe the bug
Mapping a similar path to tshield.yml will match wrong domains

To Reproduce
Steps to reproduce the behavior:

  1. Define a tshield.yml
domains:

  'http://localhost:9092':
    name: 'customer-backend'
    paths:
      - /users

  'http://localhost:9090':
    name: 'invoice-backend'
    paths:
      - /secure/users
  1. Make a request to /secure/users

Expected behavior
A request should be saved in "invoice-backend/secure-users/get/0.content"

  Scenario: Save response body for similar paths
    Given an api "http://localhost:9092" with path "/users" and name "customer-backend"
    And an api "http://localhost:9090" with path "/secure/users" and name "invoice-backend"
    When "/users" is accessed throught tshield
    And "/secure/users" is accessed throught tshield
    Then response should be saved in "customer-backend/users/get/0.content"
    And response should be saved in "invoice-backend/secure-users/get/0.content"

Additional context

  • TShield version: 0.10.0.0
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin18]

Documentation and example of filter usage

In TShield exists a resource to transform request before(transform request body) and after(transform server response) but not exists documentation to usage.

Use case: In a project, it was necessary to simulate a cache of addresses. Instead of creating a mock it was used google service to recover real data.

Original flow:

client -> request_service > cache service -> response_service -> client

With TShield:

client -> request_service -> tshield -> before_filter(request_service to google_request) -> google -> after_filter(google_response to response_service) -> response_service -> client

Enable CORS on requests

Recently I needed to access an endpoint that Tshield exposes via localhost but somehow it needed a CORS configuration and Tshield not supports this yet.
So perhaps would be interesting to this project enable this functionality and allows access from different domains.

Add run modes (capture/proxy, only simulate)

Is your feature request related to a problem? Please describe.
When using tshield as mock-server it is desirable to disable the option to access external service in some scenarios.

Describe the solution you'd like
It would be nice to have some modes to run tshield:

  • Normal mode: As it is today, if tshield does not have the requested resource, accesses the external service, writes the resource and returns it to the client, from the second call no longer accesses the external service for that match.

  • Simulation mode only: tshield responds only to previously recorded resources, if it does not have the requested resource no request is made to the external service

  • Fallback Mode: Always tries to access the external service and records the response on success, in case of communication failure with the external service returns the previously recorded resource.

After update tshield from 0.10 to 0.11 on startup it terminate with exception

Describe the bug
After update tshield from 0.10 to 0.11 on startup it terminate with exception in sessions.rb
registered': undefined local variable or method 'request' for TShield::Controllers::Sessions:Module (NameError)

To Reproduce
Steps to reproduce the behavior:

  1. A project with config/tshield.yml
  2. When run tshield the error happens

Traceback

I, [2019-08-29T14:32:10.706933 #32583]  INFO -- : == registering /v2/keys/v1/toggles/MeuVivoEmpresas for methods get with action toggles
#<Thread:0x00007fc7e5a7b930@/Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/bin/tshield:8 run> terminated with exception (report_on_exception is true):
Traceback (most recent call last):
        6: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/bin/tshield:8:in `block in <top (required)>'
        5: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/server.rb:42:in `run!'
        4: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/server.rb:23:in `register_resources'
        3: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1398:in `register'
        2: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1398:in `each'
        1: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1400:in `block in register'
/Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/controllers/sessions.rb:14:in `registered': undefined local variable or method `request' for TShield::Controllers::Sessions:Module (NameError)
Did you mean?  require
Traceback (most recent call last):
        6: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/bin/tshield:8:in `block in <top (required)>'
        5: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/server.rb:42:in `run!'
        4: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/server.rb:23:in `register_resources'
        3: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1398:in `register'
        2: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1398:in `each'
        1: from /Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sinatra-1.4.8/lib/sinatra/base.rb:1400:in `block in register'
/Users/egarcia/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/tshield-0.11.0.0/lib/tshield/controllers/sessions.rb:14:in `registered': undefined local variable or method `request' for TShield::Controllers::Sessions:Module (NameError)
Did you mean?  require

Expected behavior
TShield should start and expose mocks :)

Additional context

$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin18]

Counter by path without params

Describe the bug
Counter class count called only with path, but without query params. So, if it has two calls to the same endpoint but with different parameters, it will count two calls instead of one.

Support to request matching

Request Matching é uma feature comum em muitos mocking / stubbing servers

https://hoverfly.readthedocs.io/en/latest/pages/reference/hoverfly/request_matchers.html
https://hoverfly.readthedocs.io/en/latest/pages/keyconcepts/matching/matching.html
https://github.com/mrak/stubby4node#how-endpoints-are-matched

Essa nova feature deve determinar qual o mock deve ser usado.

Sugestão: usar um Lucene-based approach para determinar qual o request mais próximo para responder a requisição. No ruby existe o Ferret (https://github.com/dbalmain/ferret) que podemos avaliar a possibilidade

Add config option to change tshield port

Is your feature request related to a problem? Please describe.
Today the exposed port is fixed at 4567

Describe the solution you'd like
Include a option to config file to define the running port

folders not being generated correctly by the session

Describe the bug
I took this example, from component-test, but when I hit the API, it doesn't generate the folder correctly with the session and the related files

To Reproduce
Steps to reproduce the behavior:

  1. tshield
  2. cucumber -t "@TagName"

Expected behavior
I expected when running the tests with the name SOLICITANTE of the session, all the referring requestes, would be inside this folder

Additional context
image

Request verify like wiremock

Is your feature request related to a problem? Please describe.
Some external integrations are done without waiting for a response, it is desirable to be able to check at least how many times the endpoit has been called and in some scenarios check the request details.
This feature is based on wiremock verify (http://wiremock.org/docs/verifying/)

Describe the solution you'd like

  • Include option to config file to enable "save" requests details
  • Save all request details in memory
  • Expose a API with same prefix (/__admin) or other port (config port) to query requests:
    • Ex:
      • POST to /requests/count
        body: { "method": "POST", "url": "/resource/to/count" }
        response: { "count": 4 }
  • Expose API to reset requests stored

Describe alternatives you've considered
For a first release just the request count option filtering for url match and method would be very good

Create global request

  • Create new options for specific path and method.
  • Option to set common requests for all sessions.

Multiples responses for a stub

Is your feature request related to a problem? Please describe.
As it exists in vcr mode, it allows multiple responses to a stub in matching mode.

Describe the solution you'd like
It should behave like a circular row.
The response attribute should be an array.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.