001// License: GPL. For details, see LICENSE file.
002
003package org.openstreetmap.josm.io.remotecontrol.handler;
004
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.io.StringWriter;
008import java.util.Arrays;
009import java.util.stream.Stream;
010
011import javax.json.Json;
012import javax.json.JsonArrayBuilder;
013import javax.json.JsonObjectBuilder;
014
015import org.openstreetmap.josm.data.preferences.JosmUrls;
016import org.openstreetmap.josm.io.remotecontrol.PermissionPrefWithDefault;
017import org.openstreetmap.josm.io.remotecontrol.RemoteControl;
018import org.openstreetmap.josm.io.remotecontrol.RequestProcessor;
019import org.openstreetmap.josm.tools.Utils;
020
021/**
022 * Reports available commands as <a href="http://spec.openapis.org/oas/v3.0.3">OpenAPI</a>.
023 */
024public class OpenApiHandler extends RequestHandler {
025
026    /**
027     * The remote control command name.
028     */
029    public static final String command = "openapi.json";
030
031    @Override
032    protected void handleRequest() {
033        JsonObjectBuilder openapi = getOpenApi();
034        StringWriter stringWriter = new StringWriter();
035        Json.createWriter(stringWriter).write(openapi.build());
036        content = stringWriter.toString();
037        contentType = "application/json";
038    }
039
040    private JsonObjectBuilder getOpenApi() {
041        return Json.createObjectBuilder()
042                .add("openapi", "3.0.0")
043                .add("info", Json.createObjectBuilder()
044                        .add("title", RequestProcessor.JOSM_REMOTE_CONTROL)
045                        .add("version", RemoteControl.getVersion())
046                        .add("contact", Json.createObjectBuilder()
047                                .add("name", "JOSM")
048                                .add("url", JosmUrls.getInstance().getJOSMWebsite())))
049                .add("servers", Json.createArrayBuilder()
050                        .add(Json.createObjectBuilder().add("url", "http://localhost:8111/")))
051                .add("paths", getHandlers());
052    }
053
054    private JsonObjectBuilder getHandlers() {
055        JsonObjectBuilder paths = Json.createObjectBuilder();
056        RequestProcessor.getHandlersInfo(null)
057                .forEach(handler -> paths.add("/" + handler.getCommand(), getHandler(handler)));
058        return paths;
059    }
060
061    private JsonObjectBuilder getHandler(RequestHandler handler) {
062        JsonArrayBuilder parameters = Json.createArrayBuilder();
063        Stream.concat(
064                Arrays.stream(handler.getMandatoryParams()),
065                Arrays.stream(handler.getOptionalParams())
066        ).distinct().map(param -> Json.createObjectBuilder()
067                .add("name", param)
068                .add("in", "query")
069                .add("required", Arrays.asList(handler.getMandatoryParams()).contains(param))
070                .add("schema", Json.createObjectBuilder().add("type", "string")) // TODO fix type
071        ).forEach(parameters::add);
072        return Json.createObjectBuilder().add("get", Json.createObjectBuilder()
073                .add("description", getDescription(handler))
074                .add("operationId", handler.getCommand())
075                .add("parameters", parameters)
076                .add("responses", Json.createObjectBuilder()
077                        .add("200", Json.createObjectBuilder().add("description", "successful operation")))
078        );
079    }
080
081    private String getDescription(RequestHandler handler) {
082        return Utils.firstNonNull(handler.getUsage(), "")
083                + "\n\n* " + String.join("\n* ", handler.getUsageExamples());
084    }
085
086    @Override
087    public String getUsage() {
088        return "JOSM RemoteControl as OpenAPI Specification";
089    }
090
091    @Override
092    public String[] getUsageExamples() {
093        return new String[]{"https://petstore.swagger.io/?url=http://localhost:8111/openapi.json", "https://swagger.io/specification/"};
094    }
095
096    @Override
097    public String getPermissionMessage() {
098        return tr("Remote Control has been asked to report its supported features. This enables web sites to guess a running JOSM version");
099    }
100
101    @Override
102    public PermissionPrefWithDefault getPermissionPref() {
103        return PermissionPrefWithDefault.READ_PROTOCOL_VERSION;
104    }
105
106    @Override
107    public String[] getMandatoryParams() {
108        return new String[0];
109    }
110
111    @Override
112    protected void validateRequest() {
113        // Nothing to do
114    }
115}