Skip to content

fampay-inc/polyjuice

Repository files navigation

Polyjuice🥤! Introduce yourself!

After the new release of dev experience, a very crucial part of the new setup is Polyjuice. Its important to understand what Polyjuice is, and how it can help us in the future

What is Polyjuice?

Polyjuice is nothing but a piece of software that can imitate like any web server. Or in a more informal way: Shapeshifting Software for Seamless Service Simulation!

Polyjuice was built with modularity and configuration in mind. So as a developer you can add new services, endpoints and can also modify the templates accordingly

A few key terms to understand:

  • Server: This is your debug server. R1, R2, R3 or some client. Essentially the client which will be calling the third party service is termed as a server.

  • Service: This is the third party service like Juspay, Decentro, etc. Essentially service is what Polyjuice will act like.

  • Endpoints: Endpoints are the API routes of the service. So for example: Hyperverge has an endpoint: /user which operates in GET method. So this particular config is one endpoint of Hyperverge

  • Response: As the name suggests, the response which should be returned when hitting on the endpoint. An endpoint can have multiple responses, but one of them will always be default. By default it means that with no configuration, if a server pings the endpoint that response would be returned. You can through the admin dashboard set a different response to be returned for your own server when it hits a particular endpoint. The params a response contains are:

    • json_template (The JSON template)
    • is_default (boolean)
    • is_html (boolean)
    • html (html template)
    • status_code (integer)

    A response can return a response which is a json or html. (We are working on bringing XML support too!)

Ownership of entities diagram

Ownership of entities

How does Polyjuice work? How does it intercept requests that go to third party services?

  • Essentially, we usually put our third-party service urls in the config map. For example for Juspay it is: https://juspay.com
  • Now say this is server R4. I have registered R4 server with Polyjuice. In the R4 server’s config map I will put the URL for Juspay as: [https://polyjuice](https://polyjuice).kong.fampay.co/r4/juspay
  • https://polyjuice/<server_name>/<service_name>
  • The endpoint route of the service is appended in front of the above URL.
  • This way polyjuice is able to apply custom configuration setup by the server and also recognises which server is calling which service’s endpoint.

Untitled

How to configure the setup?

  • There is dashboard which can be used.

Things that work:

  • Creating new responses for endpoints
  • Assigning custom responses for your server

Things that still need work:

  • Creating a service, an endpoint for the service and creating of server through dashboard
  • Setting up Webhooks and viewing webhooks

Untitled

Untitled

  • As you can see above, you can see the list of responses for the endpoint of the service and can select the response you want returned for say in this case r1-ironbank
  • You can also turn off mock for a particular service. The moment you do this, all your requests will be proxied directly to the service’s staging server.

Untitled

  • As you can see here you can add response for an endpoint of hyperverge or delete responses. You can also change the default response. By doing this it will change globally for all servers.

How to configure endpoints & make response templates?

  • So in any kind of template HTML or JSON we use the same kind of syntax.
  • Lets take an example. The request from server is a POST request and the endpoint is as follows: /test/1?query=helloq
  • The request payload:
{
	"message": "hello"
}
  • The request header should have:
HELLO: "Raghav Sharma"
  • The response desired is:
{
	"final": "hello",
  "header": "Raghav Sharma",
  "param": 1,
  "query": "helloq"
	"timestamp": "2006-01-02T15:04:05-0700" # current timestamp
}

Lets first see the endpoint configuration:

  • Route: ^/test/\d+ # its important to be a strict regex. This way it will point to the right endpoint
  • Method POST
  • Params: {”key”: 1} (key can be anything. For referring to the number we have chosen key. 1 is the index of the path. So the 2nd param of the endpoint)

Now on to the response configuration

{
	"final": "{{.Body.message}}",
	"header": "{{.Headers.HELLO}}",
	"param": {{.Params.key}},
	"query": "{{.Query.query}}",
	"timestamp": "{{GetCurrentTimeStamp}}"
}

This would be the way you can configure your template. Using the same {{ syntax you can do it on HTML templates too!

Whats up with the timestamp ? 🤔

  • You can call functions directly from this template!! We have defined a few utility functions which you can call right from the template!!
GetCurrentTimeStamp # current timestamp
GetFullName # get a random full name
GetUniqueNumber # get random number (epoch time)
GetHostname # get hostname of polyjuice Ex: https://polyjuice
  • And you can also add more such functions. For this you will have to code the functions in Golang and contribute to Polyjuice! Check out:

This was sort of simple, lets get into something complex. Webhooks!

How do webhooks work?

  • One of the biggest things we need to understand is why do we need Polyjuice to mock webhooks. As the term suggests, can’t we trigger webhooks from Postman to our server and call it a day?
  • Well yes and no. There are situations where a simple Postman request is good enough, but in some cases we need to pass more data through the webhook.
  • Which points us to a simple conclusion: Webhooks which are stateless can be triggered through Postman or simple cURL requests, but webhooks which aren’t stateless, they should be triggered by Polyjuice
  • So the final assumption is: all webhooks in polyjuice configured would be a consequence of an API request. What does this mean? Only after a API request is done to Polyjuice then only it can allow Polyjuice to setup webhooks which can be triggered by a developer manually.
  • To understand this further lets take an example of a flow.
  • The current flow:
    • A server pings some external svc for some flow. It also provides the user_id in this request.
    • External svc provides a query parameter ?params=<some_id>
    • The mobile opens the endpoint: /waiting-room?params=<some_id>
    • Later on after the flow is done, the webview is closed the mobile pings server to check status. If external svc sends the success webhooks to the server, the flow would be deemed successful
  • As we can see that the API request 1 (asking for the waiting room link), it contains data like user_id which we need in the webhook to mark the flow successful or unsuccessful.

Solution:

  • Lets make a webhook entity. The parameters of Webhook are:
    • URL ⇒ The url to hit. {{.ServerURL}}/<path> If the server that did the API request 1 was r4 (https://server.com) then the URL will automatically become: https://server.com/
    • Method
    • Body → This is the simple JSON body you would want to send to the server as a webhook. (Will show the configuration below)
    • Name
    • Description
    • Service (which service owns these webhooks)
  • Now to share context from API request 1 with the webhook we need a map. A map between the response which would be returned in API request 1 and the webhook.
  • Each response can have N number of webhooks which can be triggered after the response is returned.
  • When you link the response to a webhook, whenever the response is going to be returned the request details of the first API request get stored and whatever webhooks were mapped to the response are made ready to be triggered. These webhooks are ready to be triggered with the power to access data from the previous request

Lets go back to the example:

  • Configuration of API request 1 (Expand me)

    • Each request has a unique ID. So in API call 1 we return ?params=<request_id>
    • Response template: {"url": "?params={{.RequestID}}", "status": "success", "statusCode": 201}
  • Since the request details (user id) is important for the webhook we need to create a webhook and link it to the response created above

  • Webhook Creation (Expand me)

    • URL: {{.ServerURL}}/webhook/ext-flow/
    • Method: POST
    • Body:
    {
      "appId": "{{.Headers.appId}}",
      "user_id": "{{.Body.userId}}",
      "event_type": "flow_status",
      "event_details": {
        "status": "APPROVED",
        "agentId": "agent@example.com"
      }
    }
    # please note the above details are referring to the request details of the API call 1
    • Name: Agent Approval
    • Description : Agent Approval Flow

    Like this you can create many number of webhooks linking to the response returned above.

Now the mobile opens: https://polyjuice/server/service/waiting-room?params=**RequestID**

On this request the response we have configured is as follows:

<!DOCTYPE html>
<html>

<head>
    <title>URL Hyperlink</title>
</head>

<body>
    <p>Click the link below:</p><a href='/browse?url=https%3A%2F%2Fgithub.com%2Ffampay-inc%2F%257B%257BGetEndpointForWebhooks%2520.Query.params%257D%257D'>Go to URL</a>
</body>

</html>
  • Notice the href carefully: It calls a function GetEndpointForWebhooks and passes .Query.params as an argument
  • Lets have a look at this function:
func GetEndpointForWebhooks(requestID string) string {
	return NewConfig().HostnameURL + "/webhooks/active/" + requestID
}
  • So in the end this gets rendered as: https://polyjuice/webhooks/active/RequestID
  • And you get this sort of a screen below:

Untitled

  • Now you can simply click on a button and trigger the appropriate webhook.

So this is how we mock webhooks. But by doing this we get the possibility of managing state across network calls. Which should hopefully allow us to integrate stateful mocking for normal API requests!

Things still left:

  • Creation + listing of webhooks (WIP)
  • Stateful mocking across API requests (Ideating!!)

To setup or see the data please in relation to webhooks contact @Raghav Sharma.

So this is Polyjuice! We are currently using it to mock:

  • juspay
  • decentro
  • hyperverge

Happy mocking!!

About

Allows the taker to assume the form of another web service

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages