Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide an option to switch off the curl snippet's use of -i #552

Open
ghost opened this issue Sep 20, 2018 · 4 comments
Open

Provide an option to switch off the curl snippet's use of -i #552

ghost opened this issue Sep 20, 2018 · 4 comments

Comments

@ghost
Copy link

ghost commented Sep 20, 2018

Situation
In case that a REST Endpoint provide a binary content ex. application/zip. The generated curl request snippet should not contains the curl options -i. Because it include HTTP headers in the curl response stream and processing of that content will fail.

HINT
Most user will copy that curl command and save the binary content with curl option -o export.zip.

@RestController
@RequestMapping("/foo")
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class FooResource {

	@GetMapping(path = "/export", produces = "application/zip")
	public ResponseEntity<byte[]> export ()
			throws IOException {

		ZipFile zipFile = ....
		return ResponseEntity.ok(toByteArray(zipFile.asInputStream()));
	}
}
curl "https://application/foo/export" -i -X GET \
    -H 'Accept: application/zip'

Proposal
In case the response payload is not text based the curl option -i will not added in the snippet

Workaround
Create a custom CurlRequestSnippet which remove the options -i and register it

private static class CustomCurlRequestSnippet extends CurlRequestSnippet {

	protected CustomCurlRequestSnippet (CommandFormatter commandFormatter) {

		super(commandFormatter);
	}

	@Override
	protected Map<String, Object> createModel(Operation operation) {
		
		Map<String, Object> model = super.createModel(operation);
		MediaType responseContentType = operation.getResponse().getHeaders().getContentType();
		if (responseContentType != null
				&& operation.getResponse().getContent().length > 0
				&& !responseContentType.isCompatibleWith(APPLICATION_JSON)) {
			
			String options = (String)model.get("options");
			model.put("options", options.replace("-i ", ""));
		}
		return model;
	}
}
MockMvcRestDocumentation.documentationConfiguration(restDocumentation)
	.uris()
		.withScheme("https")
		.withHost("foo")
		.withPort(443)
	.and()
	.snippets()
		.withAdditionalDefaults(new CustomCurlRequestSnippet(CliDocumentation.multiLineFormat()))
	.and()
	.operationPreprocessors()
		.withRequestDefaults(prettyPrint(), replaceBinaryContentWithBashDataVariable())
		.withResponseDefaults(prettyPrint(), replaceBinaryContentWithBashDataVariable());
@wilkinsona
Copy link
Member

wilkinsona commented Sep 21, 2018

It's an interesting problem, but I think it applies to more than just binary content. For example, it's quite common to use curl and then pipe the response into jq. There are really two conflicting use cases here:

  1. Exploring an API and examining the full details of the responses
  2. Using the API with curl and piping the response body to a file or another command

The current situation hinders 2 and the proposal here will hinder 1. I can't think of an alternative that will enable both. A compromise would be to make it easier to configure whether or not -i is included and allow users to decide. If it were easier to configure, changing the default could also be considered. This is a compromise though. If anyone has an alternative suggestion that even just comes closer to enabling both use cases, I'd love to hear it.

@wilkinsona
Copy link
Member

The HTTPie snippets do not have this problem. HTTPie's default behaviour is to show the full response (headers and body) when output is going to the terminal. When output has been redirected it changes its defaults so that only the body is output and binary data isn't suppressed. The problem described in this issue is largely a usability problem with curl. There's an argument to be made that the best solution is to use HTTPie rather than curl.

@ghost
Copy link
Author

ghost commented Sep 24, 2018

@wilkinsona
First i would like to thank you for the constructive critique. The jq use case is a valid objection and i don't see at the moment a solution which solve both requirements (exploring and using the API).

A compromise would be to make it easier to configure whether or not -i is included and allow users to decide.

I agree the only one who probably know the customers respective the concrete integration is the REST API writer.

There's an argument to be made that the best solution is to use HTTPie rather than curl.

hmmm...it depends...in my case the customers are system administrators which prefer the curl snippets.

@wilkinsona wilkinsona changed the title Only add curl option -i in the snippet when response is text based Provide an option to switch off the curl snippet's use of -i Nov 20, 2018
@JogoShugh
Copy link

JogoShugh commented Feb 17, 2022

This worked perfectly for me. And, for anyone interested, I used this class from Kotlin, and this worked for me after a few tweaks to what IntelliJ did to convert it for me:

package com.concur.sca

import org.springframework.http.MediaType
import org.springframework.http.MediaType.APPLICATION_JSON
import org.springframework.restdocs.cli.CommandFormatter

import org.springframework.restdocs.cli.CurlRequestSnippet
import org.springframework.restdocs.operation.Operation

class CustomCurlRequestSnippet(commandFormatter: CommandFormatter?) :
    CurlRequestSnippet(commandFormatter) {
    override fun createModel(operation: Operation): Map<String, Any> {
        val model = super.createModel(operation)
        val responseContentType : MediaType? = operation.response.headers!!.contentType
        if (responseContentType != null &&
            operation.response.content.isNotEmpty() && !responseContentType.isCompatibleWith(APPLICATION_JSON)
        ) {
            val options = model["options"] as String?
            model["options"] = options!!.replace("-i ", "")
        }
        return model
    }
}

I am now able to literally copy-paste-run examples from my API doc in our testing environment and pipe the output to jq for beautifying without any issue at all.

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants