# Authentication & Authorization REST API



## beta20 (15. Nov 2018)

Hallo zusammen, 

ich bin gerade dabei eine REST API in mein Projekt zu integrieren.
Hierzu habe ich bereits einige Klassen erstellt, die dann meine Entities werden.
Weiterhin bin ich gerade dabei Klassen zu implementieren, die dann die verschiedenen WebService Dienste nutzen (Alle Kunden bekommen etc.).

Woran ich aber gerade hänge, ist bei der Authentication & Authorization.
Das Thema der Authentifizierung ist hier erklärt:
https://developer.billwerk.io/Docs/ApiIntroduction

Allerdings verstehe ich nicht, was ich genau machen muss.
Ich brauche wohl zwei Token? Diese muss ich dann in der App generieren und ich meine Klasse integrieren?

Kann mir bitte jemand erklären, was hier zu tun ist?
Die zwei Token muss ich dann wohl über den HTTP Header auslesen?

Danke für jede Hilfe


----------



## Thallius (15. Nov 2018)

Ich habe keine Idee wie du auf die Idee kommst das du da zwei Token brauchst aber ich habe selten eine so schöne und einfache Erklärung gelesen wie man oAuth2 implementiert. Da steht alles haarklein drin. Was genau man versenden muss um das Token zu bekommen und wie man das Token einsetzt. Sogar wie man das Basic Auth benutzt indem man username undcpassword base64 encoded etc.

Also lies es dir nochmal in Ruhe durch, da bleiben echt eigentlich keine Fragen offen.

Gruß

Claus


----------



## beta20 (15. Nov 2018)

Hallo Claus, 

ich verstehe es so, dass ich folgendes benötige:
*client_id *and a *client_secret*


```
You can create OAuth clients in your account under Settings/My Apps. If you create a confidential client, it will have both a client_id and a client_secret which can be used to retrieve an access token in this way.
```

-> heißt das, dass aus der  *client_id und* *client_secret* dann mein Token generiert wird? 

Ich habe nun über: https://editor.swagger.io/ die Client API für JAVA erstellt.
Was mir nun aber nicht klar ist, wie authentifiziere ich mich? 
Wo muss ich die *client_id *und *client_secret *angeben als Konstante?

Danke für jede Hilfe


----------



## Thallius (15. Nov 2018)

Da kann auch nicht helfen da ich solche high Level Tools nicht benutze. Ich kann dir sagen was für HTTP requests du absetzen mußt, sprich wie man das programmatisch anspricht, aber nicht wie man ein Tool konfigurieren muss das das für einen macht.


----------



## mihe7 (16. Nov 2018)

beta20 hat gesagt.:


> -> heißt das, dass aus der *client_id und* *client_secret* dann mein Token generiert wird?


Du authentifizierst Dich mit client_id und client_secret (analog zu Benutzername und Passwort) und bekommst dann ein Token zurück.

Die Daten bekommst Du anscheinend, wenn Du für Dein Konto einen entsprechenden Client anlegst ("You can create OAuth clients in your account under Settings/My Apps. If you create a confidential client, it will have both a client_id and a client_secret which can be used to retrieve an access token in this way.")


----------



## beta20 (19. Nov 2018)

Danke, werde es prüfen


----------



## beta20 (20. Nov 2018)

Ich habe es nun mal getestet, allerdings bekomme ich auch nicht so wirklich Support in Sachen JAVA API Client von dem Hoster...

Mein Problem ist, wie erzeuge ich den Bearer Token?


```
public static void main(String[] args) {
        ApiClient defaultClient = Configuration.getDefaultApiClient();
       
        // Configure API key authorization: BearerAuth
        ApiKeyAuth BearerAuth = (ApiKeyAuth) defaultClient.getAuthentication("BearerAuth");       BearerAuth.setApiKey("NWJlZGM3YjI2Y2I1NGUxZjA4MmE0OTYxOjU4NmRiMTFlODBlMGZkZDI5ZWM2Yjc1YTc2NDVlODA3");        BearerAuth.setApiKeyPrefix("Bearer");
        ContractsApi apiInstance = new ContractsApi();
        String contractId = "1"; // String | Contract ID
        try {
            ComponentSubscriptionReadDTO result = apiInstance.componentSubscriptionsGetComponentSubscriptionsByContract(contractId);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling ContractsApi#componentSubscriptionsGetComponentSubscriptionsByContract");
            e.printStackTrace();
        }
    }
```

Das Problem ist, dass ich mich nicht authentifizieren kann.
Wenn ich es richtig verstehe, muss der Bearer Token hier rein:


```
BearerAuth.setApiKey("MEINTOKEN");
```

Also sprich das Problem wo ich stehe ich, wie erzeuge ich den Bearer Token?


----------



## mihe7 (20. Nov 2018)

Mit dem Swagger-Zeug kenn ich mich nicht aus. Der Doku zufolge könnte es reichen, wenn Du in den Security-Einstellungen als Typ oauth2, flow application (bzw. clientCredentials - je nach Version) einstellst und als tokenUrl die URL von billwerk zu /oauth/token angibst. In dem generierten Code solltest Du dann irgendwie den Benutzernamen und das Passwort (also client_id und client_secret) angeben können.


----------



## beta20 (20. Nov 2018)

Es gibt zwei Klassen, die vermutlich genutzt werden:


