Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FISH-1312 MP OpenAPI hidden attribute is ignored #5342

Merged
merged 1 commit into from
Jul 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2018-2020] Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) [2018-2021] Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
Expand Down Expand Up @@ -110,7 +110,10 @@ public static Header createInstance(AnnotationModel annotation, ApiContext conte
from.setExplode(annotation.getValue("explode", Boolean.class));
AnnotationModel schemaAnnotation = annotation.getValue("schema", AnnotationModel.class);
if (schemaAnnotation != null) {
from.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
Boolean hidden = schemaAnnotation.getValue("hidden", Boolean.class);
if (hidden == null || !hidden) {
from.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
}
}
extractAnnotations(annotation, context, "examples", "name", ExampleImpl::createInstance, from::addExample);
from.setExample(annotation.getValue("example", Object.class));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2018-2020] Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) [2018-2021] Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
Expand Down Expand Up @@ -80,7 +80,10 @@ public static ContentImpl createInstance(AnnotationModel annotation, ApiContext
mediaType.setExample(annotation.getValue("example", String.class));
AnnotationModel schemaAnnotation = annotation.getValue("schema", AnnotationModel.class);
if (schemaAnnotation != null) {
mediaType.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
Boolean hidden = schemaAnnotation.getValue("hidden", Boolean.class);
if (hidden == null || !hidden) {
mediaType.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
}
}
extractAnnotations(annotation, context, "encoding", "name", EncodingImpl::createInstance, mediaType::addEncoding);
return from;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2018-2020] Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) [2018-2021] Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
Expand Down Expand Up @@ -103,7 +103,10 @@ public static Parameter createInstance(AnnotationModel annotation, ApiContext co
from.setAllowReserved(annotation.getValue("allowReserved", Boolean.class));
AnnotationModel schemaAnnotation = annotation.getValue("schema", AnnotationModel.class);
if (schemaAnnotation != null) {
from.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
Boolean hidden = schemaAnnotation.getValue("hidden", Boolean.class);
if (hidden == null || !hidden) {
from.setSchema(SchemaImpl.createInstance(schemaAnnotation, context));
}
}
extractAnnotations(annotation, context, "examples", "name", ExampleImpl::createInstance, from::addExample);
from.setExample(annotation.getValue("example", Object.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,15 @@ public void visitCookieParam(AnnotationModel param, AnnotatedElement element, Ap
}

private static void addParameter(AnnotatedElement element, ApiContext context, String name, In in, Boolean required) {
Boolean hidden = false;
AnnotationModel paramAnnotation = element.getAnnotation(org.eclipse.microprofile.openapi.annotations.parameters.Parameter.class.getName());
if (paramAnnotation != null) {
hidden = paramAnnotation.getValue("hidden", Boolean.class);
}
if (hidden != null && hidden) {
return;
}

Parameter newParameter = new ParameterImpl();
newParameter.setName(name);
newParameter.setIn(in);
Expand Down Expand Up @@ -569,7 +578,16 @@ private Schema visitSchemaClass(

for (FieldModel field : clazz.getFields()) {
final String fieldName = field.getName();
if (!field.isTransient() && !fieldName.startsWith("this$")) {
Boolean hidden = false;
AnnotationModel fieldSchemaAnnotation = field
.getAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class.getName());
if (fieldSchemaAnnotation != null) {
hidden = fieldSchemaAnnotation.getValue("hidden", Boolean.class);
}

if (!Boolean.TRUE.equals(hidden)
&& !field.isTransient()
&& !fieldName.startsWith("this$")) {
final Schema existingProperty = schema.getProperties().get(fieldName);
final Schema newProperty = createSchema(null, context, field, clazz, parameterizedInterfaces);
if (existingProperty != null) {
Expand Down Expand Up @@ -634,38 +652,40 @@ private void visitSchemaField(AnnotationModel schemaAnnotation, FieldModel field
private void visitSchemaFieldOrMethod(AnnotationModel schemaAnnotation, AnnotatedElement fieldOrMethod,
ExtensibleType<?> declaringType, String typeName, ApiContext context) {
assert (fieldOrMethod instanceof FieldModel) || (fieldOrMethod instanceof MethodModel);
Boolean hidden = schemaAnnotation.getValue("hidden", Boolean.class);
if (hidden == null || !hidden) {
// Get the schema object name
String schemaName = ModelUtils.getSchemaName(context, fieldOrMethod);
SchemaImpl schema = SchemaImpl.createInstance(schemaAnnotation, context);

// Get the parent schema object name
String parentName = null;
AnnotationModel classSchemaAnnotation = context.getAnnotationInfo(declaringType)
.getAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class);
if (classSchemaAnnotation != null) {
parentName = classSchemaAnnotation.getValue("name", String.class);
}
if (parentName == null || parentName.isEmpty()) {
parentName = declaringType.getSimpleName();
}

// Get the schema object name
String schemaName = ModelUtils.getSchemaName(context, fieldOrMethod);
SchemaImpl schema = SchemaImpl.createInstance(schemaAnnotation, context);

// Get the parent schema object name
String parentName = null;
AnnotationModel classSchemaAnnotation = context.getAnnotationInfo(declaringType)
.getAnnotation(org.eclipse.microprofile.openapi.annotations.media.Schema.class);
if (classSchemaAnnotation != null) {
parentName = classSchemaAnnotation.getValue("name", String.class);
}
if (parentName == null || parentName.isEmpty()) {
parentName = declaringType.getSimpleName();
}
// Get or create the parent schema object
final Components components = context.getApi().getComponents();
Schema parentSchema = components.getSchemas().getOrDefault(parentName, new SchemaImpl());
components.addSchema(parentName, parentSchema);

// Get or create the parent schema object
final Components components = context.getApi().getComponents();
Schema parentSchema = components.getSchemas().getOrDefault(parentName, new SchemaImpl());
components.addSchema(parentName, parentSchema);
Schema property = parentSchema.getProperties().getOrDefault(schemaName, new SchemaImpl());
parentSchema.addProperty(schemaName, property);
if (schema.isRequired()) {
parentSchema.addRequired(schemaName);
}

Schema property = parentSchema.getProperties().getOrDefault(schemaName, new SchemaImpl());
parentSchema.addProperty(schemaName, property);
if (schema.isRequired()) {
parentSchema.addRequired(schemaName);
}
if (property.getRef() == null) {
property.setType(ModelUtils.getSchemaType(typeName, context));
}

if (property.getRef() == null) {
property.setType(ModelUtils.getSchemaType(typeName, context));
SchemaImpl.merge(schema, property, true, context);
}

SchemaImpl.merge(schema, property, true, context);
}

private static void visitSchemaParameter(AnnotationModel schemaAnnotation, org.glassfish.hk2.classmodel.reflect.Parameter parameter, ApiContext context) {
Expand Down Expand Up @@ -940,6 +960,10 @@ public void visitParameters(AnnotationModel annotation, AnnotatedElement element
@Override
public void visitParameter(AnnotationModel annotation, AnnotatedElement element, ApiContext context) {
Parameter matchedParam = null;
Boolean hidden = annotation.getValue("hidden", Boolean.class);
if (hidden != null && hidden) {
return;
}
Parameter parameter = ParameterImpl.createInstance(annotation, context);

if (element instanceof org.glassfish.hk2.classmodel.reflect.Parameter) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) [2019] Payara Foundation and/or its affiliates. All rights reserved.
* Copyright (c) [2019-2021] Payara Foundation and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
Expand Down Expand Up @@ -39,7 +39,9 @@
*/
package fish.payara.microprofile.openapi.test.app.application;

import com.fasterxml.jackson.databind.JsonNode;
import fish.payara.microprofile.openapi.test.app.OpenApiApplicationTest;
import fish.payara.microprofile.openapi.test.util.JsonUtils;
import static fish.payara.microprofile.openapi.test.util.JsonUtils.path;
import javax.json.Json;
import javax.json.JsonObject;
Expand All @@ -51,17 +53,75 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.media.Schema;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
import org.eclipse.microprofile.openapi.annotations.media.Content;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.junit.Test;

/**
* Test to verify that using {@code hidden} does not cause errors as suggested by PAYARA-3259.
* Test to verify that using {@code hidden} does not cause errors as suggested
* by PAYARA-3259.
*/
@Path("/example")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class HiddenTest extends OpenApiApplicationTest {

@Schema
public class User {

private Long id;

private String email;

@Schema(hidden = true)
private String passwordHash;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getPasswordHash() {
return passwordHash;
}

public void setPasswordHash(String passwordHash) {
this.passwordHash = passwordHash;
}
}

class Teacher extends User {

private int secretPin;

public int getSecretPin() {
return secretPin;
}

public void setSecretPin(int secretPin) {
this.secretPin = secretPin;
}



}

@GET
@Path("/{name}")
@Operation(hidden = true)
Expand All @@ -72,8 +132,65 @@ public Response hello(@PathParam("name") String name) {
return Response.ok(message).build();
}

@GET
@Path("/{userId}")
@Produces(MediaType.APPLICATION_JSON)
@APIResponse(
responseCode = "400",
description = "NotFound",
content = @Content(
schema = @Schema(implementation = User.class)
)
)
@APIResponse(
responseCode = "900",
description = "Invalid",
content = @Content(
schema = @Schema(implementation = User.class, hidden = true)
)
)
public User sayHello(
@Parameter(
name = "userId",
description = "ID of user",
required = true
)
@PathParam("userId") Long userId,
@Parameter(
name = "password",
description = "Password of user",
required = true,
hidden = true
)
@PathParam("password") String password) {
return new User();
}

@Test
public void hiddenPropertyDoesNotCauseErrors() {
public void hiddenOperationDoesNotCauseErrors() {
assertNotNull(path(getOpenAPIJson(), "paths./test/example/{name}"));
}

@Test
public void hiddenSchemaPropertyDoesNotCauseErrors() {
System.out.println(getOpenAPIJson());
JsonNode properties = JsonUtils.path(getOpenAPIJson(), "components.schemas.User.properties");
assertEquals("number", properties.get("id").get("type").textValue());
assertEquals("string", properties.get("email").get("type").textValue());
assertNull(properties.get("passwordHash"));
}

@Test
public void hiddenSchemaDoesNotCauseErrors() {
JsonNode responses = JsonUtils.path(getOpenAPIJson(), "paths./test/example/{userId}.get.responses");
assertNotNull(responses.get("400").get("content").get("application/json").get("schema"));
assertNull(responses.get("900").get("content").get("application/json").get("schema"));
}


@Test
public void hiddenParamDoesNotCauseErrors() {
JsonNode parameters = JsonUtils.path(getOpenAPIJson(), "paths./test/example/{userId}.get.parameters");
assertEquals(1, parameters.size());
}
}