Skip to content

Support binary data in body #669

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

Closed
mgvirtzm opened this issue Apr 21, 2015 · 3 comments · Fixed by SeppPenner/swagger-codegen#22
Closed

Support binary data in body #669

mgvirtzm opened this issue Apr 21, 2015 · 3 comments · Fixed by SeppPenner/swagger-codegen#22

Comments

@mgvirtzm
Copy link

Hello,

Our Swagger 2.0 specs include several services that pass and receive binary data in the body (type="string, format="byte"), represented by content-type application/octet-stream (with respective consumes and produces clauses).

When generating Java for these services, code generator treats input and output as String, which is not suitable for binary data.

It would be advisable to generate byte[] parameter, and also avoid serialization treatment.

I would consider contributing this code (for Java only), if you find this appropriate.

Thanks,
Michael.

@fehguy
Copy link
Contributor

fehguy commented Apr 21, 2015

Would like this implantation especially with tests. Thank you!

@mgvirtzm
Copy link
Author

The following workaround worked for me, and currently I'm not planning to modify the codegen sources.

Preconditions:

  1. binary body is defined in Swagger JSON as follows (might occur in multiple paths; 'binaryData' name is mandatory):

                {
                    "name": "BinaryData",
                    "in": "body",
                    "required": true,
                    "schema": {
                        "type": "string",
                        "format": "byte"  
                    }
                }
    
  2. The following overriding class is placed at appropriate location in the source tree:

package com.intuit.idps;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource.Builder;

import io.swagger.client.ApiException;
import io.swagger.client.ApiInvoker;

import java.util.Map;
import java.io.DataInputStream;
import javax.ws.rs.core.Response.Status.Family;
import java.io.IOException;


public class BinaryApiInvoker extends ApiInvoker {

  static {
    INSTANCE = new BinaryApiInvoker();
    setUserAgent("Java-Swagger");
  }

  public static ApiInvoker getInstance() {
    return INSTANCE;
  }

  public byte[] invokeBinaryAPI(String host, String path, String method, Map<String, String> queryParams, byte[] body, Map<String, String> headerParams, Map<String, String> formParams, String contentType) throws ApiException {
    Client client = getClient(host);

    StringBuilder b = new StringBuilder();

    for(String key : queryParams.keySet()) {
      String value = queryParams.get(key);
      if (value != null){
        if(b.toString().length() == 0 && !path.contains("?"))
          b.append("?");
        else
          b.append("&");
        b.append(escapeString(key)).append("=").append(escapeString(value));
      }
    }
    String querystring = b.toString();

    Builder builder = client.resource(host + path + querystring).accept("application/octet-stream");
    for(String key : headerParams.keySet()) {
      builder = builder.header(key, headerParams.get(key));
    }

    for(String key : defaultHeaderMap.keySet()) {
      if(!headerParams.containsKey(key)) {
        builder = builder.header(key, defaultHeaderMap.get(key));
      }
    }
    ClientResponse response = null;

    if("GET".equals(method)) {
      response = (ClientResponse) builder.get(ClientResponse.class);
    }
    else if ("POST".equals(method)) {
      if(body == null)
        response = builder.post(ClientResponse.class, null);
      else
        response = builder.type(contentType).post(ClientResponse.class, body);
    }
    else if ("PUT".equals(method)) {
      if(body == null)
        response = builder.put(ClientResponse.class);
      else {
          response = builder.type(contentType).put(ClientResponse.class, body);
      }
    }
    else if ("DELETE".equals(method)) {
      if(body == null)
        response = builder.delete(ClientResponse.class);
      else
        response = builder.type(contentType).delete(ClientResponse.class, body);
    }
    else {
      throw new ApiException(405, "unknown method " + method);
    }
    if(response.getClientResponseStatus() == ClientResponse.Status.NO_CONTENT) {
      return null;
    }
    else if(response.getClientResponseStatus().getFamily() == Family.SUCCESSFUL) {
      if(response.hasEntity()) {
    DataInputStream stream = new DataInputStream(response.getEntityInputStream());
    byte[] data = new byte[response.getLength()];
    try {
      stream.readFully(data);
    } catch (IOException ex) {
      throw new ApiException(500, "Error obtaining binary response data");
    }
        return data;
      }
      else {
        return new byte[0];
      }
    }
    else {
      String message = "error";
      if(response.hasEntity()) {
        try{
          message = String.valueOf(response.getEntity(String.class));
        }
        catch (RuntimeException e) {
          // e.printStackTrace();
        }
      }
      throw new ApiException(
                response.getClientResponseStatus().getStatusCode(),
                message);
    }
  }



}

Each time after generating the code, run the following commands:

sed -i -- '3 i\
import com.intuit.idps.BinaryApiInvoker;' src/main/java/io/swagger/client/api/DefaultApi.java
sed -i -- '/ApiInvoker apiInvoker/ c\
  BinaryApiInvoker apiInvoker = (BinaryApiInvoker)BinaryApiInvoker.getInstance();' src/main/java/io/swagger/client/api/DefaultApi.java
sed -i -- '/String binaryData/,/ApiException ex/{ s/public String/public byte[]/; s/String binaryData/byte[] binaryData/; s/String response/byte[] response/; s/invokeAPI/invokeBinaryAPI/; s/postBody,/binaryData,/; s/(String).*/response;/;}' src/main/java/io/swagger/client/api/DefaultApi.java
sed -i -- 's/\&amp;/\&/g' src/main/java/io/swagger/client/api/DefaultApi.java  # codegen escapes & in path, this may be an issue?
sed -i -- 's/private /protected /g' src/main/java/io/swagger/client/ApiInvoker.java

I'm not closing the issue (anyway, preferred to implement it without workaround).

@wing328
Copy link
Contributor

wing328 commented Jan 23, 2016

Java API client now supports binary format via #1898

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

Successfully merging a pull request may close this issue.

4 participants