```
/*
* billwerk REST API
* This document describes the endpoints of the <strong>billwerk REST API</strong>. You can find out more about billwerk <a href='https://billwerk.com/'>here</a>. <br/> <br/>This documentation offers interactive features which will enable you to test how our system behaves. To use the interactive features you have to aquire a bearer token for your active billwerk sandbox account. This procedure is described <a href='/Docs/ApiIntroduction#authentication'>here</a>. After aquiring the token you must go through the 'Authorize'-Form down below (green button). You're now able to use the interactive features. <br/>Please be aware your sandbox account will be affected trying out the endpoints!</br> </br><strong>This documentation is still work in progress!</strong>
*
* OpenAPI spec version: v1
* Contact: support@billwerk.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/


package de.dpunkt.myaktion.webserviceexternal.billwerk.client;

import com.squareup.okhttp.*;
import com.squareup.okhttp.internal.http.HttpMethod;
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
import com.squareup.okhttp.logging.HttpLoggingInterceptor.Level;

import de.dpunkt.myaktion.webserviceexternal.billwerk.client.auth.ApiKeyAuth;
import de.dpunkt.myaktion.webserviceexternal.billwerk.client.auth.Authentication;
import de.dpunkt.myaktion.webserviceexternal.billwerk.client.auth.HttpBasicAuth;
import de.dpunkt.myaktion.webserviceexternal.billwerk.client.auth.OAuth;
import okio.BufferedSink;
import okio.Okio;
import org.threeten.bp.LocalDate;
import org.threeten.bp.OffsetDateTime;
import org.threeten.bp.format.DateTimeFormatter;

import javax.net.ssl.*;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ApiClient {

    private String basePath = "https://sandbox.billwerk.com";
    private boolean debugging = false;
    private Map<String, String> defaultHeaderMap = new HashMap<String, String>();
    private String tempFolderPath = null;

    private Map<String, Authentication> authentications;

    private DateFormat dateFormat;
    private DateFormat datetimeFormat;
    private boolean lenientDatetimeFormat;
    private int dateLength;

    private InputStream sslCaCert;
    private boolean verifyingSsl;
    private KeyManager[] keyManagers;

    private OkHttpClient httpClient;
    private JSON json;

    private HttpLoggingInterceptor loggingInterceptor;

    /*
     * Constructor for ApiClient
     */
    public ApiClient() {
        httpClient = new OkHttpClient();


        verifyingSsl = true;

        json = new JSON();

        // Set default User-Agent.
        setUserAgent("Swagger-Codegen/1.0.0/java");

        // Setup authentications (key: authentication name, value: authentication).
        authentications = new HashMap<String, Authentication>();
        authentications.put("BearerAuth", new ApiKeyAuth("header", "Authorization"));
        // Prevent the authentications from being modified.
        authentications = Collections.unmodifiableMap(authentications);
    }

    /**
     * Get base path
     *
     * @return Baes path
     */
    public String getBasePath() {
        return basePath;
    }

    /**
     * Set base path
     *
     * @param basePath Base path of the URL (e.g https://sandbox.billwerk.com
     * @return An instance of OkHttpClient
     */
    public ApiClient setBasePath(String basePath) {
        this.basePath = basePath;
        return this;
    }

    /**
     * Get HTTP client
     *
     * @return An instance of OkHttpClient
     */
    public OkHttpClient getHttpClient() {
        return httpClient;
    }

    /**
     * Set HTTP client
     *
     * @param httpClient An instance of OkHttpClient
     * @return Api Client
     */
    public ApiClient setHttpClient(OkHttpClient httpClient) {
        this.httpClient = httpClient;
        return this;
    }

    /**
     * Get JSON
     *
     * @return JSON object
     */
    public JSON getJSON() {
        return json;
    }

    /**
     * Set JSON
     *
     * @param json JSON object
     * @return Api client
     */
    public ApiClient setJSON(JSON json) {
        this.json = json;
        return this;
    }

    /**
     * True if isVerifyingSsl flag is on
     *
     * @return True if isVerifySsl flag is on
     */
    public boolean isVerifyingSsl() {
        return verifyingSsl;
    }

    /**
     * Configure whether to verify certificate and hostname when making https requests.
     * Default to true.
     * NOTE: Do NOT set to false in production code, otherwise you would face multiple types of cryptographic attacks.
     *
     * @param verifyingSsl True to verify TLS/SSL connection
     * @return ApiClient
     */
    public ApiClient setVerifyingSsl(boolean verifyingSsl) {
        this.verifyingSsl = verifyingSsl;
        applySslSettings();
        return this;
    }

    /**
     * Get SSL CA cert.
     *
     * @return Input stream to the SSL CA cert
     */
    public InputStream getSslCaCert() {
        return sslCaCert;
    }

    /**
     * Configure the CA certificate to be trusted when making https requests.
     * Use null to reset to default.
     *
     * @param sslCaCert input stream for SSL CA cert
     * @return ApiClient
     */
    public ApiClient setSslCaCert(InputStream sslCaCert) {
        this.sslCaCert = sslCaCert;
        applySslSettings();
        return this;
    }

    public KeyManager[] getKeyManagers() {
        return keyManagers;
    }

    /**
     * Configure client keys to use for authorization in an SSL session.
     * Use null to reset to default.
     *
     * @param managers The KeyManagers to use
     * @return ApiClient
     */
    public ApiClient setKeyManagers(KeyManager[] managers) {
        this.keyManagers = managers;
        applySslSettings();
        return this;
    }

    public DateFormat getDateFormat() {
        return dateFormat;
    }

    public ApiClient setDateFormat(DateFormat dateFormat) {
        this.json.setDateFormat(dateFormat);
        return this;
    }

    public ApiClient setSqlDateFormat(DateFormat dateFormat) {
        this.json.setSqlDateFormat(dateFormat);
        return this;
    }

    public ApiClient setOffsetDateTimeFormat(DateTimeFormatter dateFormat) {
        this.json.setOffsetDateTimeFormat(dateFormat);
        return this;
    }

    public ApiClient setLocalDateFormat(DateTimeFormatter dateFormat) {
        this.json.setLocalDateFormat(dateFormat);
        return this;
    }

    public ApiClient setLenientOnJson(boolean lenientOnJson) {
        this.json.setLenientOnJson(lenientOnJson);
        return this;
    }

    /**
     * Get authentications (key: authentication name, value: authentication).
     *
     * @return Map of authentication objects
     */
    public Map<String, Authentication> getAuthentications() {
        return authentications;
    }

    /**
     * Get authentication for the given name.
     *
     * @param authName The authentication name
     * @return The authentication, null if not found
     */
    public Authentication getAuthentication(String authName) {
        return authentications.get(authName);
    }

    /**
     * Helper method to set username for the first HTTP basic authentication.
     *
     * @param username Username
     */
    public void setUsername(String username) {
        for (Authentication auth : authentications.values()) {
            if (auth instanceof HttpBasicAuth) {
                ((HttpBasicAuth) auth).setUsername(username);
                return;
            }
        }
        throw new RuntimeException("No HTTP basic authentication configured!");
    }

    /**
     * Helper method to set password for the first HTTP basic authentication.
     *
     * @param password Password
     */
    public void setPassword(String password) {
        for (Authentication auth : authentications.values()) {
            if (auth instanceof HttpBasicAuth) {
                ((HttpBasicAuth) auth).setPassword(password);
                return;
            }
        }
        throw new RuntimeException("No HTTP basic authentication configured!");
    }

    /**
     * Helper method to set API key value for the first API key authentication.
     *
     * @param apiKey API key
     */
    public void setApiKey(String apiKey) {
        for (Authentication auth : authentications.values()) {
            if (auth instanceof ApiKeyAuth) {
                ((ApiKeyAuth) auth).setApiKey(apiKey);
                return;
            }
        }
        throw new RuntimeException("No API key authentication configured!");
    }

    /**
     * Helper method to set API key prefix for the first API key authentication.
     *
     * @param apiKeyPrefix API key prefix
     */
    public void setApiKeyPrefix(String apiKeyPrefix) {
        for (Authentication auth : authentications.values()) {
            if (auth instanceof ApiKeyAuth) {
                ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix);
                return;
            }
        }
        throw new RuntimeException("No API key authentication configured!");
    }

    /**
     * Helper method to set access token for the first OAuth2 authentication.
     *
     * @param accessToken Access token
     */
    public void setAccessToken(String accessToken) {
        for (Authentication auth : authentications.values()) {
            if (auth instanceof OAuth) {
                ((OAuth) auth).setAccessToken(accessToken);
                return;
            }
        }
        throw new RuntimeException("No OAuth2 authentication configured!");
    }

    /**
     * Set the User-Agent header's value (by adding to the default header map).
     *
     * @param userAgent HTTP request's user agent
     * @return ApiClient
     */
    public ApiClient setUserAgent(String userAgent) {
        addDefaultHeader("User-Agent", userAgent);
        return this;
    }

    /**
     * Add a default header.
     *
     * @param key The header's key
     * @param value The header's value
     * @return ApiClient
     */
    public ApiClient addDefaultHeader(String key, String value) {
        defaultHeaderMap.put(key, value);
        return this;
    }

    /**
     * Check that whether debugging is enabled for this API client.
     *
     * @return True if debugging is enabled, false otherwise.
     */
    public boolean isDebugging() {
        return debugging;
    }

    /**
     * Enable/disable debugging for this API client.
     *
     * @param debugging To enable (true) or disable (false) debugging
     * @return ApiClient
     */
    public ApiClient setDebugging(boolean debugging) {
        if (debugging != this.debugging) {
            if (debugging) {
                loggingInterceptor = new HttpLoggingInterceptor();
                loggingInterceptor.setLevel(Level.BODY);
                httpClient.interceptors().add(loggingInterceptor);
            } else {
                httpClient.interceptors().remove(loggingInterceptor);
                loggingInterceptor = null;
            }
        }
        this.debugging = debugging;
        return this;
    }

    /**
     * The path of temporary folder used to store downloaded files from endpoints
     * with file response. The default value is <code>null</code>, i.e. using
     * the system's default tempopary folder.
     *
     * @see <a href="https://docs.oracle.com/javase/7/docs/api/java/io/File.html#createTempFile">createTempFile</a>
     * @return Temporary folder path
     */
    public String getTempFolderPath() {
        return tempFolderPath;
    }

    /**
     * Set the temporary folder path (for downloading files)
     *
     * @param tempFolderPath Temporary folder path
     * @return ApiClient
     */
    public ApiClient setTempFolderPath(String tempFolderPath) {
        this.tempFolderPath = tempFolderPath;
        return this;
    }

    /**
     * Get connection timeout (in milliseconds).
     *
     * @return Timeout in milliseconds
     */
    public int getConnectTimeout() {
        return httpClient.getConnectTimeout();
    }

    /**
     * Sets the connect timeout (in milliseconds).
     * A value of 0 means no timeout, otherwise values must be between 1 and
     * {@link Integer#MAX_VALUE}.
     *
     * @param connectionTimeout connection timeout in milliseconds
     * @return Api client
     */
    public ApiClient setConnectTimeout(int connectionTimeout) {
        httpClient.setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS);
        return this;
    }

    /**
     * Get read timeout (in milliseconds).
     *
     * @return Timeout in milliseconds
     */
    public int getReadTimeout() {
        return httpClient.getReadTimeout();
    }

    /**
     * Sets the read timeout (in milliseconds).
     * A value of 0 means no timeout, otherwise values must be between 1 and
     * {@link Integer#MAX_VALUE}.
     *
     * @param readTimeout read timeout in milliseconds
     * @return Api client
     */
    public ApiClient setReadTimeout(int readTimeout) {
        httpClient.setReadTimeout(readTimeout, TimeUnit.MILLISECONDS);
        return this;
    }

    /**
     * Get write timeout (in milliseconds).
     *
     * @return Timeout in milliseconds
     */
    public int getWriteTimeout() {
        return httpClient.getWriteTimeout();
    }

    /**
     * Sets the write timeout (in milliseconds).
     * A value of 0 means no timeout, otherwise values must be between 1 and
     * {@link Integer#MAX_VALUE}.
     *
     * @param writeTimeout connection timeout in milliseconds
     * @return Api client
     */
    public ApiClient setWriteTimeout(int writeTimeout) {
        httpClient.setWriteTimeout(writeTimeout, TimeUnit.MILLISECONDS);
        return this;
    }

    /**
     * Format the given parameter object into string.
     *
     * @param param Parameter
     * @return String representation of the parameter
     */
    public String parameterToString(Object param) {
        if (param == null) {
            return "";
        } else if (param instanceof Date || param instanceof OffsetDateTime || param instanceof LocalDate) {
            //Serialize to json string and remove the " enclosing characters
            String jsonStr = json.serialize(param);
            return jsonStr.substring(1, jsonStr.length() - 1);
        } else if (param instanceof Collection) {
            StringBuilder b = new StringBuilder();
            for (Object o : (Collection)param) {
                if (b.length() > 0) {
                    b.append(",");
                }
                b.append(String.valueOf(o));
            }
            return b.toString();
        } else {
            return String.valueOf(param);
        }
    }

    /**
     * Formats the specified query parameter to a list containing a single {@code Pair} object.
     *
     * Note that {@code value} must not be a collection.
     *
     * @param name The name of the parameter.
     * @param value The value of the parameter.
     * @return A list containing a single {@code Pair} object.
     */
    public List<Pair> parameterToPair(String name, Object value) {
        List<Pair> params = new ArrayList<Pair>();

        // preconditions
        if (name == null || name.isEmpty() || value == null || value instanceof Collection) return params;

        params.add(new Pair(name, parameterToString(value)));
        return params;
    }

    /**
     * Formats the specified collection query parameters to a list of {@code Pair} objects.
     *
     * Note that the values of each of the returned Pair objects are percent-encoded.
     *
     * @param collectionFormat The collection format of the parameter.
     * @param name The name of the parameter.
     * @param value The value of the parameter.
     * @return A list of {@code Pair} objects.
     */
    public List<Pair> parameterToPairs(String collectionFormat, String name, Collection value) {
        List<Pair> params = new ArrayList<Pair>();

        // preconditions
        if (name == null || name.isEmpty() || value == null || value.isEmpty()) {
            return params;
        }

        // create the params based on the collection format
        if ("multi".equals(collectionFormat)) {
            for (Object item : value) {
                params.add(new Pair(name, escapeString(parameterToString(item))));
            }
            return params;
        }

        // collectionFormat is assumed to be "csv" by default
        String delimiter = ",";

        // escape all delimiters except commas, which are URI reserved
        // characters
        if ("ssv".equals(collectionFormat)) {
            delimiter = escapeString(" ");
        } else if ("tsv".equals(collectionFormat)) {
            delimiter = escapeString("\t");
        } else if ("pipes".equals(collectionFormat)) {
            delimiter = escapeString("|");
        }

        StringBuilder sb = new StringBuilder() ;
        for (Object item : value) {
            sb.append(delimiter);
            sb.append(escapeString(parameterToString(item)));
        }

        params.add(new Pair(name, sb.substring(delimiter.length())));

        return params;
    }

    /**
     * Sanitize filename by removing path.
     * e.g. ../../sun.gif becomes sun.gif
     *
     * @param filename The filename to be sanitized
     * @return The sanitized filename
     */
    public String sanitizeFilename(String filename) {
        return filename.replaceAll(".*[/\\\\]", "");
    }

    /**
     * Check if the given MIME is a JSON MIME.
     * JSON MIME examples:
     *   application/json
     *   application/json; charset=UTF8
     *   APPLICATION/JSON
     *   application/vnd.company+json
     * "* / *" is also default to JSON
     * @param mime MIME (Multipurpose Internet Mail Extensions)
     * @return True if the given MIME is JSON, false otherwise.
     */
    public boolean isJsonMime(String mime) {
      String jsonMime = "(?i)^(application/json|[^;/ \t]+/[^;/ \t]+[+]json)[ \t]*(;.*)?$";
      return mime != null && (mime.matches(jsonMime) || mime.equals("*/*"));
    }

    /**
     * Select the Accept header's value from the given accepts array:
     *   if JSON exists in the given array, use it;
     *   otherwise use all of them (joining into a string)
     *
     * @param accepts The accepts array to select from
     * @return The Accept header to use. If the given array is empty,
     *   null will be returned (not to set the Accept header explicitly).
     */
    public String selectHeaderAccept(String[] accepts) {
        if (accepts.length == 0) {
            return null;
        }
        for (String accept : accepts) {
            if (isJsonMime(accept)) {
                return accept;
            }
        }
        return StringUtil.join(accepts, ",");
    }

    /**
     * Select the Content-Type header's value from the given array:
     *   if JSON exists in the given array, use it;
     *   otherwise use the first one of the array.
     *
     * @param contentTypes The Content-Type array to select from
     * @return The Content-Type header to use. If the given array is empty,
     *   or matches "any", JSON will be used.
     */
    public String selectHeaderContentType(String[] contentTypes) {
        if (contentTypes.length == 0 || contentTypes[0].equals("*/*")) {
             return "application/json";
        }
        for (String contentType : contentTypes) {
            if (isJsonMime(contentType)) {
                return contentType;
            }
        }
        return contentTypes[0];
    }

    /**
     * Escape the given string to be used as URL query value.
     *
     * @param str String to be escaped
     * @return Escaped string
     */
    public String escapeString(String str) {
        try {
            return URLEncoder.encode(str, "utf8").replaceAll("\\+", "%20");
        } catch (UnsupportedEncodingException e) {
            return str;
        }
    }

    /**
     * Deserialize response body to Java object, according to the return type and
     * the Content-Type response header.
     *
     * @param <T> Type
     * @param response HTTP response
     * @param returnType The type of the Java object
     * @return The deserialized Java object
     * @throws ApiException If fail to deserialize response body, i.e. cannot read response body
     *   or the Content-Type of the response is not supported.
     */
    @SuppressWarnings("unchecked")
    public <T> T deserialize(Response response, Type returnType) throws ApiException {
        if (response == null || returnType == null) {
            return null;
        }

        if ("byte[]".equals(returnType.toString())) {
            // Handle binary response (byte array).
            try {
                return (T) response.body().bytes();
            } catch (IOException e) {
                throw new ApiException(e);
            }
        } else if (returnType.equals(File.class)) {
            // Handle file downloading.
            return (T) downloadFileFromResponse(response);
        }

        String respBody;
        try {
            if (response.body() != null)
                respBody = response.body().string();
            else
                respBody = null;
        } catch (IOException e) {
            throw new ApiException(e);
        }

        if (respBody == null || "".equals(respBody)) {
            return null;
        }

        String contentType = response.headers().get("Content-Type");
        if (contentType == null) {
            // ensuring a default content type
            contentType = "application/json";
        }
        if (isJsonMime(contentType)) {
            return json.deserialize(respBody, returnType);
        } else if (returnType.equals(String.class)) {
            // Expecting string, return the raw response body.
            return (T) respBody;
        } else {
            throw new ApiException(
                    "Content type \"" + contentType + "\" is not supported for type: " + returnType,
                    response.code(),
                    response.headers().toMultimap(),
                    respBody);
        }
    }

    /**
     * Serialize the given Java object into request body according to the object's
     * class and the request Content-Type.
     *
     * @param obj The Java object
     * @param contentType The request Content-Type
     * @return The serialized request body
     * @throws ApiException If fail to serialize the given object
     */
    public RequestBody serialize(Object obj, String contentType) throws ApiException {
        if (obj instanceof byte[]) {
            // Binary (byte array) body parameter support.
            return RequestBody.create(MediaType.parse(contentType), (byte[]) obj);
        } else if (obj instanceof File) {
            // File body parameter support.
            return RequestBody.create(MediaType.parse(contentType), (File) obj);
        } else if (isJsonMime(contentType)) {
            String content;
            if (obj != null) {
                content = json.serialize(obj);
            } else {
                content = null;
            }
            return RequestBody.create(MediaType.parse(contentType), content);
        } else {
            throw new ApiException("Content type \"" + contentType + "\" is not supported");
        }
    }

    /**
     * Download file from the given response.
     *
     * @param response An instance of the Response object
     * @throws ApiException If fail to read file content from response and write to disk
     * @return Downloaded file
     */
    public File downloadFileFromResponse(Response response) throws ApiException {
        try {
            File file = prepareDownloadFile(response);
            BufferedSink sink = Okio.buffer(Okio.sink(file));
            sink.writeAll(response.body().source());
            sink.close();
            return file;
        } catch (IOException e) {
            throw new ApiException(e);
        }
    }

    /**
     * Prepare file for download
     *
     * @param response An instance of the Response object
     * @throws IOException If fail to prepare file for download
     * @return Prepared file for the download
     */
    public File prepareDownloadFile(Response response) throws IOException {
        String filename = null;
        String contentDisposition = response.header("Content-Disposition");
        if (contentDisposition != null && !"".equals(contentDisposition)) {
            // Get filename from the Content-Disposition header.
            Pattern pattern = Pattern.compile("filename=['\"]?([^'\"\\s]+)['\"]?");
            Matcher matcher = pattern.matcher(contentDisposition);
            if (matcher.find()) {
                filename = sanitizeFilename(matcher.group(1));
            }
        }

        String prefix = null;
        String suffix = null;
        if (filename == null) {
            prefix = "download-";
            suffix = "";
        } else {
            int pos = filename.lastIndexOf(".");
            if (pos == -1) {
                prefix = filename + "-";
            } else {
                prefix = filename.substring(0, pos) + "-";
                suffix = filename.substring(pos);
            }
            // File.createTempFile requires the prefix to be at least three characters long
            if (prefix.length() < 3)
                prefix = "download-";
        }

        if (tempFolderPath == null)
            return File.createTempFile(prefix, suffix);
        else
            return File.createTempFile(prefix, suffix, new File(tempFolderPath));
    }

    /**
     * {@link #execute(Call, Type)}
     *
     * @param <T> Type
     * @param call An instance of the Call object
     * @throws ApiException If fail to execute the call
     * @return ApiResponse&lt;T&gt;
     */
    public <T> ApiResponse<T> execute(Call call) throws ApiException {
        return execute(call, null);
    }

    /**
     * Execute HTTP call and deserialize the HTTP response body into the given return type.
     *
     * @param returnType The return type used to deserialize HTTP response body
     * @param <T> The return type corresponding to (same with) returnType
     * @param call Call
     * @return ApiResponse object containing response status, headers and
     *   data, which is a Java object deserialized from response body and would be null
     *   when returnType is null.
     * @throws ApiException If fail to execute the call
     */
    public <T> ApiResponse<T> execute(Call call, Type returnType) throws ApiException {
        try {
            Response response = call.execute();
            T data = handleResponse(response, returnType);
            return new ApiResponse<T>(response.code(), response.headers().toMultimap(), data);
        } catch (IOException e) {
            throw new ApiException(e);
        }
    }

    /**
     * {@link #executeAsync(Call, Type, ApiCallback)}
     *
     * @param <T> Type
     * @param call An instance of the Call object
     * @param callback ApiCallback&lt;T&gt;
     */
    public <T> void executeAsync(Call call, ApiCallback<T> callback) {
        executeAsync(call, null, callback);
    }

    /**
     * Execute HTTP call asynchronously.
     *
     * @see #execute(Call, Type)
     * @param <T> Type
     * @param call The callback to be executed when the API call finishes
     * @param returnType Return type
     * @param callback ApiCallback
     */
    @SuppressWarnings("unchecked")
    public <T> void executeAsync(Call call, final Type returnType, final ApiCallback<T> callback) {
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                callback.onFailure(new ApiException(e), 0, null);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                T result;
                try {
                    result = (T) handleResponse(response, returnType);
                } catch (ApiException e) {
                    callback.onFailure(e, response.code(), response.headers().toMultimap());
                    return;
                }
                callback.onSuccess(result, response.code(), response.headers().toMultimap());
            }
        });
    }

    /**
     * Handle the given response, return the deserialized object when the response is successful.
     *
     * @param <T> Type
     * @param response Response
     * @param returnType Return type
     * @throws ApiException If the response has a unsuccessful status code or
     *   fail to deserialize the response body
     * @return Type
     */
    public <T> T handleResponse(Response response, Type returnType) throws ApiException {
        if (response.isSuccessful()) {
            if (returnType == null || response.code() == 204) {
                // returning null if the returnType is not defined,
                // or the status code is 204 (No Content)
                if (response.body() != null) {
                    try {
                        response.body().close();
                    } catch (IOException e) {
                        throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap());
                    }
                }
                return null;
            } else {
                return deserialize(response, returnType);
            }
        } else {
            String respBody = null;
            if (response.body() != null) {
                try {
                    respBody = response.body().string();
                } catch (IOException e) {
                    throw new ApiException(response.message(), e, response.code(), response.headers().toMultimap());
                }
            }
            throw new ApiException(response.message(), response.code(), response.headers().toMultimap(), respBody);
        }
    }

    /**
     * Build HTTP call with the given options.
     *
     * @param path The sub-path of the HTTP URL
     * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE"
     * @param queryParams The query parameters
     * @param collectionQueryParams The collection query parameters
     * @param body The request body object
     * @param headerParams The header parameters
     * @param formParams The form parameters
     * @param authNames The authentications to apply
     * @param progressRequestListener Progress request listener
     * @return The HTTP call
     * @throws ApiException If fail to serialize the request body object
     */
    public Call buildCall(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException {
        Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener);

        return httpClient.newCall(request);
    }

    /**
     * Build an HTTP request with the given options.
     *
     * @param path The sub-path of the HTTP URL
     * @param method The request method, one of "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" and "DELETE"
     * @param queryParams The query parameters
     * @param collectionQueryParams The collection query parameters
     * @param body The request body object
     * @param headerParams The header parameters
     * @param formParams The form parameters
     * @param authNames The authentications to apply
     * @param progressRequestListener Progress request listener
     * @return The HTTP request
     * @throws ApiException If fail to serialize the request body object
     */
    public Request buildRequest(String path, String method, List<Pair> queryParams, List<Pair> collectionQueryParams, Object body, Map<String, String> headerParams, Map<String, Object> formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException {
        updateParamsForAuth(authNames, queryParams, headerParams);

        final String url = buildUrl(path, queryParams, collectionQueryParams);
        final Request.Builder reqBuilder = new Request.Builder().url(url);
        processHeaderParams(headerParams, reqBuilder);

        String contentType = (String) headerParams.get("Content-Type");
        // ensuring a default content type
        if (contentType == null) {
            contentType = "application/json";
        }

        RequestBody reqBody;
        if (!HttpMethod.permitsRequestBody(method)) {
            reqBody = null;
        } else if ("application/x-www-form-urlencoded".equals(contentType)) {
            reqBody = buildRequestBodyFormEncoding(formParams);
        } else if ("multipart/form-data".equals(contentType)) {
            reqBody = buildRequestBodyMultipart(formParams);
        } else if (body == null) {
            if ("DELETE".equals(method)) {
                // allow calling DELETE without sending a request body
                reqBody = null;
            } else {
                // use an empty request body (for POST, PUT and PATCH)
                reqBody = RequestBody.create(MediaType.parse(contentType), "");
            }
        } else {
            reqBody = serialize(body, contentType);
        }

        Request request = null;

        if(progressRequestListener != null && reqBody != null) {
            ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, progressRequestListener);
            request = reqBuilder.method(method, progressRequestBody).build();
        } else {
            request = reqBuilder.method(method, reqBody).build();
        }

        return request;
    }

    /**
     * Build full URL by concatenating base path, the given sub path and query parameters.
     *
     * @param path The sub path
     * @param queryParams The query parameters
     * @param collectionQueryParams The collection query parameters
     * @return The full URL
     */
    public String buildUrl(String path, List<Pair> queryParams, List<Pair> collectionQueryParams) {
        final StringBuilder url = new StringBuilder();
        url.append(basePath).append(path);

        if (queryParams != null && !queryParams.isEmpty()) {
            // support (constant) query string in `path`, e.g. "/posts?draft=1"
            String prefix = path.contains("?") ? "&" : "?";
            for (Pair param : queryParams) {
                if (param.getValue() != null) {
                    if (prefix != null) {
                        url.append(prefix);
                        prefix = null;
                    } else {
                        url.append("&");
                    }
                    String value = parameterToString(param.getValue());
                    url.append(escapeString(param.getName())).append("=").append(escapeString(value));
                }
            }
        }

        if (collectionQueryParams != null && !collectionQueryParams.isEmpty()) {
            String prefix = url.toString().contains("?") ? "&" : "?";
            for (Pair param : collectionQueryParams) {
                if (param.getValue() != null) {
                    if (prefix != null) {
                        url.append(prefix);
                        prefix = null;
                    } else {
                        url.append("&");
                    }
                    String value = parameterToString(param.getValue());
                    // collection query parameter value already escaped as part of parameterToPairs
                    url.append(escapeString(param.getName())).append("=").append(value);
                }
            }
        }

        return url.toString();
    }

    /**
     * Set header parameters to the request builder, including default headers.
     *
     * @param headerParams Header parameters in the ofrm of Map
     * @param reqBuilder Reqeust.Builder
     */
    public void processHeaderParams(Map<String, String> headerParams, Request.Builder reqBuilder) {
        for (Entry<String, String> param : headerParams.entrySet()) {
            reqBuilder.header(param.getKey(), parameterToString(param.getValue()));
        }
        for (Entry<String, String> header : defaultHeaderMap.entrySet()) {
            if (!headerParams.containsKey(header.getKey())) {
                reqBuilder.header(header.getKey(), parameterToString(header.getValue()));
            }
        }
    }

    /**
     * Update query and header parameters based on authentication settings.
     *
     * @param authNames The authentications to apply
     * @param queryParams  List of query parameters
     * @param headerParams  Map of header parameters
     */
    public void updateParamsForAuth(String[] authNames, List<Pair> queryParams, Map<String, String> headerParams) {
        for (String authName : authNames) {
            Authentication auth = authentications.get(authName);
            if (auth == null) throw new RuntimeException("Authentication undefined: " + authName);
            auth.applyToParams(queryParams, headerParams);
        }
    }

    /**
     * Build a form-encoding request body with the given form parameters.
     *
     * @param formParams Form parameters in the form of Map
     * @return RequestBody
     */
    public RequestBody buildRequestBodyFormEncoding(Map<String, Object> formParams) {
        FormEncodingBuilder formBuilder  = new FormEncodingBuilder();
        for (Entry<String, Object> param : formParams.entrySet()) {
            formBuilder.add(param.getKey(), parameterToString(param.getValue()));
        }
        return formBuilder.build();
    }

    /**
     * Build a multipart (file uploading) request body with the given form parameters,
     * which could contain text fields and file fields.
     *
     * @param formParams Form parameters in the form of Map
     * @return RequestBody
     */
    public RequestBody buildRequestBodyMultipart(Map<String, Object> formParams) {
        MultipartBuilder mpBuilder = new MultipartBuilder().type(MultipartBuilder.FORM);
        for (Entry<String, Object> param : formParams.entrySet()) {
            if (param.getValue() instanceof File) {
                File file = (File) param.getValue();
                Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"; filename=\"" + file.getName() + "\"");
                MediaType mediaType = MediaType.parse(guessContentTypeFromFile(file));
                mpBuilder.addPart(partHeaders, RequestBody.create(mediaType, file));
            } else {
                Headers partHeaders = Headers.of("Content-Disposition", "form-data; name=\"" + param.getKey() + "\"");
                mpBuilder.addPart(partHeaders, RequestBody.create(null, parameterToString(param.getValue())));
            }
        }
        return mpBuilder.build();
    }

    /**
     * Guess Content-Type header from the given file (defaults to "application/octet-stream").
     *
     * @param file The given file
     * @return The guessed Content-Type
     */
    public String guessContentTypeFromFile(File file) {
        String contentType = URLConnection.guessContentTypeFromName(file.getName());
        if (contentType == null) {
            return "application/octet-stream";
        } else {
            return contentType;
        }
    }

    /**
     * Apply SSL related settings to httpClient according to the current values of
     * verifyingSsl and sslCaCert.
     */
    private void applySslSettings() {
        try {
            TrustManager[] trustManagers = null;
            HostnameVerifier hostnameVerifier = null;
            if (!verifyingSsl) {
                TrustManager trustAll = new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
                    @Override
                    public X509Certificate[] getAcceptedIssuers() { return null; }
                };
                SSLContext sslContext = SSLContext.getInstance("TLS");
                trustManagers = new TrustManager[]{ trustAll };
                hostnameVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify(String hostname, SSLSession session) { return true; }
                };
            } else if (sslCaCert != null) {
                char[] password = null; // Any password will work.
                CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
                Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert);
                if (certificates.isEmpty()) {
                    throw new IllegalArgumentException("expected non-empty set of trusted certificates");
                }
                KeyStore caKeyStore = newEmptyKeyStore(password);
                int index = 0;
                for (Certificate certificate : certificates) {
                    String certificateAlias = "ca" + Integer.toString(index++);
                    caKeyStore.setCertificateEntry(certificateAlias, certificate);
                }
                TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                trustManagerFactory.init(caKeyStore);
                trustManagers = trustManagerFactory.getTrustManagers();
            }

            if (keyManagers != null || trustManagers != null) {
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(keyManagers, trustManagers, new SecureRandom());
                httpClient.setSslSocketFactory(sslContext.getSocketFactory());
            } else {
                httpClient.setSslSocketFactory(null);
            }
            httpClient.setHostnameVerifier(hostnameVerifier);
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }
    }

    private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null, password);
            return keyStore;
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }
}
```



```
/*
* billwerk REST API
* This document describes the endpoints of the <strong>billwerk REST API</strong>. You can find out more about billwerk <a href='https://billwerk.com/'>here</a>. <br/> <br/>This documentation offers interactive features which will enable you to test how our system behaves. To use the interactive features you have to aquire a bearer token for your active billwerk sandbox account. This procedure is described <a href='/Docs/ApiIntroduction#authentication'>here</a>. After aquiring the token you must go through the 'Authorize'-Form down below (green button). You're now able to use the interactive features. <br/>Please be aware your sandbox account will be affected trying out the endpoints!</br> </br><strong>This documentation is still work in progress!</strong>
*
* OpenAPI spec version: v1
* Contact: support@billwerk.com
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/


package de.dpunkt.myaktion.webserviceexternal.billwerk.client.auth;

import java.util.Map;

import de.dpunkt.myaktion.webserviceexternal.billwerk.client.Pair;

import java.util.List;

@javax.annotation.Generated(value = "io.swagger.codegen.languages.JavaClientCodegen", date = "2018-11-15T15:15:57.551Z")
public class ApiKeyAuth implements Authentication {
  private final String location;
  private final String paramName;

  private String apiKey;
  private String apiKeyPrefix;

  public ApiKeyAuth(String location, String paramName) {
    this.location = location;
    this.paramName = paramName;
  }

  public String getLocation() {
    return location;
  }

  public String getParamName() {
    return paramName;
  }

  public String getApiKey() {
    return apiKey;
  }

  public void setApiKey(String apiKey) {
    this.apiKey = apiKey;
  }

  public String getApiKeyPrefix() {
    return apiKeyPrefix;
  }

  public void setApiKeyPrefix(String apiKeyPrefix) {
    this.apiKeyPrefix = apiKeyPrefix;
  }

  @Override
  public void applyToParams(List<Pair> queryParams, Map<String, String> headerParams) {
    if (apiKey == null) {
      return;
    }
    String value;
    if (apiKeyPrefix != null) {
      value = apiKeyPrefix + " " + apiKey;
    } else {
      value = apiKey;
    }
    if ("query".equals(location)) {
      queryParams.add(new Pair(paramName, value));
    } else if ("header".equals(location)) {
      headerParams.put(paramName, value);
    }
  }
}
```


