Skip to content
Permalink
Browse files
GERONIMO-6661 OpenAPIMojo tolerance of interface design - not fully g…
…eneric yet + GERONIMO-6665 default response when possible when no openapi annotation is found
  • Loading branch information
rmannibucau committed Dec 17, 2018
1 parent 8ad36a0 commit 8ddd6859ec00c8cfef0fb0ca17471394e0367241
Showing 16 changed files with 411 additions and 80 deletions.
@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.geronimo.microprofile.openapi.impl.loader;

import java.util.HashMap;
import java.util.Map;

import org.apache.geronimo.microprofile.openapi.impl.model.APIResponseImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.APIResponsesImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.CallbackImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ComponentsImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ContactImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ContentImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.DiscriminatorImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.EncodingImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ExampleImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ExtensibleImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ExternalDocumentationImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.HeaderImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.InfoImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.LicenseImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.LinkImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.MediaTypeImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.OAuthFlowImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.OAuthFlowsImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.OpenAPIImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.OperationImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ParameterImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.PathItemImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.PathsImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ReferenceImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.RequestBodyImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.SchemaImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ScopesImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.SecurityRequirementImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.SecuritySchemeImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ServerImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ServerVariableImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.ServerVariablesImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.TagImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.XMLImpl;
import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.Extensible;
import org.eclipse.microprofile.openapi.models.ExternalDocumentation;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import org.eclipse.microprofile.openapi.models.Operation;
import org.eclipse.microprofile.openapi.models.PathItem;
import org.eclipse.microprofile.openapi.models.Paths;
import org.eclipse.microprofile.openapi.models.Reference;
import org.eclipse.microprofile.openapi.models.callbacks.Callback;
import org.eclipse.microprofile.openapi.models.examples.Example;
import org.eclipse.microprofile.openapi.models.headers.Header;
import org.eclipse.microprofile.openapi.models.info.Contact;
import org.eclipse.microprofile.openapi.models.info.Info;
import org.eclipse.microprofile.openapi.models.info.License;
import org.eclipse.microprofile.openapi.models.links.Link;
import org.eclipse.microprofile.openapi.models.media.Content;
import org.eclipse.microprofile.openapi.models.media.Discriminator;
import org.eclipse.microprofile.openapi.models.media.Encoding;
import org.eclipse.microprofile.openapi.models.media.MediaType;
import org.eclipse.microprofile.openapi.models.media.Schema;
import org.eclipse.microprofile.openapi.models.media.XML;
import org.eclipse.microprofile.openapi.models.parameters.Parameter;
import org.eclipse.microprofile.openapi.models.parameters.RequestBody;
import org.eclipse.microprofile.openapi.models.responses.APIResponse;
import org.eclipse.microprofile.openapi.models.responses.APIResponses;
import org.eclipse.microprofile.openapi.models.security.OAuthFlow;
import org.eclipse.microprofile.openapi.models.security.OAuthFlows;
import org.eclipse.microprofile.openapi.models.security.Scopes;
import org.eclipse.microprofile.openapi.models.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.models.security.SecurityScheme;
import org.eclipse.microprofile.openapi.models.servers.Server;
import org.eclipse.microprofile.openapi.models.servers.ServerVariable;
import org.eclipse.microprofile.openapi.models.servers.ServerVariables;
import org.eclipse.microprofile.openapi.models.tags.Tag;

