public class Formation implements Serializable {

    private static final long serialVersionUID = 1L;

    App app;
    String command;
    String created_at;
    String id;
    int quantity;
    String size;
    String type;
    String updated_at;
    
    public App getApp() {
        return app;
    }
    
    public void setApp(App app) {
        this.app = app;
    }
    
    public String getCommand() {
        return command;
    }
    
    public void setCommand(String command) {
        this.command = command;
    }
    
    public String getCreated_at() {
        return created_at;
    }
    
    public void setCreated_at(String created_at) {
        this.created_at = created_at;
    }
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
    }
    
    public int getQuantity() {
        return quantity;
    }
    
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
    
    public String getSize() {
        return size;
    }
    
    public void setSize(String size) {
        this.size = size;
    }
    
    public String getType() {
        return type;
    }
    
    public void setType(String type) {
        this.type = type;
    }
    
    public String getUpdated_at() {
        return updated_at;
    }
    
    public void setUpdated_at(String updated_at) {
        this.updated_at = updated_at;
    }
} public class Heroku {


    public enum Config {
        ENDPOINT("HEROKU_HOST", "heroku.host", "heroku.com");
        public final String environmentVariable;
        public final String systemProperty;
        public final String defaultValue;
        public final String value;

        Config(String environmentVariable, String systemProperty, String defaultValue) {
            this.environmentVariable = environmentVariable;
            this.systemProperty = systemProperty;
            this.defaultValue = defaultValue;
            String envVal = System.getenv(environmentVariable);
            String configVal = System.getProperty(systemProperty, envVal == null ? defaultValue : envVal);
            this.value = configVal.matches("^https?:\\/\\/.*") ? configVal : "https://api." + configVal;
        }

        public boolean isDefault() {
            return defaultValue.equals(value);
        }

    }

    public static enum JarProperties {
        ;

        static final Properties properties = new Properties();

        static {
            try {
              InputStream jarProps = JarProperties.class.getResourceAsStream("/heroku.jar.properties");
              properties.load(jarProps);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        
        public static String getProperty(String propName) {
            return properties.getProperty(propName);
        }

        public static Properties getProperties() {
            return properties;
        }
    }

    public static SSLContext herokuSSLContext() {
        return sslContext(Config.ENDPOINT.isDefault());
    }

    public static SSLContext sslContext(boolean verify) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            TrustManager[] tmgrs = null;
            if (!verify) {
                tmgrs = trustAllTrustManagers();
            }
            /*
Initializes this context.
Either of the first two parameters may be null in which case the installed security providers will be searched
for the highest priority implementation of the appropriate factory.
Likewise, the secure random parameter may be null in which case the default implementation will be used.
            */
            ctx.init(null, tmgrs, null);
            return ctx;
        } catch (NoSuchAlgorithmException e) {
            throw new HerokuAPIException("NoSuchAlgorithmException while trying to setup SSLContext", e);
        } catch (KeyManagementException e) {
            throw new HerokuAPIException("KeyManagementException while trying to setup SSLContext", e);
        }
    }

    public static HostnameVerifier hostnameVerifier(boolean verify) {
        HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier();
        if (!verify) {
            verifier = new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            };
        }
        return verifier;
    }

    public static HostnameVerifier herokuHostnameVerifier() {
        return hostnameVerifier(Config.ENDPOINT.isDefault());
    }

    public static enum ResponseKey {
        Name("name"),
        DomainName("domain_name"),
        CreateStatus("create_status"),
        Stack("stack"),
        SlugSize("slug_size"),
        RequestedStack("requested_stack"),
        CreatedAt("created_at"),
        WebUrl("web_url"),
        RepoMigrateStatus("repo_migrate_status"),
        Id("id"),
        GitUrl("git_url"),
        RepoSize("repo_size"),
        Dynos("dynos"),
        Workers("workers");

        public final String value;
        
        // From Effective Java, Second Edition
        private static final Map<String, ResponseKey> stringToResponseKey = new HashMap<String, ResponseKey>();
        static {
            for (ResponseKey key : values())
                stringToResponseKey.put(key.toString(), key);
        }
        
        ResponseKey(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return value;
        }
        
        public static ResponseKey fromString(String keyName) {
            return stringToResponseKey.get(keyName);
        }
    }

    public static enum RequestKey {
        Slug("slug"),
        StackName("name"),
        Stack("stack"),
        AppMaintenance("maintenance"),
        AddonName("addon"),
        AddonPlan("plan"),
        AddonConfig("config"),
        AddonAttachment("attachment"),
        AddonAttachmentName("name"),
        AppName("name"),
        SSHKey("public_key"),
        Collaborator("user"),
        TransferAppName("app"),
        TransferOwner("recipient"),
        Release("release"),
        CreateDomain("hostname"),
        DeleteDomain("hostname"),
        ProcessTypes("process_types"),
        Dyno("dyno"),
        LogLines("lines"),
        LogSource("source"),
        LogTail("tail"),
        SourceBlob("source_blob"),
        SourceBlobUrl("url"),
        SourceBlobChecksum("checksum"),
        SourceBlobVersion("version"),
        Buildpacks("buildpacks"),
        BuildpackUrl("url"),
        Space("space"),
        SpaceId("id"),
        SpaceName("name"),
        SpaceShield("shield"),
        Quantity("quantity"),
        Team("team"),
        TeamName("name");

        public final String queryParameter;

        RequestKey(String queryParameter) {
            this.queryParameter = queryParameter;
        }
    }


    public static enum Stack {
        Cedar14("cedar-14"),
        Container("container"),
        Heroku16("heroku-16"),
        Heroku18("heroku-18");

        public final String value;

        // From Effective Java, Second Edition
        private static final Map<String, Stack> stringToEnum = new HashMap<String, Stack>();
        static {
            for (Stack s : values())
                stringToEnum.put(s.toString(), s);
        }

        Stack(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return value;
        }

        public static Stack fromString(String stackName) {
            return stringToEnum.get(stackName);
        }
    }


    public static enum Resource {
        Apps("/apps"),
        App("/apps/%s"),
        AppTransfer("/account/app-transfers"),
        Addons("/addons"),
        AppAddons(App.value + "/addons"),
        AppAddon(AppAddons.value + "/%s"),
        Builds("/apps/%s/builds"),
        BuildInfo("/apps/%s/builds/%s"),
        BuildResult("/apps/%s/builds/%s/result"),
        User("/account"),
        Key(User.value + "/keys/%s"),
        Keys(User.value + "/keys"),
        Collaborators(App.value + "/collaborators"),
        Collaborator(Collaborators.value + "/%s"),
        ConfigVars(App.value + "/config-vars"),
        Logs(App.value + "/log-sessions"),
        Process(App.value + "/ps"),
        Restart(Process.value + "/restart"),
        Stop(Process.value + "/stop"),
        Scale(Process.value + "/scale"),
        Releases(App.value + "/releases"),
        Release(Releases.value + "/%s"),
        Slugs(App.value + "/slugs"),
        Slug(Slugs.value + "/%s"),
        Sources("/sources"),
        Status(App.value + "/status"),
        Stacks("/stacks"),
        Domains(App.value + "/domains"),
        Domain(Domains.value + "/%s"),
        Dynos("/apps/%s/dynos"),
        Dyno(Dynos.value + "/%s"),
        Formations(App.value + "/formation"),
        Formation(Formations.value + "/%s"),
        BuildpackInstalltions("/apps/%s/buildpack-installations"),
        TeamApps("/teams/%s/apps"),
        TeamAppsAll("/teams/apps"),
        TeamApp("/teams/apps/%s"),
        Team("/teams/%s"),
        Teams("/teams"),
        TeamInvoices("/teams/%s/invoices"),
        TeamInvoice("/teams/%s/invoices/%s"),;

        public final String value;

        Resource(String value) {
            this.value = value;
        }

        public String format(String... values) {
            return String.format(value, values);
        }
    }

    public enum ApiVersion implements Http.Header {

        v2(2), v3(3);

        public static final String HEADER = "Accept";

        public final int version;

        ApiVersion(int version) {
            this.version = version;
        }

        @Override
        public String getHeaderName() {
            return HEADER;
        }

        @Override
        public String getHeaderValue() {
            return "application/vnd.heroku+json; version=" + Integer.toString(version);
        }
    }

    public static TrustManager[] trustAllTrustManagers() {
        return new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(final X509Certificate[] chain, final String authType) {
            }

            @Override
            public void checkServerTrusted(final X509Certificate[] chain, final String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }};
    }
} public class RequestFailedException extends HerokuAPIException {

    String responseBody;
    int statusCode;

    public RequestFailedException(String msg, int code, byte[] in) {
        this(msg, code, getBodyFromInput(in));

    }

    private static String getBodyFromInput(byte[] in) {
        try {
            return HttpUtil.getUTF8String(in);
        } catch (Exception e) {
            return "There was also an error reading the response body.";
        }
    }

    public RequestFailedException(String msg, int code, String body) {
        super(msg + " statuscode:" + code + " responseBody:" + body);
        responseBody = body;
        statusCode = code;
    }


    public String getResponseBody() {
        return responseBody;
    }

    public int getStatusCode() {
        return statusCode;
    }
} public class Http {
    /**
     * HTTP Accept header model.
     */
    public static enum Accept implements Header {
        JSON("application/json"),
        TEXT("text/plain");

        private String value;
        static String ACCEPT = "Accept";

        Accept(String val) {
            this.value = val;
        }

        @Override
        public String getHeaderName() {
            return ACCEPT;
        }

        @Override
        public String getHeaderValue() {
            return value;
        }

    }

    /**
     * HTTP Content-Type header model.
     */
    public static enum ContentType implements Header {
        JSON("application/json"),
        FORM_URLENCODED("application/x-www-form-urlencoded"),
        SSH_AUTHKEY("text/ssh-authkey");

        private String value;
        static String CONTENT_TYPE = "Content-Type";

        ContentType(String val) {
            this.value = val;
        }

        @Override
        public String getHeaderName() {
            return CONTENT_TYPE;
        }

        @Override
        public String getHeaderValue() {
            return value;
        }

    }

    /**
     * HTTP User-Agent header model.
     *
     * @see UserAgentValueProvider
     */
    public static enum UserAgent implements Header {
        LATEST(loadValueProvider());

        static final String USER_AGENT = "User-Agent";
        private final UserAgentValueProvider userAgentValueProvider;

        UserAgent(UserAgentValueProvider userAgentValueProvider) {
            this.userAgentValueProvider = userAgentValueProvider;
        }

        @Override
        public String getHeaderName() {
            return USER_AGENT;
        }

        @Override
        public String getHeaderValue() {
          return userAgentValueProvider.getHeaderValue();
        }

        public String getHeaderValue(String customPart) {
            return userAgentValueProvider.getHeaderValue(customPart);
        }

        private static UserAgentValueProvider loadValueProvider() {
            final Iterator<UserAgentValueProvider> customProviders =
                    ServiceLoader.load(UserAgentValueProvider.class, UserAgent.class.getClassLoader()).iterator();

            if (customProviders.hasNext()) {
                return customProviders.next();
            } else {
                return new UserAgentValueProvider.DEFAULT();
            }
        }
    }

    /**
     * Represent a name/value pair for a HTTP header. Not all are implemented. Only those used by the Heroku API.
     */
    public static interface Header {

        public static class Util {
            public static Map<String, String> setHeaders(Header... headers) {
                Map<String, String> headerMap = new HashMap<String, String>();
                for (Header h : headers) {
                    headerMap.put(h.getHeaderName(), h.getHeaderValue());
                }
                return headerMap;
            }
        }

        String getHeaderName();

        String getHeaderValue();

    }

    /**
     * HTTP Methods. Not all are implemented. Only those used by the Heroku API.
     */
    public static enum Method {GET, PUT, POST, DELETE, PATCH}

    /**
     * HTTP Status codes. Not all are implemented. Only those used by the Heroku API.
     */
    public static enum Status {
        OK(200), CREATED(201), ACCEPTED(202), PAYMENT_REQUIRED(402), FORBIDDEN(403), NOT_FOUND(404), UNPROCESSABLE_ENTITY(422), INTERNAL_SERVER_ERROR(500), SERVICE_UNAVAILABLE(503);

        public final int statusCode;

        Status(int statusCode) {
            this.statusCode = statusCode;
        }

        public boolean equals(int code) {
            return statusCode == code;
        }
    }
} public static enum Accept implements Header {
    JSON("application/json"),
    TEXT("text/plain");

    private String value;
    static String ACCEPT = "Accept";

    Accept(String val) {
        this.value = val;
    }

    @Override
    public String getHeaderName() {
        return ACCEPT;
    }

    @Override
    public String getHeaderValue() {
        return value;
    }

} public static enum Method {GET, PUT, POST, DELETE, PATCH} public class HttpUtil {

    private static String ENCODE_FAIL = "Unsupported encoding exception while encoding parameters";

    /**
     * URL encode request paramaters from a {@link RequestConfig}.
     * @param config Name/value pairs for a HTTP request.
     * @param keys List of keys in the config to encode.
     * @return A string representation of encoded name/value parameters.
     */
    public static String encodeParameters(RequestConfig config, Heroku.RequestKey... keys) {

        StringBuilder encodedParameters = new StringBuilder();
        String separator = "";
        for (Heroku.RequestKey key : keys) {
            if (config.get(key) != null) {
                encodedParameters.append(separator);
                encodedParameters.append(urlencode(key.queryParameter, ENCODE_FAIL));
                encodedParameters.append("=");
                encodedParameters.append(urlencode(config.get(key), ENCODE_FAIL));
                separator = "&";
            }
        }
        return new String(encodedParameters);

    }

    // Characters to encode that URLEncoder doesn't encode.
    // See: http://docs.oracle.com/javase/6/docs/api/java/net/URLEncoder.html
    static final Map<String, String> specialChars = Collections.unmodifiableMap(new HashMap<String, String>(){{
        put(".", "%2e");
        put("-", "%2d");
        put("*", "%2a");
        put("_", "%5f");
    }});

    /**
     * Some calls in the Heroku API decode strings in a different way from URLEncoder. This is a method for handling those
     * special cases. First, urlencode() is called. Then, .-*_ are replaced with their hexadecimal equivalent.
     * @param toEncode string to encode
     * @return A string representation of encoded parameters.
     */
    public static String encodeIncludingSpecialCharacters(String toEncode) {
        String encoded = urlencode(toEncode, "Unable to urlencode " + toEncode);
        for (Map.Entry<String, String> s : specialChars.entrySet()) {
            encoded = encoded.replace(s.getKey(), s.getValue());
        }
        return encoded;
    }

    public static String urlencode(String toEncode, String messageIfFails) {
        try {
            return URLEncoder.encode(toEncode, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(messageIfFails, e);
        }
    }


    public static UnsupportedOperationException noBody() {
        return new UnsupportedOperationException("This request does not have a body. Use hasBody() to check for a body.");
    }

    public static URL toURL(String url) {
        try {
            return new URL(url);
        } catch (MalformedURLException e) {
            throw new RuntimeException("The URL was malformed: " + url, e);
        }
    }

    public static RequestFailedException insufficientPrivileges(int code, byte[] bytes) {
        return new RequestFailedException("Insufficient privileges.", code, bytes);
    }

    /**
     * Converts an {@link InputStream} to a byte array
     * @param in input stream to convert
     * @return byte array
     */
    public static byte[] getBytes(InputStream in) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        WritableByteChannel wbc = Channels.newChannel(os);
        ReadableByteChannel rbc = Channels.newChannel(in);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        try {
            while (rbc.read(byteBuffer) != -1) {
                byteBuffer.flip();
                wbc.write(byteBuffer);
                byteBuffer.clear();
            }
            wbc.close();
            rbc.close();
            return os.toByteArray();
        } catch (IOException e) {
            throw new HerokuAPIException("IOException while reading response", e);
        }
    }

    public static String getUTF8String(byte[] in) {
        try {
            return new String(in, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new HerokuAPIException("Somehow UTF-8 is unsupported", e);
        }
    }
} public interface Request<T> {


    /**
     * HTTP method. e.g. GET, POST, PUT, DELETE
     * @return The HTTP method used in the request.
     */
    Http.Method getHttpMethod();

    /**
     * Path and query parameters of a URL.
     * @return The path and query parameters as a String.
     */
    String getEndpoint();

    /**
     * Whether or not the request has a body.
     * @return true if it has a request body, otherwise false
     */
    boolean hasBody();

    /**
     * Value of the request body.
     * @return Body
     * @throws UnsupportedOperationException Generally thrown if {@link #hasBody()} returns false
     */
    String getBody();

    /**
     * Value of the request body as a Map.
     * @return Body
     * @throws UnsupportedOperationException Generally thrown if {@link #hasBody()} returns false
     */
    Map<String, ?> getBodyAsMap();

    /**
     * HTTP Accept header.
     * @return The Accept header to be used in the request. Typically "application/json" or "text/xml"
     * @see com.heroku.api.http.Http.Accept
     */
    Http.Accept getResponseType();

    /**
     * {@link Map} of request-specific HTTP headers.
     * @return Name/value pairs of HTTP headers.
     */
    Map<String, String> getHeaders();

    /**
     * Response handler.
     * @param bytes Data returned from the request.
     * @param status HTTP status code.
     * @return The type {@link T} as specified by an individual request.
     * @throws com.heroku.api.exception.RequestFailedException Generally thrown when the HTTP status code is 4XX.
     */
    T getResponse(byte[] bytes, int status, Map<String,String> headers);

} public class RequestConfig {

    private String appName;

    private final Map<Heroku.RequestKey, RequestConfig.Either> config = new EnumMap<Heroku.RequestKey, RequestConfig.Either>(Heroku.RequestKey.class);

    /**
     * Sets the {Heroku.RequestKey.AppName} parameter.
     * @param appName Name of the app to specify in the config.
     * @return A new {@link RequestConfig}
     */
    public RequestConfig app(String appName) {
        this.appName = appName;
        return this;
    }

    public String getAppName() {
        return this.appName;
    }

    /**
     * Sets a {@link com.heroku.api.Heroku.RequestKey} parameter.
     * @param key Heroku request key
     * @param value value of the property
     * @return A new {@link RequestConfig}
     */
    public RequestConfig with(Heroku.RequestKey key, String value) {
        RequestConfig newConfig = copy();
        newConfig.config.put(key, new Either(value));
        return newConfig;
    }

    /**
     * Sets a {@link com.heroku.api.Heroku.RequestKey} parameter.
     * @param key Heroku request key
     * @param value value of the property
     * @return A new {@link RequestConfig}
     */
    public RequestConfig with(Heroku.RequestKey key, Map<Heroku.RequestKey, Either> value) {
        RequestConfig newConfig = copy();
        newConfig.config.put(key, new Either(value));
        return newConfig;
    }

    /**
     * Sets a {@link com.heroku.api.Heroku.RequestKey} parameter.
     * @param key Heroku request key
     * @param data arbitrary key/value map
     * @return A new {@link RequestConfig}
     */
    public RequestConfig withOptions(Heroku.RequestKey key, Map<String, String> data) {
        RequestConfig newConfig = copy();
        newConfig.config.put(key, new Either(new Data(data)));
        return newConfig;
    }

    public String get(Heroku.RequestKey key) {
        return config.get(key).string();
    }

    public Map<Heroku.RequestKey, Either> getMap(Heroku.RequestKey key) {
        return config.get(key).map();
    }

    public boolean has(Heroku.RequestKey key){
        return config.containsKey(key);
    }

    public String asJson() {
        return Json.encode(asMap());
    }

    public Map<String,Object> asMap() {
        return stringifyMap(config);
    }

    private Map<String,Object> stringifyMap(Map<Heroku.RequestKey, Either> map) {
        Map<String,Object> jsonMap = new HashMap<>();
        for (Heroku.RequestKey key : map.keySet()) {
            RequestConfig.Either either = map.get(key);
            if (either.is(String.class)) {
                jsonMap.put(key.queryParameter, either.string());
            } else if (either.is(Boolean.class)) {
                jsonMap.put(key.queryParameter, either.bool());
            } else if (either.is(Data.class)) {
                jsonMap.put(key.queryParameter, either.data());
            } else {
                jsonMap.put(key.queryParameter, stringifyMap(either.map()));
            }
        }
        return jsonMap;
    }

    private RequestConfig copy() {
        RequestConfig newConfig = new RequestConfig();
        newConfig.app(this.appName);
        newConfig.config.putAll(config);
        return newConfig;
    }

    public static class Either {

        private Class type;

        private String string;

        private Boolean bool;

        private Data data;

        private Map<Heroku.RequestKey, Either> map;

        public Either(Boolean value) {
            this.type = Boolean.class;
            this.bool = value;
        }

        public Either(String value) {
            this.type = String.class;
            this.string = value;
        }

        public Either(Data data) {
            this.type = Data.class;
            this.data = data;
        }

        public Either(Map<Heroku.RequestKey, Either> value) {
            this.type = Map.class;
            this.map = value;
        }

        public String string() {
            return string;
        }

        public Boolean bool() {
            return bool;
        }

        public Map<String,String> data() {
            return data.map();
        }

        public Map<Heroku.RequestKey, Either> map() {
            return map;
        }

        public Boolean is(Class c) {
            return c.equals(type);
        }
    }

    public static class Data {

        private Map<String, String> map;

        public Data(Map<String, String> map) {
            this.map = map;
        }

        public Map<String, String> map() {
            return map;
        }

    }
} public static <T> T parse(byte[] data, Type type) {
    return Holder.parser.parse(data, type);
}
import com.heroku.api.Formation;
import com.heroku.api.Heroku;
import com.heroku.api.exception.RequestFailedException;
import com.heroku.api.http.Http;
import com.heroku.api.http.Http.Accept;
import com.heroku.api.http.Http.Method;
import com.heroku.api.http.HttpUtil;
import com.heroku.api.request.Request;
import com.heroku.api.request.RequestConfig;
import java.util.Map;
import static com.heroku.api.parser.Json.parse;
package com.heroku.api.request.formation;




/**
 * @author Román Sosa
 */
public class FormationUpdate implements Request<Formation> {

    private final RequestConfig config;
    private final String processType;
    
    public FormationUpdate(String appName, String processType, int quantity) {

        config = new RequestConfig().app(appName)
                .with(Heroku.RequestKey.Quantity, String.valueOf(quantity));
        this.processType = processType;
    }
    
    @Override
    public Method getHttpMethod() {