Also username und password kann ich setzen, aber wie bekomme ich dann den Token :O


----------



## mihe7 (21. Nov 2018)

Habe mir den Spaß jetzt mal auf Code-Ebene angesehen. Die Geschichte mit dem application-flow scheint nicht wirklich anderen Code zu erzeugen -> Tonne.

Mit dem aus der Billwerk-JSON-Datei generierten Client wirst Du auch keinen Token bekommen (s. Konstruktor von ApiClient). Entweder passt Du die JSON-Datei für Swagger an, damit der generierte Client auch HTTP Basic-Authentication nutzen kann und versuchst über die Methoden des Clients (buildCall usw.) einen entsprechenden Request abzusetzen oder aber Du schreibst Dir die paar Zeilen selbst.


----------



## beta20 (21. Nov 2018)

mihe7 hat gesagt.:


> oder aber Du schreibst Dir die paar Zeilen selbst.


Erstmal vielen Dank für das Anschauen des Codes.

Ich bevorzuge eigentlich eher die Zeilen Code selbst zu schreiben.
Allerdings bin ich komplett blank was das Thema angeht. Wie gehe ich hier heran?


----------



## mihe7 (21. Nov 2018)

Dafür findest Du Beispiele an jeder Ecke. 

Die akzeptierte Antwort von https://stackoverflow.com/questions...thentication-in-java-using-httpclient/3365203 enthält beispielsweise bereits fast alles. 

Du musst lediglich den Content-Type ergänzen und, vor dem Einlesen der Antwort, den OutputStream der Verbindung verwenden, um "grant_type=client_credentials" an den Server zu schicken.

Die Antwort ist ein JSON-String, den Du entsprechend weiterverarbeitest, um an das access_token zu kommen.


----------



## beta20 (21. Nov 2018)

Sorry, komme hier wirklich nicht weiter... 

Also ich habe mal versucht eine getKey() Methode zu schreiben:


```
private String getKey() {
        try {
            URL url = new URL ("https://sandbox.billwerk.com/api/v1/oauth/token/");
           
            String str = "5bedc7b26cb54e1f082a4961:586db11e80e0fdd29ec6b75a7645e807";
            // encode data using BASE64
            String encoding = DatatypeConverter.printBase64Binary(str.getBytes());
     
           
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty  ("Authorization", "Basic " + encoding);

            InputStream content = (InputStream)connection.getInputStream();
            BufferedReader in   =
                new BufferedReader (new InputStreamReader (content));
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
           
            return line;
        } catch(Exception e) {
            e.printStackTrace();
        }
        return null;
    }
```

Aber wie muss ich den Content-Type ergänzen und "grant_type=client_credentials" ergänzen?


----------



## Thallius (21. Nov 2018)