public class ApiBindings {
private ApiBindings() {
// no-op
}

public static Map<Class<?>, Class<?>> get() {
final Map<Class<?>, Class<?>> mapping = new HashMap<>(33);
mapping.put(APIResponse.class, APIResponseImpl.class);
mapping.put(APIResponses.class, APIResponsesImpl.class);
mapping.put(Callback.class, CallbackImpl.class);
mapping.put(Components.class, ComponentsImpl.class);
mapping.put(Contact.class, ContactImpl.class);
mapping.put(Content.class, ContentImpl.class);
mapping.put(Discriminator.class, DiscriminatorImpl.class);
mapping.put(Encoding.class, EncodingImpl.class);
mapping.put(Example.class, ExampleImpl.class);
mapping.put(Extensible.class, ExtensibleImpl.class);
mapping.put(ExternalDocumentation.class, ExternalDocumentationImpl.class);
mapping.put(Header.class, HeaderImpl.class);
mapping.put(Info.class, InfoImpl.class);
mapping.put(License.class, LicenseImpl.class);
mapping.put(Link.class, LinkImpl.class);
mapping.put(MediaType.class, MediaTypeImpl.class);
mapping.put(OAuthFlow.class, OAuthFlowImpl.class);
mapping.put(OAuthFlows.class, OAuthFlowsImpl.class);
mapping.put(OpenAPI.class, OpenAPIImpl.class);
mapping.put(Operation.class, OperationImpl.class);
mapping.put(Parameter.class, ParameterImpl.class);
mapping.put(PathItem.class, PathItemImpl.class);
mapping.put(Paths.class, PathsImpl.class);
mapping.put(Reference.class, ReferenceImpl.class);
mapping.put(RequestBody.class, RequestBodyImpl.class);
mapping.put(Schema.class, SchemaImpl.class);
mapping.put(Scopes.class, ScopesImpl.class);
mapping.put(SecurityRequirement.class, SecurityRequirementImpl.class);
mapping.put(SecurityScheme.class, SecuritySchemeImpl.class);
mapping.put(Server.class, ServerImpl.class);
mapping.put(ServerVariable.class, ServerVariableImpl.class);
mapping.put(ServerVariables.class, ServerVariablesImpl.class);
mapping.put(Tag.class, TagImpl.class);
mapping.put(XML.class, XMLImpl.class);
return mapping;
}
}
@@ -26,6 +26,7 @@
import javax.inject.Inject;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.servlet.ServletContext;

