Converting HTTP requests to Wiremock stubs, with Go

Featured image for sharing metadata for article

If you're using Wiremock, sometimes you want to be able to quickly generate stub mappings from an existing server, rather than hand-crafting it yourself, to give you a like-for-like stub.

I've written the following - somewhat hacky - script to produce a Wiremock stub mapping from a given HTTP response.

This can either be plugged into a command-line tool like so, or could be used with an existing HTTP call, for instance when plugged into your existing HTTP clients that integrate with the upstream server.

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"
	"os"
)

type request struct {
	Method string `json:"method"`
	URL    string `json:"url"`
}

type response struct {
	Status  int               `json:"status"`
	Body    string            `json:"body"`
	Headers map[string]string `json:"headers"`
}

type wiremockStub struct {
	Request  request  `json:"request"`
	Response response `json:"response"`
}

func toHeaders(headers http.Header) map[string]string {
	h := make(map[string]string)
	for k, v := range headers {
		h[k] = v[0]
	}
	return h
}

func toRequestURL(requestURL *url.URL) string {
	u := requestURL.Path
	if requestURL.RawQuery != "" {
		u += "?" + requestURL.RawQuery
	}
	if requestURL.RawFragment != "" {
		u += "#" + requestURL.RawFragment
	}

	return u
}

func toWiremock(resp *http.Response) *wiremockStub {
  // NOTE: we don't have too much required in the request, just method and the URL path on the incoming request, but we could do more like basing on headers, HTTP Basic Auth, and body
	wmReq := request{
		Method: resp.Request.Method,
		URL:    toRequestURL(resp.Request.URL),
	}

	wmResp := response{
		Status:  resp.StatusCode,
		Headers: toHeaders(resp.Header),
	}

	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err == nil {
		wmResp.Body = string(body)
	}

	return &wiremockStub{
		Request:  wmReq,
		Response: wmResp,
	}
}

func main() {
	url := os.Args[1]
	resp, err := http.Get(url)

	if err != nil {
		log.Fatal(err)
		return
	}

	s, err := json.Marshal(toWiremock(resp))
	if err != nil {
		log.Fatal(err)
		os.Exit(1)
	}

	fmt.Println(string(s))
}

This can be run like so:

go run main.go "https://www-api.jvt.me/micropub?q=source&url=https://www.jvt.me/mf2/2022/04/odxjc/"

And produces the following pretty-printed JSON:

{
  "request": {
    "method": "GET",
    "url": "/micropub?q=source&url=https://www.jvt.me/mf2/2022/04/odxjc/"
  },
  "response": {
    "body": "{\"type\":[\"h-entry\"],\"properties\":{\"syndication\":[\"https://brid.gy/publish/github\"],\"name\":[\"lestrrat-go/jwx at develop/v1\"],\"published\":[\"2022-04-12T12:53:47.383098795Z\"],\"category\":[\"go\",\"jose\",\"jwt\"],\"like-of\":[\"https://github.com/lestrrat-go/jwx\"],\"post-status\":[\"published\"]}}",
    "headers": {
      "Cache-Control": "no-cache, no-store, max-age=0, must-revalidate",
      "Content-Type": "application/json",
      "Date": "Fri, 12 Aug 2022 16:14:15 GMT",
      "Expires": "0",
      "Pragma": "no-cache",
      "Strict-Transport-Security": "max-age=15724800; includeSubDomains",
      "X-Content-Type-Options": "nosniff",
      "X-Frame-Options": "DENY",
      "X-Xss-Protection": "1; mode=block"
    },
    "status": 200
  }
}

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #testing #go #wiremock.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.