Die Frage ist, willst du wirklich lernen wie HTTP Requests funktionieren oder willst du einfach nur eine schnelle Lösung?

Im ersteren Fall würde ich dir empfehlen mal Postman zu installieren. Damit kannst du dir sehr einfach die Requests zusammen basteln und dann in der Console ansehen was da eigentlich verschickt wird. Also was im Header steht und was im Body etc. Wenn du damit mal ein paar Stunden herumspielst wirst du verstehen wie das ganze funktioniert und danach ist es ein Klacks für dich das in Java umzusetzen.

Im zweiten Fall, such doch einfach nach „Java geht oAuth2 Token“ und nimm einen der Hunderte Beispielcodes im Netz.

Gruß

Claus


----------



## beta20 (21. Nov 2018)

Ich habe den ContentType nun so hinzugefügt:

```
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
```

Bekomme aber allerdings schon einen Fehler beim Request:

```
Server returned HTTP response code: 411 for URL: https://sandbox.billwerk.com/api/v1/oauth/token/
```


----------



## mihe7 (21. Nov 2018)

"Please note that the OAuth endpoint and some client-facing endpoints such as file download are not under api/v1/." - die URL dürfte also https://sandbox.billwerk.com/oauth/token/ lauten.
Du musst noch "grant_type=client_credentials" rüberschicken (hol Dir conn.getOutputStream(), schreibe mit write)
Wenn der Fehler 411 immer noch kommt, musst Du auch noch content-length angeben.


----------



## beta20 (21. Nov 2018)

Habe nun als URL:

```
URL url = new URL ("https://sandbox.billwerk.com/oauth/token/");
```

Komme leider immer noch ein 411 - Fehler.
Content-lenght muss dann auch in die Property?

```
connection.setRequestProperty("Content-Length", "???????");
```
-> Was kommt bei ???? herein?


----------



## mihe7 (21. Nov 2018)

"grant_type=client_credentials".length() sollte passen.


----------



## beta20 (21. Nov 2018)

Das passt doch nicht? Warum length()?


```
connection.setRequestProperty("Content-Length", "grant_type=client_credentials".length());
```


----------



## mihe7 (21. Nov 2018)

beta20 hat gesagt.:


> Das passt doch nicht? Warum length()?


Ein HTTP-Request besteht aus einem Header und einem ggf. leeren Body, der den Inhalt (also die Nutzdaten) enthält. Die Content-Length gibt - wie der Name schon sagt - die Länge des Inhalts an. 

Dein Inhalt ist der String "grant_type=client_credentials". Daher ist die Länge "grant_type=client_credentials".length().


----------



## beta20 (21. Nov 2018)

```
The method setRequestProperty(String, String) in the type URLConnection is not applicable for the arguments (String, int)
```

Muss ich dann die Länge wieder in einen String wandeln?


----------



## mihe7 (21. Nov 2018)

beta20 hat gesagt.:


> Muss ich dann die Länge wieder in einen String wandeln?


Ja.


----------



## Thallius (21. Nov 2018)

Mein Gott das ist doch eine konstante. Zähl einmal die Buchstaben und schreib den Wert rein....


----------



## beta20 (21. Nov 2018)

Bekomme leider immer noch 411 zurück:



```
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
    private static String getKey() {
       try {
            URL url = new URL ("https://sandbox.billwerk.com/oauth/token/");
          
            String str = "5bedc7b26cb54e1f082a4961:586db11e80e0fdd29ec6b75a7645e807";
            // encode data using BASE64
            String encoding = DatatypeConverter.printBase64Binary(str.getBytes());
    
          
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            connection.setRequestProperty  ("Authorization", "Basic " + encoding);
            connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            connection.setRequestProperty("Content-Length", String.valueOf("grant_type=client_credentials".length()));

            InputStream content = (InputStream)connection.getInputStream();
            BufferedReader in   =
                new BufferedReader (new InputStreamReader (content));
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
          
            return line;
        } catch(Exception e) {
            e.printStackTrace();
        }
       return null;
      
   }
```


----------



## mihe7 (21. Nov 2018)

Sag mal, liest Du eigentlich, was ich schreibe? 


```
try(OutputStream os = connection.getOutputStream()) {
    os.write("grant_type=client_credentials".getBytes());
}
```

vor InputStream content... einfügen.


----------



## beta20 (21. Nov 2018)

Super, danke - hat nun geklappt
Führe ich jedoch nun dies aus, bekomme ich folgende Fehlermeldung:


```
myapp.billwerk.client.ApiException: Unprocessable Entity
```


----------



## mihe7 (21. Nov 2018)

beta20 hat gesagt.:


> Führe ich jedoch nun dies aus,


Was führst Du aus?!?

Ich habe den Code oben mal laufen lassen: man bekommt den JSON-String zurück, der das access_token enthält. Den musst Du jetzt noch extrahieren und als API-Key in Deinem Swagger-Client verwenden.


----------



## beta20 (22. Nov 2018)

genau - ich habe den API Key nun eingesetzt, den ich aus meiner getKey() Methode bekommen habe


```
public static void main(String[] args) {
       
//        String key = getKey();
       
        ApiClient defaultClient = Configuration.getDefaultApiClient();
       
        // Configure API key authorization: BearerAuth

        ApiKeyAuth BearerAuth = (ApiKeyAuth) defaultClient.getAuthentication("BearerAuth");
//        BearerAuth.setApiKey("NWJlZGM3YjI2Y2I1NGUxZjA4MmE0OTYxOjU4NmRiMTFlODBlMGZkZDI5ZWM2Yjc1YTc2NDVlODA3");
        BearerAuth.setApiKey("FcayCjNHzZKeNcYEXPBnL1KXo7EKm15sKG0G8MrU7DmSxU0UGXDuTDmB7DnwuiKgVVBVswzWvGccY0LJSH6R3w");
        // Uncomment the following line to set a prefix for the API key, e.g. "Token" (defaults to null)
//        BearerAuth.setApiKeyPrefix("Basic");
        BearerAuth.setApiKeyPrefix("Bearer");

        ContractsApi apiInstance = new ContractsApi();
        String contractId = "1"; // String | Contract ID
       
      
        try {
            ComponentSubscriptionReadDTO result = apiInstance.componentSubscriptionsGetComponentSubscriptionsByContract(contractId);
            System.out.println(result);
        } catch (ApiException e) {
            System.err.println("Exception when calling ContractsApi#componentSubscriptionsGetComponentSubscriptionsByContract");
            e.printStackTrace();
        }
    }
```


----------



## mihe7 (22. Nov 2018)

Naja, ich denke halt mal, dass die contractId "1" ungültig ist (Sending invalid fields will result in a 422 Unprocessable Entity response.) - In der Referenz schaut das eher nach nach einer 96-Bit-Zahl als 24-Zeichen Hex-String aus.

Ruf doch erstmal die Verträge ab (contractGetList(null, null, null, null, null)).


----------



## beta20 (22. Nov 2018)

Hast Recht - habe jetzt einfach mal einen Kunden ausgegeben. Hat prima funktioniert.
Vielen Dank für die ganze Hilfe.

Noch eine Frage habe ich:
Muss ich den Key bei jedem API-Request mir holen / erzeugen oder reicht das einmalig und ich speichere den mir als Konstante ab?


----------



## Thallius (22. Nov 2018)

Unglaublich... du liest dir aber auch gar nichts selber durch oder?

Das Token das du zurück bekommst enthält eine Ablaufzeit.


----------



## mihe7 (22. Nov 2018)

EDIT: Antwort von @Thallius übersehen -> da steht schon alles


----------



## beta20 (24. Nov 2018)

Bist du dir sicher?


```
{"access_token":"xtlTVTB5yr4kCaPP16cs3p-UGbJIPOuk2LKpygjN1lhRHMBrS_22222bp2-KLlT7UChdvJAyUmr8NHt2hKUOkd_yDFg==","expires":0,"token_type":"bearer","permissions":{"Roles":["Admin"]}}
```
-> expires":0


----------



## mihe7 (24. Nov 2018)

beta20 hat gesagt.:


> Bist du dir sicher?


Ja. Die 0 würde ich mal als "läuft nie ab" interpretieren. Ob das immer so ist/bleibt, ist die andere Frage.


----------



## beta20 (24. Nov 2018)

ok, danke...

Eine weitere Verständnisfrage habe ich noch:
Ich würde mir gerne eine Liste von Customer ausgeben:
Also theorethisch:


```
List<CustomerReadDTO> list = new ArrayList<CustomerReadDTO>();
```

Das ist meine Methode um alle Customer zu bekommen:


```
/**
     * Find all Customer
     * @throws ApiException
     */
    public ApiResponse<CustomerReadDTO> findAllCustomerLazyLoading(List<ObjectForSearchList> searchList, int startingAt, int maxPerPage,
            String sortField, SortOrder sortOrder, Map<String, Object> filters) throws ApiException {

        CustomersApi apiInstance = new CustomersApi();
        ApiResponse<CustomerReadDTO> result = apiInstance.customersGetListWithHttpInfo(null, null, String.valueOf(startingAt), String.valueOf(maxPerPage), null, null);
 
        return result;
    }
```

Die Methode kommt durch den REST Client:

```
ApiResponse<CustomerReadDTO> de.client.api.CustomersApi.customersGetListWithHttpInfo(String search, String statusFilter, String externalId, String from, Integer skip, Integer take) throws ApiException

Retrieve a list of all customers

Parameters:
search Search customers by External Id, First Name, Last Name, Company Name, Email Address and Debitor Account (optional)
statusFilter filter by status (optional)
externalId TODO (optional)
from cursor from where on the items are listed (optional)
skip If you are using pagination, you can skip n items (optional)
take limit returned items (optional)
Returns:
ApiResponse<CustomerReadDTO>
Throws:
ApiException - If fail to call the API, e.g. server error or cannot deserialize the response body
```

Unter:
result.getData() -> bekomme ich einen einzelnen Kunden...
Wie bekomme ich aber eine Liste von Kunden (List<CustomerReadDTO> list) ?


----------



## mihe7 (24. Nov 2018)

Das ist eine berechtigte Frage, die Du den Entwickler des Billwerk-Swagger-JSON-Files stellen solltest. Das Modell ist m. E. falsch.


----------



## beta20 (25. Nov 2018)