import org.apache.geronimo.microprofile.openapi.impl.model.OpenAPIImpl;
@@ -41,7 +42,9 @@ public OpenAPI loadDefaultApi() {
return Stream.of("", "/").map(prefix -> prefix + "META-INF/openapi.json")
.map(it -> ofNullable(loader.getResourceAsStream(it)).orElseGet(() -> context.getResourceAsStream(it)))
.filter(Objects::nonNull).findFirst().map(r -> {
try (final Jsonb jsonb = JsonbBuilder.create(); final InputStream stream = r) {
try (final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig()
.setProperty("johnzon.interfaceImplementationMapping", ApiBindings.get()));
final InputStream stream = r) {
return jsonb.fromJson(stream, OpenAPIImpl.class);
} catch (final Exception e) {
throw new IllegalStateException(e);
@@ -27,6 +27,7 @@
import javax.json.bind.annotation.JsonbProperty;
import javax.json.bind.annotation.JsonbTransient;

import org.apache.geronimo.microprofile.openapi.impl.loader.ApiBindings;
import org.apache.geronimo.microprofile.openapi.impl.model.APIResponseImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.APIResponsesImpl;
import org.apache.geronimo.microprofile.openapi.impl.model.CallbackImpl;
@@ -131,40 +132,7 @@ public static OpenAPI loadAPI(final InputStream stream) {
// let be reusable in integrations
public static ObjectMapper getObjectMapper() {
final SimpleAbstractTypeResolver resolver = new SimpleAbstractTypeResolver();
resolver.addMapping(APIResponse.class, APIResponseImpl.class);
resolver.addMapping(APIResponses.class, APIResponsesImpl.class);
resolver.addMapping(Callback.class, CallbackImpl.class);
resolver.addMapping(Components.class, ComponentsImpl.class);
resolver.addMapping(Contact.class, ContactImpl.class);
resolver.addMapping(Content.class, ContentImpl.class);
resolver.addMapping(Discriminator.class, DiscriminatorImpl.class);
resolver.addMapping(Encoding.class, EncodingImpl.class);
resolver.addMapping(Example.class, ExampleImpl.class);
resolver.addMapping(Extensible.class, ExtensibleImpl.class);
resolver.addMapping(ExternalDocumentation.class, ExternalDocumentationImpl.class);
resolver.addMapping(Header.class, HeaderImpl.class);
resolver.addMapping(Info.class, InfoImpl.class);
resolver.addMapping(License.class, LicenseImpl.class);
resolver.addMapping(Link.class, LinkImpl.class);
resolver.addMapping(MediaType.class, MediaTypeImpl.class);
resolver.addMapping(OAuthFlow.class, OAuthFlowImpl.class);
resolver.addMapping(OAuthFlows.class, OAuthFlowsImpl.class);
resolver.addMapping(OpenAPI.class, OpenAPIImpl.class);
resolver.addMapping(Operation.class, OperationImpl.class);
resolver.addMapping(Parameter.class, ParameterImpl.class);
resolver.addMapping(PathItem.class, PathItemImpl.class);
resolver.addMapping(Paths.class, PathsImpl.class);
resolver.addMapping(Reference.class, ReferenceImpl.class);
resolver.addMapping(RequestBody.class, RequestBodyImpl.class);
resolver.addMapping(Schema.class, SchemaImpl.class);
resolver.addMapping(Scopes.class, ScopesImpl.class);
resolver.addMapping(SecurityRequirement.class, SecurityRequirementImpl.class);
resolver.addMapping(SecurityScheme.class, SecuritySchemeImpl.class);
resolver.addMapping(Server.class, ServerImpl.class);
resolver.addMapping(ServerVariable.class, ServerVariableImpl.class);
resolver.addMapping(ServerVariables.class, ServerVariablesImpl.class);
resolver.addMapping(Tag.class, TagImpl.class);
resolver.addMapping(XML.class, XMLImpl.class);
ApiBindings.get().forEach((k, v) -> resolver.addMapping(Class.class.cast(k), v));

final SimpleModule module = new SimpleModule();
module.setAbstractTypes(resolver);
@@ -29,6 +29,10 @@ public class APIResponsesImpl extends LinkedHashMap<String, APIResponse> impleme

private APIResponse _default;

public APIResponses addAPIResponse(final String name, final APIResponse item) {
return addApiResponse(name, item);
}

@Override
public APIResponses addApiResponse(final String name, final APIResponse item) {
this.put(name, item);
@@ -250,6 +250,7 @@ public Style getStyle() {
}

@Override
@JsonbTypeAdapter(Serializers.HeaderStyleSerializer.class)
public void setStyle(final Style _style) {
this._style = _style;
}
@@ -20,9 +20,11 @@
import java.util.Map;

import javax.enterprise.inject.Vetoed;
import javax.json.bind.annotation.JsonbTypeAdapter;
import javax.json.bind.annotation.JsonbTypeDeserializer;

import org.apache.geronimo.microprofile.openapi.impl.model.codec.Deserializers;
import org.apache.geronimo.microprofile.openapi.impl.model.codec.Serializers;
import org.eclipse.microprofile.openapi.models.Extensible;
import org.eclipse.microprofile.openapi.models.examples.Example;
import org.eclipse.microprofile.openapi.models.media.Encoding;
@@ -42,6 +44,7 @@ public class MediaTypeImpl implements MediaType {
@JsonbTypeDeserializer(Deserializers.MapExamplesDeserializer.class)
private Map<String, Example> _examples;

@JsonbTypeAdapter(Serializers.SchemaTypeSerializer.class)
private Schema _schema;

@Override
@@ -219,6 +219,7 @@ public In getIn() {
}

@Override
@JsonbTypeAdapter(Serializers.InSerializer.class)
public void setIn(final In _in) {
this._in = _in;
}
@@ -302,6 +303,7 @@ public Style getStyle() {
}

@Override
@JsonbTypeAdapter(Serializers.ParameterStyleSerializer.class)
public void setStyle(final Style _style) {
this._style = _style;
}
@@ -701,6 +701,7 @@ public SchemaType getType() {
}

@Override
@JsonbTypeAdapter(Serializers.SchemaTypeSerializer.class)
public void setType(final SchemaType _type) {
this._type = _type;
}
@@ -49,4 +49,8 @@ public Scopes addScope(final String name, final String item) {
this.put(name, item);
return this;
}

public Scopes addString(final String name, final String item) {
return addScope(name, item);
}
}
@@ -120,6 +120,7 @@ public In getIn() {
}

@Override
@JsonbTypeAdapter(Serializers.SecuritySchemeInSerializer.class)
public void setIn(final In _in) {
this._in = _in;
}
@@ -203,6 +204,7 @@ public Type getType() {
}

@Override
@JsonbTypeAdapter(Serializers.SecuritySchemeTypeSerializer.class)
public void setType(final Type _type) {
this._type = _type;
}
@@ -19,6 +19,7 @@
import static java.util.Locale.ROOT;

import java.math.BigDecimal;
import java.util.stream.Stream;

import javax.enterprise.inject.Vetoed;
import javax.json.bind.adapter.JsonbAdapter;
@@ -68,7 +69,13 @@ public String adaptToJson(final E obj) {

@Override
public E adaptFromJson(final String obj) {
return Enum.valueOf(type, obj.toUpperCase(ROOT));
try {
return Enum.valueOf(type, obj.toUpperCase(ROOT));
} catch (final IllegalArgumentException iae) {
return Stream.of(type.getEnumConstants())
.filter(it -> it.toString().equals(obj)).findFirst()
.orElseThrow(() -> iae);
}
}
}

@@ -352,6 +352,21 @@ private Operation buildOperation(final OpenAPI api, final AnnotatedMethodElement
produces.filter(it -> !it.isEmpty()).map(it -> it.iterator().next()).orElse(null), p,
api.getComponents(), ofNullable(p.getAnnotation(RequestBody.class))
.orElseGet(() -> m.getAnnotation(RequestBody.class)))));
if (operation.getResponses() == null) {
final APIResponsesImpl responses = new APIResponsesImpl();
operation.responses(responses);
final boolean normalResponse = Stream.of(m.getParameters()).noneMatch(it -> it.isAnnotationPresent(Suspended.class));
final ContentImpl content = new ContentImpl();
if (normalResponse) {
final MediaType impl = new MediaTypeImpl();
impl.setSchema(schemaProcessor.mapSchemaFromClass(api.getComponents(), m.getReturnType()));
produces.orElseGet(() -> singletonList("*/*")).forEach(key -> content.put(key, impl));
}
responses.put(
m.getReturnType() == void.class || m.getReturnType() == Void.class && normalResponse ?
"204" : "200",
new APIResponseImpl().content(content));
}
return operation;
}

@@ -386,7 +401,7 @@ private Stream<Tag> mapTagsAnnotationToTags(Tags t) {
return Stream.concat(Stream.of(t.value()), Stream.of(t.refs()).map(TagAnnotation::new));
}

private Optional<List<String>> findProduces(AnnotatedMethodElement m) {
private Optional<List<String>> findProduces(final AnnotatedMethodElement m) {
return ofNullable(ofNullable(m.getAnnotation(Produces.class))
.orElseGet(() -> m.getDeclaringClass().getAnnotation(Produces.class)))
.map(p -> Stream.of(p.value()).collect(toList()));
@@ -105,7 +105,14 @@
<dependency>
<groupId>org.apache.johnzon</groupId>
<artifactId>johnzon-jsonb</artifactId>
<version>1.1.7</version>
<version>${johnzon.version}</version>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

0 comments on commit 8ddd685

Please sign in to comment.