Hm ok, dann werde ich hier mal nachfragen....
Nichtsdestotrotz habe ich nun versucht einen Kunden zu erstellen, auch das geht nicht.
Hier bekomme ich folgende Exception:


```
de.webserviceexternal.billwerk.client.ApiException: Unprocessable Entity
```


```
public static void main(String[] args) {
               
        ApiClient defaultClient = Configuration.getDefaultApiClient();
       
        // Configure API key authorization: BearerAuth

        ApiKeyAuth BearerAuth = (ApiKeyAuth) defaultClient.getAuthentication("BearerAuth");
//        BearerAuth.setApiKey("NWJlZGM3YjI2Y2I1NGUxZjA4MmE0OTYxOjU333334NmRiMTFlODBlMGZkZDI5ZWM2Yjc1YTc2NDVlODA3");
        BearerAuth.setApiKey("FcayCjNHzZKeNcYEXPBnL1KXo7EKm15sKG0G8MrU7DmSxU0UGXDuTDmB7DnwuiKgVVBVswzWvGccY0LJSH6R3w");

        BearerAuth.setApiKeyPrefix("Bearer");

        CustomersApi apiInstance = new CustomersApi();
        String customerId = "5be18f9681b1f00a807bbe9d"; // String | Contract ID
       
        AddressDTO address = new AddressDTO();
        address.setAddressLine1("Test");
        address.setAddressLine2("Test");
        address.setCity("Pasadena");
        address.setCountry("CA");
        address.setHouseNumber("145");
        address.setPostalCode("91001");
        address.setState("Test");
        address.setStreet("Test");
       
        CustomerDTO customerDto = new CustomerDTO();
        customerDto.setEmailAddress("test@webde");
        customerDto.setFirstName("Test");
        customerDto.setLastName("Test");
        customerDto.setCompanyName("Test");
        customerDto.setAddress(address);
        customerDto.setCustomerType(CustomerDTO.CustomerTypeEnum.BUSINESS);
        customerDto.setDebitorAccount("Test");
        customerDto.setDefaultBearerMedium(CustomerDTO.DefaultBearerMediumEnum.EMAIL);
        customerDto.setExternalCustomerId("Test");
        customerDto.setHidden(false);
        customerDto.setLocale("de-DE");
        customerDto.setNotes("Test");
        customerDto.setPhoneNumber("Test");
//        customerDto.setServiceCountry("Test");
        customerDto.setTag("Test");
//        customerDto.setTimeZoneKey("Test");
        customerDto.setVatId("Test");
        customerDto.customerType(CustomerDTO.CustomerTypeEnum.CONSUMER);
   
       
       
        try {
          
            CustomerReadDTO customerReadDTO = apiInstance.customersPostGeneric(customerDto);
           
            System.out.println(customerReadDTO);
        } catch (ApiException e) {
            System.err.println("Exception");
            e.printStackTrace();
        }
    }
```


----------



## beta20 (26. Nov 2018)

Hier die Antwort von deren Support bzgl. "alle Kunden" zu bekommen:



> rein von der Deklaration deiner Methode würde ich vermuten, dass du einen falschen Rückgabetyp verwendest. Für generierten Fremdcode können wir aber, wie schon gesagt, keinen Support leisten.
> Der Endpunkt GET /api/v1/Customers läuft problemlos und liefert eine Liste aller Kunden.



Ist der Support einfach unfähig und die Swagger - Datei ist falsch oder liegt es nun wirklich an mir?
"generierten Fremdcode" -> der Code wurde doch von der Swagger - Datei erzeugt?


----------



## mihe7 (26. Nov 2018)

@beta20 Die Antwort ist m. E. völlig korrekt. Scheinbar hast Du Ihnen die Signatur der generierten Swagger-Methode gezeigt und damit haben sie nichts (bzw. nur am Rande) zu tun.

Allerdings: es geht nicht um den generierten Code, sondern der Fehler ist in *ihrer* Swagger-Datei.

Dort wird unter #/paths/api/v1/Customers/get/responses/schema als Rückgabe der GET-Operation das Schema "#/definitions/CustomerReadDTO" referenziert. Dieses Schema besteht aus einem einzigen Objekt und genau das ist IMO der Fehler.

Das referenzierte Schema müsste ein Array von CustomerReadDTO-Objekten sein, also etwas wie

```
{
    "type": "array",
    "items": {
        "$ref": "#/definitions/CustomerReadDTO"
    }
}
```

Das gilt im übrigen nicht nur für Customers, sondern für alle Objekte.


----------



## Thallius (26. Nov 2018)

Mit ein paar einfachen HTTP Requests und ohne diesen ganzen Swagger Mist wärst du seit Tagen fertig...


----------



## mihe7 (26. Nov 2018)

Thallius hat gesagt.:


> Mit ein paar einfachen HTTP Requests und ohne diesen ganzen Swagger Mist wärst du seit Tagen fertig...


Das ist der Witz am Ganzen. Einfacher gehts noch mit JAX-RS.

```
Client client = ClientBuilder.newClient();
WebTarget billwerk = new WebTarget("irgendwas-billwerk.com/api/v1");

JsonArray customers = billwerk.path("Customers")
    .request(MediaType.APPLICATION_JSON)
    .get(JsonArray.class);
```
Das API-Token packt man entweder mit headers(map) dazu oder wendet einen ClientRequestFilter an. Wenn man will, kann man mit JSON-B das JSON auch noch auf die entsprechenden Klassen abbilden lassen. Asynchrone Aufrufe erreicht man ebenso einfach. Done.


----------



## beta20 (26. Nov 2018)

Danke, die Swagger - Datei wurde angepasst und nun bekomme ich auch das gewünschte Ergebnis...

Noch eine Idee bzgl. der Post - Methode, warum dies nicht funktioniert? 
Die Fehlermeldung ist leider auch nicht wirklich aussagekräftig...


----------



## beta20 (26. Nov 2018)

Es scheint so, dass die Adresse das Problem macht?
Ohne Adresse erzeugt es mir den Kunden.


```
AddressDTO address = new AddressDTO();
//        address.setAddressLine1("Test1");
//        address.setAddressLine2("Test2");
        address.setCity("Pasadena");
        address.setCountry("CA");
        address.setHouseNumber("145");
        address.setPostalCode("91001");
//        address.setState("Test");
        address.setStreet("Raymond Ave (Holly)");

      
        CustomerDTO customerDto = new CustomerDTO();
        customerDto.setEmailAddress("marcellus@example.com");
        customerDto.setFirstName("Test");
        customerDto.setLastName("Test");
        customerDto.setCompanyName("Test");
        customerDto.setAddress(address);
        customerDto.setCustomerType(CustomerDTO.CustomerTypeEnum.BUSINESS);
        customerDto.setDebitorAccount("Test");
        customerDto.setDefaultBearerMedium(CustomerDTO.DefaultBearerMediumEnum.EMAIL);
        customerDto.setExternalCustomerId("Test");
        customerDto.setHidden(false);
        customerDto.setLocale("de-DE");
        customerDto.setNotes("Test");
        customerDto.setPhoneNumber("Test");
//        customerDto.setServiceCountry("Test");
        customerDto.setTag("Test");
        customerDto.setTimeZoneKey("Test");
//        customerDto.setVatId("Test");
        customerDto.customerType(CustomerDTO.CustomerTypeEnum.CONSUMER);
```

Irgendeine Idee?


----------



## mihe7 (26. Nov 2018)

beta20 hat gesagt.:


> die Swagger - Datei wurde angepasst und nun bekomme ich auch das gewünschte Ergebnis...


Aha. Hast wenigstens eine Halbe ausgegeben bekommen? 



beta20 hat gesagt.:


> Irgendeine Idee?


Da kann ich im Schema jetzt auf den ersten Blick kein Problem erkennen. Ein AddressDTO-Objekt muss das Land ("Country") angeben. Dieses muss ein String mit einer Länge von exakt zwei Zeichen sein.


----------

