From fe8f096616e0cd3b75630ac3d94b807fa7de6510 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 May 2022 13:52:47 +0200 Subject: [PATCH 01/75] OpenAPI working VERY incomplete implementation --- core/pom.xml | 12 +- .../core/endpoint/admin/AdminHandler.java | 6 + .../core/endpoint/admin/RestInfoEndpoint.java | 26 ++ .../generator/AbstractEndpointGenerator.java | 194 ++++++++++++++ .../mesh/generator/OpenAPIv3Generator.java | 243 ++++++++++++++++++ .../gentics/mesh/generator/RAMLGenerator.java | 174 +------------ 6 files changed, 482 insertions(+), 173 deletions(-) create mode 100644 core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java create mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java diff --git a/core/pom.xml b/core/pom.xml index feaaf03665..b768de9a84 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -177,8 +177,18 @@ com.gentics.mesh mesh-service-aws-s3-storage - + + io.swagger.core.v3 + swagger-core + 2.2.0 + + + org.reflections + reflections + + + diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 54457c1959..24d00ff97d 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -30,6 +30,7 @@ import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.generator.OpenAPIv3Generator; import com.gentics.mesh.generator.RAMLGenerator; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.router.RouterStorageImpl; @@ -224,6 +225,11 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { return info; } + public void handleOpenAPIv3(InternalActionContext ac, String format) { + OpenAPIv3Generator generator = new OpenAPIv3Generator(); + generator.generate(ac, format); + } + /** * Generate and return the RAML of the server. * diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index 6ad3f0045f..104ada0d2a 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -65,6 +65,32 @@ public void registerEndPoints() { adminHandler.handleRAML(ac); }, false); + secure("/openapi.yaml"); + InternalEndpointRoute openapiYml = createRoute(); + openapiYml.path("/openapi.yaml"); + openapiYml.method(GET); + openapiYml.description("Endpoint which provides a OpenAPIv3 YAML document for all registed endpoints."); + openapiYml.displayName("OpenAPI YAML specification"); + openapiYml.exampleResponse(OK, "Not yet specified"); + openapiYml.produces(APPLICATION_YAML); + openapiYml.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + adminHandler.handleOpenAPIv3(ac, "yaml"); + }, false); + + secure("/openapi.json"); + InternalEndpointRoute openapiJson = createRoute(); + openapiJson.path("/openapi.json"); + openapiJson.method(GET); + openapiJson.description("Endpoint which provides a OpenAPIv3 JSON document for all registed endpoints."); + openapiJson.displayName("OpenAPI JSON specification"); + openapiJson.exampleResponse(OK, "Not yet specified"); + openapiJson.produces(APPLICATION_JSON); + openapiJson.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + adminHandler.handleOpenAPIv3(ac, "json"); + }, false); + secure("/"); InternalEndpointRoute infoEndpoint = createRoute(); infoEndpoint.path("/"); diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java new file mode 100644 index 0000000000..3d4b2bb553 --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -0,0 +1,194 @@ +package com.gentics.mesh.generator; + +import static org.mockito.Mockito.mock; + +import java.io.File; +import java.io.IOException; + +import org.mockito.Mockito; + +import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; +import com.gentics.mesh.core.endpoint.admin.HealthEndpoint; +import com.gentics.mesh.core.endpoint.admin.RestInfoEndpoint; +import com.gentics.mesh.core.endpoint.auth.AuthenticationEndpoint; +import com.gentics.mesh.core.endpoint.branch.BranchEndpoint; +import com.gentics.mesh.core.endpoint.eventbus.EventbusEndpoint; +import com.gentics.mesh.core.endpoint.group.GroupEndpoint; +import com.gentics.mesh.core.endpoint.microschema.MicroschemaEndpoint; +import com.gentics.mesh.core.endpoint.microschema.ProjectMicroschemaEndpoint; +import com.gentics.mesh.core.endpoint.navroot.NavRootEndpoint; +import com.gentics.mesh.core.endpoint.node.NodeEndpoint; +import com.gentics.mesh.core.endpoint.project.ProjectEndpoint; +import com.gentics.mesh.core.endpoint.project.ProjectInfoEndpoint; +import com.gentics.mesh.core.endpoint.role.RoleEndpoint; +import com.gentics.mesh.core.endpoint.schema.ProjectSchemaEndpoint; +import com.gentics.mesh.core.endpoint.schema.SchemaEndpoint; +import com.gentics.mesh.core.endpoint.tagfamily.TagFamilyEndpoint; +import com.gentics.mesh.core.endpoint.user.UserEndpoint; +import com.gentics.mesh.core.endpoint.utility.UtilityEndpoint; +import com.gentics.mesh.core.endpoint.webroot.WebRootEndpoint; +import com.gentics.mesh.core.endpoint.webrootfield.WebRootFieldEndpoint; +import com.gentics.mesh.graphql.GraphQLEndpoint; +import com.gentics.mesh.router.APIRouterImpl; +import com.gentics.mesh.router.RootRouterImpl; +import com.gentics.mesh.router.RouterStorageImpl; +import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import com.gentics.mesh.search.ProjectRawSearchEndpointImpl; +import com.gentics.mesh.search.ProjectSearchEndpointImpl; +import com.gentics.mesh.search.RawSearchEndpointImpl; +import com.gentics.mesh.search.SearchEndpointImpl; + +import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; + +public abstract class AbstractEndpointGenerator extends AbstractGenerator { + + public AbstractEndpointGenerator() { + } + + public AbstractEndpointGenerator(File outputFolder, boolean cleanup) throws IOException { + super(outputFolder, cleanup); + } + + public AbstractEndpointGenerator(File outputFolder) throws IOException { + super(outputFolder); + } + + /** + * Add all project verticles to the list resources. + * + * @param consumer + * @throws IOException + * @throws Exception + */ + protected void addProjectEndpoints(T consumer) throws IOException { + NodeEndpoint nodeEndpoint = Mockito.spy(new NodeEndpoint()); + initEndpoint(nodeEndpoint); + String projectBasePath = "/{project}"; + addEndpoints(projectBasePath, consumer, nodeEndpoint); + + TagFamilyEndpoint tagFamilyEndpoint = Mockito.spy(new TagFamilyEndpoint()); + initEndpoint(tagFamilyEndpoint); + addEndpoints(projectBasePath, consumer, tagFamilyEndpoint); + + NavRootEndpoint navEndpoint = Mockito.spy(new NavRootEndpoint()); + initEndpoint(navEndpoint); + addEndpoints(projectBasePath, consumer, navEndpoint); + + WebRootEndpoint webEndpoint = Mockito.spy(new WebRootEndpoint()); + initEndpoint(webEndpoint); + addEndpoints(projectBasePath, consumer, webEndpoint); + + WebRootFieldEndpoint webFieldEndpoint = Mockito.spy(new WebRootFieldEndpoint()); + initEndpoint(webFieldEndpoint); + addEndpoints(projectBasePath, consumer, webFieldEndpoint); + + BranchEndpoint branchEndpoint = Mockito.spy(new BranchEndpoint()); + initEndpoint(branchEndpoint); + addEndpoints(projectBasePath, consumer, branchEndpoint); + + GraphQLEndpoint graphqlEndpoint = Mockito.spy(new GraphQLEndpoint()); + initEndpoint(graphqlEndpoint); + addEndpoints(projectBasePath, consumer, graphqlEndpoint); + + ProjectSearchEndpointImpl projectSearchEndpoint = Mockito.spy(new ProjectSearchEndpointImpl()); + initEndpoint(projectSearchEndpoint); + addEndpoints(projectBasePath, consumer, projectSearchEndpoint); + + ProjectRawSearchEndpointImpl projectRawSearchEndpoint = Mockito.spy(new ProjectRawSearchEndpointImpl()); + initEndpoint(projectRawSearchEndpoint); + addEndpoints(projectBasePath, consumer, projectRawSearchEndpoint); + + ProjectSchemaEndpoint projectSchemaEndpoint = Mockito.spy(new ProjectSchemaEndpoint()); + initEndpoint(projectSchemaEndpoint); + addEndpoints(projectBasePath, consumer, projectSchemaEndpoint); + + ProjectMicroschemaEndpoint projectMicroschemaEndpoint = Mockito.spy(new ProjectMicroschemaEndpoint()); + initEndpoint(projectMicroschemaEndpoint); + addEndpoints(projectBasePath, consumer, projectMicroschemaEndpoint); + } + + /** + * Add all core verticles to the map of RAML resources. + * + * @param consumer + * @throws IOException + * @throws Exception + */ + protected void addCoreEndpoints(T consumer) throws IOException { + String coreBasePath = ""; + UserEndpoint userEndpoint = Mockito.spy(new UserEndpoint()); + initEndpoint(userEndpoint); + addEndpoints(coreBasePath, consumer, userEndpoint); + + RoleEndpoint roleEndpoint = Mockito.spy(new RoleEndpoint()); + initEndpoint(roleEndpoint); + addEndpoints(coreBasePath, consumer, roleEndpoint); + + GroupEndpoint groupEndpoint = Mockito.spy(new GroupEndpoint()); + initEndpoint(groupEndpoint); + addEndpoints(coreBasePath, consumer, groupEndpoint); + + ProjectEndpoint projectEndpoint = Mockito.spy(new ProjectEndpoint()); + initEndpoint(projectEndpoint); + addEndpoints(coreBasePath, consumer, projectEndpoint); + + SchemaEndpoint schemaEndpoint = Mockito.spy(new SchemaEndpoint()); + initEndpoint(schemaEndpoint); + addEndpoints(coreBasePath, consumer, schemaEndpoint); + + MicroschemaEndpoint microschemaEndpoint = Mockito.spy(new MicroschemaEndpoint()); + initEndpoint(microschemaEndpoint); + addEndpoints(coreBasePath, consumer, microschemaEndpoint); + + AdminEndpoint adminEndpoint = Mockito.spy(new AdminEndpoint()); + initEndpoint(adminEndpoint); + addEndpoints(coreBasePath, consumer, adminEndpoint); + + HealthEndpoint healthEndpoint = Mockito.spy(new HealthEndpoint()); + initEndpoint(healthEndpoint); + addEndpoints(coreBasePath, consumer, healthEndpoint); + + SearchEndpointImpl searchEndpoint = Mockito.spy(new SearchEndpointImpl()); + initEndpoint(searchEndpoint); + addEndpoints(coreBasePath, consumer, searchEndpoint); + + RawSearchEndpointImpl rawSearchEndpoint = Mockito.spy(new RawSearchEndpointImpl()); + initEndpoint(rawSearchEndpoint); + addEndpoints(coreBasePath, consumer, rawSearchEndpoint); + + UtilityEndpoint utilityEndpoint = Mockito.spy(new UtilityEndpoint()); + initEndpoint(utilityEndpoint); + addEndpoints(coreBasePath, consumer, utilityEndpoint); + + AuthenticationEndpoint authEndpoint = Mockito.spy(new AuthenticationEndpoint()); + initEndpoint(authEndpoint); + addEndpoints(coreBasePath, consumer, authEndpoint); + + EventbusEndpoint eventbusEndpoint = Mockito.spy(new EventbusEndpoint()); + initEndpoint(eventbusEndpoint); + addEndpoints(coreBasePath, consumer, eventbusEndpoint); + + RouterStorageImpl rs = Mockito.mock(RouterStorageImpl.class); + RootRouterImpl rootRouter = Mockito.mock(RootRouterImpl.class); + Mockito.when(rs.root()).thenReturn(rootRouter); + APIRouterImpl apiRouter = Mockito.mock(APIRouterImpl.class); + Mockito.when(rootRouter.apiRouter()).thenReturn(apiRouter); + RestInfoEndpoint infoEndpoint = Mockito.spy(new RestInfoEndpoint("")); + infoEndpoint.init(null, rs); + initEndpoint(infoEndpoint); + addEndpoints(coreBasePath, consumer, infoEndpoint); + + ProjectInfoEndpoint projectInfoEndpoint = Mockito.spy(new ProjectInfoEndpoint()); + initEndpoint(projectInfoEndpoint); + addEndpoints(coreBasePath, consumer, projectInfoEndpoint); + } + + protected abstract void addEndpoints(String coreBasePath, T consumer, AbstractInternalEndpoint projectInfoEndpoint) throws IOException; + + protected void initEndpoint(AbstractInternalEndpoint endpoint) { + Vertx vertx = mock(Vertx.class); + Mockito.when(endpoint.getRouter()).thenReturn(Router.router(vertx)); + endpoint.registerEndPoints(); + } +} diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java new file mode 100644 index 0000000000..eb77a381e1 --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -0,0 +1,243 @@ +package com.gentics.mesh.generator; + +import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; +import static com.gentics.mesh.core.rest.error.Errors.error; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static org.apache.commons.lang3.StringUtils.isEmpty; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; +import org.raml.model.parameter.AbstractParam; +import org.reflections.Reflections; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.context.InternalActionContext; +import com.gentics.mesh.rest.InternalEndpointRoute; +import com.gentics.mesh.router.route.AbstractInternalEndpoint; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.servers.Server; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; +import io.vertx.core.logging.Logger; +import io.vertx.core.logging.LoggerFactory; + +public class OpenAPIv3Generator extends AbstractEndpointGenerator { + + private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); + + public OpenAPIv3Generator() { + } + + public OpenAPIv3Generator(File outputFolder, boolean cleanup) throws IOException { + super(outputFolder, cleanup); + } + + public OpenAPIv3Generator(File outputFolder) throws IOException { + super(outputFolder); + } + + @SuppressWarnings("rawtypes") + public void generate(InternalActionContext ac, String format) { + log.info("Starting OpenAPIv3 generation..."); + OpenAPI openApi = new OpenAPI(); + Info info = new Info(); + Server server = new Server(); + info.setTitle("Gentics Mesh REST API"); + info.setVersion(MeshVersion.getBuildInfo().getVersion()); + //server.setUrl("http://localhost:8080/api/v" + CURRENT_API_VERSION); + openApi.servers(Collections.singletonList(server)); + openApi.setInfo(info); + Components components = new Components(); + Reflections reflections = new Reflections("com.gentics.mesh"); + // TODO parse RestModels into components + openApi.setComponents(components); + try { + addCoreEndpoints(openApi); + addProjectEndpoints(openApi); + } catch (IOException e) { + throw new RuntimeException("Could not add all verticles to raml generator", e); + } + + String formatted; + switch (format) { + case "yaml": + try { + formatted = Yaml.pretty().writeValueAsString(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException("Could not generate YAML", e); + } + break; + case "json": + formatted = Json.pretty(openApi); + break; + default: + throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); + } + ac.send(formatted, OK, APPLICATION_JSON); + } + + @Override + protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle) + throws IOException { + // Check whether the resource was already added. Maybe we just need to extend it + Paths paths = consumer.getPaths(); + if (paths == null) { + paths = new Paths(); + consumer.setPaths(paths); + } + for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { + String fullPath = "api/v" + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + endpoint.getRamlPath(); + if (isEmpty(verticle.getBasePath())) { + fullPath = "api/v" + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); + } + PathItem pathItem = paths.get(fullPath); + if (pathItem == null) { + pathItem = new PathItem(); + pathItem.setSummary(endpoint.getDisplayName()); + pathItem.setDescription(endpoint.getDescription()); + consumer.path(fullPath, pathItem); + } + Operation operation = new Operation(); + HttpMethod method = endpoint.getMethod(); + if (method == null) { + method = HttpMethod.GET; + } + switch (method) { + case DELETE: + pathItem.setDelete(operation); + break; + case GET: + pathItem.setGet(operation); + break; + case HEAD: + pathItem.setHead(operation); + break; + case OPTIONS: + pathItem.setOptions(operation); + break; + case PATCH: + pathItem.setPatch(operation); + break; + case POST: + pathItem.setPost(operation); + break; + case PUT: + pathItem.setPut(operation); + break; + case TRACE: + pathItem.setTrace(operation); + break; + default: + break; + } + List> params = List.of( + endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), + endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH)) + ); + operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); + RequestBody requestBody = new RequestBody(); + Content content = new Content(); + if (endpoint.getExampleRequestMap() != null) { + endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> { + MediaType mediaType = new MediaType(); + mediaType.setExample(e.getValue().getExample()); + JsonObject jschema = new JsonObject(e.getValue().getSchema()); + Schema schema = new Schema<>(); + schema.setType(jschema.getString("type", "string")); + schema.set$id(jschema.getString("id")); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(e.getKey(), mediaType); + }).filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + } + requestBody.setContent(content); + operation.setRequestBody(requestBody); + //action.setIs(Arrays.asList(endpoint.getTraits())); + } + } + + + private final Parameter parameter(String name, AbstractParam param, InParameter inType) { + Schema schema; + switch (param.getType()) { + case BOOLEAN: + schema = new Schema(); + break; + case DATE: + schema = new Schema(); + break; + case FILE: + schema = new Schema(); + break; + case INTEGER: + schema = new Schema(); + break; + case NUMBER: + schema = new Schema(); + break; + case STRING: + schema = new Schema(); + break; + default: + return null; + } + schema.setMinimum(param.getMinimum()); + schema.setMaximum(param.getMaximum()); + schema.setMinLength(param.getMinLength()); + schema.setMaxLength(param.getMaxLength()); + schema.setDefault(param.getDefaultValue()); + schema.setEnum(param.getEnumeration()); + schema.setExample(param.getExample()); + schema.setPattern(param.getPattern()); + schema.setDescription(param.getDescription()); + Parameter p = new Parameter(); + p.setRequired(param.isRequired()); + p.setDescription(param.getDescription()); + p.setIn(inType.value); + p.setSchema(schema); + p.setName(name); + return p; + } + + private enum InParameter { + PATH("path"), + QUERY("query"), + HEADER("header"), + COOKIE("cookie"); + + private final String value; + + private InParameter(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } +} diff --git a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java index 55bbae1c79..2d9432dad6 100644 --- a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java @@ -2,7 +2,6 @@ import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; import static org.apache.commons.lang3.StringUtils.isEmpty; -import static org.mockito.Mockito.mock; import java.io.File; import java.io.IOException; @@ -13,7 +12,6 @@ import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; -import org.mockito.Mockito; import org.raml.emitter.RamlEmitter; import org.raml.model.Action; import org.raml.model.ActionType; @@ -24,48 +22,17 @@ import org.raml.model.Response; import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; -import com.gentics.mesh.core.endpoint.admin.HealthEndpoint; -import com.gentics.mesh.core.endpoint.admin.RestInfoEndpoint; -import com.gentics.mesh.core.endpoint.auth.AuthenticationEndpoint; -import com.gentics.mesh.core.endpoint.branch.BranchEndpoint; -import com.gentics.mesh.core.endpoint.eventbus.EventbusEndpoint; -import com.gentics.mesh.core.endpoint.group.GroupEndpoint; -import com.gentics.mesh.core.endpoint.microschema.MicroschemaEndpoint; -import com.gentics.mesh.core.endpoint.microschema.ProjectMicroschemaEndpoint; -import com.gentics.mesh.core.endpoint.navroot.NavRootEndpoint; -import com.gentics.mesh.core.endpoint.node.NodeEndpoint; -import com.gentics.mesh.core.endpoint.project.ProjectEndpoint; -import com.gentics.mesh.core.endpoint.project.ProjectInfoEndpoint; -import com.gentics.mesh.core.endpoint.role.RoleEndpoint; -import com.gentics.mesh.core.endpoint.schema.ProjectSchemaEndpoint; -import com.gentics.mesh.core.endpoint.schema.SchemaEndpoint; -import com.gentics.mesh.core.endpoint.tagfamily.TagFamilyEndpoint; -import com.gentics.mesh.core.endpoint.user.UserEndpoint; -import com.gentics.mesh.core.endpoint.utility.UtilityEndpoint; -import com.gentics.mesh.core.endpoint.webroot.WebRootEndpoint; -import com.gentics.mesh.core.endpoint.webrootfield.WebRootFieldEndpoint; -import com.gentics.mesh.graphql.GraphQLEndpoint; import com.gentics.mesh.rest.InternalEndpointRoute; -import com.gentics.mesh.router.APIRouterImpl; -import com.gentics.mesh.router.RootRouterImpl; -import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.route.AbstractInternalEndpoint; -import com.gentics.mesh.search.ProjectRawSearchEndpointImpl; -import com.gentics.mesh.search.ProjectSearchEndpointImpl; -import com.gentics.mesh.search.RawSearchEndpointImpl; -import com.gentics.mesh.search.SearchEndpointImpl; -import io.vertx.core.Vertx; import io.vertx.core.http.HttpMethod; import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; -import io.vertx.ext.web.Router; /** * Generator for RAML documentation. The generation mocks all endpoint classes and extracts the routes from these endpoints in order to generate the RAML. */ -public class RAMLGenerator extends AbstractGenerator { +public class RAMLGenerator extends AbstractEndpointGenerator> { private static final Logger log = LoggerFactory.getLogger(RAMLGenerator.class); @@ -137,7 +104,7 @@ public String generate() { * Endpoint which provides endpoints * @throws IOException */ - private void addEndpoints(String basePath, Map resources, AbstractInternalEndpoint verticle) throws IOException { + protected void addEndpoints(String basePath, Map resources, AbstractInternalEndpoint verticle) throws IOException { String ramlPath = basePath + "/" + verticle.getBasePath(); // Check whether the resource was already added. Maybe we just need to extend it @@ -273,143 +240,6 @@ private ActionType getActionType(HttpMethod method) { return ActionType.valueOf(method.name()); } - private void initEndpoint(AbstractInternalEndpoint endpoint) { - Vertx vertx = mock(Vertx.class); - Mockito.when(endpoint.getRouter()).thenReturn(Router.router(vertx)); - endpoint.registerEndPoints(); - } - - /** - * Add all project verticles to the list resources. - * - * @param resources - * @throws IOException - * @throws Exception - */ - private void addProjectEndpoints(Map resources) throws IOException { - NodeEndpoint nodeEndpoint = Mockito.spy(new NodeEndpoint()); - initEndpoint(nodeEndpoint); - String projectBasePath = "/{project}"; - addEndpoints(projectBasePath, resources, nodeEndpoint); - - TagFamilyEndpoint tagFamilyEndpoint = Mockito.spy(new TagFamilyEndpoint()); - initEndpoint(tagFamilyEndpoint); - addEndpoints(projectBasePath, resources, tagFamilyEndpoint); - - NavRootEndpoint navEndpoint = Mockito.spy(new NavRootEndpoint()); - initEndpoint(navEndpoint); - addEndpoints(projectBasePath, resources, navEndpoint); - - WebRootEndpoint webEndpoint = Mockito.spy(new WebRootEndpoint()); - initEndpoint(webEndpoint); - addEndpoints(projectBasePath, resources, webEndpoint); - - WebRootFieldEndpoint webFieldEndpoint = Mockito.spy(new WebRootFieldEndpoint()); - initEndpoint(webFieldEndpoint); - addEndpoints(projectBasePath, resources, webFieldEndpoint); - - BranchEndpoint branchEndpoint = Mockito.spy(new BranchEndpoint()); - initEndpoint(branchEndpoint); - addEndpoints(projectBasePath, resources, branchEndpoint); - - GraphQLEndpoint graphqlEndpoint = Mockito.spy(new GraphQLEndpoint()); - initEndpoint(graphqlEndpoint); - addEndpoints(projectBasePath, resources, graphqlEndpoint); - - ProjectSearchEndpointImpl projectSearchEndpoint = Mockito.spy(new ProjectSearchEndpointImpl()); - initEndpoint(projectSearchEndpoint); - addEndpoints(projectBasePath, resources, projectSearchEndpoint); - - ProjectRawSearchEndpointImpl projectRawSearchEndpoint = Mockito.spy(new ProjectRawSearchEndpointImpl()); - initEndpoint(projectRawSearchEndpoint); - addEndpoints(projectBasePath, resources, projectRawSearchEndpoint); - - ProjectSchemaEndpoint projectSchemaEndpoint = Mockito.spy(new ProjectSchemaEndpoint()); - initEndpoint(projectSchemaEndpoint); - addEndpoints(projectBasePath, resources, projectSchemaEndpoint); - - ProjectMicroschemaEndpoint projectMicroschemaEndpoint = Mockito.spy(new ProjectMicroschemaEndpoint()); - initEndpoint(projectMicroschemaEndpoint); - addEndpoints(projectBasePath, resources, projectMicroschemaEndpoint); - - } - - /** - * Add all core verticles to the map of RAML resources. - * - * @param resources - * @throws IOException - * @throws Exception - */ - private void addCoreEndpoints(Map resources) throws IOException { - String coreBasePath = ""; - UserEndpoint userEndpoint = Mockito.spy(new UserEndpoint()); - initEndpoint(userEndpoint); - addEndpoints(coreBasePath, resources, userEndpoint); - - RoleEndpoint roleEndpoint = Mockito.spy(new RoleEndpoint()); - initEndpoint(roleEndpoint); - addEndpoints(coreBasePath, resources, roleEndpoint); - - GroupEndpoint groupEndpoint = Mockito.spy(new GroupEndpoint()); - initEndpoint(groupEndpoint); - addEndpoints(coreBasePath, resources, groupEndpoint); - - ProjectEndpoint projectEndpoint = Mockito.spy(new ProjectEndpoint()); - initEndpoint(projectEndpoint); - addEndpoints(coreBasePath, resources, projectEndpoint); - - SchemaEndpoint schemaEndpoint = Mockito.spy(new SchemaEndpoint()); - initEndpoint(schemaEndpoint); - addEndpoints(coreBasePath, resources, schemaEndpoint); - - MicroschemaEndpoint microschemaEndpoint = Mockito.spy(new MicroschemaEndpoint()); - initEndpoint(microschemaEndpoint); - addEndpoints(coreBasePath, resources, microschemaEndpoint); - - AdminEndpoint adminEndpoint = Mockito.spy(new AdminEndpoint()); - initEndpoint(adminEndpoint); - addEndpoints(coreBasePath, resources, adminEndpoint); - - HealthEndpoint healthEndpoint = Mockito.spy(new HealthEndpoint()); - initEndpoint(healthEndpoint); - addEndpoints(coreBasePath, resources, healthEndpoint); - - SearchEndpointImpl searchEndpoint = Mockito.spy(new SearchEndpointImpl()); - initEndpoint(searchEndpoint); - addEndpoints(coreBasePath, resources, searchEndpoint); - - RawSearchEndpointImpl rawSearchEndpoint = Mockito.spy(new RawSearchEndpointImpl()); - initEndpoint(rawSearchEndpoint); - addEndpoints(coreBasePath, resources, rawSearchEndpoint); - - UtilityEndpoint utilityEndpoint = Mockito.spy(new UtilityEndpoint()); - initEndpoint(utilityEndpoint); - addEndpoints(coreBasePath, resources, utilityEndpoint); - - AuthenticationEndpoint authEndpoint = Mockito.spy(new AuthenticationEndpoint()); - initEndpoint(authEndpoint); - addEndpoints(coreBasePath, resources, authEndpoint); - - EventbusEndpoint eventbusEndpoint = Mockito.spy(new EventbusEndpoint()); - initEndpoint(eventbusEndpoint); - addEndpoints(coreBasePath, resources, eventbusEndpoint); - - RouterStorageImpl rs = Mockito.mock(RouterStorageImpl.class); - RootRouterImpl rootRouter = Mockito.mock(RootRouterImpl.class); - Mockito.when(rs.root()).thenReturn(rootRouter); - APIRouterImpl apiRouter = Mockito.mock(APIRouterImpl.class); - Mockito.when(rootRouter.apiRouter()).thenReturn(apiRouter); - RestInfoEndpoint infoEndpoint = Mockito.spy(new RestInfoEndpoint("")); - infoEndpoint.init(null, rs); - initEndpoint(infoEndpoint); - addEndpoints(coreBasePath, resources, infoEndpoint); - - ProjectInfoEndpoint projectInfoEndpoint = Mockito.spy(new ProjectInfoEndpoint()); - initEndpoint(projectInfoEndpoint); - addEndpoints(coreBasePath, resources, projectInfoEndpoint); - - } /** * Start the generator. From d469f492cf89286a7a07c099c7faadd517cf003e Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 May 2022 17:31:43 +0200 Subject: [PATCH 02/75] Type schema fill --- .../mesh/generator/OpenAPIv3Generator.java | 144 ++++++++++++++---- 1 file changed, 117 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index eb77a381e1..56ec1b0033 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -9,20 +9,32 @@ import java.io.File; import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; +import org.apache.commons.lang.StringUtils; import org.raml.model.parameter.AbstractParam; import org.reflections.Reflections; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.context.InternalActionContext; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; @@ -68,12 +80,52 @@ public void generate(InternalActionContext ac, String format) { Server server = new Server(); info.setTitle("Gentics Mesh REST API"); info.setVersion(MeshVersion.getBuildInfo().getVersion()); - //server.setUrl("http://localhost:8080/api/v" + CURRENT_API_VERSION); + // server.setUrl("http://localhost:8080/api/v" + CURRENT_API_VERSION); openApi.servers(Collections.singletonList(server)); - openApi.setInfo(info); - Components components = new Components(); - Reflections reflections = new Reflections("com.gentics.mesh"); - // TODO parse RestModels into components + openApi.setInfo(info); + Components components = new Components(); + Reflections reflections = new Reflections("com.gentics.mesh"); + Map schemas = reflections.getSubTypesOf(RestModel.class).stream().map(cls -> { + Schema schema = new Schema(); + List> fieldStreams = new ArrayList<>(); + Class tclass = cls; + Type[] genericTypes = ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : null; + while (tclass != null) { + fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); + tclass = tclass.getSuperclass(); + } + Map properties = fieldStreams.stream().flatMap(Function.identity()).map(f -> { + String name = f.getName(); + Schema fieldSchema = new Schema(); + JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); + if (description != null) { + fieldSchema.setDescription(description.value()); + } + JsonProperty property = f.getAnnotation(JsonProperty.class); + if (property != null) { + if (StringUtils.isNotBlank(property.defaultValue())) { + fieldSchema.setDefault(property.defaultValue()); + } + if (property.required()) { + schema.addRequiredItem(name); + } + } + Class t = f.getType(); + JsonDeserialize jdes = f.getAnnotation(JsonDeserialize.class); + if (jdes != null && jdes.as() != null) { + t = jdes.as(); + fieldSchema.setType("object"); + fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + } else { + Type[] args = ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : genericTypes; + fillType(t, fieldSchema, args); + } + return new UnmodifiableMapEntry<>(name, fieldSchema); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + schema.setProperties(properties); + return new UnmodifiableMapEntry<>(cls.getSimpleName(), schema); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + components.setSchemas(schemas); openApi.setComponents(components); try { addCoreEndpoints(openApi); @@ -96,7 +148,7 @@ public void generate(InternalActionContext ac, String format) { break; default: throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } + } ac.send(formatted, OK, APPLICATION_JSON); } @@ -110,7 +162,8 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE consumer.setPaths(paths); } for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { - String fullPath = "api/v" + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + endpoint.getRamlPath(); + String fullPath = "api/v" + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + + endpoint.getRamlPath(); if (isEmpty(verticle.getBasePath())) { fullPath = "api/v" + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); } @@ -152,34 +205,74 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE pathItem.setTrace(operation); break; default: - break; + break; } List> params = List.of( endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), - endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH)) - ); + endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); RequestBody requestBody = new RequestBody(); Content content = new Content(); if (endpoint.getExampleRequestMap() != null) { endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - MediaType mediaType = new MediaType(); - mediaType.setExample(e.getValue().getExample()); - JsonObject jschema = new JsonObject(e.getValue().getSchema()); - Schema schema = new Schema<>(); - schema.setType(jschema.getString("type", "string")); - schema.set$id(jschema.getString("id")); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(e.getKey(), mediaType); - }).filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + .map(e -> { + if (e.getValue().getSchema() == null) { + return null; + } + MediaType mediaType = new MediaType(); + mediaType.setExample(e.getValue().getExample()); + JsonObject jschema = new JsonObject(e.getValue().getSchema()); + Schema schema = new Schema<>(); + schema.setType(jschema.getString("type", "string")); + schema.set$id(jschema.getString("id")); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(e.getKey(), mediaType); + }).filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); } requestBody.setContent(content); operation.setRequestBody(requestBody); - //action.setIs(Arrays.asList(endpoint.getTraits())); + // action.setIs(Arrays.asList(endpoint.getTraits())); } } + private void fillType(Class t, Schema fieldSchema, Type[] typeArgs) { + if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { + if (int.class.isAssignableFrom(t) || Integer.class.isAssignableFrom(t)) { + fieldSchema.setType("integer"); + fieldSchema.setFormat("int32"); + } else if (boolean.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { + fieldSchema.setType("boolean"); + } else if (float.class.isAssignableFrom(t) || Float.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); + fieldSchema.setFormat("float"); + } else if (long.class.isAssignableFrom(t) || Long.class.isAssignableFrom(t)) { + fieldSchema.setType("integer"); + fieldSchema.setFormat("int64"); + } else if (double.class.isAssignableFrom(t) || Double.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); + fieldSchema.setFormat("double"); + } else { + fieldSchema.setType("object"); + } + } else if (CharSequence.class.isAssignableFrom(t)) { + fieldSchema.setType("string"); + } else if (t.isArray() || List.class.isAssignableFrom(t)) { + fieldSchema.setType("array"); + Schema itemSchema = new Schema(); + if (t.isArray()) { + fillType(Array.newInstance(t, 0).getClass(), itemSchema, typeArgs); + } else if (typeArgs.length > 0 && Class.class.isInstance(typeArgs[0])) { + Class itemClass = Class.class.cast(typeArgs[0]); + fillType(itemClass, itemSchema, null); + } else { + System.err.println(t + " / " + Arrays.toString(typeArgs)); + } + fieldSchema.setItems(itemSchema); + } else { + fieldSchema.setType("object"); + fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + } + } private final Parameter parameter(String name, AbstractParam param, InParameter inType) { Schema schema; @@ -203,7 +296,7 @@ private final Parameter parameter(String name, AbstractParam param, InParameter schema = new Schema(); break; default: - return null; + return null; } schema.setMinimum(param.getMinimum()); schema.setMaximum(param.getMaximum()); @@ -224,11 +317,8 @@ private final Parameter parameter(String name, AbstractParam param, InParameter } private enum InParameter { - PATH("path"), - QUERY("query"), - HEADER("header"), - COOKIE("cookie"); - + PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); + private final String value; private InParameter(String value) { From a1b103552260f0bb56b118fe2f2982d63fa69e4e Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 18 May 2022 18:22:47 +0200 Subject: [PATCH 03/75] Components filled --- .../core/endpoint/admin/AdminHandler.java | 8 +- .../mesh/generator/OpenAPIv3Generator.java | 193 ++++++++++++------ .../endpoint/admin/OrientDBAdminHandler.java | 5 +- .../com/gentics/mesh/util/JsonUtilTest.java | 4 +- 4 files changed, 145 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 24d00ff97d..5c511b40eb 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -17,6 +17,7 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.endpoint.handler.AbstractHandler; import com.gentics.mesh.core.rest.MeshServerInfoModel; @@ -73,10 +74,12 @@ public abstract class AdminHandler extends AbstractHandler { private final CacheRegistry cacheRegistry; + protected final ClusterManager clusterManager; + protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry) { + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ClusterManager clusterManager) { this.vertx = vertx; this.db = db; this.routerStorage = routerStorage; @@ -89,6 +92,7 @@ protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage this.writeLock = writeLock; this.consistencyCheckHandler = consistencyCheckHandler; this.cacheRegistry = cacheRegistry; + this.clusterManager = clusterManager; } /** @@ -226,7 +230,7 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { } public void handleOpenAPIv3(InternalActionContext ac, String format) { - OpenAPIv3Generator generator = new OpenAPIv3Generator(); + OpenAPIv3Generator generator = new OpenAPIv3Generator(clusterManager.getHazelcast()); generator.generate(ac, format); } diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 56ec1b0033..36a45896ba 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -11,8 +11,10 @@ import java.io.IOException; import java.lang.reflect.Array; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -37,6 +39,7 @@ import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import com.hazelcast.core.HazelcastInstance; import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.Yaml; @@ -57,22 +60,32 @@ import io.vertx.core.logging.Logger; import io.vertx.core.logging.LoggerFactory; +/** + * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. + * + * @author plyhun + * + */ public class OpenAPIv3Generator extends AbstractEndpointGenerator { private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); - public OpenAPIv3Generator() { + private final HazelcastInstance hz; + + public OpenAPIv3Generator(HazelcastInstance hz) { + this.hz = hz; } - public OpenAPIv3Generator(File outputFolder, boolean cleanup) throws IOException { + public OpenAPIv3Generator(HazelcastInstance hz, File outputFolder, boolean cleanup) throws IOException { super(outputFolder, cleanup); + this.hz = hz; } - public OpenAPIv3Generator(File outputFolder) throws IOException { + public OpenAPIv3Generator(HazelcastInstance hz, File outputFolder) throws IOException { super(outputFolder); + this.hz = hz; } - @SuppressWarnings("rawtypes") public void generate(InternalActionContext ac, String format) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); @@ -80,22 +93,65 @@ public void generate(InternalActionContext ac, String format) { Server server = new Server(); info.setTitle("Gentics Mesh REST API"); info.setVersion(MeshVersion.getBuildInfo().getVersion()); - // server.setUrl("http://localhost:8080/api/v" + CURRENT_API_VERSION); + if (hz != null) { + hz.getCluster().getMembers().stream().forEach(m -> server.setUrl(m.getAddress().toString())); + } openApi.servers(Collections.singletonList(server)); - openApi.setInfo(info); + openApi.setInfo(info); + try { + addComponents(openApi); + addCoreEndpoints(openApi); + addProjectEndpoints(openApi); + } catch (IOException e) { + throw new RuntimeException("Could not add all verticles to raml generator", e); + } + + String formatted; + switch (format) { + case "yaml": + try { + formatted = Yaml.pretty().writeValueAsString(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException("Could not generate YAML", e); + } + break; + case "json": + formatted = Json.pretty(openApi); + break; + default: + throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); + } + ac.send(formatted, OK, APPLICATION_JSON); + } + + @SuppressWarnings("rawtypes") + private void addComponents(OpenAPI openApi) { Components components = new Components(); Reflections reflections = new Reflections("com.gentics.mesh"); - Map schemas = reflections.getSubTypesOf(RestModel.class).stream().map(cls -> { + final List generics = new ArrayList<>(); + openApi.setComponents(components); + reflections.getSubTypesOf(RestModel.class).stream().map(cls -> { + if (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || cls.getTypeParameters().length > 0) { + return null; + } Schema schema = new Schema(); List> fieldStreams = new ArrayList<>(); Class tclass = cls; - Type[] genericTypes = ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : null; + log.debug("Class: " + tclass.getCanonicalName()); + generics.clear(); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); + System.err.println(Arrays.toString(generics.toArray())); while (tclass != null) { fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); tclass = tclass.getSuperclass(); } + if (generics.size() > 0) { + log.debug(" - Generics: " + Arrays.toString(generics.toArray())); + } Map properties = fieldStreams.stream().flatMap(Function.identity()).map(f -> { String name = f.getName(); + log.debug(" - Field: " + f); Schema fieldSchema = new Schema(); JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); if (description != null) { @@ -117,39 +173,17 @@ public void generate(InternalActionContext ac, String format) { fieldSchema.setType("object"); fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); } else { - Type[] args = ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : genericTypes; - fillType(t, fieldSchema, args); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : new Type[0])); + if (generics.size() > 0) { + log.debug(" - Generics: " + Arrays.toString(generics.toArray())); + } + fillType(components, t, fieldSchema, generics); } return new UnmodifiableMapEntry<>(name, fieldSchema); }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); schema.setProperties(properties); return new UnmodifiableMapEntry<>(cls.getSimpleName(), schema); - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - components.setSchemas(schemas); - openApi.setComponents(components); - try { - addCoreEndpoints(openApi); - addProjectEndpoints(openApi); - } catch (IOException e) { - throw new RuntimeException("Could not add all verticles to raml generator", e); - } - - String formatted; - switch (format) { - case "yaml": - try { - formatted = Yaml.pretty().writeValueAsString(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException("Could not generate YAML", e); - } - break; - case "json": - formatted = Json.pretty(openApi); - break; - default: - throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } - ac.send(formatted, OK, APPLICATION_JSON); + }).filter(Objects::nonNull).forEach(e -> components.addSchemas(e.getKey(), e.getValue())); } @Override @@ -169,6 +203,7 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE } PathItem pathItem = paths.get(fullPath); if (pathItem == null) { + log.debug("Path " + fullPath); pathItem = new PathItem(); pathItem.setSummary(endpoint.getDisplayName()); pathItem.setDescription(endpoint.getDescription()); @@ -207,35 +242,47 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE default: break; } + if (fullPath.indexOf("binary") > 0) { + System.out.println(fullPath); + } List> params = List.of( endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); - RequestBody requestBody = new RequestBody(); - Content content = new Content(); if (endpoint.getExampleRequestMap() != null) { + RequestBody requestBody = new RequestBody(); + Content content = new Content(); endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) .map(e -> { - if (e.getValue().getSchema() == null) { - return null; - } MediaType mediaType = new MediaType(); mediaType.setExample(e.getValue().getExample()); - JsonObject jschema = new JsonObject(e.getValue().getSchema()); - Schema schema = new Schema<>(); - schema.setType(jschema.getString("type", "string")); - schema.set$id(jschema.getString("id")); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(e.getKey(), mediaType); + if (e.getValue().getFormParameters() != null) { + Map props = e.getValue().getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) + .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); + Schema schema = new Schema<>(); + schema.setType("object"); + schema.setProperties(props); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry("multipart/form-data", mediaType); + } else if (e.getValue().getSchema() != null) { + JsonObject jschema = new JsonObject(e.getValue().getSchema()); + Schema schema = new Schema<>(); + schema.setType(jschema.getString("type", "string")); + schema.set$id(jschema.getString("id")); + schema.set$ref("#/components/schemas/" + endpoint.getExampleRequestClass().getSimpleName()); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(e.getKey(), mediaType); + } else { return null; } }).filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + requestBody.setContent(content); + operation.setRequestBody(requestBody); } - requestBody.setContent(content); - operation.setRequestBody(requestBody); // action.setIs(Arrays.asList(endpoint.getTraits())); } } - private void fillType(Class t, Schema fieldSchema, Type[] typeArgs) { + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void fillType(Components components, Class t, Schema fieldSchema, List generics) { if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { if (int.class.isAssignableFrom(t) || Integer.class.isAssignableFrom(t)) { fieldSchema.setType("integer"); @@ -251,6 +298,8 @@ private void fillType(Class t, Schema fieldSchema, Type[] typeArgs) { } else if (double.class.isAssignableFrom(t) || Double.class.isAssignableFrom(t)) { fieldSchema.setType("number"); fieldSchema.setFormat("double"); + } else if (BigDecimal.class.isAssignableFrom(t) || Number.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); } else { fieldSchema.setType("object"); } @@ -260,43 +309,65 @@ private void fillType(Class t, Schema fieldSchema, Type[] typeArgs) { fieldSchema.setType("array"); Schema itemSchema = new Schema(); if (t.isArray()) { - fillType(Array.newInstance(t, 0).getClass(), itemSchema, typeArgs); - } else if (typeArgs.length > 0 && Class.class.isInstance(typeArgs[0])) { - Class itemClass = Class.class.cast(typeArgs[0]); - fillType(itemClass, itemSchema, null); + fillType(components, Array.newInstance(t, 0).getClass(), itemSchema, generics); + } else if (generics.size() > 0 && Class.class.isInstance(generics.get(0))) { + Class itemClass = Class.class.cast(generics.get(0)); + fillType(components, itemClass, itemSchema, null); } else { - System.err.println(t + " / " + Arrays.toString(typeArgs)); + System.err.println(t + " / " + Arrays.toString(generics.toArray())); } fieldSchema.setItems(itemSchema); +// } else if (t.isEnum()) { +// fieldSchema.setType("string"); +// fieldSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); } else { fieldSchema.setType("object"); fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + if (t.isEnum()) { + Schema enumSchema = new Schema(); + enumSchema.setType("string"); + enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); + components.addSchemas(t.getSimpleName(), enumSchema); + } } } + @SuppressWarnings("rawtypes") private final Parameter parameter(String name, AbstractParam param, InParameter inType) { Schema schema; switch (param.getType()) { case BOOLEAN: schema = new Schema(); + schema.setType("boolean"); break; case DATE: schema = new Schema(); + schema.setType("integer"); + schema.setFormat("int64"); break; case FILE: schema = new Schema(); + schema.setType("string"); + schema.setFormat("binary"); break; case INTEGER: schema = new Schema(); + schema.setType("integer"); + schema.setFormat("int32"); break; case NUMBER: - schema = new Schema(); + schema = new Schema(); + schema.setType("number"); + schema.setFormat("double"); break; case STRING: schema = new Schema(); + schema.setType("string"); break; default: - return null; + schema = new Schema(); + schema.setType("object"); + break; } schema.setMinimum(param.getMinimum()); schema.setMaximum(param.getMaximum()); @@ -304,15 +375,19 @@ private final Parameter parameter(String name, AbstractParam param, InParameter schema.setMaxLength(param.getMaxLength()); schema.setDefault(param.getDefaultValue()); schema.setEnum(param.getEnumeration()); - schema.setExample(param.getExample()); schema.setPattern(param.getPattern()); schema.setDescription(param.getDescription()); + if (StringUtils.isNotBlank(param.getExample())) { + schema.setExample(param.getExample()); + } Parameter p = new Parameter(); p.setRequired(param.isRequired()); p.setDescription(param.getDescription()); - p.setIn(inType.value); p.setSchema(schema); p.setName(name); + if (inType != null) { + p.setIn(inType.value); + } return p; } diff --git a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/endpoint/admin/OrientDBAdminHandler.java b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/endpoint/admin/OrientDBAdminHandler.java index 837b787442..41c8d59d00 100644 --- a/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/endpoint/admin/OrientDBAdminHandler.java +++ b/mdm/orientdb-wrapper/src/main/java/com/gentics/mesh/core/endpoint/admin/OrientDBAdminHandler.java @@ -31,6 +31,7 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.project.HibProject; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; @@ -56,9 +57,9 @@ public class OrientDBAdminHandler extends AdminHandler { public OrientDBAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry) { + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ClusterManager clusterManager) { super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, - consistencyCheckHandler, cacheRegistry); + consistencyCheckHandler, cacheRegistry, clusterManager); } @Override diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/util/JsonUtilTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/util/JsonUtilTest.java index 8c6baa83f2..7b24f6cc58 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/util/JsonUtilTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/util/JsonUtilTest.java @@ -19,7 +19,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.gentics.mesh.FieldUtil; -import com.gentics.mesh.core.rest.common.ListResponse; import com.gentics.mesh.core.rest.common.PermissionInfo; import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.graphql.GraphQLResponse; @@ -32,6 +31,7 @@ import com.gentics.mesh.core.rest.schema.impl.MicronodeFieldSchemaImpl; import com.gentics.mesh.core.rest.schema.impl.SchemaResponse; import com.gentics.mesh.core.rest.schema.impl.SchemaUpdateRequest; +import com.gentics.mesh.core.rest.user.UserListResponse; import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.example.GraphQLExamples; import com.gentics.mesh.json.JsonUtil; @@ -81,7 +81,7 @@ public void testCompareJson() { @Test public void testJsonList() { - ListResponse list = new ListResponse<>(); + UserListResponse list = new UserListResponse(); UserResponse user = new UserResponse(); list.getData().add(user); assertNotNull(list.toJson()); From 1d34be3ebaeca85e3d1fc3e8afded03b04383f48 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 23 May 2022 15:42:44 +0200 Subject: [PATCH 04/75] Security layer. Better type support. --- .../rest/impl/InternalEndpointRouteImpl.java | 46 +++++-- .../core/endpoint/admin/HealthEndpoint.java | 3 + .../core/endpoint/admin/RestInfoEndpoint.java | 1 + .../endpoint/auth/AuthenticationEndpoint.java | 1 + .../mesh/generator/OpenAPIv3Generator.java | 125 ++++++++++++++---- .../mesh/rest/InternalEndpointRoute.java | 29 ++++ 6 files changed, 172 insertions(+), 33 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index 240ca4b02e..d65a607c42 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -97,8 +98,11 @@ public class InternalEndpointRouteImpl implements InternalEndpointRoute { private Boolean mutating; + private boolean insecure = false; + /** - * Create a new endpoint wrapper using the provided router to create the wrapped route instance. + * Create a new endpoint wrapper using the provided router to create the wrapped + * route instance. * * @param router * @param localConfigApi @@ -187,7 +191,8 @@ public InternalEndpointRoute validate() { log.error("Endpoint {" + getRamlPath() + "} has no example response."); throw new RuntimeException("Endpoint {" + getRamlPath() + "} has no example responses."); } - if ((consumes.contains(APPLICATION_JSON) || consumes.contains(APPLICATION_JSON_UTF8)) && exampleRequestMap == null) { + if ((consumes.contains(APPLICATION_JSON) || consumes.contains(APPLICATION_JSON_UTF8)) + && exampleRequestMap == null) { log.error("Endpoint {" + getPath() + "} has no example request."); throw new RuntimeException("Endpoint has no example request."); } @@ -200,7 +205,8 @@ public InternalEndpointRoute validate() { List segments = getNamedSegments(); for (String segment : segments) { if (!getUriParameters().containsKey(segment)) { - throw new RuntimeException("Missing URI description for path {" + getRamlPath() + "} segment {" + segment + "}"); + throw new RuntimeException( + "Missing URI description for path {" + getRamlPath() + "} segment {" + segment + "}"); } } return this; @@ -294,7 +300,8 @@ public String getDisplayName() { } @Override - public InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description, String headerName, String example, String headerDescription) { + public InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description, String headerName, + String example, String headerDescription) { Response response = new Response(); response.setDescription(description); exampleResponses.put(status.code(), response); @@ -330,7 +337,11 @@ public InternalEndpointRoute exampleResponse(HttpResponseStatus status, Object m map.put("application/json", mimeType); } else { mimeType.setExample(model.toString()); - map.put("text/plain", mimeType); + if (model.getClass().getSimpleName().toLowerCase().startsWith("json")) { + map.put("application/json", mimeType); + } else { + map.put("text/plain", mimeType); + } } exampleResponses.put(status.code(), response); @@ -522,9 +533,7 @@ public Set getEvents() { @Override public boolean isMutating() { return Optional.ofNullable(mutating) - .orElse(Optional.ofNullable(getMethod()) - .map(mutatingMethods::contains) - .orElse(false)); + .orElse(Optional.ofNullable(getMethod()).map(mutatingMethods::contains).orElse(false)); } @Override @@ -537,4 +546,25 @@ public InternalEndpointRouteImpl setMutating(Boolean mutating) { public Route getRoute() { return route; } + + @Override + public boolean isInsecure() { + return insecure; + } + + @Override + public InternalEndpointRoute setInsecure(boolean insecure) { + this.insecure = insecure; + return this; + } + + @Override + public Set getProduces() { + return Collections.unmodifiableSet(produces); + } + + @Override + public Set getConsumes() { + return Collections.unmodifiableSet(consumes); + } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java index b5ce414126..3046301671 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java @@ -40,6 +40,7 @@ public String getDescription() { private void addLive() { InternalEndpointRoute deployEndpoint = createRoute(); + deployEndpoint.setInsecure(true); deployEndpoint.path("/live"); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is alive."); @@ -49,6 +50,7 @@ private void addLive() { private void addReady() { InternalEndpointRoute deployEndpoint = createRoute(); deployEndpoint.path("/ready"); + deployEndpoint.setInsecure(true); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is ready. Responds with 503 otherwise."); deployEndpoint.handler(rc -> monitoringCrudHandler.handleReady(rc)); @@ -57,6 +59,7 @@ private void addReady() { private void addWritable() { InternalEndpointRoute deployEndpoint = createRoute(); deployEndpoint.path("/writable"); + deployEndpoint.setInsecure(true); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is writable. Responds with 503 otherwise."); deployEndpoint.handler(rc -> monitoringCrudHandler.handleWritable(rc)); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index 104ada0d2a..08c9dd1e59 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -93,6 +93,7 @@ public void registerEndPoints() { secure("/"); InternalEndpointRoute infoEndpoint = createRoute(); + infoEndpoint.setInsecure(true); infoEndpoint.path("/"); infoEndpoint.description("Endpoint which returns version information"); infoEndpoint.displayName("Version Information"); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/auth/AuthenticationEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/auth/AuthenticationEndpoint.java index 790970ef12..099a29f91c 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/auth/AuthenticationEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/auth/AuthenticationEndpoint.java @@ -67,6 +67,7 @@ public void registerEndPoints() { loginEndpoint.path("/login"); loginEndpoint.method(POST); loginEndpoint.setMutating(false); + loginEndpoint.setInsecure(true); loginEndpoint.consumes(APPLICATION_JSON); loginEndpoint.produces(APPLICATION_JSON); loginEndpoint.description("Login via this dedicated login endpoint."); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 36a45896ba..9a03a6afca 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -2,7 +2,8 @@ import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; import static com.gentics.mesh.core.rest.error.Errors.error; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.apache.commons.lang3.StringUtils.isEmpty; @@ -27,6 +28,7 @@ import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; import org.apache.commons.lang.StringUtils; +import org.raml.model.MimeType; import org.raml.model.parameter.AbstractParam; import org.reflections.Reflections; @@ -54,6 +56,10 @@ import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.servers.Server; import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; @@ -99,6 +105,7 @@ public void generate(InternalActionContext ac, String format) { openApi.servers(Collections.singletonList(server)); openApi.setInfo(info); try { + addSecurity(openApi); addComponents(openApi); addCoreEndpoints(openApi); addProjectEndpoints(openApi); @@ -107,29 +114,56 @@ public void generate(InternalActionContext ac, String format) { } String formatted; + String mime; switch (format) { case "yaml": try { + mime = APPLICATION_YAML_UTF8; formatted = Yaml.pretty().writeValueAsString(openApi); } catch (JsonProcessingException e) { throw new RuntimeException("Could not generate YAML", e); } break; case "json": + mime = APPLICATION_JSON_UTF8; formatted = Json.pretty(openApi); break; default: throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); } - ac.send(formatted, OK, APPLICATION_JSON); + ac.send(formatted, OK, mime); + } + + private void addSecurity(OpenAPI openApi) { + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } + SecurityScheme securityBearerAuth = new SecurityScheme(); + securityBearerAuth.setScheme("bearer"); + securityBearerAuth.setType(SecurityScheme.Type.HTTP); + securityBearerAuth.setBearerFormat("JWT"); + components.addSecuritySchemes("bearerAuth", securityBearerAuth); + //TODO OAuth2 + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + openApi.addSecurityItem(reqBearerAuth); } @SuppressWarnings("rawtypes") private void addComponents(OpenAPI openApi) { - Components components = new Components(); + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } Reflections reflections = new Reflections("com.gentics.mesh"); final List generics = new ArrayList<>(); - openApi.setComponents(components); reflections.getSubTypesOf(RestModel.class).stream().map(cls -> { if (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || cls.getTypeParameters().length > 0) { return null; @@ -214,6 +248,10 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE if (method == null) { method = HttpMethod.GET; } + if (endpoint.isInsecure()) { + // Reset the default security requirements + operation.setSecurity(Collections.emptyList()); + } switch (method) { case DELETE: pathItem.setDelete(operation); @@ -253,34 +291,71 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE RequestBody requestBody = new RequestBody(); Content content = new Content(); endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - MediaType mediaType = new MediaType(); - mediaType.setExample(e.getValue().getExample()); - if (e.getValue().getFormParameters() != null) { - Map props = e.getValue().getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) - .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); - Schema schema = new Schema<>(); - schema.setType("object"); - schema.setProperties(props); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry("multipart/form-data", mediaType); - } else if (e.getValue().getSchema() != null) { - JsonObject jschema = new JsonObject(e.getValue().getSchema()); - Schema schema = new Schema<>(); - schema.setType(jschema.getString("type", "string")); - schema.set$id(jschema.getString("id")); - schema.set$ref("#/components/schemas/" + endpoint.getExampleRequestClass().getSimpleName()); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(e.getKey(), mediaType); - } else { return null; } - }).filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) + .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); requestBody.setContent(content); operation.setRequestBody(requestBody); + ApiResponses responses = new ApiResponses(); + endpoint.getProduces().stream().map(e -> { + ApiResponse response = new ApiResponse(); + Content responseBody = new Content(); + responseBody.addMediaType(e, new MediaType()); + response.setContent(responseBody); + return new UnmodifiableMapEntry("default", response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); + endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> { + ApiResponse response = new ApiResponse(); + if (e.getValue().getDescription().startsWith("Generated login token")) { + e.getValue().getHeaders(); + } + response.setDescription(e.getValue().getDescription()); + if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { + Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); + if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { + response.set$ref("#/components/schemas/" + ref.getSimpleName()); + } else { + Content responseBody = new Content(); + // response.setContent(responseBody); + e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) + .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) + .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); + response.setContent(responseBody); + } + } + return new UnmodifiableMapEntry(e.getKey(), response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); + operation.setResponses(responses); } // action.setIs(Arrays.asList(endpoint.getTraits())); } } + private Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { + MediaType mediaType = new MediaType(); + mediaType.setExample(mimeType.getExample()); + if (mimeType.getFormParameters() != null) { + Map props = mimeType.getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) + .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); + Schema schema = new Schema<>(); + schema.setType("object"); + schema.setProperties(props); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry("multipart/form-data", mediaType); + } else if (mimeType.getSchema() != null) { + JsonObject jschema = new JsonObject(mimeType.getSchema()); + Schema schema = new Schema<>(); + schema.setType(jschema.getString("type", "string")); + schema.set$id(jschema.getString("id")); + schema.set$ref("#/components/schemas/" + refClass.getSimpleName()); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(key, mediaType); + } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { + mediaType.setExample(mimeType.getExample()); + return new UnmodifiableMapEntry(key, mediaType); + } else { return null; } + } + @SuppressWarnings({ "rawtypes", "unchecked" }) private void fillType(Components components, Class t, Schema fieldSchema, List generics) { if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { diff --git a/mdm/common/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/common/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index fc3880cdc5..b452b6093a 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/common/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -3,6 +3,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.codehaus.jettison.json.JSONObject; import org.raml.model.MimeType; @@ -422,6 +423,21 @@ public interface InternalEndpointRoute extends Comparable */ InternalEndpointRoute setMutating(Boolean mutating); + /** + * If true, the endpoint can be used with no authentication. + * + * @return + */ + boolean isInsecure(); + + /** + * Set the endpoint to omit the secure token requirement. + * + * @param insecure + * @return + */ + InternalEndpointRoute setInsecure(boolean insecure); + /** * Return underlying route. * @@ -429,4 +445,17 @@ public interface InternalEndpointRoute extends Comparable */ Route getRoute(); + /** + * Return set of produced MIME types; + * + * @return + */ + Set getProduces(); + + /** + * Return set of accepted request body MIME types; + * + * @return + */ + Set getConsumes(); } From d81d41eef547aead9dc350685b2c69169d4eb569 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 20 Jul 2022 12:38:29 +0200 Subject: [PATCH 05/75] Off the System.outs --- .../com/gentics/mesh/generator/OpenAPIv3Generator.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 9a03a6afca..ad269c8691 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -174,7 +174,6 @@ private void addComponents(OpenAPI openApi) { log.debug("Class: " + tclass.getCanonicalName()); generics.clear(); generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); - System.err.println(Arrays.toString(generics.toArray())); while (tclass != null) { fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); @@ -280,9 +279,6 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE default: break; } - if (fullPath.indexOf("binary") > 0) { - System.out.println(fullPath); - } List> params = List.of( endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); @@ -389,7 +385,8 @@ private void fillType(Components components, Class t, Schema fieldSchema, Lis Class itemClass = Class.class.cast(generics.get(0)); fillType(components, itemClass, itemSchema, null); } else { - System.err.println(t + " / " + Arrays.toString(generics.toArray())); + // TODO + log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); } fieldSchema.setItems(itemSchema); // } else if (t.isEnum()) { From acf16f9bafd43fd98319d186144c22eaed2bad49 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 20 Jul 2022 12:41:00 +0200 Subject: [PATCH 06/75] Changelog entry --- CHANGELOG.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 1336c58cf4..fa1ef9fc8d 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -36,6 +36,8 @@ icon:check[] Cluster: The plugin initialization now waits not only for the write icon:check[] Core: Schema field names are now checked for uniqueness regardless of case sensitivity. This behavior was already enforced in the UI, and is now enforced in the REST API. The check is not performed for existing schemas. +icon:check[] Rest: New OpenAPI secure endpoints `/openapi.yaml` and `/openapi.json` have been added. + [[v1.8.6]] == 1.8.6 (06.07.2022) From 683380859dcfdbf9cf0893d3728fe0b1fe801554 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 7 Nov 2022 15:19:06 +0100 Subject: [PATCH 07/75] Build fixes --- .../mesh/generator/OpenAPIv3Generator.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index ad269c8691..e4efc46cad 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -251,29 +251,29 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE // Reset the default security requirements operation.setSecurity(Collections.emptyList()); } - switch (method) { - case DELETE: + switch (method.name()) { + case "DELETE": pathItem.setDelete(operation); break; - case GET: + case "GET": pathItem.setGet(operation); break; - case HEAD: + case "HEAD": pathItem.setHead(operation); break; - case OPTIONS: + case "OPTIONS": pathItem.setOptions(operation); break; - case PATCH: + case "PATCH": pathItem.setPatch(operation); break; - case POST: + case "POST": pathItem.setPost(operation); break; - case PUT: + case "PUT": pathItem.setPut(operation); break; - case TRACE: + case "TRACE": pathItem.setTrace(operation); break; default: From 38728dc6f7f572880bb3bb54b2af5688a33d6a6d Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 16 Nov 2022 19:40:49 +0100 Subject: [PATCH 08/75] Improved result --- .../endpoint/tagfamily/TagFamilyEndpoint.java | 8 +- .../generator/AbstractEndpointGenerator.java | 54 +++---- .../mesh/generator/OpenAPIv3Generator.java | 151 ++++++++++-------- .../gentics/mesh/generator/RAMLGenerator.java | 2 +- .../core/rest/common/NameUuidReference.java | 2 +- .../mesh/core/rest/common/PagingMetaInfo.java | 2 +- .../rest/navigation/NavigationElement.java | 3 +- .../rest/node/field/NodeFieldListItem.java | 4 +- .../rest/schema/BinaryExtractOptions.java | 4 +- .../mesh/core/rest/schema/FieldSchema.java | 3 +- .../rest/schema/S3BinaryExtractOptions.java | 4 +- .../mesh/core/rest/user/ExpandableNode.java | 4 +- .../mesh/core/rest/user/NodeReference.java | 3 +- .../mesh/parameter/image/ImageRect.java | 3 +- 14 files changed, 139 insertions(+), 108 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index 632bf81211..ce0a074147 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -195,8 +195,8 @@ private void addTagRolePermissionHandler() { InternalEndpointRoute grantPermissionsEndpoint = createRoute(); grantPermissionsEndpoint.path("/:tagFamilyUuid/tags/:tagUuid/rolePermissions"); - readPermissionsEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - readPermissionsEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + grantPermissionsEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + grantPermissionsEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); grantPermissionsEndpoint.method(POST); grantPermissionsEndpoint.description("Grant permissions on the tag to multiple roles."); grantPermissionsEndpoint.consumes(APPLICATION_JSON); @@ -213,8 +213,8 @@ private void addTagRolePermissionHandler() { InternalEndpointRoute revokePermissionsEndpoint = createRoute(); revokePermissionsEndpoint.path("/:tagFamilyUuid/tags/:tagUuid/rolePermissions"); - readPermissionsEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - readPermissionsEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + revokePermissionsEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + revokePermissionsEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); revokePermissionsEndpoint.method(DELETE); revokePermissionsEndpoint.description("Revoke permissions on the tag from multiple roles."); revokePermissionsEndpoint.consumes(APPLICATION_JSON); diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java index 3d4b2bb553..ce7b569c32 100644 --- a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -65,47 +65,47 @@ protected void addProjectEndpoints(T consumer) throws IOException { NodeEndpoint nodeEndpoint = Mockito.spy(new NodeEndpoint()); initEndpoint(nodeEndpoint); String projectBasePath = "/{project}"; - addEndpoints(projectBasePath, consumer, nodeEndpoint); + addEndpoints(projectBasePath, consumer, nodeEndpoint, true); TagFamilyEndpoint tagFamilyEndpoint = Mockito.spy(new TagFamilyEndpoint()); initEndpoint(tagFamilyEndpoint); - addEndpoints(projectBasePath, consumer, tagFamilyEndpoint); + addEndpoints(projectBasePath, consumer, tagFamilyEndpoint, true); NavRootEndpoint navEndpoint = Mockito.spy(new NavRootEndpoint()); initEndpoint(navEndpoint); - addEndpoints(projectBasePath, consumer, navEndpoint); + addEndpoints(projectBasePath, consumer, navEndpoint, true); WebRootEndpoint webEndpoint = Mockito.spy(new WebRootEndpoint()); initEndpoint(webEndpoint); - addEndpoints(projectBasePath, consumer, webEndpoint); + addEndpoints(projectBasePath, consumer, webEndpoint, true); WebRootFieldEndpoint webFieldEndpoint = Mockito.spy(new WebRootFieldEndpoint()); initEndpoint(webFieldEndpoint); - addEndpoints(projectBasePath, consumer, webFieldEndpoint); + addEndpoints(projectBasePath, consumer, webFieldEndpoint, true); BranchEndpoint branchEndpoint = Mockito.spy(new BranchEndpoint()); initEndpoint(branchEndpoint); - addEndpoints(projectBasePath, consumer, branchEndpoint); + addEndpoints(projectBasePath, consumer, branchEndpoint, true); GraphQLEndpoint graphqlEndpoint = Mockito.spy(new GraphQLEndpoint()); initEndpoint(graphqlEndpoint); - addEndpoints(projectBasePath, consumer, graphqlEndpoint); + addEndpoints(projectBasePath, consumer, graphqlEndpoint, true); ProjectSearchEndpointImpl projectSearchEndpoint = Mockito.spy(new ProjectSearchEndpointImpl()); initEndpoint(projectSearchEndpoint); - addEndpoints(projectBasePath, consumer, projectSearchEndpoint); + addEndpoints(projectBasePath, consumer, projectSearchEndpoint, true); ProjectRawSearchEndpointImpl projectRawSearchEndpoint = Mockito.spy(new ProjectRawSearchEndpointImpl()); initEndpoint(projectRawSearchEndpoint); - addEndpoints(projectBasePath, consumer, projectRawSearchEndpoint); + addEndpoints(projectBasePath, consumer, projectRawSearchEndpoint, true); ProjectSchemaEndpoint projectSchemaEndpoint = Mockito.spy(new ProjectSchemaEndpoint()); initEndpoint(projectSchemaEndpoint); - addEndpoints(projectBasePath, consumer, projectSchemaEndpoint); + addEndpoints(projectBasePath, consumer, projectSchemaEndpoint, true); ProjectMicroschemaEndpoint projectMicroschemaEndpoint = Mockito.spy(new ProjectMicroschemaEndpoint()); initEndpoint(projectMicroschemaEndpoint); - addEndpoints(projectBasePath, consumer, projectMicroschemaEndpoint); + addEndpoints(projectBasePath, consumer, projectMicroschemaEndpoint, true); } /** @@ -119,55 +119,55 @@ protected void addCoreEndpoints(T consumer) throws IOException { String coreBasePath = ""; UserEndpoint userEndpoint = Mockito.spy(new UserEndpoint()); initEndpoint(userEndpoint); - addEndpoints(coreBasePath, consumer, userEndpoint); + addEndpoints(coreBasePath, consumer, userEndpoint, false); RoleEndpoint roleEndpoint = Mockito.spy(new RoleEndpoint()); initEndpoint(roleEndpoint); - addEndpoints(coreBasePath, consumer, roleEndpoint); + addEndpoints(coreBasePath, consumer, roleEndpoint, false); GroupEndpoint groupEndpoint = Mockito.spy(new GroupEndpoint()); initEndpoint(groupEndpoint); - addEndpoints(coreBasePath, consumer, groupEndpoint); + addEndpoints(coreBasePath, consumer, groupEndpoint, false); ProjectEndpoint projectEndpoint = Mockito.spy(new ProjectEndpoint()); initEndpoint(projectEndpoint); - addEndpoints(coreBasePath, consumer, projectEndpoint); + addEndpoints(coreBasePath, consumer, projectEndpoint, false); SchemaEndpoint schemaEndpoint = Mockito.spy(new SchemaEndpoint()); initEndpoint(schemaEndpoint); - addEndpoints(coreBasePath, consumer, schemaEndpoint); + addEndpoints(coreBasePath, consumer, schemaEndpoint, false); MicroschemaEndpoint microschemaEndpoint = Mockito.spy(new MicroschemaEndpoint()); initEndpoint(microschemaEndpoint); - addEndpoints(coreBasePath, consumer, microschemaEndpoint); + addEndpoints(coreBasePath, consumer, microschemaEndpoint, false); AdminEndpoint adminEndpoint = Mockito.spy(new AdminEndpoint()); initEndpoint(adminEndpoint); - addEndpoints(coreBasePath, consumer, adminEndpoint); + addEndpoints(coreBasePath, consumer, adminEndpoint, false); HealthEndpoint healthEndpoint = Mockito.spy(new HealthEndpoint()); initEndpoint(healthEndpoint); - addEndpoints(coreBasePath, consumer, healthEndpoint); + addEndpoints(coreBasePath, consumer, healthEndpoint, false); SearchEndpointImpl searchEndpoint = Mockito.spy(new SearchEndpointImpl()); initEndpoint(searchEndpoint); - addEndpoints(coreBasePath, consumer, searchEndpoint); + addEndpoints(coreBasePath, consumer, searchEndpoint, false); RawSearchEndpointImpl rawSearchEndpoint = Mockito.spy(new RawSearchEndpointImpl()); initEndpoint(rawSearchEndpoint); - addEndpoints(coreBasePath, consumer, rawSearchEndpoint); + addEndpoints(coreBasePath, consumer, rawSearchEndpoint, false); UtilityEndpoint utilityEndpoint = Mockito.spy(new UtilityEndpoint()); initEndpoint(utilityEndpoint); - addEndpoints(coreBasePath, consumer, utilityEndpoint); + addEndpoints(coreBasePath, consumer, utilityEndpoint, false); AuthenticationEndpoint authEndpoint = Mockito.spy(new AuthenticationEndpoint()); initEndpoint(authEndpoint); - addEndpoints(coreBasePath, consumer, authEndpoint); + addEndpoints(coreBasePath, consumer, authEndpoint, false); EventbusEndpoint eventbusEndpoint = Mockito.spy(new EventbusEndpoint()); initEndpoint(eventbusEndpoint); - addEndpoints(coreBasePath, consumer, eventbusEndpoint); + addEndpoints(coreBasePath, consumer, eventbusEndpoint, false); RouterStorageImpl rs = Mockito.mock(RouterStorageImpl.class); RootRouterImpl rootRouter = Mockito.mock(RootRouterImpl.class); @@ -177,14 +177,14 @@ protected void addCoreEndpoints(T consumer) throws IOException { RestInfoEndpoint infoEndpoint = Mockito.spy(new RestInfoEndpoint("")); infoEndpoint.init(null, rs); initEndpoint(infoEndpoint); - addEndpoints(coreBasePath, consumer, infoEndpoint); + addEndpoints(coreBasePath, consumer, infoEndpoint, false); ProjectInfoEndpoint projectInfoEndpoint = Mockito.spy(new ProjectInfoEndpoint()); initEndpoint(projectInfoEndpoint); - addEndpoints(coreBasePath, consumer, projectInfoEndpoint); + addEndpoints(coreBasePath, consumer, projectInfoEndpoint, false); } - protected abstract void addEndpoints(String coreBasePath, T consumer, AbstractInternalEndpoint projectInfoEndpoint) throws IOException; + protected abstract void addEndpoints(String coreBasePath, T consumer, AbstractInternalEndpoint projectInfoEndpoint, boolean isProject) throws IOException; protected void initEndpoint(AbstractInternalEndpoint endpoint) { Vertx vertx = mock(Vertx.class); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index e4efc46cad..2462ac4025 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -10,7 +10,6 @@ import java.io.File; import java.io.IOException; -import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; @@ -60,7 +59,6 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.servers.Server; import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; import io.vertx.core.logging.Logger; @@ -74,6 +72,8 @@ */ public class OpenAPIv3Generator extends AbstractEndpointGenerator { + private static final String API_VERSION_PATH_PREFIX = "/api/v"; + private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); private final HazelcastInstance hz; @@ -96,13 +96,13 @@ public void generate(InternalActionContext ac, String format) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); Info info = new Info(); - Server server = new Server(); info.setTitle("Gentics Mesh REST API"); info.setVersion(MeshVersion.getBuildInfo().getVersion()); - if (hz != null) { - hz.getCluster().getMembers().stream().forEach(m -> server.setUrl(m.getAddress().toString())); - } - openApi.servers(Collections.singletonList(server)); +// if (hz != null) { +// Server server = new Server(); +// hz.getCluster().getMembers().stream().forEach(m -> server.setUrl(m.getAddress().toString())); +// openApi.servers(Collections.singletonList(server)); +// } openApi.setInfo(info); try { addSecurity(openApi); @@ -163,26 +163,32 @@ private void addComponents(OpenAPI openApi) { components = openApi.getComponents(); } Reflections reflections = new Reflections("com.gentics.mesh"); + reflections.getSubTypesOf(RestModel.class).stream().forEach(cls -> fillComponent(cls, components)); + } + + private void fillComponent(Class cls, Components components) { + Schema schema = new Schema(); + List> fieldStreams = new ArrayList<>(); + Class tclass = cls; + log.debug("Class: " + tclass.getCanonicalName()); final List generics = new ArrayList<>(); - reflections.getSubTypesOf(RestModel.class).stream().map(cls -> { - if (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || cls.getTypeParameters().length > 0) { - return null; - } - Schema schema = new Schema(); - List> fieldStreams = new ArrayList<>(); - Class tclass = cls; - log.debug("Class: " + tclass.getCanonicalName()); - generics.clear(); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); - while (tclass != null) { - fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); - tclass = tclass.getSuperclass(); - } - if (generics.size() > 0) { - log.debug(" - Generics: " + Arrays.toString(generics.toArray())); - } - Map properties = fieldStreams.stream().flatMap(Function.identity()).map(f -> { + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); + while (tclass != null) { + fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); + tclass = tclass.getSuperclass(); + } + if (generics.size() > 0) { + log.debug(" - Generics: " + Arrays.toString(generics.toArray())); + } + Map properties = fieldStreams.stream().flatMap(Function.identity()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).peek(f -> { + Class t = f.getType(); + if (!RestModel.class.isAssignableFrom(t) && !t.isPrimitive() && !t.getCanonicalName().startsWith("java.lang")) { + fillComponent(t, components); + } + }) + .map(f -> { String name = f.getName(); log.debug(" - Field: " + f); Schema fieldSchema = new Schema(); @@ -195,6 +201,9 @@ private void addComponents(OpenAPI openApi) { if (StringUtils.isNotBlank(property.defaultValue())) { fieldSchema.setDefault(property.defaultValue()); } + if (StringUtils.isNotBlank(property.value())) { + name = property.value(); + } if (property.required()) { schema.addRequiredItem(name); } @@ -214,13 +223,12 @@ private void addComponents(OpenAPI openApi) { } return new UnmodifiableMapEntry<>(name, fieldSchema); }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - schema.setProperties(properties); - return new UnmodifiableMapEntry<>(cls.getSimpleName(), schema); - }).filter(Objects::nonNull).forEach(e -> components.addSchemas(e.getKey(), e.getValue())); + schema.setProperties(properties); + components.addSchemas(cls.getSimpleName(), schema); } @Override - protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle) + protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle, boolean isProject) throws IOException { // Check whether the resource was already added. Maybe we just need to extend it Paths paths = consumer.getPaths(); @@ -229,10 +237,10 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE consumer.setPaths(paths); } for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { - String fullPath = "api/v" + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + String fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + endpoint.getRamlPath(); if (isEmpty(verticle.getBasePath())) { - fullPath = "api/v" + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); + fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); } PathItem pathItem = paths.get(fullPath); if (pathItem == null) { @@ -280,9 +288,47 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE break; } List> params = List.of( + isProject ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) : Stream.of(), endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); + ApiResponses responses = new ApiResponses(); + endpoint.getProduces().stream().map(e -> { + ApiResponse response = new ApiResponse(); + Content responseBody = new Content(); + responseBody.addMediaType(e, new MediaType()); + response.setContent(responseBody); + response.setDescription(e); + return new UnmodifiableMapEntry("default", response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); + endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> { + ApiResponse response = new ApiResponse(); + if (e.getValue().getDescription().startsWith("Generated login token")) { + e.getValue().getHeaders(); + } + response.setDescription(e.getValue().getDescription()); + Content responseBody = new Content(); + if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { + Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); + if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { + Schema schema = new Schema<>(); + schema.set$ref("#/components/schemas/" + ref.getSimpleName()); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + mediaType.setExample(e.getValue()); + responseBody.addMediaType("*/*", mediaType); + response.setContent(responseBody); + } else { + e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) + .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) + .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); + response.setContent(responseBody); + } + } + return new UnmodifiableMapEntry(e.getKey(), response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); + operation.setResponses(responses); if (endpoint.getExampleRequestMap() != null) { RequestBody requestBody = new RequestBody(); Content content = new Content(); @@ -291,37 +337,6 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); requestBody.setContent(content); operation.setRequestBody(requestBody); - ApiResponses responses = new ApiResponses(); - endpoint.getProduces().stream().map(e -> { - ApiResponse response = new ApiResponse(); - Content responseBody = new Content(); - responseBody.addMediaType(e, new MediaType()); - response.setContent(responseBody); - return new UnmodifiableMapEntry("default", response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); - endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - ApiResponse response = new ApiResponse(); - if (e.getValue().getDescription().startsWith("Generated login token")) { - e.getValue().getHeaders(); - } - response.setDescription(e.getValue().getDescription()); - if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { - Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); - if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { - response.set$ref("#/components/schemas/" + ref.getSimpleName()); - } else { - Content responseBody = new Content(); - // response.setContent(responseBody); - e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) - .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) - .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); - response.setContent(responseBody); - } - } - return new UnmodifiableMapEntry(e.getKey(), response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); - operation.setResponses(responses); } // action.setIs(Arrays.asList(endpoint.getTraits())); } @@ -349,7 +364,9 @@ private Map.Entry fillMediaType(String key, MimeType mimeType } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { mediaType.setExample(mimeType.getExample()); return new UnmodifiableMapEntry(key, mediaType); - } else { return null; } + } else { + return new UnmodifiableMapEntry(key, mediaType); + } } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -380,10 +397,12 @@ private void fillType(Components components, Class t, Schema fieldSchema, Lis fieldSchema.setType("array"); Schema itemSchema = new Schema(); if (t.isArray()) { - fillType(components, Array.newInstance(t, 0).getClass(), itemSchema, generics); + List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); + fillType(components, t.getComponentType(), itemSchema, generics1); } else if (generics.size() > 0 && Class.class.isInstance(generics.get(0))) { Class itemClass = Class.class.cast(generics.get(0)); - fillType(components, itemClass, itemSchema, null); + List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); + fillType(components, itemClass, itemSchema, generics1); } else { // TODO log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); diff --git a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java index 2d9432dad6..58504d419f 100644 --- a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java @@ -104,7 +104,7 @@ public String generate() { * Endpoint which provides endpoints * @throws IOException */ - protected void addEndpoints(String basePath, Map resources, AbstractInternalEndpoint verticle) throws IOException { + protected void addEndpoints(String basePath, Map resources, AbstractInternalEndpoint verticle, boolean isProject) throws IOException { String ramlPath = basePath + "/" + verticle.getBasePath(); // Check whether the resource was already added. Maybe we just need to extend it diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/NameUuidReference.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/NameUuidReference.java index d36d462926..9f0a22ef3e 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/NameUuidReference.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/NameUuidReference.java @@ -5,7 +5,7 @@ * * @param */ -public interface NameUuidReference { +public interface NameUuidReference extends RestModel { /** * Return the name of the referenced element. diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java index cfebe14491..a2e5f7bbd9 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java @@ -6,7 +6,7 @@ /** * Paging meta info model. */ -public class PagingMetaInfo { +public class PagingMetaInfo implements RestModel { @JsonProperty(required = true) @JsonPropertyDescription("Number of the current page.") diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/navigation/NavigationElement.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/navigation/NavigationElement.java index 919821822a..00326b25c5 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/navigation/NavigationElement.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/navigation/NavigationElement.java @@ -3,12 +3,13 @@ import java.util.List; import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.node.NodeResponse; /** * A navigation element is a reference to a node within the navigation tree. */ -public class NavigationElement { +public class NavigationElement implements RestModel { @JsonPropertyDescription("Uuid of the node within this navigation element.") private String uuid; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/node/field/NodeFieldListItem.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/node/field/NodeFieldListItem.java index 77b483b70c..379d078e8b 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/node/field/NodeFieldListItem.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/node/field/NodeFieldListItem.java @@ -1,9 +1,11 @@ package com.gentics.mesh.core.rest.node.field; +import com.gentics.mesh.core.rest.common.RestModel; + /** * Entry for a node list REST model. */ -public interface NodeFieldListItem { +public interface NodeFieldListItem extends RestModel { /** * Return the item node uuid. diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/BinaryExtractOptions.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/BinaryExtractOptions.java index c746219e8f..1b8d1dddc8 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/BinaryExtractOptions.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/BinaryExtractOptions.java @@ -1,9 +1,11 @@ package com.gentics.mesh.core.rest.schema; +import com.gentics.mesh.core.rest.common.RestModel; + /** * Determines what parts of the binary data should be extracted and sent to Elasticsearch. */ -public class BinaryExtractOptions { +public class BinaryExtractOptions implements RestModel { private boolean content; private boolean metadata; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/FieldSchema.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/FieldSchema.java index edb9ae6e70..632bd0552a 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/FieldSchema.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/FieldSchema.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.schema.change.impl.SchemaChangeModel; import com.gentics.mesh.etc.config.search.ElasticSearchOptions; import com.gentics.mesh.etc.config.search.MappingMode; @@ -15,7 +16,7 @@ * A field schema is a field within a schema. In contradiction to node fields a field schema is the blueprint of a field and will not store any data. Instead it * only defines a field within a schema. */ -public interface FieldSchema { +public interface FieldSchema extends RestModel { /** * Return the type of the field schema. diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/S3BinaryExtractOptions.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/S3BinaryExtractOptions.java index ef8919272a..5f4b4928b7 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/S3BinaryExtractOptions.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/schema/S3BinaryExtractOptions.java @@ -1,9 +1,11 @@ package com.gentics.mesh.core.rest.schema; +import com.gentics.mesh.core.rest.common.RestModel; + /** * Determines what parts of the s3binary data should be extracted and sent to Elasticsearch. */ -public class S3BinaryExtractOptions { +public class S3BinaryExtractOptions implements RestModel { private boolean content; private boolean metadata; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/user/ExpandableNode.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/user/ExpandableNode.java index 4c1d37b03c..3386e7755b 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/user/ExpandableNode.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/user/ExpandableNode.java @@ -1,9 +1,11 @@ package com.gentics.mesh.core.rest.user; +import com.gentics.mesh.core.rest.common.RestModel; + /** * Marker interface which is used to identify a node field which can be expanded. */ -public interface ExpandableNode { +public interface ExpandableNode extends RestModel { /** * Return the node uuid. diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/user/NodeReference.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/user/NodeReference.java index c6e70c5558..1878ecee2c 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/user/NodeReference.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/user/NodeReference.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.node.NodeResponse; import com.gentics.mesh.core.rest.schema.SchemaReference; import com.gentics.mesh.core.rest.schema.impl.SchemaReferenceImpl; @@ -12,7 +13,7 @@ * A node reference contains the bare minimum of useful information which identifies a node. Various field in the {@link NodeResponse} utilize these references * in order to reduce data. */ -public class NodeReference implements ExpandableNode { +public class NodeReference implements ExpandableNode, RestModel { @JsonProperty(required = true) @JsonPropertyDescription("Name of the project to which the node belongs") diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/image/ImageRect.java b/rest-model/src/main/java/com/gentics/mesh/parameter/image/ImageRect.java index eb742adf5b..59ef65bd00 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/image/ImageRect.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/image/ImageRect.java @@ -3,12 +3,13 @@ import static com.gentics.mesh.core.rest.error.Errors.error; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.parameter.ImageManipulationParameters; /** * Class which represents an image rectangular. */ -public class ImageRect { +public class ImageRect implements RestModel { private int startX; private int startY; From a9c9aa6551326d9f3297da628bbc00e3b2d8c59a Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 21 Nov 2022 13:49:16 +0100 Subject: [PATCH 09/75] Minor fixes --- .../com/gentics/mesh/generator/OpenAPIv3Generator.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 2462ac4025..cbfd2d3097 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -256,8 +256,11 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE method = HttpMethod.GET; } if (endpoint.isInsecure()) { - // Reset the default security requirements operation.setSecurity(Collections.emptyList()); + } else { + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + operation.setSecurity(List.of(reqBearerAuth)); } switch (method.name()) { case "DELETE": @@ -342,6 +345,7 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE } } + @SuppressWarnings("rawtypes") private Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { MediaType mediaType = new MediaType(); mediaType.setExample(mimeType.getExample()); @@ -363,6 +367,9 @@ private Map.Entry fillMediaType(String key, MimeType mimeType return new UnmodifiableMapEntry(key, mediaType); } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { mediaType.setExample(mimeType.getExample()); + Schema schema = new Schema<>(); + schema.setType("object"); + mediaType.setSchema(schema); return new UnmodifiableMapEntry(key, mediaType); } else { return new UnmodifiableMapEntry(key, mediaType); From 608d530f51c9f0b83dca5e7d6f04b426a3af8fd6 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 12 Jan 2024 17:55:23 +0100 Subject: [PATCH 10/75] JRE 9+ Runtime fix --- core/pom.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 69f18ea73c..e9b30ac3bd 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -193,7 +193,11 @@ org.reflections reflections - + + jakarta.xml.bind + jakarta.xml.bind-api + 4.0.1 + From 8a65b5e12be80a4eb8591ebefa467ac63ab0584c Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 15 Jan 2024 12:18:17 +0100 Subject: [PATCH 11/75] Works --- core/pom.xml | 2 +- .../RolePermissionHandlingEndpoint.java | 17 +++++++++++ .../core/endpoint/admin/AdminEndpoint.java | 7 +++++ .../core/endpoint/admin/HealthEndpoint.java | 5 ++++ .../endpoint/eventbus/EventbusEndpoint.java | 6 ++-- .../mesh/core/endpoint/node/NodeEndpoint.java | 3 ++ .../endpoint/webroot/WebRootEndpoint.java | 2 ++ .../webrootfield/WebRootFieldEndpoint.java | 2 ++ .../mesh/generator/OpenAPIv3Generator.java | 29 +++++++++++++++---- .../com/gentics/mesh/http/HttpConstants.java | 2 ++ 10 files changed, 66 insertions(+), 9 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index e9b30ac3bd..7091e3d6da 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -187,7 +187,7 @@ io.swagger.core.v3 swagger-core - 2.2.0 + 2.2.20 org.reflections diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java index 6b5065fcb3..040c3f1691 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java @@ -6,6 +6,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import com.gentics.mesh.auth.MeshAuthChainImpl; import com.gentics.mesh.context.InternalActionContext; @@ -77,5 +78,21 @@ protected void addRolePermissionHandler(String uuidParameterName, String uuidPar String uuid = rc.request().getParam(uuidParameterName); crudHandler.handleRevokePermissions(ac, uuid); }); + + InternalEndpointRoute revokePermissionsEndpointStandard = createRoute(); + revokePermissionsEndpointStandard.path(path); + revokePermissionsEndpointStandard.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); + revokePermissionsEndpointStandard.method(PUT); + revokePermissionsEndpointStandard.description("Revoke permissions on the " + typeDescription + " from multiple roles."); + revokePermissionsEndpointStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); + revokePermissionsEndpointStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); + revokePermissionsEndpointStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointStandard.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = rc.request().getParam(uuidParameterName); + crudHandler.handleRevokePermissions(ac, uuid); + }); } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index e7a171104e..19eaa6f426 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -17,6 +17,8 @@ import static com.gentics.mesh.example.ExampleUuids.JOB_UUID; import static com.gentics.mesh.example.ExampleUuids.PLUGIN_1_ID; import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_OCTET_STREAM; +import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; @@ -359,6 +361,7 @@ private void addJobHandler() { deleteJob.method(DELETE); deleteJob.description("Deletes the job. Note that it is only possible to delete failed jobs"); deleteJob.addUriParameter("jobUuid", "Uuid of the job.", JOB_UUID); + deleteJob.exampleResponse(NO_CONTENT, "Job has been deleted."); deleteJob.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("jobUuid"); @@ -369,6 +372,7 @@ private void addJobHandler() { processJob.path("/jobs/:jobUuid/process"); processJob.method(POST); processJob.description("Process the job. Failed jobs will be automatically reset and put in queued state."); + processJob.exampleResponse(OK, "Job has been queued for processing."); processJob.addUriParameter("jobUuid", "Uuid of the job.", JOB_UUID); processJob.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); @@ -380,6 +384,7 @@ private void addJobHandler() { resetJob.path("/jobs/:jobUuid/error"); resetJob.method(DELETE); resetJob.description("Deletes error state from the job. This will make it possible to execute the job once again."); + resetJob.exampleResponse(NO_CONTENT, "Job has been reset."); resetJob.addUriParameter("jobUuid", "Uuid of the job.", JOB_UUID); resetJob.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); @@ -392,6 +397,8 @@ private void addDebugInfoHandler() { InternalEndpointRoute route = createRoute(); route.path("/debuginfo"); route.method(GET); + route.produces(APPLICATION_OCTET_STREAM); + route.exampleResponse(OK, "ZIP file"); route.description("Downloads a zip file of various [debug information](/docs/administration-guide/#debuginfo) files."); route.addQueryParameter("include", "Information to include. See the [documentation](/docs/administration-guide/#debuginfo) for possible values.", "-backup,consistencyCheck"); route.handler(rc -> debugInfoHandler.handle(rc)); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java index 3046301671..3943ef933c 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/HealthEndpoint.java @@ -9,6 +9,8 @@ import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import io.netty.handler.codec.http.HttpResponseStatus; + /** * Endpoint definition for health / readiness checks */ @@ -44,6 +46,7 @@ private void addLive() { deployEndpoint.path("/live"); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is alive."); + deployEndpoint.exampleResponse(HttpResponseStatus.OK, "Mesh is alive."); deployEndpoint.handler(rc -> monitoringCrudHandler.handleLive(rc)); } @@ -53,6 +56,7 @@ private void addReady() { deployEndpoint.setInsecure(true); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is ready. Responds with 503 otherwise."); + deployEndpoint.exampleResponse(HttpResponseStatus.OK, "Mesh is ready."); deployEndpoint.handler(rc -> monitoringCrudHandler.handleReady(rc)); } @@ -62,6 +66,7 @@ private void addWritable() { deployEndpoint.setInsecure(true); deployEndpoint.method(GET); deployEndpoint.description("Returns an empty response with status code 200 if Gentics Mesh is writable. Responds with 503 otherwise."); + deployEndpoint.exampleResponse(HttpResponseStatus.OK, "Mesh is writable."); deployEndpoint.handler(rc -> monitoringCrudHandler.handleWritable(rc)); } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java index 6d1d7021f2..066e93f3ff 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java @@ -53,7 +53,7 @@ private void addEventBusHandler() { endpoint.setRAMLPath("/"); endpoint.description("This endpoint provides a sockjs compliant websocket which can be used to interface with the vert.x eventbus."); - if (!isRamlGeneratorContext()) { + if (!isSpecGeneratorContext()) { SockJSHandlerOptions sockJSoptions = new SockJSHandlerOptions().setHeartbeatInterval(2000); SockJSHandler handler = SockJSHandler.create(vertx, sockJSoptions); SockJSBridgeOptions bridgeOptions = new SockJSBridgeOptions(); @@ -87,10 +87,10 @@ private void addEventBusHandler() { } /** - * Returns whether the method is called from during the documentation generation context. + * Returns whether the method is called from during the specification generation context. * @return */ - private boolean isRamlGeneratorContext() { + private boolean isSpecGeneratorContext() { return localRouter == null; } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index a11a1bcd99..2e6c88c3ff 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -16,6 +16,7 @@ import static com.gentics.mesh.example.ExampleUuids.TAG_RED_UUID; import static com.gentics.mesh.example.ExampleUuids.UUID_1; import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_OCTET_STREAM; import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; @@ -221,6 +222,8 @@ private void addBinaryHandlers() { fieldGet.addQueryParameters(ImageManipulationParametersImpl.class); fieldGet.addQueryParameters(VersioningParametersImpl.class); fieldGet.method(GET); + fieldGet.produces(APPLICATION_OCTET_STREAM); + fieldGet.exampleResponse(OK, "Binary data"); fieldGet.description( "Download the binary field with the given name. You can use image query parameters for crop and resize if the binary data represents an image."); fieldGet.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java index b39a8dea4c..6ea431ff84 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java @@ -3,6 +3,7 @@ import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; @@ -85,6 +86,7 @@ private void addPathUpdateCreateHandler() { private void addErrorHandlers() { InternalEndpointRoute endpoint = createRoute(); endpoint.path("/error/404"); + endpoint.exampleResponse(NOT_FOUND, "Web root not found"); endpoint.description("Fallback endpoint for unresolvable links which returns 404."); endpoint.handler(rc -> { rc.data().put("statuscode", "404"); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webrootfield/WebRootFieldEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webrootfield/WebRootFieldEndpoint.java index 573d877aec..12ca09b542 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webrootfield/WebRootFieldEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webrootfield/WebRootFieldEndpoint.java @@ -1,5 +1,6 @@ package com.gentics.mesh.core.endpoint.webrootfield; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.GET; @@ -70,6 +71,7 @@ private void addErrorHandlers() { InternalEndpointRoute endpoint = createRoute(); endpoint.path("/error/404"); endpoint.description("Fallback endpoint for unresolvable links which returns 404."); + endpoint.exampleResponse(NOT_FOUND, "Web root field not found"); endpoint.handler(rc -> { rc.data().put("statuscode", "404"); rc.next(); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index cbfd2d3097..963e0b1af9 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -37,18 +37,22 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.context.InternalActionContext; +import com.gentics.mesh.core.endpoint.eventbus.EventbusEndpoint; import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; import com.hazelcast.core.HazelcastInstance; -import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Json31; +import io.swagger.v3.core.util.OpenAPI30To31; +import io.swagger.v3.core.util.Yaml31; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.SpecVersion; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; @@ -113,20 +117,22 @@ public void generate(InternalActionContext ac, String format) { throw new RuntimeException("Could not add all verticles to raml generator", e); } + //new OpenAPI30To31().process(openApi); + //openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); String formatted; String mime; switch (format) { case "yaml": try { mime = APPLICATION_YAML_UTF8; - formatted = Yaml.pretty().writeValueAsString(openApi); + formatted = Yaml31.pretty().writeValueAsString(openApi); } catch (JsonProcessingException e) { throw new RuntimeException("Could not generate YAML", e); } break; case "json": mime = APPLICATION_JSON_UTF8; - formatted = Json.pretty(openApi); + formatted = Json31.pretty(openApi); break; default: throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); @@ -237,6 +243,9 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE consumer.setPaths(paths); } for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { + if ("eventbus".equals(verticle.getBasePath())) { + continue; + } String fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + endpoint.getRamlPath(); if (isEmpty(verticle.getBasePath())) { @@ -302,6 +311,16 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE responseBody.addMediaType(e, new MediaType()); response.setContent(responseBody); response.setDescription(e); + if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { + response.setExtensions(Map.of("x-is-file", true)); + Schema schema = new Schema<>(); + schema.setType("string"); + schema.setFormat("binary"); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + responseBody.addMediaType("application/octet-stream", mediaType); + response.setContent(responseBody); + } return new UnmodifiableMapEntry("default", response); }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) @@ -332,7 +351,7 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE return new UnmodifiableMapEntry(e.getKey(), response); }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); operation.setResponses(responses); - if (endpoint.getExampleRequestMap() != null) { + if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { RequestBody requestBody = new RequestBody(); Content content = new Content(); endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) diff --git a/rest-model/src/main/java/com/gentics/mesh/http/HttpConstants.java b/rest-model/src/main/java/com/gentics/mesh/http/HttpConstants.java index 3c62fa3716..61093fd030 100644 --- a/rest-model/src/main/java/com/gentics/mesh/http/HttpConstants.java +++ b/rest-model/src/main/java/com/gentics/mesh/http/HttpConstants.java @@ -17,6 +17,8 @@ public final class HttpConstants { public static final String APPLICATION_JSON = "application/json"; + public static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; + public static final String TEXT_PLAIN = "text/plain"; public static final String TEXT_PLAIN_UTF8 = TEXT_PLAIN + "; charset=utf-8"; From 28432e5d40008d8263439ffd143d658093360087 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 13 Feb 2025 14:53:44 +0100 Subject: [PATCH 12/75] Java client generated, built and able to login --- .../core/endpoint/admin/AdminHandler.java | 2 +- .../mesh/core/endpoint/node/NodeEndpoint.java | 2 +- .../mesh/generator/OpenAPIv3Generator.java | 141 ++++++++++++++---- 3 files changed, 118 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index e8126e8332..2bb1d1601d 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -232,7 +232,7 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { } public void handleOpenAPIv3(InternalActionContext ac, String format) { - OpenAPIv3Generator generator = new OpenAPIv3Generator(clusterManager.getHazelcast()); + OpenAPIv3Generator generator = new OpenAPIv3Generator(clusterManager.getHazelcast(), options.getHttpServerOptions()); generator.generate(ac, format); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index c1b8f56df0..fd74dc5c9c 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -192,7 +192,7 @@ private void addBinaryHandlers() { fieldUpdate.path("/:nodeUuid/binary/:fieldName"); fieldUpdate.addUriParameter("nodeUuid", "Uuid of the node.", NODE_DELOREAN_UUID); fieldUpdate.addUriParameter("fieldName", "Name of the field which should be created.", "stringField"); - fieldUpdate.addUriParameter("publish", "Whether the node shall be published after updating the binary field", "true"); + fieldUpdate.addQueryParameter("publish", "Whether the node shall be published after updating the binary field", "true"); fieldUpdate.method(POST); fieldUpdate.consumes(MULTIPART_FORM_DATA); fieldUpdate.produces(APPLICATION_JSON); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 963e0b1af9..9e9239710a 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -15,12 +15,16 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -30,29 +34,30 @@ import org.raml.model.MimeType; import org.raml.model.parameter.AbstractParam; import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializable; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.context.InternalActionContext; -import com.gentics.mesh.core.endpoint.eventbus.EventbusEndpoint; import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; import com.hazelcast.core.HazelcastInstance; import io.swagger.v3.core.util.Json31; -import io.swagger.v3.core.util.OpenAPI30To31; import io.swagger.v3.core.util.Yaml31; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.SpecVersion; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.media.MediaType; @@ -63,10 +68,9 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; -import io.vertx.core.logging.Logger; -import io.vertx.core.logging.LoggerFactory; /** * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. @@ -81,19 +85,23 @@ public class OpenAPIv3Generator extends AbstractEndpointGenerator { private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); private final HazelcastInstance hz; + private final HttpServerConfig httpServerConfig; - public OpenAPIv3Generator(HazelcastInstance hz) { + public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig) { this.hz = hz; + this.httpServerConfig = httpServerConfig; } - public OpenAPIv3Generator(HazelcastInstance hz, File outputFolder, boolean cleanup) throws IOException { + public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder, boolean cleanup) throws IOException { super(outputFolder, cleanup); this.hz = hz; + this.httpServerConfig = httpServerConfig; } - public OpenAPIv3Generator(HazelcastInstance hz, File outputFolder) throws IOException { + public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder) throws IOException { super(outputFolder); this.hz = hz; + this.httpServerConfig = httpServerConfig; } public void generate(InternalActionContext ac, String format) { @@ -102,11 +110,18 @@ public void generate(InternalActionContext ac, String format) { Info info = new Info(); info.setTitle("Gentics Mesh REST API"); info.setVersion(MeshVersion.getBuildInfo().getVersion()); -// if (hz != null) { -// Server server = new Server(); -// hz.getCluster().getMembers().stream().forEach(m -> server.setUrl(m.getAddress().toString())); -// openApi.servers(Collections.singletonList(server)); -// } + if (hz != null) { + List servers = hz.getCluster().getMembers().stream().map(m -> { + Server server = new Server(); + server.setUrl(m.getAddress().toString()); + return server; + }).collect(Collectors.toList()); + openApi.servers(servers); + } else { + Server server = new Server(); + server.setUrl((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + openApi.servers(Collections.singletonList(null)); + } openApi.setInfo(info); try { addSecurity(openApi); @@ -125,14 +140,18 @@ public void generate(InternalActionContext ac, String format) { case "yaml": try { mime = APPLICATION_YAML_UTF8; - formatted = Yaml31.pretty().writeValueAsString(openApi); + formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); } catch (JsonProcessingException e) { throw new RuntimeException("Could not generate YAML", e); } break; case "json": mime = APPLICATION_JSON_UTF8; - formatted = Json31.pretty(openApi); + try { + formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } break; default: throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); @@ -159,7 +178,10 @@ private void addSecurity(OpenAPI openApi) { openApi.addSecurityItem(reqBearerAuth); } - @SuppressWarnings("rawtypes") + private String makeSchemaName(Class cls) { + return "Mesh" + cls.getSimpleName(); + } + private void addComponents(OpenAPI openApi) { Components components; if (openApi.getComponents() == null) { @@ -168,21 +190,43 @@ private void addComponents(OpenAPI openApi) { } else { components = openApi.getComponents(); } + components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); Reflections reflections = new Reflections("com.gentics.mesh"); reflections.getSubTypesOf(RestModel.class).stream().forEach(cls -> fillComponent(cls, components)); } + @SuppressWarnings("rawtypes") private void fillComponent(Class cls, Components components) { - Schema schema = new Schema(); + if (!cls.getPackageName().startsWith("com.gentics.mesh") || StringUtils.isBlank(cls.getSimpleName())) { + return; + } + Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); + schema.setName(cls.getSimpleName()); List> fieldStreams = new ArrayList<>(); - Class tclass = cls; - log.debug("Class: " + tclass.getCanonicalName()); final List generics = new ArrayList<>(); generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); - while (tclass != null) { + Deque> dq = new ArrayDeque<>(2); + dq.addLast(cls); + while(!dq.isEmpty()) { + Class tclass = dq.pop(); + log.debug("Class: " + tclass.getCanonicalName()); + /*if (tclass != cls) { + Schema tschema = components.getSchemas().computeIfAbsent(tclass.getSimpleName(), key -> new Schema()); + tschema.setName(tclass.getSimpleName()); + Schema refSchema = new Schema<>(); + refSchema.$ref("#/components/schemas/" + tclass.getSimpleName()); + schema.addAllOfItem(refSchema); + refSchema = new Schema<>(); + refSchema.$ref("#/components/schemas/" + cls.getSimpleName()); + tschema.addAnyOfItem(refSchema); + }*/ fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); + dq.addAll(Arrays.stream(tclass.getInterfaces()).filter(i -> i.getPackageName().startsWith("com.gentics.mesh")).collect(Collectors.toList())); tclass = tclass.getSuperclass(); + if (tclass != null) { + dq.addLast(tclass); + } } if (generics.size() > 0) { log.debug(" - Generics: " + Arrays.toString(generics.toArray())); @@ -342,9 +386,13 @@ protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalE responseBody.addMediaType("*/*", mediaType); response.setContent(responseBody); } else { - e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) - .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) - .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); + if (e.getValue().getBody() != null) { + e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) + .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) + .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); + } else { + log.warn("Body of " + e.getKey() + " is null!"); + } response.setContent(responseBody); } } @@ -438,14 +486,57 @@ private void fillType(Components components, Class t, Schema fieldSchema, Lis // fieldSchema.setType("string"); // fieldSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); } else { - fieldSchema.setType("object"); - fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); if (t.isEnum()) { Schema enumSchema = new Schema(); enumSchema.setType("string"); enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); components.addSchemas(t.getSimpleName(), enumSchema); } + if (Map.class.isAssignableFrom(t)) { + if (generics.size() == 2) { + BiConsumer innerTypeMapper = (ty, tfieldSchema) -> { + Class tt = Class.class.isInstance(ty) ? Class.class.cast(ty) : generics.get(1).getClass(); + if (tt.isPrimitive() || Number.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { + if (int.class.isAssignableFrom(tt) || Integer.class.isAssignableFrom(tt)) { + tfieldSchema.setType("integer"); + tfieldSchema.setFormat("int32"); + } else if (boolean.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { + tfieldSchema.setType("boolean"); + } else if (float.class.isAssignableFrom(tt) || Float.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + tfieldSchema.setFormat("float"); + } else if (long.class.isAssignableFrom(tt) || Long.class.isAssignableFrom(tt)) { + tfieldSchema.setType("integer"); + tfieldSchema.setFormat("int64"); + } else if (double.class.isAssignableFrom(tt) || Double.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + tfieldSchema.setFormat("double"); + } else if (BigDecimal.class.isAssignableFrom(tt) || Number.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + } else { + tfieldSchema.setType("object"); + } + } else if (CharSequence.class.isAssignableFrom(tt)) { + tfieldSchema.setType("string"); + } else { + tfieldSchema.setType("object"); + } + }; + fieldSchema.setType("object"); // TODO why object? + //innerTypeMapper.accept(generics.get(0).getClass(), fieldSchema); + Schema valueSchema = new Schema<>(); + innerTypeMapper.accept(generics.get(1), valueSchema); + fieldSchema.setAdditionalProperties(valueSchema); + } else { + fieldSchema.setType("object"); + fieldSchema.setAdditionalProperties(new Schema().type("object")); + } + } else if (JsonObject.class.isAssignableFrom(t) || JsonSerializable.class.isAssignableFrom(t)) { + fieldSchema.set$ref("#/components/schemas/AnyJson"); + } else { + fieldSchema.setType("object"); + fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + } } } From d8149a825026bcbe92280fccc255801535d31c61 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 13 Feb 2025 15:55:23 +0100 Subject: [PATCH 13/75] Correct spec types --- .../gentics/mesh/generator/OpenAPIv3Generator.java | 13 +++++++++---- .../com/gentics/mesh/example/MiscExamples.java | 14 +++++--------- .../gentics/mesh/core/rest/auth/TokenResponse.java | 5 +++++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 9e9239710a..df3ef3bc8e 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -51,8 +51,8 @@ import com.gentics.mesh.router.route.AbstractInternalEndpoint; import com.hazelcast.core.HazelcastInstance; -import io.swagger.v3.core.util.Json31; -import io.swagger.v3.core.util.Yaml31; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -140,7 +140,8 @@ public void generate(InternalActionContext ac, String format) { case "yaml": try { mime = APPLICATION_YAML_UTF8; - formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); + //formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); + formatted = ac.isMinify(httpServerConfig) ? Yaml.mapper().writer().writeValueAsString(openApi) : Yaml.pretty().writeValueAsString(openApi); } catch (JsonProcessingException e) { throw new RuntimeException("Could not generate YAML", e); } @@ -148,7 +149,8 @@ public void generate(InternalActionContext ac, String format) { case "json": mime = APPLICATION_JSON_UTF8; try { - formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + //formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + formatted = ac.isMinify(httpServerConfig) ? Json.mapper().writer().writeValueAsString(openApi) : Json.pretty(openApi); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -201,6 +203,7 @@ private void fillComponent(Class cls, Components components) { return; } Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); + schema.setType("object"); schema.setName(cls.getSimpleName()); List> fieldStreams = new ArrayList<>(); final List generics = new ArrayList<>(); @@ -242,6 +245,7 @@ private void fillComponent(Class cls, Components components) { String name = f.getName(); log.debug(" - Field: " + f); Schema fieldSchema = new Schema(); + fieldSchema.setName(name); JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); if (description != null) { fieldSchema.setDescription(description.value()); @@ -271,6 +275,7 @@ private void fillComponent(Class cls, Components components) { } fillType(components, t, fieldSchema, generics); } + fieldSchema.setTypes(Collections.singleton(fieldSchema.getType())); return new UnmodifiableMapEntry<>(name, fieldSchema); }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); schema.setProperties(properties); diff --git a/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java b/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java index 1155d66e77..af44589b25 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java +++ b/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java @@ -8,6 +8,7 @@ import org.codehaus.jettison.json.JSONObject; import com.gentics.mesh.core.rest.auth.LoginRequest; +import com.gentics.mesh.core.rest.auth.TokenResponse; import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.core.rest.search.EntityMetrics; import com.gentics.mesh.core.rest.search.SearchStatusResponse; @@ -76,15 +77,10 @@ public JSONObject getSearchQueryExample() { return node; } - public JSONObject getAuthTokenResponse() { - JSONObject node = new JSONObject(); - try { - node.put("token", - "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyVXVpZCI6IlVVSURPRlVTRVIxIiwiZXhwIjoxNDY5MTE3MjQ3LCJpYXQiOjE0NjkxMTM2NDd9.i1u4RMs4K7zBkGhmcpp1P79Wpz2UQYJkZKJTVdFp_iU="); - } catch (JSONException e) { - e.printStackTrace(); - } - return node; + public TokenResponse getAuthTokenResponse() { + TokenResponse response = new TokenResponse(); + response.setToken("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyVXVpZCI6IlVVSURPRlVTRVIxIiwiZXhwIjoxNDY5MTE3MjQ3LCJpYXQiOjE0NjkxMTM2NDd9.i1u4RMs4K7zBkGhmcpp1P79Wpz2UQYJkZKJTVdFp_iU="); + return response; } public JsonObject createSearchResponse() { diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/auth/TokenResponse.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/auth/TokenResponse.java index 64ba7d72d9..3e057f67dc 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/auth/TokenResponse.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/auth/TokenResponse.java @@ -1,11 +1,16 @@ package com.gentics.mesh.core.rest.auth; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.gentics.mesh.core.rest.common.RestModel; /** * This response is returned when a new JWToken is requested. */ public class TokenResponse implements RestModel { + + @JsonProperty(required = false) + @JsonPropertyDescription("A current JWT access token") private String token; public String getToken() { From 0a03e37524d0c4a4662ea5e86030246bb9224efe Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 30 Jan 2026 18:23:57 +0100 Subject: [PATCH 14/75] Runtime based generation --- .../rest/impl/InternalEndpointRouteImpl.java | 1 + .../core/endpoint/admin/AdminHandler.java | 24 +- .../generator/OpenAPIRuntimeGenerator.java | 103 +++ .../mesh/generator/OpenAPIv3Generator.java | 393 ++++++----- .../mesh/generator/OpenAPIv3GeneratorOld.java | 621 ++++++++++++++++++ 5 files changed, 961 insertions(+), 181 deletions(-) create mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java create mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index 728105376d..7848f6077f 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -114,6 +114,7 @@ public InternalEndpointRouteImpl(Router router, LocalConfigApi localConfigApi, D this.route = router.route(); ReadOnlyHandler readOnlyHandler = new ReadOnlyHandler(localConfigApi, db); route.handler(readOnlyHandler); + route.putMetadata(InternalEndpointRoute.class.getCanonicalName(), this); } @Override diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 2c06bb21e3..ea667e9d2f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -11,7 +11,11 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; +import java.util.Collections; +import java.util.List; +import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,9 +37,12 @@ import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; +import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.generator.OpenAPIv3Generator; +import com.gentics.mesh.generator.OpenAPIv3Generator.Format; import com.gentics.mesh.generator.RAMLGenerator; +import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; @@ -232,8 +239,21 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { } public void handleOpenAPIv3(InternalActionContext ac, String format) { - OpenAPIv3Generator generator = new OpenAPIv3Generator(clusterManager.getHazelcast(), options.getHttpServerOptions()); - generator.generate(ac, format); + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + List servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(() -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort()))); + + OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(List.of("/api/v1", "/api/:apiversion")), Optional.empty()); + ac.send(generator.generate( + routerStorageRegistry.getInstances().stream().map(rr -> rr.root().getRouter()).toList(), + Format.parse(format), + !httpServerConfig.isMinifyJson()), + OK, + "yaml".equalsIgnoreCase(format) + ? HttpConstants.APPLICATION_YAML_UTF8 + : HttpConstants.APPLICATION_JSON_UTF8 + ); } /** diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java new file mode 100644 index 0000000000..37403b531e --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java @@ -0,0 +1,103 @@ +package com.gentics.mesh.generator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import com.gentics.mesh.rest.InternalEndpointRoute; +import com.github.jknack.handlebars.internal.lang3.StringUtils; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.servers.Server; +import io.swagger.v3.oas.models.tags.Tag; +import io.vertx.ext.web.Router; + +public final class OpenAPIRuntimeGenerator { + + public static String fromRouter(Router router) { + OpenAPI openApi = new OpenAPI(); + Info info = new Info(); + List servers = new ArrayList<>(); + List tags = new ArrayList<>(); + Paths paths = new Paths(); + + info.setTitle("Gentics OpenAPI"); + + openApi.setInfo(info); + openApi.setServers(servers); + openApi.setPaths(paths); + openApi.setTags(tags); + + router(router, info, paths); + + return Json.pretty(openApi); + } + + private static final void router(Router router, Info info, Paths paths) { + router.getRoutes().stream() + .filter(r -> StringUtils.isNotBlank(r.getPath())) + .forEach(r -> { + String path = Arrays.stream(r.getPath().split("/")) + .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) + .collect(Collectors.joining("/")); + + Operation o = new Operation(); + o.setParameters(Arrays.stream(r.getPath().split("/")) + .filter(segment -> segment.startsWith(":")) + .map(segment -> segment.substring(1)) + .map(segment -> { + Parameter p = new Parameter(); + p.setName(segment); + p.setRequired(true); + p.setAllowEmptyValue(false); + return p; + }).collect(Collectors.toList())); + PathItem i = new PathItem(); + r.methods().stream().forEach(m -> { + switch (m.name()) { + case "GET": + i.setGet(o); + break; + case "POST": + i.setPost(o); + break; + case "PUT": + i.setPut(o); + break; + case "DELETE": + i.setDelete(o); + break; + case "OPTIONS": + i.setOptions(o); + break; + case "PATCH": + i.setPatch(o); + break; + case "TRACE": + i.setTrace(o); + break; + case "HEAD": + i.setHead(o); + break; + } + }); + Optional.ofNullable(r.getMetadata(InternalEndpointRoute.class.getCanonicalName())) + .map(InternalEndpointRoute.class::cast) + .ifPresent(ie -> { + ie.getQueryParameters().entrySet().stream(); + }); + paths.put(path, i); + + Optional.ofNullable(r.getSubRouter()) + .ifPresent(s -> router(s, info, paths)); + }); + } +} diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index df3ef3bc8e..d92a6b95da 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -1,12 +1,7 @@ package com.gentics.mesh.generator; -import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; import static com.gentics.mesh.core.rest.error.Errors.error; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static org.apache.commons.lang3.StringUtils.isEmpty; import java.io.File; import java.io.IOException; @@ -24,11 +19,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nonnull; + import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; import org.apache.commons.lang.StringUtils; import org.raml.model.MimeType; @@ -43,13 +41,9 @@ import com.fasterxml.jackson.databind.JsonSerializable; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.rest.common.RestModel; -import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.rest.InternalEndpointRoute; -import com.gentics.mesh.router.route.AbstractInternalEndpoint; -import com.hazelcast.core.HazelcastInstance; import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.Yaml; @@ -71,6 +65,8 @@ import io.swagger.v3.oas.models.servers.Server; import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; /** * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. @@ -78,56 +74,42 @@ * @author plyhun * */ -public class OpenAPIv3Generator extends AbstractEndpointGenerator { - - private static final String API_VERSION_PATH_PREFIX = "/api/v"; +public class OpenAPIv3Generator { private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); - private final HazelcastInstance hz; - private final HttpServerConfig httpServerConfig; - - public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig) { - this.hz = hz; - this.httpServerConfig = httpServerConfig; - } + private final Optional> maybePathBlacklist; + private final Optional> maybePathWhitelist; - public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder, boolean cleanup) throws IOException { - super(outputFolder, cleanup); - this.hz = hz; - this.httpServerConfig = httpServerConfig; - } + private final List servers; - public OpenAPIv3Generator(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder) throws IOException { - super(outputFolder); - this.hz = hz; - this.httpServerConfig = httpServerConfig; + public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, @Nonnull Optional> maybePathWhitelist) { + this.maybePathBlacklist = maybePathBlacklist; + this.maybePathWhitelist = maybePathWhitelist; + this.servers = servers; } - public void generate(InternalActionContext ac, String format) { + public String generate(List routers, Format format, boolean pretty) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); + openApi.setPaths(new Paths()); Info info = new Info(); info.setTitle("Gentics Mesh REST API"); info.setVersion(MeshVersion.getBuildInfo().getVersion()); - if (hz != null) { - List servers = hz.getCluster().getMembers().stream().map(m -> { - Server server = new Server(); - server.setUrl(m.getAddress().toString()); - return server; - }).collect(Collectors.toList()); - openApi.servers(servers); - } else { + + openApi.servers(servers.stream().map(url -> { Server server = new Server(); - server.setUrl((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - openApi.servers(Collections.singletonList(null)); - } + server.setUrl(url); + return server; + }).collect(Collectors.toList())); + openApi.setInfo(info); try { addSecurity(openApi); addComponents(openApi); - addCoreEndpoints(openApi); - addProjectEndpoints(openApi); + for (Router router : routers) { + addRouter(StringUtils.EMPTY, router, openApi); + } } catch (IOException e) { throw new RuntimeException("Could not add all verticles to raml generator", e); } @@ -135,22 +117,19 @@ public void generate(InternalActionContext ac, String format) { //new OpenAPI30To31().process(openApi); //openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); String formatted; - String mime; switch (format) { - case "yaml": + case YAML: try { - mime = APPLICATION_YAML_UTF8; //formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); - formatted = ac.isMinify(httpServerConfig) ? Yaml.mapper().writer().writeValueAsString(openApi) : Yaml.pretty().writeValueAsString(openApi); + formatted = pretty ? Yaml.pretty().writeValueAsString(openApi) : Yaml.mapper().writer().writeValueAsString(openApi) ; } catch (JsonProcessingException e) { throw new RuntimeException("Could not generate YAML", e); } break; - case "json": - mime = APPLICATION_JSON_UTF8; + case JSON: try { //formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); - formatted = ac.isMinify(httpServerConfig) ? Json.mapper().writer().writeValueAsString(openApi) : Json.pretty(openApi); + formatted = pretty ? Json.pretty(openApi) : Json.mapper().writer().writeValueAsString(openApi); } catch (JsonProcessingException e) { throw new RuntimeException(e); } @@ -158,10 +137,10 @@ public void generate(InternalActionContext ac, String format) { default: throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); } - ac.send(formatted, OK, mime); + return formatted; } - private void addSecurity(OpenAPI openApi) { + protected void addSecurity(OpenAPI openApi) { Components components; if (openApi.getComponents() == null) { components = new Components(); @@ -184,7 +163,7 @@ private String makeSchemaName(Class cls) { return "Mesh" + cls.getSimpleName(); } - private void addComponents(OpenAPI openApi) { + protected void addComponents(OpenAPI openApi) { Components components; if (openApi.getComponents() == null) { components = new Components(); @@ -198,7 +177,7 @@ private void addComponents(OpenAPI openApi) { } @SuppressWarnings("rawtypes") - private void fillComponent(Class cls, Components components) { + protected void fillComponent(Class cls, Components components) { if (!cls.getPackageName().startsWith("com.gentics.mesh") || StringUtils.isBlank(cls.getSimpleName())) { return; } @@ -282,143 +261,181 @@ private void fillComponent(Class cls, Components components) { components.addSchemas(cls.getSimpleName(), schema); } - @Override - protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle, boolean isProject) - throws IOException { - // Check whether the resource was already added. Maybe we just need to extend it - Paths paths = consumer.getPaths(); - if (paths == null) { - paths = new Paths(); - consumer.setPaths(paths); + protected void resolveEndpointRoute(String path, PathItem pathItem, InternalEndpointRoute endpoint) { + Operation operation = new Operation(); + HttpMethod method = endpoint.getMethod(); + if (method == null) { + method = HttpMethod.GET; } - for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { - if ("eventbus".equals(verticle.getBasePath())) { - continue; - } - String fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() - + endpoint.getRamlPath(); - if (isEmpty(verticle.getBasePath())) { - fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); - } - PathItem pathItem = paths.get(fullPath); - if (pathItem == null) { - log.debug("Path " + fullPath); - pathItem = new PathItem(); - pathItem.setSummary(endpoint.getDisplayName()); - pathItem.setDescription(endpoint.getDescription()); - consumer.path(fullPath, pathItem); - } - Operation operation = new Operation(); - HttpMethod method = endpoint.getMethod(); - if (method == null) { - method = HttpMethod.GET; - } - if (endpoint.isInsecure()) { - operation.setSecurity(Collections.emptyList()); - } else { - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - operation.setSecurity(List.of(reqBearerAuth)); - } - switch (method.name()) { - case "DELETE": - pathItem.setDelete(operation); - break; - case "GET": - pathItem.setGet(operation); - break; - case "HEAD": - pathItem.setHead(operation); - break; - case "OPTIONS": - pathItem.setOptions(operation); - break; - case "PATCH": - pathItem.setPatch(operation); - break; - case "POST": - pathItem.setPost(operation); - break; - case "PUT": - pathItem.setPut(operation); - break; - case "TRACE": - pathItem.setTrace(operation); - break; - default: - break; + if (endpoint.isInsecure()) { + operation.setSecurity(Collections.emptyList()); + } else { + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + operation.setSecurity(List.of(reqBearerAuth)); + } + resolveMethod(method.name(), pathItem, operation); + List> params = List.of( + path.contains("/{project}/") + ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) + : Stream.empty(), + endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), + endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); + operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); + ApiResponses responses = new ApiResponses(); + endpoint.getProduces().stream().map(e -> { + ApiResponse response = new ApiResponse(); + Content responseBody = new Content(); + responseBody.addMediaType(e, new MediaType()); + response.setContent(responseBody); + response.setDescription(e); + if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { + response.setExtensions(Map.of("x-is-file", true)); + Schema schema = new Schema<>(); + schema.setType("string"); + schema.setFormat("binary"); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + responseBody.addMediaType("application/octet-stream", mediaType); + response.setContent(responseBody); } - List> params = List.of( - isProject ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) : Stream.of(), - endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), - endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); - operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); - ApiResponses responses = new ApiResponses(); - endpoint.getProduces().stream().map(e -> { + return new UnmodifiableMapEntry("default", response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); + endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> { ApiResponse response = new ApiResponse(); - Content responseBody = new Content(); - responseBody.addMediaType(e, new MediaType()); - response.setContent(responseBody); - response.setDescription(e); - if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { - response.setExtensions(Map.of("x-is-file", true)); - Schema schema = new Schema<>(); - schema.setType("string"); - schema.setFormat("binary"); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - responseBody.addMediaType("application/octet-stream", mediaType); - response.setContent(responseBody); + if (e.getValue().getDescription().startsWith("Generated login token")) { + e.getValue().getHeaders(); } - return new UnmodifiableMapEntry("default", response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); - endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - ApiResponse response = new ApiResponse(); - if (e.getValue().getDescription().startsWith("Generated login token")) { - e.getValue().getHeaders(); - } - response.setDescription(e.getValue().getDescription()); - Content responseBody = new Content(); - if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { - Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); - if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { - Schema schema = new Schema<>(); - schema.set$ref("#/components/schemas/" + ref.getSimpleName()); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - mediaType.setExample(e.getValue()); - responseBody.addMediaType("*/*", mediaType); - response.setContent(responseBody); + response.setDescription(e.getValue().getDescription()); + Content responseBody = new Content(); + if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { + Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); + if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { + Schema schema = new Schema<>(); + schema.set$ref("#/components/schemas/" + ref.getSimpleName()); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + mediaType.setExample(e.getValue()); + responseBody.addMediaType("*/*", mediaType); + response.setContent(responseBody); + } else { + if (e.getValue().getBody() != null) { + e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) + .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) + .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); } else { - if (e.getValue().getBody() != null) { - e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) - .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) - .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); - } else { - log.warn("Body of " + e.getKey() + " is null!"); - } - response.setContent(responseBody); + log.warn("Body of " + e.getKey() + " is null!"); } - } - return new UnmodifiableMapEntry(e.getKey(), response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); - operation.setResponses(responses); - if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { - RequestBody requestBody = new RequestBody(); - Content content = new Content(); - endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) - .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); - requestBody.setContent(content); - operation.setRequestBody(requestBody); + response.setContent(responseBody); + } + } + return new UnmodifiableMapEntry(e.getKey(), response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); + operation.setResponses(responses); + if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { + RequestBody requestBody = new RequestBody(); + Content content = new Content(); + endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) + .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + requestBody.setContent(content); + operation.setRequestBody(requestBody); + } + // action.setIs(Arrays.asList(endpoint.getTraits())); + } + + protected void resolveMethod(String methodName, PathItem pathItem, Operation operation) { + switch (methodName.toUpperCase()) { + case "DELETE": + pathItem.setDelete(operation); + break; + case "GET": + pathItem.setGet(operation); + break; + case "HEAD": + pathItem.setHead(operation); + break; + case "OPTIONS": + pathItem.setOptions(operation); + break; + case "PATCH": + pathItem.setPatch(operation); + break; + case "POST": + pathItem.setPost(operation); + break; + case "PUT": + pathItem.setPut(operation); + break; + case "TRACE": + pathItem.setTrace(operation); + break; + default: + break; + } + } + + protected void addRouter(String parent, Router router, OpenAPI consumer) + throws IOException { + Paths paths = consumer.getPaths(); + + for (Route route : router.getRoutes()) { + if (StringUtils.isBlank(route.getPath()) + || maybePathBlacklist.map(list -> list.contains(route.getPath())).filter(blacklisted -> blacklisted).isPresent() + || (maybePathWhitelist.isPresent() && maybePathWhitelist.map(list -> list.contains(route.getPath())).filter(whitelisted -> whitelisted).isEmpty())) { + continue; + } + String path = parent + (StringUtils.equals(route.getPath(), "/") ? "/" : Arrays.stream(route.getPath().split("/")) + .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) + .collect(Collectors.joining("/"))); + + log.info("Processing path {}", path); + Optional.ofNullable(route.getMetadata(InternalEndpointRoute.class.getCanonicalName())) + .map(InternalEndpointRoute.class::cast) + .ifPresentOrElse(endpoint -> { + PathItem pathItem = paths.get(path); + if (pathItem == null) { + log.debug("Path " + path); + pathItem = new PathItem(); + pathItem.setSummary(endpoint.getDisplayName()); + pathItem.setDescription(endpoint.getDescription()); + paths.put(path, pathItem); + } + resolveEndpointRoute(path, pathItem, endpoint); + }, () -> { + PathItem pathItem = paths.get(path); + if (pathItem == null) { + log.debug("Path " + path); + pathItem = new PathItem(); + pathItem.setSummary(route.getName()); + paths.put(path, pathItem); + } + resolveFallbackRoute(route, pathItem); + }); + if (route.getSubRouter() != null) { + addRouter(path, route.getSubRouter(), consumer); } - // action.setIs(Arrays.asList(endpoint.getTraits())); } } + protected void resolveFallbackRoute(Route r, PathItem pathItem) { + Operation o = new Operation(); + o.setParameters(Arrays.stream(r.getPath().split("/")) + .filter(segment -> segment.startsWith(":")) + .map(segment -> segment.substring(1)) + .map(segment -> { + Parameter p = new Parameter(); + p.setName(segment); + p.setRequired(true); + p.setAllowEmptyValue(false); + return p; + }).collect(Collectors.toList())); + Optional.ofNullable(r.methods()).ifPresent(methods -> methods.stream().forEach(m -> resolveMethod(m.name(), pathItem, o))); + } + @SuppressWarnings("rawtypes") - private Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { + protected Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { MediaType mediaType = new MediaType(); mediaType.setExample(mimeType.getExample()); if (mimeType.getFormParameters() != null) { @@ -618,4 +635,22 @@ public String toString() { return value; } } + + /** + * OpenAPI output format + */ + public static enum Format { + YAML, + JSON; + + public static final Format parse(String text) { + if (text == null) { + throw new IllegalArgumentException("Cannot parse null to OpenAPI Format"); + } + return Arrays.stream(values()) + .filter(v -> v.name().equals(text.trim().toUpperCase())) + .findAny() + .orElseThrow(() -> new IllegalStateException("Unsupported OpenAPI Format:" + text)); + } + } } diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java new file mode 100644 index 0000000000..3816c5f7a9 --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java @@ -0,0 +1,621 @@ +package com.gentics.mesh.generator; + +import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; +import static com.gentics.mesh.core.rest.error.Errors.error; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static org.apache.commons.lang3.StringUtils.isEmpty; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; +import org.apache.commons.lang.StringUtils; +import org.raml.model.MimeType; +import org.raml.model.parameter.AbstractParam; +import org.reflections.Reflections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.context.InternalActionContext; +import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.mesh.etc.config.HttpServerConfig; +import com.gentics.mesh.http.HttpConstants; +import com.gentics.mesh.rest.InternalEndpointRoute; +import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import com.hazelcast.core.HazelcastInstance; + +import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.Paths; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; + +/** + * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. + * + * @author plyhun + * + */ +public class OpenAPIv3GeneratorOld extends AbstractEndpointGenerator { + + private static final String API_VERSION_PATH_PREFIX = "/api/v"; + + private static final Logger log = LoggerFactory.getLogger(OpenAPIv3GeneratorOld.class); + + private final HazelcastInstance hz; + private final HttpServerConfig httpServerConfig; + + public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig) { + this.hz = hz; + this.httpServerConfig = httpServerConfig; + } + + public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder, boolean cleanup) throws IOException { + super(outputFolder, cleanup); + this.hz = hz; + this.httpServerConfig = httpServerConfig; + } + + public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder) throws IOException { + super(outputFolder); + this.hz = hz; + this.httpServerConfig = httpServerConfig; + } + + public void generate(InternalActionContext ac, String format) { + log.info("Starting OpenAPIv3 generation..."); + OpenAPI openApi = new OpenAPI(); + Info info = new Info(); + info.setTitle("Gentics Mesh REST API"); + info.setVersion(MeshVersion.getBuildInfo().getVersion()); + if (hz != null) { + List servers = hz.getCluster().getMembers().stream().map(m -> { + Server server = new Server(); + server.setUrl(m.getAddress().toString()); + return server; + }).collect(Collectors.toList()); + openApi.servers(servers); + } else { + Server server = new Server(); + server.setUrl((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + openApi.servers(Collections.singletonList(null)); + } + openApi.setInfo(info); + try { + addSecurity(openApi); + addComponents(openApi); + addCoreEndpoints(openApi); + addProjectEndpoints(openApi); + } catch (IOException e) { + throw new RuntimeException("Could not add all verticles to raml generator", e); + } + + //new OpenAPI30To31().process(openApi); + //openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); + String formatted; + String mime; + switch (format) { + case "yaml": + try { + mime = APPLICATION_YAML_UTF8; + //formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); + formatted = ac.isMinify(httpServerConfig) ? Yaml.mapper().writer().writeValueAsString(openApi) : Yaml.pretty().writeValueAsString(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException("Could not generate YAML", e); + } + break; + case "json": + mime = APPLICATION_JSON_UTF8; + try { + //formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + formatted = ac.isMinify(httpServerConfig) ? Json.mapper().writer().writeValueAsString(openApi) : Json.pretty(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + break; + default: + throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); + } + ac.send(formatted, OK, mime); + } + + private void addSecurity(OpenAPI openApi) { + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } + SecurityScheme securityBearerAuth = new SecurityScheme(); + securityBearerAuth.setScheme("bearer"); + securityBearerAuth.setType(SecurityScheme.Type.HTTP); + securityBearerAuth.setBearerFormat("JWT"); + components.addSecuritySchemes("bearerAuth", securityBearerAuth); + //TODO OAuth2 + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + openApi.addSecurityItem(reqBearerAuth); + } + + private String makeSchemaName(Class cls) { + return "Mesh" + cls.getSimpleName(); + } + + private void addComponents(OpenAPI openApi) { + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } + components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); + Reflections reflections = new Reflections("com.gentics.mesh"); + reflections.getSubTypesOf(RestModel.class).stream().forEach(cls -> fillComponent(cls, components)); + } + + @SuppressWarnings("rawtypes") + private void fillComponent(Class cls, Components components) { + if (!cls.getPackageName().startsWith("com.gentics.mesh") || StringUtils.isBlank(cls.getSimpleName())) { + return; + } + Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); + schema.setType("object"); + schema.setName(cls.getSimpleName()); + List> fieldStreams = new ArrayList<>(); + final List generics = new ArrayList<>(); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); + Deque> dq = new ArrayDeque<>(2); + dq.addLast(cls); + while(!dq.isEmpty()) { + Class tclass = dq.pop(); + log.debug("Class: " + tclass.getCanonicalName()); + /*if (tclass != cls) { + Schema tschema = components.getSchemas().computeIfAbsent(tclass.getSimpleName(), key -> new Schema()); + tschema.setName(tclass.getSimpleName()); + Schema refSchema = new Schema<>(); + refSchema.$ref("#/components/schemas/" + tclass.getSimpleName()); + schema.addAllOfItem(refSchema); + refSchema = new Schema<>(); + refSchema.$ref("#/components/schemas/" + cls.getSimpleName()); + tschema.addAnyOfItem(refSchema); + }*/ + fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); + dq.addAll(Arrays.stream(tclass.getInterfaces()).filter(i -> i.getPackageName().startsWith("com.gentics.mesh")).collect(Collectors.toList())); + tclass = tclass.getSuperclass(); + if (tclass != null) { + dq.addLast(tclass); + } + } + if (generics.size() > 0) { + log.debug(" - Generics: " + Arrays.toString(generics.toArray())); + } + Map properties = fieldStreams.stream().flatMap(Function.identity()) + .filter(f -> !Modifier.isStatic(f.getModifiers())).peek(f -> { + Class t = f.getType(); + if (!RestModel.class.isAssignableFrom(t) && !t.isPrimitive() && !t.getCanonicalName().startsWith("java.lang")) { + fillComponent(t, components); + } + }) + .map(f -> { + String name = f.getName(); + log.debug(" - Field: " + f); + Schema fieldSchema = new Schema(); + fieldSchema.setName(name); + JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); + if (description != null) { + fieldSchema.setDescription(description.value()); + } + JsonProperty property = f.getAnnotation(JsonProperty.class); + if (property != null) { + if (StringUtils.isNotBlank(property.defaultValue())) { + fieldSchema.setDefault(property.defaultValue()); + } + if (StringUtils.isNotBlank(property.value())) { + name = property.value(); + } + if (property.required()) { + schema.addRequiredItem(name); + } + } + Class t = f.getType(); + JsonDeserialize jdes = f.getAnnotation(JsonDeserialize.class); + if (jdes != null && jdes.as() != null) { + t = jdes.as(); + fieldSchema.setType("object"); + fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + } else { + generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : new Type[0])); + if (generics.size() > 0) { + log.debug(" - Generics: " + Arrays.toString(generics.toArray())); + } + fillType(components, t, fieldSchema, generics); + } + fieldSchema.setTypes(Collections.singleton(fieldSchema.getType())); + return new UnmodifiableMapEntry<>(name, fieldSchema); + }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + schema.setProperties(properties); + components.addSchemas(cls.getSimpleName(), schema); + } + + @Override + protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle, boolean isProject) + throws IOException { + // Check whether the resource was already added. Maybe we just need to extend it + Paths paths = consumer.getPaths(); + if (paths == null) { + paths = new Paths(); + consumer.setPaths(paths); + } + for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { + if ("eventbus".equals(verticle.getBasePath())) { + continue; + } + String fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() + + endpoint.getRamlPath(); + if (isEmpty(verticle.getBasePath())) { + fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); + } + PathItem pathItem = paths.get(fullPath); + if (pathItem == null) { + log.debug("Path " + fullPath); + pathItem = new PathItem(); + pathItem.setSummary(endpoint.getDisplayName()); + pathItem.setDescription(endpoint.getDescription()); + consumer.path(fullPath, pathItem); + } + Operation operation = new Operation(); + HttpMethod method = endpoint.getMethod(); + if (method == null) { + method = HttpMethod.GET; + } + if (endpoint.isInsecure()) { + operation.setSecurity(Collections.emptyList()); + } else { + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + operation.setSecurity(List.of(reqBearerAuth)); + } + switch (method.name()) { + case "DELETE": + pathItem.setDelete(operation); + break; + case "GET": + pathItem.setGet(operation); + break; + case "HEAD": + pathItem.setHead(operation); + break; + case "OPTIONS": + pathItem.setOptions(operation); + break; + case "PATCH": + pathItem.setPatch(operation); + break; + case "POST": + pathItem.setPost(operation); + break; + case "PUT": + pathItem.setPut(operation); + break; + case "TRACE": + pathItem.setTrace(operation); + break; + default: + break; + } + List> params = List.of( + isProject ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) : Stream.of(), + endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), + endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); + operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); + ApiResponses responses = new ApiResponses(); + endpoint.getProduces().stream().map(e -> { + ApiResponse response = new ApiResponse(); + Content responseBody = new Content(); + responseBody.addMediaType(e, new MediaType()); + response.setContent(responseBody); + response.setDescription(e); + if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { + response.setExtensions(Map.of("x-is-file", true)); + Schema schema = new Schema<>(); + schema.setType("string"); + schema.setFormat("binary"); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + responseBody.addMediaType("application/octet-stream", mediaType); + response.setContent(responseBody); + } + return new UnmodifiableMapEntry("default", response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); + endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> { + ApiResponse response = new ApiResponse(); + if (e.getValue().getDescription().startsWith("Generated login token")) { + e.getValue().getHeaders(); + } + response.setDescription(e.getValue().getDescription()); + Content responseBody = new Content(); + if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { + Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); + if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { + Schema schema = new Schema<>(); + schema.set$ref("#/components/schemas/" + ref.getSimpleName()); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + mediaType.setExample(e.getValue()); + responseBody.addMediaType("*/*", mediaType); + response.setContent(responseBody); + } else { + if (e.getValue().getBody() != null) { + e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) + .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) + .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); + } else { + log.warn("Body of " + e.getKey() + " is null!"); + } + response.setContent(responseBody); + } + } + return new UnmodifiableMapEntry(e.getKey(), response); + }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); + operation.setResponses(responses); + if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { + RequestBody requestBody = new RequestBody(); + Content content = new Content(); + endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) + .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) + .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); + requestBody.setContent(content); + operation.setRequestBody(requestBody); + } + // action.setIs(Arrays.asList(endpoint.getTraits())); + } + } + + @SuppressWarnings("rawtypes") + private Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { + MediaType mediaType = new MediaType(); + mediaType.setExample(mimeType.getExample()); + if (mimeType.getFormParameters() != null) { + Map props = mimeType.getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) + .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); + Schema schema = new Schema<>(); + schema.setType("object"); + schema.setProperties(props); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry("multipart/form-data", mediaType); + } else if (mimeType.getSchema() != null) { + JsonObject jschema = new JsonObject(mimeType.getSchema()); + Schema schema = new Schema<>(); + schema.setType(jschema.getString("type", "string")); + schema.set$id(jschema.getString("id")); + schema.set$ref("#/components/schemas/" + refClass.getSimpleName()); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(key, mediaType); + } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { + mediaType.setExample(mimeType.getExample()); + Schema schema = new Schema<>(); + schema.setType("object"); + mediaType.setSchema(schema); + return new UnmodifiableMapEntry(key, mediaType); + } else { + return new UnmodifiableMapEntry(key, mediaType); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void fillType(Components components, Class t, Schema fieldSchema, List generics) { + if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { + if (int.class.isAssignableFrom(t) || Integer.class.isAssignableFrom(t)) { + fieldSchema.setType("integer"); + fieldSchema.setFormat("int32"); + } else if (boolean.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { + fieldSchema.setType("boolean"); + } else if (float.class.isAssignableFrom(t) || Float.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); + fieldSchema.setFormat("float"); + } else if (long.class.isAssignableFrom(t) || Long.class.isAssignableFrom(t)) { + fieldSchema.setType("integer"); + fieldSchema.setFormat("int64"); + } else if (double.class.isAssignableFrom(t) || Double.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); + fieldSchema.setFormat("double"); + } else if (BigDecimal.class.isAssignableFrom(t) || Number.class.isAssignableFrom(t)) { + fieldSchema.setType("number"); + } else { + fieldSchema.setType("object"); + } + } else if (CharSequence.class.isAssignableFrom(t)) { + fieldSchema.setType("string"); + } else if (t.isArray() || List.class.isAssignableFrom(t)) { + fieldSchema.setType("array"); + Schema itemSchema = new Schema(); + if (t.isArray()) { + List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); + fillType(components, t.getComponentType(), itemSchema, generics1); + } else if (generics.size() > 0 && Class.class.isInstance(generics.get(0))) { + Class itemClass = Class.class.cast(generics.get(0)); + List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); + fillType(components, itemClass, itemSchema, generics1); + } else { + // TODO + log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); + } + fieldSchema.setItems(itemSchema); +// } else if (t.isEnum()) { +// fieldSchema.setType("string"); +// fieldSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); + } else { + if (t.isEnum()) { + Schema enumSchema = new Schema(); + enumSchema.setType("string"); + enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); + components.addSchemas(t.getSimpleName(), enumSchema); + } + if (Map.class.isAssignableFrom(t)) { + if (generics.size() == 2) { + BiConsumer innerTypeMapper = (ty, tfieldSchema) -> { + Class tt = Class.class.isInstance(ty) ? Class.class.cast(ty) : generics.get(1).getClass(); + if (tt.isPrimitive() || Number.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { + if (int.class.isAssignableFrom(tt) || Integer.class.isAssignableFrom(tt)) { + tfieldSchema.setType("integer"); + tfieldSchema.setFormat("int32"); + } else if (boolean.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { + tfieldSchema.setType("boolean"); + } else if (float.class.isAssignableFrom(tt) || Float.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + tfieldSchema.setFormat("float"); + } else if (long.class.isAssignableFrom(tt) || Long.class.isAssignableFrom(tt)) { + tfieldSchema.setType("integer"); + tfieldSchema.setFormat("int64"); + } else if (double.class.isAssignableFrom(tt) || Double.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + tfieldSchema.setFormat("double"); + } else if (BigDecimal.class.isAssignableFrom(tt) || Number.class.isAssignableFrom(tt)) { + tfieldSchema.setType("number"); + } else { + tfieldSchema.setType("object"); + } + } else if (CharSequence.class.isAssignableFrom(tt)) { + tfieldSchema.setType("string"); + } else { + tfieldSchema.setType("object"); + } + }; + fieldSchema.setType("object"); // TODO why object? + //innerTypeMapper.accept(generics.get(0).getClass(), fieldSchema); + Schema valueSchema = new Schema<>(); + innerTypeMapper.accept(generics.get(1), valueSchema); + fieldSchema.setAdditionalProperties(valueSchema); + } else { + fieldSchema.setType("object"); + fieldSchema.setAdditionalProperties(new Schema().type("object")); + } + } else if (JsonObject.class.isAssignableFrom(t) || JsonSerializable.class.isAssignableFrom(t)) { + fieldSchema.set$ref("#/components/schemas/AnyJson"); + } else { + fieldSchema.setType("object"); + fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); + } + } + } + + @SuppressWarnings("rawtypes") + private final Parameter parameter(String name, AbstractParam param, InParameter inType) { + Schema schema; + switch (param.getType()) { + case BOOLEAN: + schema = new Schema(); + schema.setType("boolean"); + break; + case DATE: + schema = new Schema(); + schema.setType("integer"); + schema.setFormat("int64"); + break; + case FILE: + schema = new Schema(); + schema.setType("string"); + schema.setFormat("binary"); + break; + case INTEGER: + schema = new Schema(); + schema.setType("integer"); + schema.setFormat("int32"); + break; + case NUMBER: + schema = new Schema(); + schema.setType("number"); + schema.setFormat("double"); + break; + case STRING: + schema = new Schema(); + schema.setType("string"); + break; + default: + schema = new Schema(); + schema.setType("object"); + break; + } + schema.setMinimum(param.getMinimum()); + schema.setMaximum(param.getMaximum()); + schema.setMinLength(param.getMinLength()); + schema.setMaxLength(param.getMaxLength()); + schema.setDefault(param.getDefaultValue()); + schema.setEnum(param.getEnumeration()); + schema.setPattern(param.getPattern()); + schema.setDescription(param.getDescription()); + if (StringUtils.isNotBlank(param.getExample())) { + schema.setExample(param.getExample()); + } + Parameter p = new Parameter(); + p.setRequired(param.isRequired()); + p.setDescription(param.getDescription()); + p.setSchema(schema); + p.setName(name); + if (inType != null) { + p.setIn(inType.value); + } + return p; + } + + private enum InParameter { + PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); + + private final String value; + + private InParameter(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } +} From a4416a4147b560d1fdcc357c76bf0139669a388c Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 30 Jan 2026 18:57:06 +0100 Subject: [PATCH 15/75] Generates --- .../com/gentics/mesh/generator/OpenAPIv3Generator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index d92a6b95da..55f10593b0 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -431,6 +431,14 @@ protected void resolveFallbackRoute(Route r, PathItem pathItem) { p.setAllowEmptyValue(false); return p; }).collect(Collectors.toList())); + ApiResponses responses = new ApiResponses(); + ApiResponse response = new ApiResponse(); + Content responseBody = new Content(); + responseBody.addMediaType("*/*", new MediaType()); + response.setDescription("Auto generated response description for " + r.getPath()); + response.setContent(responseBody); + responses.addApiResponse("200", response); + o.setResponses(responses); Optional.ofNullable(r.methods()).ifPresent(methods -> methods.stream().forEach(m -> resolveMethod(m.name(), pathItem, o))); } From 2d8c58f0ca8e4c2c58aa0d0754e3a21fe6763546 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 13 Feb 2026 13:58:42 +0100 Subject: [PATCH 16/75] Refactoring pt1 --- .../gentics/mesh/router/CustomRouterImpl.java | 1 + .../gentics/mesh/router/PluginRouterImpl.java | 14 +++- .../mesh/router/ProjectsRouterImpl.java | 8 +- .../core/endpoint/admin/AdminHandler.java | 33 +++++++- .../mesh/generator/OpenAPIv3Generator.java | 84 +++++++++++-------- .../com/gentics/mesh/router/APIRouter.java | 9 +- .../com/gentics/mesh/router/CustomRouter.java | 2 +- .../gentics/mesh/router/InternalRouter.java | 16 ++++ .../com/gentics/mesh/router/PluginRouter.java | 10 ++- .../gentics/mesh/router/ProjectRouter.java | 9 +- .../gentics/mesh/router/ProjectsRouter.java | 2 +- .../com/gentics/mesh/router/RootRouter.java | 11 +-- 12 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 mdm/api/src/main/java/com/gentics/mesh/router/InternalRouter.java diff --git a/common/src/main/java/com/gentics/mesh/router/CustomRouterImpl.java b/common/src/main/java/com/gentics/mesh/router/CustomRouterImpl.java index fa2f34ad2a..09a1df02a9 100644 --- a/common/src/main/java/com/gentics/mesh/router/CustomRouterImpl.java +++ b/common/src/main/java/com/gentics/mesh/router/CustomRouterImpl.java @@ -38,6 +38,7 @@ public CustomRouterImpl(Vertx vertx, RootRouterImpl root) { * * @return */ + @Override public Router getRouter() { return this.router; } diff --git a/common/src/main/java/com/gentics/mesh/router/PluginRouterImpl.java b/common/src/main/java/com/gentics/mesh/router/PluginRouterImpl.java index a05d945dc8..c33c6de651 100644 --- a/common/src/main/java/com/gentics/mesh/router/PluginRouterImpl.java +++ b/common/src/main/java/com/gentics/mesh/router/PluginRouterImpl.java @@ -1,5 +1,6 @@ package com.gentics.mesh.router; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -23,9 +24,9 @@ public class PluginRouterImpl implements PluginRouter { private static final Logger log = LoggerFactory.getLogger(APIRouterImpl.class); - private Map pluginRouters = new HashMap<>(); + private final Map pluginRouters = new HashMap<>(); - private Router router; + private final Router router; /** * Create a new plugin router. @@ -73,4 +74,13 @@ public void removeRouter(String name) { } } + @Override + public Map pluginRouters() { + return Collections.unmodifiableMap(pluginRouters); + } + + @Override + public Router getRouter() { + return router; + } } diff --git a/common/src/main/java/com/gentics/mesh/router/ProjectsRouterImpl.java b/common/src/main/java/com/gentics/mesh/router/ProjectsRouterImpl.java index 75c7d7c139..5d2e13ab58 100644 --- a/common/src/main/java/com/gentics/mesh/router/ProjectsRouterImpl.java +++ b/common/src/main/java/com/gentics/mesh/router/ProjectsRouterImpl.java @@ -36,9 +36,9 @@ public class ProjectsRouterImpl implements ProjectsRouter { private final Vertx vertx; - private APIRouterImpl apiRouter; + private final APIRouterImpl apiRouter; - private Router router; + private final Router router; public ProjectsRouterImpl(Vertx vertx, APIRouterImpl apiRouter) { this.vertx = vertx; @@ -121,4 +121,8 @@ public ProjectRouter projectRouter() { return projectRouter; } + @Override + public Router getRouter() { + return router; + } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index ea667e9d2f..e5fe3d8a25 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -12,11 +12,17 @@ import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,6 +47,7 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.generator.OpenAPIv3Generator; import com.gentics.mesh.generator.OpenAPIv3Generator.Format; +import com.gentics.mesh.generator.OpenAPIv3Generator.InParameter; import com.gentics.mesh.generator.RAMLGenerator; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.parameter.BackupParameters; @@ -50,6 +57,8 @@ import com.gentics.mesh.util.RxUtil; import com.gentics.mesh.util.UUIDUtil; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; import io.vertx.core.Vertx; /** @@ -244,11 +253,29 @@ public void handleOpenAPIv3(InternalActionContext ac, String format) { .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) .orElseGet(() -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort()))); - OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(List.of("/api/v1", "/api/:apiversion")), Optional.empty()); + Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() + .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) + .map(project -> "\\/api\\/v2\\/" + project + "[.]*").collect(Collectors.toSet())); + blacklistedRouteRegex.addAll(List.of("\\/api\\/v2", "\\/api\\/v1[.]*", "\\/api\\/\\{apiversion\\}[.]*")); + OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(blacklistedRouteRegex), Optional.empty()); ac.send(generator.generate( - routerStorageRegistry.getInstances().stream().map(rr -> rr.root().getRouter()).toList(), + Stream.of( + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v2/{projectName}")) + ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), Format.parse(format), - !httpServerConfig.isMinifyJson()), + !httpServerConfig.isMinifyJson(), + Optional.of((path, item) -> { + if (path.contains("/{projectName}/")) { + Parameter projectNameParam = new Parameter().name("projectName").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); + item.readOperations().stream() + .forEach(o -> o.getParameters().stream().filter(p -> "projectName".equals(p.getName())).findAny() + .ifPresentOrElse(present -> { + // already exists, no action + }, () -> o.addParametersItem(projectNameParam))); + } + return path; + })), OK, "yaml".equalsIgnoreCase(format) ? HttpConstants.APPLICATION_YAML_UTF8 diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 55f10593b0..a6d90ab973 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -13,15 +13,19 @@ import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Optional; import java.util.function.BiConsumer; +import java.util.function.BiFunction; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -42,6 +46,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.mesh.generator.OpenAPIv3Generator.InParameter; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -78,18 +83,18 @@ public class OpenAPIv3Generator { private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); - private final Optional> maybePathBlacklist; - private final Optional> maybePathWhitelist; + private final Optional> maybePathBlacklist; + private final Optional> maybePathWhitelist; private final List servers; - public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, @Nonnull Optional> maybePathWhitelist) { + public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, @Nonnull Optional> maybePathWhitelist) { this.maybePathBlacklist = maybePathBlacklist; this.maybePathWhitelist = maybePathWhitelist; this.servers = servers; } - public String generate(List routers, Format format, boolean pretty) { + public String generate(Map routers, Format format, boolean pretty, Optional> maybePathItemTransformer) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); openApi.setPaths(new Paths()); @@ -107,8 +112,8 @@ public String generate(List routers, Format format, boolean pretty) { try { addSecurity(openApi); addComponents(openApi); - for (Router router : routers) { - addRouter(StringUtils.EMPTY, router, openApi); + for (Entry routerAndParent : routers.entrySet()) { + addRouter(routerAndParent.getValue(), routerAndParent.getKey(), openApi, maybePathItemTransformer); } } catch (IOException e) { throw new RuntimeException("Could not add all verticles to raml generator", e); @@ -276,9 +281,6 @@ protected void resolveEndpointRoute(String path, PathItem pathItem, InternalEndp } resolveMethod(method.name(), pathItem, operation); List> params = List.of( - path.contains("/{project}/") - ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) - : Stream.empty(), endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); @@ -376,45 +378,56 @@ protected void resolveMethod(String methodName, PathItem pathItem, Operation ope } } - protected void addRouter(String parent, Router router, OpenAPI consumer) + protected void addRouter(String parent, Router router, OpenAPI consumer, Optional> maybePathItemTransformer) throws IOException { Paths paths = consumer.getPaths(); for (Route route : router.getRoutes()) { - if (StringUtils.isBlank(route.getPath()) - || maybePathBlacklist.map(list -> list.contains(route.getPath())).filter(blacklisted -> blacklisted).isPresent() - || (maybePathWhitelist.isPresent() && maybePathWhitelist.map(list -> list.contains(route.getPath())).filter(whitelisted -> whitelisted).isEmpty())) { + String rawPath = route.getPath(); + if (StringUtils.isBlank(rawPath)) { continue; } - String path = parent + (StringUtils.equals(route.getPath(), "/") ? "/" : Arrays.stream(route.getPath().split("/")) - .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) - .collect(Collectors.joining("/"))); - - log.info("Processing path {}", path); + String path = (parent + (StringUtils.equals(rawPath, "/") ? "/" : Arrays.stream(rawPath.split("/")) + .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) + .collect(Collectors.joining("/")))) + .replace("//", "/"); + + if(maybePathBlacklist.flatMap(list -> list.stream().filter(blacklisted -> Pattern.matches(blacklisted, path)).findAny()).isPresent() + || (maybePathWhitelist.isPresent() && maybePathWhitelist.flatMap(list -> list.stream().filter(whitelisted -> Pattern.matches(whitelisted, path)).findAny()).isEmpty())) { + log.debug("Path filtered off: " + path); + continue; + } + PathItem pathItem = Optional.ofNullable(paths.get(path)).orElseGet(() -> { + log.debug("Raw path: " + path); + PathItem item = new PathItem(); + item.setSummary(route.getName()); + paths.put(path, item); + return item; + }); Optional.ofNullable(route.getMetadata(InternalEndpointRoute.class.getCanonicalName())) .map(InternalEndpointRoute.class::cast) .ifPresentOrElse(endpoint -> { - PathItem pathItem = paths.get(path); - if (pathItem == null) { - log.debug("Path " + path); - pathItem = new PathItem(); - pathItem.setSummary(endpoint.getDisplayName()); - pathItem.setDescription(endpoint.getDescription()); - paths.put(path, pathItem); - } + log.debug("Path with metadata: " + path); + pathItem.setSummary(endpoint.getDisplayName()); + pathItem.setDescription(endpoint.getDescription()); resolveEndpointRoute(path, pathItem, endpoint); }, () -> { - PathItem pathItem = paths.get(path); - if (pathItem == null) { - log.debug("Path " + path); - pathItem = new PathItem(); - pathItem.setSummary(route.getName()); - paths.put(path, pathItem); - } resolveFallbackRoute(route, pathItem); }); + String path1 = maybePathItemTransformer.map(pathItemTransformer -> { + String newPath = pathItemTransformer.apply(path, pathItem); + if (!StringUtils.equals(path, newPath)) { + paths.remove(path, pathItem); + paths.put(newPath, pathItem); + } + return path; + }).orElse(path); + if (pathItem.readOperations().isEmpty()) { + log.debug("Path removed due to having no operations: " + path1); + paths.remove(path1, pathItem); + } if (route.getSubRouter() != null) { - addRouter(path, route.getSubRouter(), consumer); + addRouter(path, route.getSubRouter(), consumer, maybePathItemTransformer); } } } @@ -429,6 +442,7 @@ protected void resolveFallbackRoute(Route r, PathItem pathItem) { p.setName(segment); p.setRequired(true); p.setAllowEmptyValue(false); + p.in(InParameter.PATH.toString()).schema(new Schema().type("string").description("A path parameter `" + segment + "` of a fallback type `string`")); return p; }).collect(Collectors.toList())); ApiResponses responses = new ApiResponses(); @@ -629,7 +643,7 @@ private final Parameter parameter(String name, AbstractParam param, InParameter return p; } - private enum InParameter { + public enum InParameter { PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); private final String value; diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/APIRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/APIRouter.java index 12448d80e9..60a57be4cd 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/APIRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/APIRouter.java @@ -5,14 +5,7 @@ /** * Central router for /api/v1 routes */ -public interface APIRouter { - - /** - * Internal vert.x router for the API router. - * - * @return - */ - Router getRouter(); +public interface APIRouter extends InternalRouter { /** * Return the router to which all projects will be mounted. diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/CustomRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/CustomRouter.java index 538169e3dc..35151892a6 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/CustomRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/CustomRouter.java @@ -3,6 +3,6 @@ /** * Marker interface for custom routers. */ -public interface CustomRouter { +public interface CustomRouter extends InternalRouter { } diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/InternalRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/InternalRouter.java new file mode 100644 index 0000000000..8225ad9486 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/router/InternalRouter.java @@ -0,0 +1,16 @@ +package com.gentics.mesh.router; + +import io.vertx.ext.web.Router; + +/** + * A base for all internal routers + */ +public interface InternalRouter { + + /** + * Internal vert.x router for the API router. + * + * @return + */ + Router getRouter(); +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/PluginRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/PluginRouter.java index 1657e5e2cb..6d228adbdd 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/PluginRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/PluginRouter.java @@ -1,11 +1,13 @@ package com.gentics.mesh.router; +import java.util.Map; + import io.vertx.ext.web.Router; /** * Router to track plugin sub routers. */ -public interface PluginRouter { +public interface PluginRouter extends InternalRouter { /** * Remove the plugin router. @@ -22,4 +24,10 @@ public interface PluginRouter { */ void addRouter(String name, Router pluginRouter); + /** + * Get currently registered routers. + * + * @return immutable name/router map + */ + Map pluginRouters(); } diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/ProjectRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/ProjectRouter.java index 7f4abbd90f..aa14048040 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/ProjectRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/ProjectRouter.java @@ -7,14 +7,7 @@ /** * This class manages the project routers (e.g. routers for endpoints like :apibase:/:projectName/nodes) */ -public interface ProjectRouter { - - /** - * Return the Vert.x router. - * - * @return - */ - Router getRouter(); +public interface ProjectRouter extends InternalRouter { /** * Return all routers that have been registered. diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/ProjectsRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/ProjectsRouter.java index c12a9ca330..a78a6fa52e 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/ProjectsRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/ProjectsRouter.java @@ -6,7 +6,7 @@ import io.vertx.ext.web.Router; -public interface ProjectsRouter { +public interface ProjectsRouter extends InternalRouter { /** * Common router which holds project specific routes (e.g: /nodes /tagFamilies) diff --git a/mdm/api/src/main/java/com/gentics/mesh/router/RootRouter.java b/mdm/api/src/main/java/com/gentics/mesh/router/RootRouter.java index ea72c30611..52f91c9202 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/router/RootRouter.java +++ b/mdm/api/src/main/java/com/gentics/mesh/router/RootRouter.java @@ -1,11 +1,9 @@ package com.gentics.mesh.router; -import io.vertx.ext.web.Router; - /** * The root router is the top level router of the routing stack. */ -public interface RootRouter { +public interface RootRouter extends InternalRouter { /** * Return the /api/v1 router @@ -14,13 +12,6 @@ public interface RootRouter { */ APIRouter apiRouter(); - /** - * Return the Vert.x router. - * - * @return - */ - Router getRouter(); - /** * Return the central router storage. * From ed4bedd3067f808421b5eb37126ea9f486b5d36b Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 Feb 2026 09:21:04 +0100 Subject: [PATCH 17/75] REST API refactor + tests base --- .../core/endpoint/admin/AdminHandler.java | 7 +- .../core/endpoint/admin/RestInfoEndpoint.java | 28 ++--- .../mesh/generator/OpenAPIv3Generator.java | 113 +++++++++++++----- .../parameter/ParameterProviderContext.java | 5 + .../parameter/impl/OpenAPIParametersImpl.java | 67 +++++++++++ .../parameter/impl/SortingParametersImpl.java | 2 + .../mesh/core/rest/openapi/Format.java | 34 ++++++ .../mesh/core/rest/openapi/Version.java | 39 ++++++ .../mesh/parameter/OpenAPIParameters.java | 60 ++++++++++ .../mesh/parameter/ParameterProvider.java | 18 +++ 10 files changed, 319 insertions(+), 54 deletions(-) create mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index e5fe3d8a25..8e6c5bf70d 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -39,6 +39,7 @@ import com.gentics.mesh.core.rest.admin.cluster.coordinator.CoordinatorMasterResponse; import com.gentics.mesh.core.rest.admin.consistency.ConsistencyCheckResponse; import com.gentics.mesh.core.rest.admin.status.MeshStatusResponse; +import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; @@ -247,7 +248,9 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { return info; } - public void handleOpenAPIv3(InternalActionContext ac, String format) { + public void handleOpenAPIv3(InternalActionContext ac) { + boolean useVersion31 = ac.getOpenAPIParameters().getVersion() == Version.V31; + String format = ac.getOpenAPIParameters().getFormat().name().toLowerCase(); HttpServerConfig httpServerConfig = options.getHttpServerOptions(); List servers = Optional.ofNullable(clusterManager.getHazelcast()) .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) @@ -275,7 +278,7 @@ public void handleOpenAPIv3(InternalActionContext ac, String format) { }, () -> o.addParametersItem(projectNameParam))); } return path; - })), + }), useVersion31), OK, "yaml".equalsIgnoreCase(format) ? HttpConstants.APPLICATION_YAML_UTF8 diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index ae911ec6be..3f7f6a3ba6 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -2,6 +2,7 @@ import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML; +import static com.gentics.mesh.http.HttpConstants.TEXT_PLAIN_UTF8; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.GET; @@ -12,6 +13,7 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.example.RestInfoExamples; +import com.gentics.mesh.parameter.OpenAPIParameters; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.RouterStorage; import com.gentics.mesh.router.route.AbstractInternalEndpoint; @@ -67,30 +69,18 @@ public void registerEndPoints() { adminHandler.handleRAML(ac); }, false); - secure("/openapi.yaml"); + secure("/openapi"); InternalEndpointRoute openapiYml = createRoute(); - openapiYml.path("/openapi.yaml"); + openapiYml.path("/openapi"); openapiYml.method(GET); - openapiYml.description("Endpoint which provides a OpenAPIv3 YAML document for all registed endpoints."); - openapiYml.displayName("OpenAPI YAML specification"); + openapiYml.description("Endpoint which provides a OpenAPIv3 document for all registed endpoints."); + openapiYml.displayName("OpenAPI specification"); openapiYml.exampleResponse(OK, "Not yet specified"); - openapiYml.produces(APPLICATION_YAML); + openapiYml.produces(TEXT_PLAIN_UTF8); + openapiYml.addQueryParameters(OpenAPIParameters.class); openapiYml.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac, "yaml"); - }, false); - - secure("/openapi.json"); - InternalEndpointRoute openapiJson = createRoute(); - openapiJson.path("/openapi.json"); - openapiJson.method(GET); - openapiJson.description("Endpoint which provides a OpenAPIv3 JSON document for all registed endpoints."); - openapiJson.displayName("OpenAPI JSON specification"); - openapiJson.exampleResponse(OK, "Not yet specified"); - openapiJson.produces(APPLICATION_JSON); - openapiJson.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac, "json"); + adminHandler.handleOpenAPIv3(ac); }, false); secure("/"); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index a6d90ab973..386c523d92 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -43,15 +43,18 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializable; +import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.core.rest.common.RestModel; -import com.gentics.mesh.generator.OpenAPIv3Generator.InParameter; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.rest.InternalEndpointRoute; import io.swagger.v3.core.util.Json; +import io.swagger.v3.core.util.Json31; +import io.swagger.v3.core.util.OpenAPI30To31; import io.swagger.v3.core.util.Yaml; +import io.swagger.v3.core.util.Yaml31; import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; @@ -88,6 +91,13 @@ public class OpenAPIv3Generator { private final List servers; + /** + * + * + * @param servers + * @param maybePathBlacklist + * @param maybePathWhitelist + */ public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, @Nonnull Optional> maybePathWhitelist) { this.maybePathBlacklist = maybePathBlacklist; this.maybePathWhitelist = maybePathWhitelist; @@ -95,6 +105,10 @@ public OpenAPIv3Generator(List servers, @Nonnull Optional routers, Format format, boolean pretty, Optional> maybePathItemTransformer) { + return generate(routers, format, pretty, maybePathItemTransformer, false); + } + + public String generate(Map routers, Format format, boolean pretty, Optional> maybePathItemTransformer, boolean useVersion31) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); openApi.setPaths(new Paths()); @@ -119,30 +133,8 @@ public String generate(Map routers, Format format, boolean prett throw new RuntimeException("Could not add all verticles to raml generator", e); } - //new OpenAPI30To31().process(openApi); - //openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); - String formatted; - switch (format) { - case YAML: - try { - //formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); - formatted = pretty ? Yaml.pretty().writeValueAsString(openApi) : Yaml.mapper().writer().writeValueAsString(openApi) ; - } catch (JsonProcessingException e) { - throw new RuntimeException("Could not generate YAML", e); - } - break; - case JSON: - try { - //formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); - formatted = pretty ? Json.pretty(openApi) : Json.mapper().writer().writeValueAsString(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - break; - default: - throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } - return formatted; + OpenAPIVersionWriter writer = useVersion31 ? new V31Writer() : new V30Writer(); + return writer.write(openApi, format, pretty); } protected void addSecurity(OpenAPI openApi) { @@ -437,14 +429,15 @@ protected void resolveFallbackRoute(Route r, PathItem pathItem) { o.setParameters(Arrays.stream(r.getPath().split("/")) .filter(segment -> segment.startsWith(":")) .map(segment -> segment.substring(1)) - .map(segment -> { - Parameter p = new Parameter(); - p.setName(segment); - p.setRequired(true); - p.setAllowEmptyValue(false); - p.in(InParameter.PATH.toString()).schema(new Schema().type("string").description("A path parameter `" + segment + "` of a fallback type `string`")); - return p; - }).collect(Collectors.toList())); + .map(segment -> new Parameter() + .name(segment) + .required(true) + .allowEmptyValue(false) + .in(InParameter.PATH.toString()) + .schema(new Schema() + .type("string") + .description("A path parameter `" + segment + "` of a fallback type `string`"))) + .collect(Collectors.toList())); ApiResponses responses = new ApiResponses(); ApiResponse response = new ApiResponse(); Content responseBody = new Content(); @@ -675,4 +668,58 @@ public static final Format parse(String text) { .orElseThrow(() -> new IllegalStateException("Unsupported OpenAPI Format:" + text)); } } + + public interface OpenAPIVersionWriter { + String write(OpenAPI api, Format format, boolean pretty); + } + private class V30Writer implements OpenAPIVersionWriter { + + @Override + public String write(OpenAPI openApi, Format format, boolean pretty) { + switch (format) { + case YAML: + try { + //formatted = pretty ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); + return pretty ? Yaml.pretty().writeValueAsString(openApi) : Yaml.mapper().writer().writeValueAsString(openApi) ; + } catch (JsonProcessingException e) { + throw new RuntimeException("Could not generate YAML", e); + } + case JSON: + try { + //formatted = pretty ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + return pretty ? Json.pretty(openApi) : Json.mapper().writer().writeValueAsString(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + default: + throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); + } + } + + } + private class V31Writer implements OpenAPIVersionWriter { + + @Override + public String write(OpenAPI openApi, Format format, boolean pretty) { + new OpenAPI30To31().process(openApi); + openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); + + switch (format) { + case YAML: + try { + return pretty ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException("Could not generate YAML", e); + } + case JSON: + try { + return pretty ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + default: + throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); + } + } + } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index dea6134b71..059667acb2 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -12,6 +12,7 @@ import com.gentics.mesh.parameter.impl.IndexMaintenanceParametersImpl; import com.gentics.mesh.parameter.impl.JobParametersImpl; import com.gentics.mesh.parameter.impl.NodeParametersImpl; +import com.gentics.mesh.parameter.impl.OpenAPIParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.ProjectLoadParametersImpl; import com.gentics.mesh.parameter.impl.ProjectPurgeParametersImpl; @@ -111,4 +112,8 @@ default DisplayParameters getDisplayParameters() { default ConsistencyCheckParameters getConsistencyCheckParameters() { return new ConsistencyCheckParametersImpl(this); } + + default OpenAPIParameters getOpenAPIParameters() { + return new OpenAPIParametersImpl(this); + } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java new file mode 100644 index 0000000000..eb020ec8c8 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java @@ -0,0 +1,67 @@ +package com.gentics.mesh.parameter.impl; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import org.raml.model.ParamType; +import org.raml.model.parameter.QueryParameter; + +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.handler.ActionContext; +import com.gentics.mesh.parameter.AbstractParameters; +import com.gentics.mesh.parameter.OpenAPIParameters; + +/** + * Parameters implementation of {@link OpenAPIParameters} + */ +public class OpenAPIParametersImpl extends AbstractParameters implements OpenAPIParameters { + + /** + * Create instance on given action context + * @param ac action context + */ + public OpenAPIParametersImpl(ActionContext ac) { + super(ac); + } + + /** + * Create empty instance + */ + public OpenAPIParametersImpl() { + } + + @Override + public Map getRAMLParameters() { + Map parameters = new HashMap<>(); + + // version + QueryParameter versionParameter = new QueryParameter(); + versionParameter.setDefaultValue(Version.V30.name()); + versionParameter.setDescription("Specify, whether the OpenAPI standard version 3.1 should be generated. If false or unset, a version 3.0 of standard will be used."); + versionParameter.setExample(Version.V31.name()); + versionParameter.setRequired(false); + versionParameter.setEnumeration(Arrays.asList(Version.values()).stream().map(Version::name).collect(Collectors.toList())); + versionParameter.setType(ParamType.STRING); + parameters.put(VERSION_PARAMETER_KEY, versionParameter); + + // format + QueryParameter formatParameter = new QueryParameter(); + formatParameter.setDefaultValue(Format.JSON.name()); + formatParameter.setDescription("Specify the output format. Default is JSON."); + formatParameter.setExample(Format.YAML.name()); + formatParameter.setRequired(false); + formatParameter.setEnumeration(Arrays.asList(Format.values()).stream().map(Format::name).collect(Collectors.toList())); + formatParameter.setType(ParamType.STRING); + parameters.put(FORMAT_PARAMETER_KEY, formatParameter); + + return parameters; + } + + @Override + public String getName() { + return "OpenAPI request parameters"; + } +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java index 17805f4b71..45a4caabfd 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java @@ -3,6 +3,7 @@ import static com.gentics.mesh.core.rest.error.Errors.error; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -61,6 +62,7 @@ public SortingParametersImpl(ActionContext ac) { sortOrderParameter.setExample(SortOrder.ASCENDING.name()); sortOrderParameter.setRequired(false); sortOrderParameter.setType(ParamType.STRING); + sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(SortOrder::name).collect(Collectors.toList())); parameters.put(SortingParameters.SORT_ORDER_PARAMETER_KEY, sortOrderParameter); return parameters; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java new file mode 100644 index 0000000000..5bfa4f288c --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java @@ -0,0 +1,34 @@ +package com.gentics.mesh.core.rest.openapi; + +import org.apache.commons.lang.StringUtils; + +/** + * OpenAPI output format + */ +public enum Format { + /** + * Yaml + */ + YAML, + /** + * JSON + */ + JSON; + + /** + * Safe parse string value + * + * @param format + * @param defaultValue + * @return + */ + public static Format parse(String format, Format defaultValue) { + if (StringUtils.isNotBlank(format)) { + switch (format.toUpperCase()) { + case "YAML": return YAML; + case "JSON": return JSON; + } + } + return defaultValue; + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java new file mode 100644 index 0000000000..9bb3d7156f --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java @@ -0,0 +1,39 @@ +package com.gentics.mesh.core.rest.openapi; + +import org.apache.commons.lang.StringUtils; + +/** + * OpenAPI specification version + */ +public enum Version { + + /** + * v3.0 + */ + V30, + /** + * v3.1 + */ + V31; + + /** + * Safe parse string value + * + * @param version + * @param defaultValue + * @return + */ + public static Version parse(String version, Version defaultValue) { + if (StringUtils.isNotBlank(version)) { + switch (version.toUpperCase()) { + case "V30": + case "3.0": + return V30; + case "V31": + case "3.1": + return V31; + } + } + return defaultValue; + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java new file mode 100644 index 0000000000..f16054439f --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java @@ -0,0 +1,60 @@ +package com.gentics.mesh.parameter; + +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; + +/** + * Parameters for an OpenAPI definition request + */ +public interface OpenAPIParameters extends ParameterProvider { + + /** + * Key of the "version" parameter + */ + public static final String VERSION_PARAMETER_KEY = "version"; + + /** + * Key of the "format" parameter + */ + public static final String FORMAT_PARAMETER_KEY = "format"; + + /** + * Set the OpenAPI standard version + * + * @param flag + * @return Fluent API + */ + default OpenAPIParameters setVersion(Version version) { + setParameter(VERSION_PARAMETER_KEY, String.valueOf(version)); + return this; + } + + /** + * Check whether OpenAPI spec version + * + * @return flag value + */ + default Version getVersion() { + return Version.parse(getParameter(VERSION_PARAMETER_KEY), Version.V30); + } + + /** + * Set the format + * + * @param flag + * @return Fluent API + */ + default OpenAPIParameters setFormat(Format format) { + setParameter(FORMAT_PARAMETER_KEY, String.valueOf(format)); + return this; + } + + /** + * Check the spec format + * + * @return flag value + */ + default Format getFormat() { + return Format.parse(getParameter(FORMAT_PARAMETER_KEY), Format.JSON); + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java b/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java index ab08130ff8..60368ba6f4 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java @@ -73,6 +73,24 @@ default void setMultivalueParameter(String name, Collection values) { */ String getParameter(String name); + /** + * Return the query parameter value for the given name, or the default value. + * + * @param name + * Parameter name + * @param defaultValue + * Default value + * @return Loaded value or null + */ + default String getParameter(String name, String defaultValue) { + String parameter = getParameter(name); + if (parameter != null) { + return parameter; + } else { + return defaultValue; + } + } + /** * Return the query parameter values for the given name. * @param value type From 11dd4dba06e097b87c38817edaccd014578f52d6 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 Feb 2026 14:15:00 +0100 Subject: [PATCH 18/75] Tests base --- .../rest/impl/InternalEndpointRouteImpl.java | 15 + .../core/endpoint/admin/AdminHandler.java | 61 +- .../core/endpoint/admin/RestInfoEndpoint.java | 39 +- .../mesh/generator/OpenAPIv3Generator.java | 91 ++- .../mesh/generator/OpenAPIv3GeneratorOld.java | 621 ------------------ .../mesh/rest/MeshLocalClientImpl.java | 8 + .../parameter/impl/OpenAPIParametersImpl.java | 4 +- .../parameter/impl/SortingParametersImpl.java | 2 +- .../mesh/rest/InternalEndpointRoute.java | 18 +- .../database/cluster/HibClusterManager.java | 3 +- .../client/OpenAPIParametersImpl.java | 10 + .../client/impl/MeshOkHttpRequestImpl.java | 2 +- .../client/impl/MeshRestHttpClientImpl.java | 11 + .../client/method/ApiInfoClientMethods.java | 9 + tests/tests-core/pom.xml | 5 + .../core/openapi/OpenAPIEndpointTest.java | 82 +++ 16 files changed, 306 insertions(+), 675 deletions(-) delete mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java create mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index 7848f6077f..b7c52ee0c7 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -11,6 +11,7 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -102,6 +103,8 @@ public class InternalEndpointRouteImpl implements InternalEndpointRoute { private boolean insecure = false; + private final Collection> modelComponents = new HashSet<>(); + /** * Create a new endpoint wrapper using the provided router to create the wrapped * route instance. @@ -590,4 +593,16 @@ public void handle(RoutingContext rc) { } } } + + @Override + public InternalEndpointRoute setModel(Collection> modelComponents) { + this.modelComponents.addAll(modelComponents); + return this; + } + + @Override + public Collection> getModel() { + // TODO Auto-generated method stub + return modelComponents; + } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 8e6c5bf70d..8c5654d509 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -18,15 +18,19 @@ import java.util.Set; import java.util.UUID; import java.util.function.Function; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gentics.mesh.Mesh; +import com.gentics.mesh.MeshVersion; import com.gentics.mesh.cache.CacheRegistry; import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.context.InternalActionContext; @@ -39,6 +43,7 @@ import com.gentics.mesh.core.rest.admin.cluster.coordinator.CoordinatorMasterResponse; import com.gentics.mesh.core.rest.admin.consistency.ConsistencyCheckResponse; import com.gentics.mesh.core.rest.admin.status.MeshStatusResponse; +import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; import com.gentics.mesh.core.verticle.handler.WriteLock; @@ -248,26 +253,61 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { return info; } + /** + * Handle a request of generating OpenAPI spec for all the routes. All the necessary parameters will be taken from an action context. + * + * @param ac internal action context. + */ public void handleOpenAPIv3(InternalActionContext ac) { + handleOpenAPIv3(ac, ac.getOpenAPIParameters().getFormat()); + } + + /** + * Handle a request of generating OpenAPI spec for all the routes. + * + * @param ac internal action context + * @param format2 manually picked specification format + */ + public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest.openapi.Format format2) { boolean useVersion31 = ac.getOpenAPIParameters().getVersion() == Version.V31; - String format = ac.getOpenAPIParameters().getFormat().name().toLowerCase(); - HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + String format = Optional.ofNullable(format2).orElse(com.gentics.mesh.core.rest.openapi.Format.JSON).name().toLowerCase(); + + // Collect available servers + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); List servers = Optional.ofNullable(clusterManager.getHazelcast()) .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) .orElseGet(() -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort()))); + /* + * Blacklist + * a) all the actual project roots + * b) old api version roots + * c) an `apiversion` selector parameter root + */ Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) - .map(project -> "\\/api\\/v2\\/" + project + "[.]*").collect(Collectors.toSet())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/v2", "\\/api\\/v1[.]*", "\\/api\\/\\{apiversion\\}[.]*")); - OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(blacklistedRouteRegex), Optional.empty()); + .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); + blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); + blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "", "\\/api\\/\\{apiversion\\}[.]*")); + + // Make an instance with blacklist path patterns + OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + + // Generate... ac.send(generator.generate( Stream.of( + //... from base root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v2/{projectName}")) + //... from generic project root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{projectName}")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), + // ...with desired format Format.parse(format), + // ...with desired pretty printing !httpServerConfig.isMinifyJson(), + // ...with desired spec version + useVersion31, + // ...with a manipulator injecting a `projectName` path parameter, where unavailable Optional.of((path, item) -> { if (path.contains("/{projectName}/")) { Parameter projectNameParam = new Parameter().name("projectName").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); @@ -278,7 +318,14 @@ public void handleOpenAPIv3(InternalActionContext ac) { }, () -> o.addParametersItem(projectNameParam))); } return path; - }), useVersion31), + }), + // ...with component model provider + Optional.of(() -> Collections.unmodifiableCollection( + new Reflections("com.gentics.mesh") + .getSubTypesOf(RestModel.class) + .stream() + .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) + .collect(Collectors.toSet())))), OK, "yaml".equalsIgnoreCase(format) ? HttpConstants.APPLICATION_YAML_UTF8 diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index 3f7f6a3ba6..26356b53ab 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -11,9 +11,10 @@ import com.gentics.mesh.auth.MeshAuthChain; import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.rest.openapi.Format; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.example.RestInfoExamples; -import com.gentics.mesh.parameter.OpenAPIParameters; +import com.gentics.mesh.parameter.impl.OpenAPIParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.RouterStorage; import com.gentics.mesh.router.route.AbstractInternalEndpoint; @@ -70,17 +71,43 @@ public void registerEndPoints() { }, false); secure("/openapi"); + InternalEndpointRoute openapi = createRoute(); + openapi.path("/openapi"); + openapi.method(GET); + openapi.description("Endpoint which provides a OpenAPIv3 document for all registed endpoints."); + openapi.displayName("OpenAPI specification"); + openapi.exampleResponse(OK, "Not yet specified"); + openapi.produces(TEXT_PLAIN_UTF8); + openapi.addQueryParameters(OpenAPIParametersImpl.class); + openapi.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + adminHandler.handleOpenAPIv3(ac); + }, false); + + secure("/openapi.yaml"); InternalEndpointRoute openapiYml = createRoute(); - openapiYml.path("/openapi"); + openapiYml.path("/openapi.yaml"); openapiYml.method(GET); - openapiYml.description("Endpoint which provides a OpenAPIv3 document for all registed endpoints."); + openapiYml.description("Endpoint which provides a OpenAPIv3.0 YAML document for all registed endpoints."); openapiYml.displayName("OpenAPI specification"); openapiYml.exampleResponse(OK, "Not yet specified"); - openapiYml.produces(TEXT_PLAIN_UTF8); - openapiYml.addQueryParameters(OpenAPIParameters.class); + openapiYml.produces(APPLICATION_YAML); openapiYml.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac); + adminHandler.handleOpenAPIv3(ac, Format.YAML); + }, false); + + secure("/openapi.json"); + InternalEndpointRoute openapiJson = createRoute(); + openapiJson.path("/openapi.json"); + openapiJson.method(GET); + openapiJson.description("Endpoint which provides a OpenAPIv3.0 JSON document for all registed endpoints."); + openapiJson.displayName("OpenAPI specification"); + openapiJson.exampleResponse(OK, "Not yet specified"); + openapiJson.produces(APPLICATION_JSON); + openapiJson.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + adminHandler.handleOpenAPIv3(ac, Format.JSON); }, false); secure("/"); diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java index 386c523d92..42033d0130 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java @@ -25,6 +25,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -35,7 +36,6 @@ import org.apache.commons.lang.StringUtils; import org.raml.model.MimeType; import org.raml.model.parameter.AbstractParam; -import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +43,6 @@ import com.fasterxml.jackson.annotation.JsonPropertyDescription; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializable; -import com.fasterxml.jackson.databind.ObjectWriter; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.gentics.mesh.MeshVersion; import com.gentics.mesh.core.rest.common.RestModel; @@ -86,29 +85,53 @@ public class OpenAPIv3Generator { private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); - private final Optional> maybePathBlacklist; - private final Optional> maybePathWhitelist; + private final Optional> maybePathBlacklist; + private final Optional> maybePathWhitelist; private final List servers; /** + * Ctor * - * - * @param servers - * @param maybePathBlacklist - * @param maybePathWhitelist + * @param servers a list of available servers; may be empty. + * @param maybePathBlacklist optional regex blacklist + * @param maybePathWhitelist optional regex whitelist */ - public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, @Nonnull Optional> maybePathWhitelist) { + public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, + @Nonnull Optional> maybePathWhitelist) { this.maybePathBlacklist = maybePathBlacklist; this.maybePathWhitelist = maybePathWhitelist; this.servers = servers; } - public String generate(Map routers, Format format, boolean pretty, Optional> maybePathItemTransformer) { - return generate(routers, format, pretty, maybePathItemTransformer, false); + /** + * Generate the spec out of the given routes and format + * + * @param routers + * @param format + * @param pretty + * @param maybePathItemTransformer an optional custon path and path item transformer + * @return + */ + public String generate(Map routers, Format format, boolean pretty, + @Nonnull Optional> maybePathItemTransformer, + @Nonnull Optional>>> maybeExtraComponentSupplier) { + return generate(routers, format, pretty, false, maybePathItemTransformer, maybeExtraComponentSupplier); } - public String generate(Map routers, Format format, boolean pretty, Optional> maybePathItemTransformer, boolean useVersion31) { + /** + * Generate the spec out of given routes and parameters + * + * @param routers + * @param format + * @param pretty + * @param useVersion31 + * @param maybePathItemTransformer an optional custon path and path item transformer + * @return + */ + public String generate(Map routers, Format format, boolean pretty, boolean useVersion31, + @Nonnull Optional> maybePathItemTransformer, + @Nonnull Optional>>> maybeExtraComponentSupplier) { log.info("Starting OpenAPIv3 generation..."); OpenAPI openApi = new OpenAPI(); openApi.setPaths(new Paths()); @@ -125,7 +148,9 @@ public String generate(Map routers, Format format, boolean prett openApi.setInfo(info); try { addSecurity(openApi); - addComponents(openApi); + maybeExtraComponentSupplier.ifPresent(componentSupplier -> { + componentSupplier.get().forEach(componentClass -> fillComponent(componentClass, openApi)); + }); for (Entry routerAndParent : routers.entrySet()) { addRouter(routerAndParent.getValue(), routerAndParent.getKey(), openApi, maybePathItemTransformer); } @@ -137,6 +162,11 @@ public String generate(Map routers, Format format, boolean prett return writer.write(openApi, format, pretty); } + /** + * Add a security to the spec + * + * @param openApi + */ protected void addSecurity(OpenAPI openApi) { Components components; if (openApi.getComponents() == null) { @@ -156,27 +186,20 @@ protected void addSecurity(OpenAPI openApi) { openApi.addSecurityItem(reqBearerAuth); } - private String makeSchemaName(Class cls) { - return "Mesh" + cls.getSimpleName(); - } - - protected void addComponents(OpenAPI openApi) { + @SuppressWarnings("rawtypes") + protected void fillComponent(Class cls, OpenAPI openApi) { + if (StringUtils.isBlank(cls.getSimpleName())) { + return; + } Components components; if (openApi.getComponents() == null) { - components = new Components(); + components = new Components(); openApi.setComponents(components); } else { components = openApi.getComponents(); } - components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); - Reflections reflections = new Reflections("com.gentics.mesh"); - reflections.getSubTypesOf(RestModel.class).stream().forEach(cls -> fillComponent(cls, components)); - } - - @SuppressWarnings("rawtypes") - protected void fillComponent(Class cls, Components components) { - if (!cls.getPackageName().startsWith("com.gentics.mesh") || StringUtils.isBlank(cls.getSimpleName())) { - return; + if (components.getSchemas() == null) { + components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); } Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); schema.setType("object"); @@ -214,7 +237,7 @@ protected void fillComponent(Class cls, Components components) { .filter(f -> !Modifier.isStatic(f.getModifiers())).peek(f -> { Class t = f.getType(); if (!RestModel.class.isAssignableFrom(t) && !t.isPrimitive() && !t.getCanonicalName().startsWith("java.lang")) { - fillComponent(t, components); + fillComponent(t, openApi); } }) .map(f -> { @@ -384,8 +407,8 @@ protected void addRouter(String parent, Router router, OpenAPI consumer, Optiona .collect(Collectors.joining("/")))) .replace("//", "/"); - if(maybePathBlacklist.flatMap(list -> list.stream().filter(blacklisted -> Pattern.matches(blacklisted, path)).findAny()).isPresent() - || (maybePathWhitelist.isPresent() && maybePathWhitelist.flatMap(list -> list.stream().filter(whitelisted -> Pattern.matches(whitelisted, path)).findAny()).isEmpty())) { + if(maybePathBlacklist.flatMap(list -> list.stream().filter(blacklisted -> blacklisted.matcher(path).matches()).findAny()).isPresent() + || (maybePathWhitelist.isPresent() && maybePathWhitelist.flatMap(list -> list.stream().filter(whitelisted -> whitelisted.matcher(path).matches()).findAny()).isEmpty())) { log.debug("Path filtered off: " + path); continue; } @@ -402,6 +425,7 @@ protected void addRouter(String parent, Router router, OpenAPI consumer, Optiona log.debug("Path with metadata: " + path); pathItem.setSummary(endpoint.getDisplayName()); pathItem.setDescription(endpoint.getDescription()); + endpoint.getModel().forEach(modelComponent -> fillComponent(modelComponent, consumer)); resolveEndpointRoute(path, pathItem, endpoint); }, () -> { resolveFallbackRoute(route, pathItem); @@ -519,14 +543,11 @@ private void fillType(Components components, Class t, Schema fieldSchema, Lis log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); } fieldSchema.setItems(itemSchema); -// } else if (t.isEnum()) { -// fieldSchema.setType("string"); -// fieldSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); } else { if (t.isEnum()) { Schema enumSchema = new Schema(); enumSchema.setType("string"); - enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); + enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); components.addSchemas(t.getSimpleName(), enumSchema); } if (Map.class.isAssignableFrom(t)) { diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java deleted file mode 100644 index 3816c5f7a9..0000000000 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3GeneratorOld.java +++ /dev/null @@ -1,621 +0,0 @@ -package com.gentics.mesh.generator; - -import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; -import static com.gentics.mesh.core.rest.error.Errors.error; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static org.apache.commons.lang3.StringUtils.isEmpty; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiConsumer; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; -import org.apache.commons.lang.StringUtils; -import org.raml.model.MimeType; -import org.raml.model.parameter.AbstractParam; -import org.reflections.Reflections; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializable; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.context.InternalActionContext; -import com.gentics.mesh.core.rest.common.RestModel; -import com.gentics.mesh.etc.config.HttpServerConfig; -import com.gentics.mesh.http.HttpConstants; -import com.gentics.mesh.rest.InternalEndpointRoute; -import com.gentics.mesh.router.route.AbstractInternalEndpoint; -import com.hazelcast.core.HazelcastInstance; - -import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Yaml; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.RequestBody; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.servers.Server; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.json.JsonObject; - -/** - * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. - * - * @author plyhun - * - */ -public class OpenAPIv3GeneratorOld extends AbstractEndpointGenerator { - - private static final String API_VERSION_PATH_PREFIX = "/api/v"; - - private static final Logger log = LoggerFactory.getLogger(OpenAPIv3GeneratorOld.class); - - private final HazelcastInstance hz; - private final HttpServerConfig httpServerConfig; - - public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig) { - this.hz = hz; - this.httpServerConfig = httpServerConfig; - } - - public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder, boolean cleanup) throws IOException { - super(outputFolder, cleanup); - this.hz = hz; - this.httpServerConfig = httpServerConfig; - } - - public OpenAPIv3GeneratorOld(HazelcastInstance hz, HttpServerConfig httpServerConfig, File outputFolder) throws IOException { - super(outputFolder); - this.hz = hz; - this.httpServerConfig = httpServerConfig; - } - - public void generate(InternalActionContext ac, String format) { - log.info("Starting OpenAPIv3 generation..."); - OpenAPI openApi = new OpenAPI(); - Info info = new Info(); - info.setTitle("Gentics Mesh REST API"); - info.setVersion(MeshVersion.getBuildInfo().getVersion()); - if (hz != null) { - List servers = hz.getCluster().getMembers().stream().map(m -> { - Server server = new Server(); - server.setUrl(m.getAddress().toString()); - return server; - }).collect(Collectors.toList()); - openApi.servers(servers); - } else { - Server server = new Server(); - server.setUrl((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - openApi.servers(Collections.singletonList(null)); - } - openApi.setInfo(info); - try { - addSecurity(openApi); - addComponents(openApi); - addCoreEndpoints(openApi); - addProjectEndpoints(openApi); - } catch (IOException e) { - throw new RuntimeException("Could not add all verticles to raml generator", e); - } - - //new OpenAPI30To31().process(openApi); - //openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); - String formatted; - String mime; - switch (format) { - case "yaml": - try { - mime = APPLICATION_YAML_UTF8; - //formatted = ac.isMinify(httpServerConfig) ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); - formatted = ac.isMinify(httpServerConfig) ? Yaml.mapper().writer().writeValueAsString(openApi) : Yaml.pretty().writeValueAsString(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException("Could not generate YAML", e); - } - break; - case "json": - mime = APPLICATION_JSON_UTF8; - try { - //formatted = ac.isMinify(httpServerConfig) ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); - formatted = ac.isMinify(httpServerConfig) ? Json.mapper().writer().writeValueAsString(openApi) : Json.pretty(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - break; - default: - throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } - ac.send(formatted, OK, mime); - } - - private void addSecurity(OpenAPI openApi) { - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - SecurityScheme securityBearerAuth = new SecurityScheme(); - securityBearerAuth.setScheme("bearer"); - securityBearerAuth.setType(SecurityScheme.Type.HTTP); - securityBearerAuth.setBearerFormat("JWT"); - components.addSecuritySchemes("bearerAuth", securityBearerAuth); - //TODO OAuth2 - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - openApi.addSecurityItem(reqBearerAuth); - } - - private String makeSchemaName(Class cls) { - return "Mesh" + cls.getSimpleName(); - } - - private void addComponents(OpenAPI openApi) { - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); - Reflections reflections = new Reflections("com.gentics.mesh"); - reflections.getSubTypesOf(RestModel.class).stream().forEach(cls -> fillComponent(cls, components)); - } - - @SuppressWarnings("rawtypes") - private void fillComponent(Class cls, Components components) { - if (!cls.getPackageName().startsWith("com.gentics.mesh") || StringUtils.isBlank(cls.getSimpleName())) { - return; - } - Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); - schema.setType("object"); - schema.setName(cls.getSimpleName()); - List> fieldStreams = new ArrayList<>(); - final List generics = new ArrayList<>(); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); - Deque> dq = new ArrayDeque<>(2); - dq.addLast(cls); - while(!dq.isEmpty()) { - Class tclass = dq.pop(); - log.debug("Class: " + tclass.getCanonicalName()); - /*if (tclass != cls) { - Schema tschema = components.getSchemas().computeIfAbsent(tclass.getSimpleName(), key -> new Schema()); - tschema.setName(tclass.getSimpleName()); - Schema refSchema = new Schema<>(); - refSchema.$ref("#/components/schemas/" + tclass.getSimpleName()); - schema.addAllOfItem(refSchema); - refSchema = new Schema<>(); - refSchema.$ref("#/components/schemas/" + cls.getSimpleName()); - tschema.addAnyOfItem(refSchema); - }*/ - fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); - dq.addAll(Arrays.stream(tclass.getInterfaces()).filter(i -> i.getPackageName().startsWith("com.gentics.mesh")).collect(Collectors.toList())); - tclass = tclass.getSuperclass(); - if (tclass != null) { - dq.addLast(tclass); - } - } - if (generics.size() > 0) { - log.debug(" - Generics: " + Arrays.toString(generics.toArray())); - } - Map properties = fieldStreams.stream().flatMap(Function.identity()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).peek(f -> { - Class t = f.getType(); - if (!RestModel.class.isAssignableFrom(t) && !t.isPrimitive() && !t.getCanonicalName().startsWith("java.lang")) { - fillComponent(t, components); - } - }) - .map(f -> { - String name = f.getName(); - log.debug(" - Field: " + f); - Schema fieldSchema = new Schema(); - fieldSchema.setName(name); - JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); - if (description != null) { - fieldSchema.setDescription(description.value()); - } - JsonProperty property = f.getAnnotation(JsonProperty.class); - if (property != null) { - if (StringUtils.isNotBlank(property.defaultValue())) { - fieldSchema.setDefault(property.defaultValue()); - } - if (StringUtils.isNotBlank(property.value())) { - name = property.value(); - } - if (property.required()) { - schema.addRequiredItem(name); - } - } - Class t = f.getType(); - JsonDeserialize jdes = f.getAnnotation(JsonDeserialize.class); - if (jdes != null && jdes.as() != null) { - t = jdes.as(); - fieldSchema.setType("object"); - fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); - } else { - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : new Type[0])); - if (generics.size() > 0) { - log.debug(" - Generics: " + Arrays.toString(generics.toArray())); - } - fillType(components, t, fieldSchema, generics); - } - fieldSchema.setTypes(Collections.singleton(fieldSchema.getType())); - return new UnmodifiableMapEntry<>(name, fieldSchema); - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - schema.setProperties(properties); - components.addSchemas(cls.getSimpleName(), schema); - } - - @Override - protected void addEndpoints(String basePath, OpenAPI consumer, AbstractInternalEndpoint verticle, boolean isProject) - throws IOException { - // Check whether the resource was already added. Maybe we just need to extend it - Paths paths = consumer.getPaths(); - if (paths == null) { - paths = new Paths(); - consumer.setPaths(paths); - } - for (InternalEndpointRoute endpoint : verticle.getEndpoints().stream().sorted().collect(Collectors.toList())) { - if ("eventbus".equals(verticle.getBasePath())) { - continue; - } - String fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath() - + endpoint.getRamlPath(); - if (isEmpty(verticle.getBasePath())) { - fullPath = API_VERSION_PATH_PREFIX + CURRENT_API_VERSION + basePath + endpoint.getRamlPath(); - } - PathItem pathItem = paths.get(fullPath); - if (pathItem == null) { - log.debug("Path " + fullPath); - pathItem = new PathItem(); - pathItem.setSummary(endpoint.getDisplayName()); - pathItem.setDescription(endpoint.getDescription()); - consumer.path(fullPath, pathItem); - } - Operation operation = new Operation(); - HttpMethod method = endpoint.getMethod(); - if (method == null) { - method = HttpMethod.GET; - } - if (endpoint.isInsecure()) { - operation.setSecurity(Collections.emptyList()); - } else { - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - operation.setSecurity(List.of(reqBearerAuth)); - } - switch (method.name()) { - case "DELETE": - pathItem.setDelete(operation); - break; - case "GET": - pathItem.setGet(operation); - break; - case "HEAD": - pathItem.setHead(operation); - break; - case "OPTIONS": - pathItem.setOptions(operation); - break; - case "PATCH": - pathItem.setPatch(operation); - break; - case "POST": - pathItem.setPost(operation); - break; - case "PUT": - pathItem.setPut(operation); - break; - case "TRACE": - pathItem.setTrace(operation); - break; - default: - break; - } - List> params = List.of( - isProject ? Stream.of(new Parameter().name("project").in(InParameter.PATH.value).schema(new Schema().type("string").description("Uuid of the related project"))) : Stream.of(), - endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), - endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); - operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); - ApiResponses responses = new ApiResponses(); - endpoint.getProduces().stream().map(e -> { - ApiResponse response = new ApiResponse(); - Content responseBody = new Content(); - responseBody.addMediaType(e, new MediaType()); - response.setContent(responseBody); - response.setDescription(e); - if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { - response.setExtensions(Map.of("x-is-file", true)); - Schema schema = new Schema<>(); - schema.setType("string"); - schema.setFormat("binary"); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - responseBody.addMediaType("application/octet-stream", mediaType); - response.setContent(responseBody); - } - return new UnmodifiableMapEntry("default", response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); - endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - ApiResponse response = new ApiResponse(); - if (e.getValue().getDescription().startsWith("Generated login token")) { - e.getValue().getHeaders(); - } - response.setDescription(e.getValue().getDescription()); - Content responseBody = new Content(); - if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { - Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); - if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { - Schema schema = new Schema<>(); - schema.set$ref("#/components/schemas/" + ref.getSimpleName()); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - mediaType.setExample(e.getValue()); - responseBody.addMediaType("*/*", mediaType); - response.setContent(responseBody); - } else { - if (e.getValue().getBody() != null) { - e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) - .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) - .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); - } else { - log.warn("Body of " + e.getKey() + " is null!"); - } - response.setContent(responseBody); - } - } - return new UnmodifiableMapEntry(e.getKey(), response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); - operation.setResponses(responses); - if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { - RequestBody requestBody = new RequestBody(); - Content content = new Content(); - endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) - .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); - requestBody.setContent(content); - operation.setRequestBody(requestBody); - } - // action.setIs(Arrays.asList(endpoint.getTraits())); - } - } - - @SuppressWarnings("rawtypes") - private Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { - MediaType mediaType = new MediaType(); - mediaType.setExample(mimeType.getExample()); - if (mimeType.getFormParameters() != null) { - Map props = mimeType.getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) - .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); - Schema schema = new Schema<>(); - schema.setType("object"); - schema.setProperties(props); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry("multipart/form-data", mediaType); - } else if (mimeType.getSchema() != null) { - JsonObject jschema = new JsonObject(mimeType.getSchema()); - Schema schema = new Schema<>(); - schema.setType(jschema.getString("type", "string")); - schema.set$id(jschema.getString("id")); - schema.set$ref("#/components/schemas/" + refClass.getSimpleName()); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(key, mediaType); - } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { - mediaType.setExample(mimeType.getExample()); - Schema schema = new Schema<>(); - schema.setType("object"); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(key, mediaType); - } else { - return new UnmodifiableMapEntry(key, mediaType); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void fillType(Components components, Class t, Schema fieldSchema, List generics) { - if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { - if (int.class.isAssignableFrom(t) || Integer.class.isAssignableFrom(t)) { - fieldSchema.setType("integer"); - fieldSchema.setFormat("int32"); - } else if (boolean.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { - fieldSchema.setType("boolean"); - } else if (float.class.isAssignableFrom(t) || Float.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - fieldSchema.setFormat("float"); - } else if (long.class.isAssignableFrom(t) || Long.class.isAssignableFrom(t)) { - fieldSchema.setType("integer"); - fieldSchema.setFormat("int64"); - } else if (double.class.isAssignableFrom(t) || Double.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - fieldSchema.setFormat("double"); - } else if (BigDecimal.class.isAssignableFrom(t) || Number.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - } else { - fieldSchema.setType("object"); - } - } else if (CharSequence.class.isAssignableFrom(t)) { - fieldSchema.setType("string"); - } else if (t.isArray() || List.class.isAssignableFrom(t)) { - fieldSchema.setType("array"); - Schema itemSchema = new Schema(); - if (t.isArray()) { - List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); - fillType(components, t.getComponentType(), itemSchema, generics1); - } else if (generics.size() > 0 && Class.class.isInstance(generics.get(0))) { - Class itemClass = Class.class.cast(generics.get(0)); - List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); - fillType(components, itemClass, itemSchema, generics1); - } else { - // TODO - log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); - } - fieldSchema.setItems(itemSchema); -// } else if (t.isEnum()) { -// fieldSchema.setType("string"); -// fieldSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); - } else { - if (t.isEnum()) { - Schema enumSchema = new Schema(); - enumSchema.setType("string"); - enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).collect(Collectors.toList())); - components.addSchemas(t.getSimpleName(), enumSchema); - } - if (Map.class.isAssignableFrom(t)) { - if (generics.size() == 2) { - BiConsumer innerTypeMapper = (ty, tfieldSchema) -> { - Class tt = Class.class.isInstance(ty) ? Class.class.cast(ty) : generics.get(1).getClass(); - if (tt.isPrimitive() || Number.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { - if (int.class.isAssignableFrom(tt) || Integer.class.isAssignableFrom(tt)) { - tfieldSchema.setType("integer"); - tfieldSchema.setFormat("int32"); - } else if (boolean.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { - tfieldSchema.setType("boolean"); - } else if (float.class.isAssignableFrom(tt) || Float.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - tfieldSchema.setFormat("float"); - } else if (long.class.isAssignableFrom(tt) || Long.class.isAssignableFrom(tt)) { - tfieldSchema.setType("integer"); - tfieldSchema.setFormat("int64"); - } else if (double.class.isAssignableFrom(tt) || Double.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - tfieldSchema.setFormat("double"); - } else if (BigDecimal.class.isAssignableFrom(tt) || Number.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - } else { - tfieldSchema.setType("object"); - } - } else if (CharSequence.class.isAssignableFrom(tt)) { - tfieldSchema.setType("string"); - } else { - tfieldSchema.setType("object"); - } - }; - fieldSchema.setType("object"); // TODO why object? - //innerTypeMapper.accept(generics.get(0).getClass(), fieldSchema); - Schema valueSchema = new Schema<>(); - innerTypeMapper.accept(generics.get(1), valueSchema); - fieldSchema.setAdditionalProperties(valueSchema); - } else { - fieldSchema.setType("object"); - fieldSchema.setAdditionalProperties(new Schema().type("object")); - } - } else if (JsonObject.class.isAssignableFrom(t) || JsonSerializable.class.isAssignableFrom(t)) { - fieldSchema.set$ref("#/components/schemas/AnyJson"); - } else { - fieldSchema.setType("object"); - fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); - } - } - } - - @SuppressWarnings("rawtypes") - private final Parameter parameter(String name, AbstractParam param, InParameter inType) { - Schema schema; - switch (param.getType()) { - case BOOLEAN: - schema = new Schema(); - schema.setType("boolean"); - break; - case DATE: - schema = new Schema(); - schema.setType("integer"); - schema.setFormat("int64"); - break; - case FILE: - schema = new Schema(); - schema.setType("string"); - schema.setFormat("binary"); - break; - case INTEGER: - schema = new Schema(); - schema.setType("integer"); - schema.setFormat("int32"); - break; - case NUMBER: - schema = new Schema(); - schema.setType("number"); - schema.setFormat("double"); - break; - case STRING: - schema = new Schema(); - schema.setType("string"); - break; - default: - schema = new Schema(); - schema.setType("object"); - break; - } - schema.setMinimum(param.getMinimum()); - schema.setMaximum(param.getMaximum()); - schema.setMinLength(param.getMinLength()); - schema.setMaxLength(param.getMaxLength()); - schema.setDefault(param.getDefaultValue()); - schema.setEnum(param.getEnumeration()); - schema.setPattern(param.getPattern()); - schema.setDescription(param.getDescription()); - if (StringUtils.isNotBlank(param.getExample())) { - schema.setExample(param.getExample()); - } - Parameter p = new Parameter(); - p.setRequired(param.isRequired()); - p.setDescription(param.getDescription()); - p.setSchema(schema); - p.setName(name); - if (inType != null) { - p.setIn(inType.value); - } - return p; - } - - private enum InParameter { - PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); - - private final String value; - - private InParameter(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - } -} diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 21254dafe7..c7e3d0ca53 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -82,6 +82,8 @@ import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; import com.gentics.mesh.core.rest.plugin.PluginListResponse; import com.gentics.mesh.core.rest.plugin.PluginResponse; @@ -1621,6 +1623,12 @@ public MeshRequest getRAML() { return null; } + @Override + public MeshRequest getOpenAPI(Format format, Version version) { + // TODO Auto-generated method stub + return null; + } + @Override public MeshRequest graphql(String projectName, GraphQLRequest request, ParameterProvider... parameters) { // TODO Auto-generated method stub diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java index eb020ec8c8..f15aa929cc 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java @@ -43,7 +43,7 @@ public OpenAPIParametersImpl() { versionParameter.setDescription("Specify, whether the OpenAPI standard version 3.1 should be generated. If false or unset, a version 3.0 of standard will be used."); versionParameter.setExample(Version.V31.name()); versionParameter.setRequired(false); - versionParameter.setEnumeration(Arrays.asList(Version.values()).stream().map(Version::name).collect(Collectors.toList())); + versionParameter.setEnumeration(Arrays.asList(Version.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); versionParameter.setType(ParamType.STRING); parameters.put(VERSION_PARAMETER_KEY, versionParameter); @@ -53,7 +53,7 @@ public OpenAPIParametersImpl() { formatParameter.setDescription("Specify the output format. Default is JSON."); formatParameter.setExample(Format.YAML.name()); formatParameter.setRequired(false); - formatParameter.setEnumeration(Arrays.asList(Format.values()).stream().map(Format::name).collect(Collectors.toList())); + formatParameter.setEnumeration(Arrays.asList(Format.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); formatParameter.setType(ParamType.STRING); parameters.put(FORMAT_PARAMETER_KEY, formatParameter); diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java index 45a4caabfd..da97df93be 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java @@ -62,7 +62,7 @@ public SortingParametersImpl(ActionContext ac) { sortOrderParameter.setExample(SortOrder.ASCENDING.name()); sortOrderParameter.setRequired(false); sortOrderParameter.setType(ParamType.STRING); - sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(SortOrder::name).collect(Collectors.toList())); + sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); parameters.put(SortingParameters.SORT_ORDER_PARAMETER_KEY, sortOrderParameter); return parameters; diff --git a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index 4fc4a1bef3..d6a49a663d 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -1,11 +1,11 @@ package com.gentics.mesh.rest; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import io.vertx.ext.web.Router; import org.codehaus.jettison.json.JSONObject; import org.raml.model.MimeType; import org.raml.model.Response; @@ -22,6 +22,7 @@ import io.vertx.core.Handler; import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Route; +import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; /** @@ -467,4 +468,19 @@ public interface InternalEndpointRoute extends Comparable * @return */ Set getConsumes(); + + /** + * Set the custom model components, additionally required for this route + * + * @param modelComponents + * @return + */ + InternalEndpointRoute setModel(Collection> modelComponents); + + /** + * Get the custom model components, additionally required for this route + * + * @return + */ + Collection> getModel(); } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/database/cluster/HibClusterManager.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/database/cluster/HibClusterManager.java index be40844990..5fcdacc087 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/database/cluster/HibClusterManager.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/database/cluster/HibClusterManager.java @@ -6,6 +6,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import javax.inject.Inject; @@ -57,7 +58,7 @@ public HazelcastInstance getHazelcast() { config = new Config(); } config.setInstanceName(options.getNodeName()); - config.setClusterName(options.getClusterOptions().getClusterName()); + config.setClusterName(Optional.ofNullable(options.getClusterOptions().getClusterName()).orElseGet(() -> options.getNodeName())); config.setClassLoader(Thread.currentThread().getContextClassLoader()); config.getMemberAttributeConfig().setAttribute("__vertx.nodeId", options.getNodeName()); hazelcastInstance = Hazelcast.getOrCreateHazelcastInstance(config); diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java new file mode 100644 index 0000000000..fa41f68d3e --- /dev/null +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java @@ -0,0 +1,10 @@ +package com.gentics.mesh.parameter.client; + +import com.gentics.mesh.parameter.OpenAPIParameters; + +/** + * Implemetation of the OpenAPI parameters + */ +public class OpenAPIParametersImpl extends AbstractParameters implements OpenAPIParameters { + +} diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java index e63e1b6e87..b828fb1409 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java @@ -309,7 +309,7 @@ private T mapResponse(Response response) throws IOException, MeshRestClientMessa return (T) new OkHttpWebrootResponse(response); } else if (resultClass.isAssignableFrom(MeshWebrootFieldResponse.class)) { return (T) new OkHttpWebrootFieldResponse(response, config.isMinifyJson()); - } else if (contentType != null && contentType.startsWith("application/json")) { + } else if (contentType != null && contentType.startsWith("application/json") && !resultClass.isAssignableFrom(String.class)) { return JsonUtil.readValue(response.body().string(), resultClass); } else if (resultClass.isAssignableFrom(String.class)) { return (T) response.body().string(); diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index a67556838c..9b4a49f58d 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -1,5 +1,6 @@ package com.gentics.mesh.rest.client.impl; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; import static com.gentics.mesh.rest.client.impl.HttpMethod.DELETE; import static com.gentics.mesh.rest.client.impl.HttpMethod.GET; @@ -64,6 +65,8 @@ import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; import com.gentics.mesh.core.rest.plugin.PluginListResponse; import com.gentics.mesh.core.rest.plugin.PluginResponse; @@ -111,6 +114,7 @@ import com.gentics.mesh.parameter.ParameterProvider; import com.gentics.mesh.parameter.client.BinaryCheckParametersImpl; import com.gentics.mesh.parameter.client.NodeParametersImpl; +import com.gentics.mesh.parameter.client.OpenAPIParametersImpl; import com.gentics.mesh.parameter.client.VersioningParametersImpl; import com.gentics.mesh.rest.client.AbstractMeshRestHttpClient; import com.gentics.mesh.rest.client.MeshBinaryResponse; @@ -1378,6 +1382,13 @@ public MeshRequest getRAML() { return request; } + @Override + public MeshRequest getOpenAPI(Format format, Version version) { + MeshRequest request = prepareRequest(GET, "/openapi" + getQuery(getConfig(), new OpenAPIParametersImpl().setFormat(format).setVersion(version)), String.class); + request.setHeader("Accept", "*/*"); + return request; + } + @Override public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid) { Objects.requireNonNull(projectName, "projectName must not be null"); diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java index 374e94cc4b..a5c0e39512 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java @@ -1,6 +1,8 @@ package com.gentics.mesh.rest.client.method; import com.gentics.mesh.core.rest.MeshServerInfoModel; +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.monitoring.MonitoringRestClient; @@ -24,4 +26,11 @@ public interface ApiInfoClientMethods { * @return */ MeshRequest getRAML(); + + /** + * Generate an OpenAPI + * + * @return + */ + MeshRequest getOpenAPI(Format format, Version version); } diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index 8dd04042c0..dc19ff8f1d 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -85,6 +85,11 @@ httpcore 4.4.13 + + io.swagger + swagger-parser + 2.0.0-rc1 + diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java new file mode 100644 index 0000000000..41a9735b86 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java @@ -0,0 +1,82 @@ +package com.gentics.mesh.core.openapi; + +import static com.gentics.mesh.test.ClientHelper.call; +import static com.gentics.mesh.test.TestSize.FULL; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.test.MeshTestSetting; +import com.gentics.mesh.test.context.AbstractMeshTest; + +import io.swagger.parser.OpenAPIParser; +import io.swagger.parser.models.ParseOptions; +import io.swagger.parser.models.SwaggerParseResult; + +@MeshTestSetting(testSize = FULL, startServer = true) +@RunWith(Parameterized.class) +public class OpenAPIEndpointTest extends AbstractMeshTest { + + @Parameters(name = "{index}: no Mesh Client = {0}, format = {1}, version = {2}") + public static Collection parameters() throws Exception { + List params = new ArrayList<>(); + for (boolean noClient : new Boolean[] {false, true}) { + for (Format format : Format.values()) { + for (Version version : Version.values()) { + params.add(new Object[] {noClient, format, version}); + } + } + } + return params; + } + + @Parameter(0) + public boolean noClient; + + @Parameter(1) + public Format format; + + @Parameter(2) + public Version version; + + @Test + public void testOpenAPI() { + String input = null; + if (noClient) { + input = "http://localhost:" + + options().getHttpServerOptions().getPort() + + MeshVersion.CURRENT_API_BASE_PATH + + "/openapi?format=" + + format.name().toLowerCase() + + "&version=" + version.name().toLowerCase(); + } else { + grantAdmin(); + input = call(() -> client().getOpenAPI(format, version)); + } + assertNoErrors(input); + } + + protected void assertNoErrors(String input) { + OpenAPIParser parser = new OpenAPIParser(); + ParseOptions options = new ParseOptions(); + options.setResolve(true); + options.setResolveFully(true); + SwaggerParseResult result = noClient + ? parser.readLocation(input, null, null) + : parser.readContents(input, null, null); + + assertThat(result.getOpenAPI()).as("Parsed API").isNotNull(); + assertThat(result.getMessages()).as("Error messages").isNullOrEmpty(); + } +} From 42f271d39a9babc48d6424c51240cb4444c9cee3 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 17 Feb 2026 14:13:34 +0100 Subject: [PATCH 19/75] Use library --- bom/pom.xml | 6 + .../rest/impl/InternalEndpointRouteImpl.java | 523 +----------- .../core/endpoint/admin/AdminHandler.java | 87 +- .../mesh/generator/OpenAPIv3Generator.java | 746 ------------------ mdm/api/pom.xml | 30 + .../mesh/parameter/AbstractParameters.java | 42 +- .../mesh/rest/InternalEndpointRoute.java | 469 +---------- plugin-parent/pom.xml | 31 + rest-model/pom.xml | 47 +- .../rest/common/GenericMessageResponse.java | 29 +- .../mesh/core/rest/common/RestModel.java | 16 +- .../java/com/gentics/mesh/json/JsonUtil.java | 8 +- .../deserializer/JsonArrayDeserializer.java | 26 - .../deserializer/JsonObjectDeserializer.java | 26 - .../json/serializer/JsonArraySerializer.java | 20 - .../json/serializer/JsonObjectSerializer.java | 20 - .../mesh/parameter/ParameterProvider.java | 169 +--- 17 files changed, 188 insertions(+), 2107 deletions(-) delete mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonArrayDeserializer.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonObjectDeserializer.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonArraySerializer.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonObjectSerializer.java diff --git a/bom/pom.xml b/bom/pom.xml index 4bd2a8baa3..8944ebaade 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -46,6 +46,7 @@ 4.3.1 2.7.3 1.15.5 + 0.0.1-SNAPSHOT @@ -107,6 +108,11 @@ commons-lang3 3.18.0 + + com.gentics + vertx-openapi + ${vertx.openapi.version} + commons-io commons-io diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index b7c52ee0c7..241c3b15de 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -1,52 +1,23 @@ package com.gentics.mesh.rest.impl; import static com.gentics.mesh.core.rest.error.Errors.error; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; -import static io.vertx.core.http.HttpMethod.DELETE; -import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; -import static org.apache.commons.lang3.StringUtils.isEmpty; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.codehaus.jettison.json.JSONObject; import org.raml.model.MimeType; -import org.raml.model.Response; -import org.raml.model.parameter.FormParameter; -import org.raml.model.parameter.Header; -import org.raml.model.parameter.QueryParameter; -import org.raml.model.parameter.UriParameter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.core.rest.MeshEvent; -import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.json.JsonUtil; -import com.gentics.mesh.parameter.ParameterProvider; import com.gentics.mesh.rest.InternalEndpointRoute; -import com.google.common.collect.ImmutableSet; import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Handler; -import io.vertx.core.http.HttpMethod; -import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.PlatformHandler; @@ -54,56 +25,9 @@ /** * @see InternalEndpointRoute */ -public class InternalEndpointRouteImpl implements InternalEndpointRoute { +public class InternalEndpointRouteImpl extends com.gentics.vertx.openapi.metadata.InternalEndpointRouteImpl implements InternalEndpointRoute { - private static final Logger log = LoggerFactory.getLogger(InternalEndpointRoute.class); - - private static final Map, String> SCHEMA_CACHE = new ConcurrentHashMap<>(); - - private static final Set mutatingMethods = ImmutableSet.of(POST, PUT, DELETE); - - private Route route; - - private String displayName; - - private String description; - - /** - * Uri Parameters which map to the used path segments - */ - private Map uriParameters = new HashMap<>(); - - /** - * Map of example responses for the corresponding status code. - */ - private Map exampleResponses = new HashMap<>(); - - private Map> exampleResponseClasses = new HashMap<>(); - - private Set events = new HashSet<>(); - - private String[] traits = new String[] {}; - - private HashMap exampleRequestMap = null; - - private Class exampleRequestClass = null; - - private String pathRegex; - - private HttpMethod method; - - private String ramlPath; - - private final Set consumes = new LinkedHashSet<>(); - private final Set produces = new LinkedHashSet<>(); - - private Map parameters = new HashMap<>(); - - private Boolean mutating; - - private boolean insecure = false; - - private final Collection> modelComponents = new HashSet<>(); + protected Set events = new HashSet<>(); /** * Create a new endpoint wrapper using the provided router to create the wrapped @@ -114,277 +38,26 @@ public class InternalEndpointRouteImpl implements InternalEndpointRoute { * @param db */ public InternalEndpointRouteImpl(Router router, LocalConfigApi localConfigApi, Database db) { - this.route = router.route(); + super(router); ReadOnlyHandler readOnlyHandler = new ReadOnlyHandler(localConfigApi, db); route.handler(readOnlyHandler); - route.putMetadata(InternalEndpointRoute.class.getCanonicalName(), this); - } - - @Override - public InternalEndpointRoute path(String path) { - route.path(path); - return this; - } - - @Override - public InternalEndpointRoute method(HttpMethod method) { - if (this.method != null) { - throw new RuntimeException( - "The method for the endpoint was already set. The endpoint wrapper currently does not support more than one method per route."); - } - this.method = method; - route.method(method); - return this; - } - - @Override - public InternalEndpointRoute pathRegex(String path) { - this.pathRegex = path; - route.pathRegex(path); - return this; - } - - @Override - public InternalEndpointRoute produces(String contentType) { - produces.add(contentType); - route.produces(contentType); - return this; - } - - @Override - public InternalEndpointRoute consumes(String contentType) { - consumes.add(contentType); - route.consumes(contentType); - return this; - } - - @Override - public InternalEndpointRoute order(int order) { - route.order(order); - return this; - } - - @Override - public InternalEndpointRoute last() { - route.last(); - return this; - } - - @Override - public InternalEndpointRoute handler(Handler requestHandler) { - validate(); - route.handler(requestHandler); - return this; - } - - @Override - public InternalEndpointRoute subRouter(Router subRouter) { - validate(); - route.subRouter(subRouter); - return this; - } - - @Override - public InternalEndpointRoute validate() { - if (!produces.isEmpty() && produces.contains(APPLICATION_JSON) && exampleResponses.isEmpty()) { - throw new RuntimeException("Endpoint {" + getRamlPath() + "} has no example responses."); - } - if ((consumes.contains(APPLICATION_JSON) || consumes.contains(APPLICATION_JSON_UTF8)) - && exampleRequestMap == null) { - log.error("Endpoint {" + getPath() + "} has no example request."); - throw new RuntimeException("Endpoint has no example request."); - } - if (isEmpty(description)) { - throw new RuntimeException("Endpoint {" + getPath() + "} has no description."); - } - - // Check whether all segments have a description. - List segments = getNamedSegments(); - for (String segment : segments) { - if (!getUriParameters().containsKey(segment)) { - throw new RuntimeException( - "Missing URI description for path {" + getRamlPath() + "} segment {" + segment + "}"); - } - } - return this; - } - - @Override - public List getNamedSegments() { - List allMatches = new ArrayList(); - Matcher m = Pattern.compile("\\{[^}]*\\}").matcher(getRamlPath()); - while (m.find()) { - allMatches.add(m.group().substring(1, m.group().length() - 1)); - } - return allMatches; } @Override + @Deprecated public InternalEndpointRoute blockingHandler(Handler requestHandler) { - route.blockingHandler(requestHandler); - return this; - } - - @Override - public InternalEndpointRoute blockingHandler(Handler requestHandler, boolean ordered) { - route.blockingHandler(requestHandler, ordered); + super.blockingHandler(requestHandler); return this; } @Override - public InternalEndpointRoute failureHandler(Handler failureHandler) { - route.failureHandler(failureHandler); - return this; + protected String getJsonSchema(Class clazz) { + return JsonUtil.getJsonSchema(clazz); } @Override - public InternalEndpointRoute remove() { - route.remove(); - return this; - } - - @Override - public InternalEndpointRoute disable() { - route.disable(); - return this; - } - - @Override - public InternalEndpointRoute enable() { - route.enable(); - return this; - } - - @Override - public InternalEndpointRoute useNormalisedPath(boolean useNormalisedPath) { - route.useNormalizedPath(useNormalisedPath); - return this; - } - - @Override - public String getPath() { - return route.getPath(); - } - - @Override - public String getRamlPath() { - if (ramlPath == null) { - return convertPath(route.getPath()); - } - return ramlPath; - } - - @Override - public InternalEndpointRoute displayName(String name) { - this.displayName = name; - return this; - } - - @Override - public InternalEndpointRoute description(String description) { - this.description = description; - return this; - } - - @Override - public String getDescription() { - return description; - } - - @Override - public String getDisplayName() { - return displayName; - } - - @Override - public InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description, String headerName, - String example, String headerDescription) { - Response response = new Response(); - response.setDescription(description); - exampleResponses.put(status.code(), response); - if (headerName != null) { - Header header = new Header(); - header.setDescription(headerDescription); - header.setExample(example); - Map headers = new HashMap<>(); - headers.put(headerName, header); - response.setHeaders(headers); - } - return this; - } - - @Override - public InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description) { - return exampleResponse(status, description, null, null, null); - } - - @Override - public InternalEndpointRoute exampleResponse(HttpResponseStatus status, Object model, String description) { - Response response = new Response(); - response.setDescription(description); - - HashMap map = new HashMap<>(); - response.setBody(map); - - MimeType mimeType = new MimeType(); - if (model instanceof RestModel) { - String json = ((RestModel) model).toJson(false); - mimeType.setExample(json); - mimeType.setSchema(getSchema(model.getClass())); - map.put("application/json", mimeType); - } else { - mimeType.setExample(model.toString()); - if (model.getClass().getSimpleName().toLowerCase().startsWith("json")) { - map.put("application/json", mimeType); - } else { - map.put("text/plain", mimeType); - } - } - - exampleResponses.put(status.code(), response); - exampleResponseClasses.put(status.code(), model.getClass()); - return this; - } - - private String getSchema(Class clazz) { - return SCHEMA_CACHE.computeIfAbsent(clazz, JsonUtil::getJsonSchema); - } - - @Override - public Map> getExampleResponseClasses() { - return exampleResponseClasses; - } - - @Override - public InternalEndpointRoute exampleRequest(String bodyText) { - HashMap bodyMap = new HashMap<>(); - MimeType mimeType = new MimeType(); - mimeType.setExample(bodyText); - bodyMap.put("text/plain", mimeType); - this.exampleRequestMap = bodyMap; - return this; - } - - @Override - public InternalEndpointRoute exampleRequest(Map> parameters) { - HashMap bodyMap = new HashMap<>(); - MimeType mimeType = new MimeType(); - mimeType.setFormParameters(parameters); - bodyMap.put("multipart/form-data", mimeType); - this.exampleRequestMap = bodyMap; - return this; - } - - @Override - public InternalEndpointRoute exampleRequest(RestModel model) { - HashMap bodyMap = new HashMap<>(); - MimeType mimeType = new MimeType(); - String json = model.toJson(false); - mimeType.setExample(json); - mimeType.setSchema(getSchema(model.getClass())); - bodyMap.put("application/json", mimeType); - this.exampleRequestMap = bodyMap; - this.exampleRequestClass = model.getClass(); + public InternalEndpointRoute events(MeshEvent... events) { + this.events.addAll(Arrays.asList(events)); return this; } @@ -399,172 +72,6 @@ public InternalEndpointRoute exampleRequest(JSONObject jsonObject) { return this; } - @Override - public InternalEndpointRoute traits(String... traits) { - this.traits = traits; - return this; - } - - @Override - public String[] getTraits() { - return traits; - } - - @Override - public Map getExampleResponses() { - return exampleResponses; - } - - @Override - public HashMap getExampleRequestMap() { - return exampleRequestMap; - } - - @Override - public String getPathRegex() { - return pathRegex; - } - - @Override - public HttpMethod getMethod() { - return method; - } - - @Override - public Map getQueryParameters() { - return parameters; - } - - @Override - public InternalEndpointRoute addQueryParameters(Class clazz) { - try { - ParameterProvider provider = clazz.getConstructor().newInstance(); - parameters.putAll(provider.getRAMLParameters()); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - return this; - } - - @Override - public InternalEndpointRoute addQueryParameter(String name, String description, String example) { - QueryParameter param = new QueryParameter(); - param.setDescription(description); - if (example != null) { - param.setExample(example); - } - parameters.put(name, param); - return this; - } - - @Override - public InternalEndpointRoute setRAMLPath(String path) { - this.ramlPath = path; - return this; - } - - @Override - public Map getUriParameters() { - return uriParameters; - } - - @Override - public InternalEndpointRoute addUriParameter(String key, String description, String example) { - UriParameter param = new UriParameter(key); - param.setDescription(description); - param.setExample(example); - uriParameters.put(key, param); - return this; - } - - @Override - public int compareTo(InternalEndpointRoute o) { - return getRamlPath().compareTo(o.getRamlPath()); - } - - /** - * Convert the provided vertx path to a RAML path. - * - * @param path - * @return RAML Path which contains '{}' instead of ':' characters - */ - private String convertPath(String path) { - StringBuilder builder = new StringBuilder(); - String[] segments = path.split("/"); - for (int i = 0; i < segments.length; i++) { - String segment = segments[i]; - if (segment.startsWith(":")) { - segment = "{" + segment.substring(1) + "}"; - } - builder.append(segment); - if (i != segments.length - 1) { - builder.append("/"); - } - } - if (path.endsWith("/")) { - builder.append("/"); - } - return builder.toString(); - } - - @Override - public Class getExampleRequestClass() { - return exampleRequestClass; - } - - @Override - public InternalEndpointRoute events(MeshEvent... events) { - this.events.addAll(Arrays.asList(events)); - return this; - } - - /** - * Return list of events for the endpoint. - * - * @return - */ - public Set getEvents() { - return events; - } - - @Override - public boolean isMutating() { - return Optional.ofNullable(mutating) - .orElse(Optional.ofNullable(getMethod()).map(mutatingMethods::contains).orElse(false)); - } - - @Override - public InternalEndpointRouteImpl setMutating(Boolean mutating) { - this.mutating = mutating; - return this; - } - - @Override - public Route getRoute() { - return route; - } - - @Override - public boolean isInsecure() { - return insecure; - } - - @Override - public InternalEndpointRoute setInsecure(boolean insecure) { - this.insecure = insecure; - return this; - } - - @Override - public Set getProduces() { - return Collections.unmodifiableSet(produces); - } - - @Override - public Set getConsumes() { - return Collections.unmodifiableSet(consumes); - } - private class ReadOnlyHandler implements PlatformHandler { private final LocalConfigApi localConfigApi; @@ -593,16 +100,4 @@ public void handle(RoutingContext rc) { } } } - - @Override - public InternalEndpointRoute setModel(Collection> modelComponents) { - this.modelComponents.addAll(modelComponents); - return this; - } - - @Override - public Collection> getModel() { - // TODO Auto-generated method stub - return modelComponents; - } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 8c5654d509..d2f1d5c321 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -51,9 +51,6 @@ import com.gentics.mesh.distributed.coordinator.MasterServer; import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; -import com.gentics.mesh.generator.OpenAPIv3Generator; -import com.gentics.mesh.generator.OpenAPIv3Generator.Format; -import com.gentics.mesh.generator.OpenAPIv3Generator.InParameter; import com.gentics.mesh.generator.RAMLGenerator; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.parameter.BackupParameters; @@ -62,6 +59,10 @@ import com.gentics.mesh.search.SearchProvider; import com.gentics.mesh.util.RxUtil; import com.gentics.mesh.util.UUIDUtil; +import com.gentics.vertx.openapi.OpenAPIv3Generator; +import com.gentics.vertx.openapi.OpenAPIv3Generator.InParameter; +import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.OpenAPIGenerationException; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; @@ -291,46 +292,50 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "", "\\/api\\/\\{apiversion\\}[.]*")); // Make an instance with blacklist path patterns - OpenAPIv3Generator generator = new OpenAPIv3Generator(servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + OpenAPIv3Generator generator = new OpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); // Generate... - ac.send(generator.generate( - Stream.of( - //... from base root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), - //... from generic project root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{projectName}")) - ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), - // ...with desired format - Format.parse(format), - // ...with desired pretty printing - !httpServerConfig.isMinifyJson(), - // ...with desired spec version - useVersion31, - // ...with a manipulator injecting a `projectName` path parameter, where unavailable - Optional.of((path, item) -> { - if (path.contains("/{projectName}/")) { - Parameter projectNameParam = new Parameter().name("projectName").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); - item.readOperations().stream() - .forEach(o -> o.getParameters().stream().filter(p -> "projectName".equals(p.getName())).findAny() - .ifPresentOrElse(present -> { - // already exists, no action - }, () -> o.addParametersItem(projectNameParam))); - } - return path; - }), - // ...with component model provider - Optional.of(() -> Collections.unmodifiableCollection( - new Reflections("com.gentics.mesh") - .getSubTypesOf(RestModel.class) - .stream() - .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) - .collect(Collectors.toSet())))), - OK, - "yaml".equalsIgnoreCase(format) - ? HttpConstants.APPLICATION_YAML_UTF8 - : HttpConstants.APPLICATION_JSON_UTF8 - ); + try { + ac.send(generator.generate( + Stream.of( + //... from base root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), + //... from generic project root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{projectName}")) + ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), + // ...with desired format + Format.parse(format), + // ...with desired pretty printing + !httpServerConfig.isMinifyJson(), + // ...with desired spec version + useVersion31, + // ...with a manipulator injecting a `projectName` path parameter, where unavailable + Optional.of((path, item) -> { + if (path.contains("/{projectName}/")) { + Parameter projectNameParam = new Parameter().name("projectName").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); + item.readOperations().stream() + .forEach(o -> o.getParameters().stream().filter(p -> "projectName".equals(p.getName())).findAny() + .ifPresentOrElse(present -> { + // already exists, no action + }, () -> o.addParametersItem(projectNameParam))); + } + return path; + }), + // ...with component model provider + Optional.of(() -> Collections.unmodifiableCollection( + new Reflections("com.gentics.mesh") + .getSubTypesOf(RestModel.class) + .stream() + .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) + .collect(Collectors.toSet())))), + OK, + "yaml".equalsIgnoreCase(format) + ? HttpConstants.APPLICATION_YAML_UTF8 + : HttpConstants.APPLICATION_JSON_UTF8 + ); + } catch (OpenAPIGenerationException e) { + ac.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); + } } /** diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java deleted file mode 100644 index 42033d0130..0000000000 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIv3Generator.java +++ /dev/null @@ -1,746 +0,0 @@ -package com.gentics.mesh.generator; - -import static com.gentics.mesh.core.rest.error.Errors.error; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.math.BigDecimal; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Optional; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import javax.annotation.Nonnull; - -import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry; -import org.apache.commons.lang.StringUtils; -import org.raml.model.MimeType; -import org.raml.model.parameter.AbstractParam; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonSerializable; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.core.rest.common.RestModel; -import com.gentics.mesh.http.HttpConstants; -import com.gentics.mesh.rest.InternalEndpointRoute; - -import io.swagger.v3.core.util.Json; -import io.swagger.v3.core.util.Json31; -import io.swagger.v3.core.util.OpenAPI30To31; -import io.swagger.v3.core.util.Yaml; -import io.swagger.v3.core.util.Yaml31; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.media.Content; -import io.swagger.v3.oas.models.media.MediaType; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.RequestBody; -import io.swagger.v3.oas.models.responses.ApiResponse; -import io.swagger.v3.oas.models.responses.ApiResponses; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; -import io.swagger.v3.oas.models.servers.Server; -import io.vertx.core.http.HttpMethod; -import io.vertx.core.json.JsonObject; -import io.vertx.ext.web.Route; -import io.vertx.ext.web.Router; - -/** - * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. - * - * @author plyhun - * - */ -public class OpenAPIv3Generator { - - private static final Logger log = LoggerFactory.getLogger(OpenAPIv3Generator.class); - - private final Optional> maybePathBlacklist; - private final Optional> maybePathWhitelist; - - private final List servers; - - /** - * Ctor - * - * @param servers a list of available servers; may be empty. - * @param maybePathBlacklist optional regex blacklist - * @param maybePathWhitelist optional regex whitelist - */ - public OpenAPIv3Generator(List servers, @Nonnull Optional> maybePathBlacklist, - @Nonnull Optional> maybePathWhitelist) { - this.maybePathBlacklist = maybePathBlacklist; - this.maybePathWhitelist = maybePathWhitelist; - this.servers = servers; - } - - /** - * Generate the spec out of the given routes and format - * - * @param routers - * @param format - * @param pretty - * @param maybePathItemTransformer an optional custon path and path item transformer - * @return - */ - public String generate(Map routers, Format format, boolean pretty, - @Nonnull Optional> maybePathItemTransformer, - @Nonnull Optional>>> maybeExtraComponentSupplier) { - return generate(routers, format, pretty, false, maybePathItemTransformer, maybeExtraComponentSupplier); - } - - /** - * Generate the spec out of given routes and parameters - * - * @param routers - * @param format - * @param pretty - * @param useVersion31 - * @param maybePathItemTransformer an optional custon path and path item transformer - * @return - */ - public String generate(Map routers, Format format, boolean pretty, boolean useVersion31, - @Nonnull Optional> maybePathItemTransformer, - @Nonnull Optional>>> maybeExtraComponentSupplier) { - log.info("Starting OpenAPIv3 generation..."); - OpenAPI openApi = new OpenAPI(); - openApi.setPaths(new Paths()); - Info info = new Info(); - info.setTitle("Gentics Mesh REST API"); - info.setVersion(MeshVersion.getBuildInfo().getVersion()); - - openApi.servers(servers.stream().map(url -> { - Server server = new Server(); - server.setUrl(url); - return server; - }).collect(Collectors.toList())); - - openApi.setInfo(info); - try { - addSecurity(openApi); - maybeExtraComponentSupplier.ifPresent(componentSupplier -> { - componentSupplier.get().forEach(componentClass -> fillComponent(componentClass, openApi)); - }); - for (Entry routerAndParent : routers.entrySet()) { - addRouter(routerAndParent.getValue(), routerAndParent.getKey(), openApi, maybePathItemTransformer); - } - } catch (IOException e) { - throw new RuntimeException("Could not add all verticles to raml generator", e); - } - - OpenAPIVersionWriter writer = useVersion31 ? new V31Writer() : new V30Writer(); - return writer.write(openApi, format, pretty); - } - - /** - * Add a security to the spec - * - * @param openApi - */ - protected void addSecurity(OpenAPI openApi) { - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - SecurityScheme securityBearerAuth = new SecurityScheme(); - securityBearerAuth.setScheme("bearer"); - securityBearerAuth.setType(SecurityScheme.Type.HTTP); - securityBearerAuth.setBearerFormat("JWT"); - components.addSecuritySchemes("bearerAuth", securityBearerAuth); - //TODO OAuth2 - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - openApi.addSecurityItem(reqBearerAuth); - } - - @SuppressWarnings("rawtypes") - protected void fillComponent(Class cls, OpenAPI openApi) { - if (StringUtils.isBlank(cls.getSimpleName())) { - return; - } - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - if (components.getSchemas() == null) { - components.setSchemas(new HashMap<>(Map.of("AnyJson", new Schema()))); - } - Schema schema = components.getSchemas().getOrDefault(cls.getSimpleName(), new Schema()); - schema.setType("object"); - schema.setName(cls.getSimpleName()); - List> fieldStreams = new ArrayList<>(); - final List generics = new ArrayList<>(); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(cls) ? ParameterizedType.class.cast(cls).getActualTypeArguments() : new Type[0])); - Deque> dq = new ArrayDeque<>(2); - dq.addLast(cls); - while(!dq.isEmpty()) { - Class tclass = dq.pop(); - log.debug("Class: " + tclass.getCanonicalName()); - /*if (tclass != cls) { - Schema tschema = components.getSchemas().computeIfAbsent(tclass.getSimpleName(), key -> new Schema()); - tschema.setName(tclass.getSimpleName()); - Schema refSchema = new Schema<>(); - refSchema.$ref("#/components/schemas/" + tclass.getSimpleName()); - schema.addAllOfItem(refSchema); - refSchema = new Schema<>(); - refSchema.$ref("#/components/schemas/" + cls.getSimpleName()); - tschema.addAnyOfItem(refSchema); - }*/ - fieldStreams.add(Arrays.stream(tclass.getDeclaredFields())); - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(tclass.getGenericSuperclass()) ? ParameterizedType.class.cast(tclass.getGenericSuperclass()).getActualTypeArguments() : new Type[0])); - dq.addAll(Arrays.stream(tclass.getInterfaces()).filter(i -> i.getPackageName().startsWith("com.gentics.mesh")).collect(Collectors.toList())); - tclass = tclass.getSuperclass(); - if (tclass != null) { - dq.addLast(tclass); - } - } - if (generics.size() > 0) { - log.debug(" - Generics: " + Arrays.toString(generics.toArray())); - } - Map properties = fieldStreams.stream().flatMap(Function.identity()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).peek(f -> { - Class t = f.getType(); - if (!RestModel.class.isAssignableFrom(t) && !t.isPrimitive() && !t.getCanonicalName().startsWith("java.lang")) { - fillComponent(t, openApi); - } - }) - .map(f -> { - String name = f.getName(); - log.debug(" - Field: " + f); - Schema fieldSchema = new Schema(); - fieldSchema.setName(name); - JsonPropertyDescription description = f.getAnnotation(JsonPropertyDescription.class); - if (description != null) { - fieldSchema.setDescription(description.value()); - } - JsonProperty property = f.getAnnotation(JsonProperty.class); - if (property != null) { - if (StringUtils.isNotBlank(property.defaultValue())) { - fieldSchema.setDefault(property.defaultValue()); - } - if (StringUtils.isNotBlank(property.value())) { - name = property.value(); - } - if (property.required()) { - schema.addRequiredItem(name); - } - } - Class t = f.getType(); - JsonDeserialize jdes = f.getAnnotation(JsonDeserialize.class); - if (jdes != null && jdes.as() != null) { - t = jdes.as(); - fieldSchema.setType("object"); - fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); - } else { - generics.addAll(Arrays.asList(ParameterizedType.class.isInstance(f.getGenericType()) ? ParameterizedType.class.cast(f.getGenericType()).getActualTypeArguments() : new Type[0])); - if (generics.size() > 0) { - log.debug(" - Generics: " + Arrays.toString(generics.toArray())); - } - fillType(components, t, fieldSchema, generics); - } - fieldSchema.setTypes(Collections.singleton(fieldSchema.getType())); - return new UnmodifiableMapEntry<>(name, fieldSchema); - }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); - schema.setProperties(properties); - components.addSchemas(cls.getSimpleName(), schema); - } - - protected void resolveEndpointRoute(String path, PathItem pathItem, InternalEndpointRoute endpoint) { - Operation operation = new Operation(); - HttpMethod method = endpoint.getMethod(); - if (method == null) { - method = HttpMethod.GET; - } - if (endpoint.isInsecure()) { - operation.setSecurity(Collections.emptyList()); - } else { - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - operation.setSecurity(List.of(reqBearerAuth)); - } - resolveMethod(method.name(), pathItem, operation); - List> params = List.of( - endpoint.getQueryParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.QUERY)), - endpoint.getUriParameters().entrySet().stream().map(e -> parameter(e.getKey(), e.getValue(), InParameter.PATH))); - operation.setParameters(params.stream().flatMap(Function.identity()).filter(Objects::nonNull).collect(Collectors.toList())); - ApiResponses responses = new ApiResponses(); - endpoint.getProduces().stream().map(e -> { - ApiResponse response = new ApiResponse(); - Content responseBody = new Content(); - responseBody.addMediaType(e, new MediaType()); - response.setContent(responseBody); - response.setDescription(e); - if (HttpConstants.APPLICATION_OCTET_STREAM.equals(e)) { - response.setExtensions(Map.of("x-is-file", true)); - Schema schema = new Schema<>(); - schema.setType("string"); - schema.setFormat("binary"); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - responseBody.addMediaType("application/octet-stream", mediaType); - response.setContent(responseBody); - } - return new UnmodifiableMapEntry("default", response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(e.getKey(), e.getValue())); - endpoint.getExampleResponses().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> { - ApiResponse response = new ApiResponse(); - if (e.getValue().getDescription().startsWith("Generated login token")) { - e.getValue().getHeaders(); - } - response.setDescription(e.getValue().getDescription()); - Content responseBody = new Content(); - if (endpoint.getExampleResponseClasses() != null && endpoint.getExampleResponseClasses().get(e.getKey()) != null) { - Class ref = endpoint.getExampleResponseClasses().get(e.getKey()); - if (ref.getCanonicalName().startsWith("com.gentics.mesh")) { - Schema schema = new Schema<>(); - schema.set$ref("#/components/schemas/" + ref.getSimpleName()); - MediaType mediaType = new MediaType(); - mediaType.setSchema(schema); - mediaType.setExample(e.getValue()); - responseBody.addMediaType("*/*", mediaType); - response.setContent(responseBody); - } else { - if (e.getValue().getBody() != null) { - e.getValue().getBody().entrySet().stream().filter(r -> Objects.nonNull(r.getValue())) - .map(r -> fillMediaType(r.getKey(), r.getValue(), ref)) - .filter(Objects::nonNull).forEach(r -> responseBody.addMediaType(r.getKey(), r.getValue())); - } else { - log.warn("Body of " + e.getKey() + " is null!"); - } - response.setContent(responseBody); - } - } - return new UnmodifiableMapEntry(e.getKey(), response); - }).filter(Objects::nonNull).forEach(e -> responses.addApiResponse(Integer.toString(e.getKey()), e.getValue())); - operation.setResponses(responses); - if (endpoint.getExampleRequestMap() != null && !HttpMethod.DELETE.equals(method)) { - RequestBody requestBody = new RequestBody(); - Content content = new Content(); - endpoint.getExampleRequestMap().entrySet().stream().filter(e -> Objects.nonNull(e.getValue())) - .map(e -> fillMediaType(e.getKey(), e.getValue(), endpoint.getExampleRequestClass())) - .filter(Objects::nonNull).forEach(e -> content.addMediaType(e.getKey(), e.getValue())); - requestBody.setContent(content); - operation.setRequestBody(requestBody); - } - // action.setIs(Arrays.asList(endpoint.getTraits())); - } - - protected void resolveMethod(String methodName, PathItem pathItem, Operation operation) { - switch (methodName.toUpperCase()) { - case "DELETE": - pathItem.setDelete(operation); - break; - case "GET": - pathItem.setGet(operation); - break; - case "HEAD": - pathItem.setHead(operation); - break; - case "OPTIONS": - pathItem.setOptions(operation); - break; - case "PATCH": - pathItem.setPatch(operation); - break; - case "POST": - pathItem.setPost(operation); - break; - case "PUT": - pathItem.setPut(operation); - break; - case "TRACE": - pathItem.setTrace(operation); - break; - default: - break; - } - } - - protected void addRouter(String parent, Router router, OpenAPI consumer, Optional> maybePathItemTransformer) - throws IOException { - Paths paths = consumer.getPaths(); - - for (Route route : router.getRoutes()) { - String rawPath = route.getPath(); - if (StringUtils.isBlank(rawPath)) { - continue; - } - String path = (parent + (StringUtils.equals(rawPath, "/") ? "/" : Arrays.stream(rawPath.split("/")) - .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) - .collect(Collectors.joining("/")))) - .replace("//", "/"); - - if(maybePathBlacklist.flatMap(list -> list.stream().filter(blacklisted -> blacklisted.matcher(path).matches()).findAny()).isPresent() - || (maybePathWhitelist.isPresent() && maybePathWhitelist.flatMap(list -> list.stream().filter(whitelisted -> whitelisted.matcher(path).matches()).findAny()).isEmpty())) { - log.debug("Path filtered off: " + path); - continue; - } - PathItem pathItem = Optional.ofNullable(paths.get(path)).orElseGet(() -> { - log.debug("Raw path: " + path); - PathItem item = new PathItem(); - item.setSummary(route.getName()); - paths.put(path, item); - return item; - }); - Optional.ofNullable(route.getMetadata(InternalEndpointRoute.class.getCanonicalName())) - .map(InternalEndpointRoute.class::cast) - .ifPresentOrElse(endpoint -> { - log.debug("Path with metadata: " + path); - pathItem.setSummary(endpoint.getDisplayName()); - pathItem.setDescription(endpoint.getDescription()); - endpoint.getModel().forEach(modelComponent -> fillComponent(modelComponent, consumer)); - resolveEndpointRoute(path, pathItem, endpoint); - }, () -> { - resolveFallbackRoute(route, pathItem); - }); - String path1 = maybePathItemTransformer.map(pathItemTransformer -> { - String newPath = pathItemTransformer.apply(path, pathItem); - if (!StringUtils.equals(path, newPath)) { - paths.remove(path, pathItem); - paths.put(newPath, pathItem); - } - return path; - }).orElse(path); - if (pathItem.readOperations().isEmpty()) { - log.debug("Path removed due to having no operations: " + path1); - paths.remove(path1, pathItem); - } - if (route.getSubRouter() != null) { - addRouter(path, route.getSubRouter(), consumer, maybePathItemTransformer); - } - } - } - - protected void resolveFallbackRoute(Route r, PathItem pathItem) { - Operation o = new Operation(); - o.setParameters(Arrays.stream(r.getPath().split("/")) - .filter(segment -> segment.startsWith(":")) - .map(segment -> segment.substring(1)) - .map(segment -> new Parameter() - .name(segment) - .required(true) - .allowEmptyValue(false) - .in(InParameter.PATH.toString()) - .schema(new Schema() - .type("string") - .description("A path parameter `" + segment + "` of a fallback type `string`"))) - .collect(Collectors.toList())); - ApiResponses responses = new ApiResponses(); - ApiResponse response = new ApiResponse(); - Content responseBody = new Content(); - responseBody.addMediaType("*/*", new MediaType()); - response.setDescription("Auto generated response description for " + r.getPath()); - response.setContent(responseBody); - responses.addApiResponse("200", response); - o.setResponses(responses); - Optional.ofNullable(r.methods()).ifPresent(methods -> methods.stream().forEach(m -> resolveMethod(m.name(), pathItem, o))); - } - - @SuppressWarnings("rawtypes") - protected Map.Entry fillMediaType(String key, MimeType mimeType, Class refClass) { - MediaType mediaType = new MediaType(); - mediaType.setExample(mimeType.getExample()); - if (mimeType.getFormParameters() != null) { - Map props = mimeType.getFormParameters().entrySet().stream().map(p -> parameter(p.getKey(), p.getValue().get(0), null)) - .collect(Collectors.toMap(p -> p.getName(), p -> p.getSchema())); - Schema schema = new Schema<>(); - schema.setType("object"); - schema.setProperties(props); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry("multipart/form-data", mediaType); - } else if (mimeType.getSchema() != null) { - JsonObject jschema = new JsonObject(mimeType.getSchema()); - Schema schema = new Schema<>(); - schema.setType(jschema.getString("type", "string")); - schema.set$id(jschema.getString("id")); - schema.set$ref("#/components/schemas/" + refClass.getSimpleName()); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(key, mediaType); - } else if (refClass != null && refClass.getSimpleName().toLowerCase().startsWith("json")) { - mediaType.setExample(mimeType.getExample()); - Schema schema = new Schema<>(); - schema.setType("object"); - mediaType.setSchema(schema); - return new UnmodifiableMapEntry(key, mediaType); - } else { - return new UnmodifiableMapEntry(key, mediaType); - } - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - private void fillType(Components components, Class t, Schema fieldSchema, List generics) { - if (t.isPrimitive() || Number.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { - if (int.class.isAssignableFrom(t) || Integer.class.isAssignableFrom(t)) { - fieldSchema.setType("integer"); - fieldSchema.setFormat("int32"); - } else if (boolean.class.isAssignableFrom(t) || Boolean.class.isAssignableFrom(t)) { - fieldSchema.setType("boolean"); - } else if (float.class.isAssignableFrom(t) || Float.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - fieldSchema.setFormat("float"); - } else if (long.class.isAssignableFrom(t) || Long.class.isAssignableFrom(t)) { - fieldSchema.setType("integer"); - fieldSchema.setFormat("int64"); - } else if (double.class.isAssignableFrom(t) || Double.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - fieldSchema.setFormat("double"); - } else if (BigDecimal.class.isAssignableFrom(t) || Number.class.isAssignableFrom(t)) { - fieldSchema.setType("number"); - } else { - fieldSchema.setType("object"); - } - } else if (CharSequence.class.isAssignableFrom(t)) { - fieldSchema.setType("string"); - } else if (t.isArray() || List.class.isAssignableFrom(t)) { - fieldSchema.setType("array"); - Schema itemSchema = new Schema(); - if (t.isArray()) { - List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); - fillType(components, t.getComponentType(), itemSchema, generics1); - } else if (generics.size() > 0 && Class.class.isInstance(generics.get(0))) { - Class itemClass = Class.class.cast(generics.get(0)); - List generics1 = Arrays.asList(ParameterizedType.class.isInstance(t) ? ParameterizedType.class.cast(t).getActualTypeArguments() : new Type[0]); - fillType(components, itemClass, itemSchema, generics1); - } else { - // TODO - log.error("Unknown array type" + t + " / " + Arrays.toString(generics.toArray())); - } - fieldSchema.setItems(itemSchema); - } else { - if (t.isEnum()) { - Schema enumSchema = new Schema(); - enumSchema.setType("string"); - enumSchema.setEnum(Arrays.stream(t.getEnumConstants()).map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); - components.addSchemas(t.getSimpleName(), enumSchema); - } - if (Map.class.isAssignableFrom(t)) { - if (generics.size() == 2) { - BiConsumer innerTypeMapper = (ty, tfieldSchema) -> { - Class tt = Class.class.isInstance(ty) ? Class.class.cast(ty) : generics.get(1).getClass(); - if (tt.isPrimitive() || Number.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { - if (int.class.isAssignableFrom(tt) || Integer.class.isAssignableFrom(tt)) { - tfieldSchema.setType("integer"); - tfieldSchema.setFormat("int32"); - } else if (boolean.class.isAssignableFrom(tt) || Boolean.class.isAssignableFrom(tt)) { - tfieldSchema.setType("boolean"); - } else if (float.class.isAssignableFrom(tt) || Float.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - tfieldSchema.setFormat("float"); - } else if (long.class.isAssignableFrom(tt) || Long.class.isAssignableFrom(tt)) { - tfieldSchema.setType("integer"); - tfieldSchema.setFormat("int64"); - } else if (double.class.isAssignableFrom(tt) || Double.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - tfieldSchema.setFormat("double"); - } else if (BigDecimal.class.isAssignableFrom(tt) || Number.class.isAssignableFrom(tt)) { - tfieldSchema.setType("number"); - } else { - tfieldSchema.setType("object"); - } - } else if (CharSequence.class.isAssignableFrom(tt)) { - tfieldSchema.setType("string"); - } else { - tfieldSchema.setType("object"); - } - }; - fieldSchema.setType("object"); // TODO why object? - //innerTypeMapper.accept(generics.get(0).getClass(), fieldSchema); - Schema valueSchema = new Schema<>(); - innerTypeMapper.accept(generics.get(1), valueSchema); - fieldSchema.setAdditionalProperties(valueSchema); - } else { - fieldSchema.setType("object"); - fieldSchema.setAdditionalProperties(new Schema().type("object")); - } - } else if (JsonObject.class.isAssignableFrom(t) || JsonSerializable.class.isAssignableFrom(t)) { - fieldSchema.set$ref("#/components/schemas/AnyJson"); - } else { - fieldSchema.setType("object"); - fieldSchema.set$ref("#/components/schemas/" + t.getSimpleName()); - } - } - } - - @SuppressWarnings("rawtypes") - private final Parameter parameter(String name, AbstractParam param, InParameter inType) { - Schema schema; - switch (param.getType()) { - case BOOLEAN: - schema = new Schema(); - schema.setType("boolean"); - break; - case DATE: - schema = new Schema(); - schema.setType("integer"); - schema.setFormat("int64"); - break; - case FILE: - schema = new Schema(); - schema.setType("string"); - schema.setFormat("binary"); - break; - case INTEGER: - schema = new Schema(); - schema.setType("integer"); - schema.setFormat("int32"); - break; - case NUMBER: - schema = new Schema(); - schema.setType("number"); - schema.setFormat("double"); - break; - case STRING: - schema = new Schema(); - schema.setType("string"); - break; - default: - schema = new Schema(); - schema.setType("object"); - break; - } - schema.setMinimum(param.getMinimum()); - schema.setMaximum(param.getMaximum()); - schema.setMinLength(param.getMinLength()); - schema.setMaxLength(param.getMaxLength()); - schema.setDefault(param.getDefaultValue()); - schema.setEnum(param.getEnumeration()); - schema.setPattern(param.getPattern()); - schema.setDescription(param.getDescription()); - if (StringUtils.isNotBlank(param.getExample())) { - schema.setExample(param.getExample()); - } - Parameter p = new Parameter(); - p.setRequired(param.isRequired()); - p.setDescription(param.getDescription()); - p.setSchema(schema); - p.setName(name); - if (inType != null) { - p.setIn(inType.value); - } - return p; - } - - public enum InParameter { - PATH("path"), QUERY("query"), HEADER("header"), COOKIE("cookie"); - - private final String value; - - private InParameter(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - } - - /** - * OpenAPI output format - */ - public static enum Format { - YAML, - JSON; - - public static final Format parse(String text) { - if (text == null) { - throw new IllegalArgumentException("Cannot parse null to OpenAPI Format"); - } - return Arrays.stream(values()) - .filter(v -> v.name().equals(text.trim().toUpperCase())) - .findAny() - .orElseThrow(() -> new IllegalStateException("Unsupported OpenAPI Format:" + text)); - } - } - - public interface OpenAPIVersionWriter { - String write(OpenAPI api, Format format, boolean pretty); - } - private class V30Writer implements OpenAPIVersionWriter { - - @Override - public String write(OpenAPI openApi, Format format, boolean pretty) { - switch (format) { - case YAML: - try { - //formatted = pretty ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); - return pretty ? Yaml.pretty().writeValueAsString(openApi) : Yaml.mapper().writer().writeValueAsString(openApi) ; - } catch (JsonProcessingException e) { - throw new RuntimeException("Could not generate YAML", e); - } - case JSON: - try { - //formatted = pretty ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); - return pretty ? Json.pretty(openApi) : Json.mapper().writer().writeValueAsString(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - default: - throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } - } - - } - private class V31Writer implements OpenAPIVersionWriter { - - @Override - public String write(OpenAPI openApi, Format format, boolean pretty) { - new OpenAPI30To31().process(openApi); - openApi.jsonSchemaDialect("https://spec.openapis.org/oas/3.1/dialect/base"); - - switch (format) { - case YAML: - try { - return pretty ? Yaml31.mapper().writer().writeValueAsString(openApi) : Yaml31.pretty().writeValueAsString(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException("Could not generate YAML", e); - } - case JSON: - try { - return pretty ? Json31.mapper().writer().writeValueAsString(openApi) : Json31.pretty(openApi); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - default: - throw error(BAD_REQUEST, "Please specify a response format: YAML or JSON"); - } - } - } -} diff --git a/mdm/api/pom.xml b/mdm/api/pom.xml index a528f0270e..0c7990b128 100644 --- a/mdm/api/pom.xml +++ b/mdm/api/pom.xml @@ -28,5 +28,35 @@ com.gentics.graphqlfilter graphql-java-filter + + com.gentics + vertx-openapi + + + org.slf4j + slf4j-parent + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-api + + + commons-validator + commons-validator + + + com.github.fge + json-schema-validator + + + com.fasterxml.jackson.module + jackson-module-jsonSchema + + + diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/AbstractParameters.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/AbstractParameters.java index 4a18a516e4..e040590f9c 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/AbstractParameters.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/AbstractParameters.java @@ -1,19 +1,20 @@ package com.gentics.mesh.parameter; +import java.util.Map; + +import org.raml.model.parameter.QueryParameter; + import com.gentics.mesh.doc.GenerateDocumentation; import com.gentics.mesh.handler.ActionContext; +import com.gentics.vertx.openapi.model.parameters.SimpleParameterProviderImpl; import io.vertx.core.MultiMap; -import java.util.Map; - -import static com.gentics.mesh.util.HttpQueryUtils.toMap; - /** * Abstract class for parameter provider implementations. */ @GenerateDocumentation -public abstract class AbstractParameters implements ParameterProvider { +public abstract class AbstractParameters extends SimpleParameterProviderImpl { protected MultiMap parameters; @@ -23,38 +24,25 @@ public AbstractParameters(ActionContext ac) { } public AbstractParameters(MultiMap parameters) { - this.parameters = parameters; + super(parameters); } public AbstractParameters() { this(MultiMap.caseInsensitiveMultiMap()); } + /** + * Return the RAML parameters for this provider. + * + * @return + */ + @Override + public abstract Map getRAMLParameters(); + /** * Returns the human readable name of the parameters. * * @return */ public abstract String getName(); - - @Override - public String getParameter(String name) { - return parameters.get(name); - } - - @Override - public Map getParameters() { - return toMap(parameters); - } - - @Override - public void setParameter(String name, String value) { - parameters.set(name, value); - } - - @Override - public String toString() { - return getQueryParameters(); - } - } diff --git a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index d6a49a663d..3a53b3dd37 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -1,359 +1,21 @@ package com.gentics.mesh.rest; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import static com.gentics.mesh.core.rest.error.Errors.error; +import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; -import org.raml.model.MimeType; -import org.raml.model.Response; -import org.raml.model.parameter.FormParameter; -import org.raml.model.parameter.QueryParameter; -import org.raml.model.parameter.UriParameter; import com.gentics.mesh.core.rest.MeshEvent; -import com.gentics.mesh.core.rest.admin.localconfig.LocalConfigModel; -import com.gentics.mesh.core.rest.common.RestModel; -import com.gentics.mesh.parameter.ParameterProvider; -import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Handler; -import io.vertx.core.http.HttpMethod; -import io.vertx.ext.web.Route; -import io.vertx.ext.web.Router; +import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; /** * Simple wrapper for vert.x routes. The wrapper is commonly used to generate RAML descriptions for the route. */ -public interface InternalEndpointRoute extends Comparable { - - /** - * Wrapper for {@link Route#path(String)}. - * - * @param path - * @return Fluent API - */ - InternalEndpointRoute path(String path); - - /** - * Set the http method of the endpoint. - * - * @param method - * @return Fluent API - */ - InternalEndpointRoute method(HttpMethod method); - - /** - * Add a content type consumed by this endpoint. Used for content based routing. - * - * @param contentType - * the content type - * @return Fluent API - */ - InternalEndpointRoute consumes(String contentType); - - /** - * Set the request handler for the endpoint. - * - * @param requestHandler - * @return Fluent API - */ - InternalEndpointRoute handler(Handler requestHandler); - - /** - * Create a sub router - * @param router - * @return - */ - InternalEndpointRoute subRouter(Router router); - - /** - * Wrapper for {@link Route#last()} - * - * @return Fluent API - */ - InternalEndpointRoute last(); - - /** - * Wrapper for {@link Route#order(int)} - * - * @param order - * @return Fluent API - */ - InternalEndpointRoute order(int order); - - /** - * Validate that all mandatory fields have been set. - * - * @return Fluent API - */ - InternalEndpointRoute validate(); - - /** - * Wrapper for {@link Route#remove()} - * - * @return Fluent API - */ - InternalEndpointRoute remove(); - - /** - * Wrapper for {@link Route#disable()} - * - * @return Fluent API - */ - InternalEndpointRoute disable(); - - /** - * Wrapper for {@link Route#enable()} - * - * @return Fluent API - */ - InternalEndpointRoute enable(); - - /** - * Wrapper for {@link Route#useNormalisedPath(boolean)}. - * - * @param useNormalisedPath - * @return - */ - InternalEndpointRoute useNormalisedPath(boolean useNormalisedPath); - - /** - * Wrapper for {@link Route#getPath()} - * - * @return the path prefix (if any) for this route - */ - String getPath(); - - /** - * Return the endpoint description. - * - * @return Endpoint description - */ - String getDescription(); - - /** - * Return the display name for the endpoint. - * - * @return Endpoint display name - */ - String getDisplayName(); - - /** - * Add the given response to the example responses. - * - * @param status - * Status code of the response - * @param description - * Description of the response - * @return Fluent API - */ - InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description); - - /** - * Add the given response to the example responses. - * - * @param status - * Status code for the example response - * @param model - * Model which will be turned into JSON - * @param description - * Description of the example response - * @return Fluent API - */ - InternalEndpointRoute exampleResponse(HttpResponseStatus status, Object model, String description); - - /** - * Add the given response to the example responses. - * - * @param status - * Status code of the example response - * @param description - * Description of the example - * @param headerName - * Name of the header value - * @param example - * Example header value - * @param headerDescription - * Description of the header - * @return - */ - InternalEndpointRoute exampleResponse(HttpResponseStatus status, String description, String headerName, String example, String headerDescription); - - /** - * Create a blocking handler for the endpoint. - * The handler will be created "ordered", which means that handlers will not be called concurrently. - * This should only be used, when absolutely necessary and only for mutating requests. - * In all other cases, {@link #blockingHandler(Handler, boolean)} with ordered: false should be used. - * - * @param requestHandler request handler - * @return Fluent API - * @deprecated since requests will only be "ordered" when running in the same http verticle - */ - InternalEndpointRoute blockingHandler(Handler requestHandler); - - /** - * Create a blocking handler for the endpoint. - * - * @param requestHandler request handler - * @param ordered if the handlers should be called in order or may be called concurrently - * @return Fluent API - */ - InternalEndpointRoute blockingHandler(Handler requestHandler, boolean ordered); - - /** - * Create a failure handler for the endpoint. - * - * @param failureHandler - * @return Fluent API - */ - InternalEndpointRoute failureHandler(Handler failureHandler); - - /** - * Parse the RAML path and return a list of all segment name variables. - * - * @return List of path segments - */ - List getNamedSegments(); - - /** - * Set the content type for elements which are returned by the endpoint. - * - * @param contentType - * @return Fluent API - */ - InternalEndpointRoute produces(String contentType); - - /** - * Set the path using a regex. - * - * @param path - * @return Fluent API - */ - InternalEndpointRoute pathRegex(String path); - - /** - * Return the path used for RAML. If non null the path which was previously set using {@link #setRAMLPath(String)} will be returned. Otherwise the converted - * vert.x route path is returned. A vert.x path /:nodeUuid is converted to a RAML path /{nodeUuid}. - * - * @return RAML path - */ - String getRamlPath(); - - /** - * Set the endpoint display name. - * - * @param name - * @return Fluent API - */ - InternalEndpointRoute displayName(String name); - - /** - * Set the endpoint description. - * - * @param description - * Description of the endpoint. - * @return Fluent API - */ - InternalEndpointRoute description(String description); - - /** - * Add an uri parameter with description and example to the endpoint. - * - * @param key - * Key of the endpoint (e.g.: query, perPage) - * @param description - * @param example - * Example URI parameter value - */ - InternalEndpointRoute addUriParameter(String key, String description, String example); - - /** - * Return the uri parameters for the endpoint. - * - * @return Map with uri parameters - */ - Map getUriParameters(); - - /** - * Explicitly set the RAML path. This will override the path which is otherwise transformed using the vertx route path. - * - * @param path - */ - InternalEndpointRoute setRAMLPath(String path); - - /** - * - * @param name - * @param description - * @param example - * @return - */ - InternalEndpointRoute addQueryParameter(String name, String description, String example); - - /** - * Add a query parameter provider to the endpoint. The query parameter provider will in turn provide examples, descriptions for all query parameters which - * the parameter provider provides. - * - * @param clazz - * Class which provides the parameters - * @return Fluent API - */ - InternalEndpointRoute addQueryParameters(Class clazz); - - /** - * Return the list of query parameters for the endpoint. - * - * @return - */ - Map getQueryParameters(); - - /** - * Return the Vert.x route path regex. - * - * @return configured path regex or null if no path regex has been set - */ - String getPathRegex(); - - /** - * Return the endpoint HTTP example request map. - * - * @return - */ - HashMap getExampleRequestMap(); - - /** - * Return the map of example responses. The map contains examples per http status code. - * - * @return - */ - Map getExampleResponses(); - - /** - * Return the method used for the endpoint. - * - * @return - */ - HttpMethod getMethod(); - - /** - * Return the traits which were set for this endpoint. - * - * @return - */ - String[] getTraits(); - - /** - * Set the traits information. - * - * @param traits - * Traits which the endpoint should inherit - * @return Fluent API - */ - InternalEndpointRoute traits(String... traits); +public interface InternalEndpointRoute extends com.gentics.vertx.openapi.metadata.InternalEndpointRoute { /** * Set the endpoint json example request via the provided json object. The JSON schema will not be generated. @@ -363,45 +25,6 @@ public interface InternalEndpointRoute extends Comparable */ InternalEndpointRoute exampleRequest(JSONObject jsonObject); - /** - * Set the endpoint example request via a JSON example model. The json schema will automatically be generated. - * - * @param model - * Example Rest Model - * @return Fluent API - */ - InternalEndpointRoute exampleRequest(RestModel model); - - /** - * Set the endpoint request example via a form parameter list. - * - * @param parameters - * @return Fluent API - */ - InternalEndpointRoute exampleRequest(Map> parameters); - - /** - * Set the endpoint request example via a plain text body. - * - * @param bodyText - * @return Fluent API - */ - InternalEndpointRoute exampleRequest(String bodyText); - - /** - * Return map with status code and the response class. - * - * @return - */ - Map> getExampleResponseClasses(); - - /** - * Return the rest model class for the example request. - * - * @return - */ - Class getExampleRequestClass(); - /** * Set the events which are emitted by the action of the endpoint. * @@ -411,76 +34,20 @@ public interface InternalEndpointRoute extends Comparable InternalEndpointRoute events(MeshEvent... events); /** - * If true, this endpoint will create, update or delete items in the database. - * The route will throw an error if this instance is in read only mode. - * - * Per default, all POST, DELETE and PUT requests are mutating, other requests are not. - * - * @see LocalConfigModel#isReadOnly() - * @return - */ - boolean isMutating(); - - /** - * If true, this endpoint will create, update or delete items in the database. - * The route will throw an error if this instance is in read only mode. - * - * Per default, all POST, DELETE and PUT requests are mutating, other requests are not. + * @see {@link com.gentics.vertx.openapi.metadata.InternalEndpointRoute#blockingHandler(Handler)} * - * @see LocalConfigModel#isReadOnly() - * @param mutating - * @return - */ - InternalEndpointRoute setMutating(Boolean mutating); - - /** - * If true, the endpoint can be used with no authentication. - * - * @return - */ - boolean isInsecure(); - - /** - * Set the endpoint to omit the secure token requirement. - * - * @param insecure - * @return - */ - InternalEndpointRoute setInsecure(boolean insecure); - - /** - * Return underlying route. - * - * @return - */ - Route getRoute(); - - /** - * Return set of produced MIME types; - * - * @return - */ - Set getProduces(); - - /** - * Return set of accepted request body MIME types; - * - * @return - */ - Set getConsumes(); - - /** - * Set the custom model components, additionally required for this route - * - * @param modelComponents - * @return + * @deprecated since requests will only be "ordered" when running in the same http verticle */ - InternalEndpointRoute setModel(Collection> modelComponents); + @Override + @Deprecated + InternalEndpointRoute blockingHandler(Handler requestHandler); - /** - * Get the custom model components, additionally required for this route - * - * @return - */ - Collection> getModel(); + @Override + default com.gentics.vertx.openapi.metadata.InternalEndpointRoute exampleRequest(JsonObject jsonObject) { + try { + return exampleRequest(new JSONObject(jsonObject.encode())); + } catch (JSONException e) { + throw error(INTERNAL_SERVER_ERROR, "error_internal", e); + } + } } diff --git a/plugin-parent/pom.xml b/plugin-parent/pom.xml index 90129d2d06..48d51e0b52 100644 --- a/plugin-parent/pom.xml +++ b/plugin-parent/pom.xml @@ -42,6 +42,37 @@ mesh-plugin-api provided + + com.gentics + vertx-openapi + provided + + + org.slf4j + slf4j-parent + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-api + + + commons-validator + commons-validator + + + com.github.fge + json-schema-validator + + + com.fasterxml.jackson.module + jackson-module-jsonSchema + + + diff --git a/rest-model/pom.xml b/rest-model/pom.xml index f60789fc2f..d826722cd7 100644 --- a/rest-model/pom.xml +++ b/rest-model/pom.xml @@ -34,31 +34,9 @@ com.gentics.mesh mesh-api - - - com.fasterxml.jackson.core - jackson-annotations - - - com.google.guava - guava - - - com.fasterxml.jackson.module - jackson-module-jsonSchema - - - org.codehaus.jettison - jettison - 1.5.4 - - - commons-io - commons-io - - org.raml - raml-parser + com.gentics + vertx-openapi org.slf4j @@ -86,6 +64,27 @@ + + com.fasterxml.jackson.core + jackson-annotations + + + com.google.guava + guava + + + com.fasterxml.jackson.module + jackson-module-jsonSchema + + + org.codehaus.jettison + jettison + 1.5.4 + + + commons-io + commons-io + diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java index 9692005e64..7e5b36e66f 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java @@ -4,15 +4,12 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.gentics.vertx.openapi.model.MessageResponse; /** * The {@link GenericMessageResponse} is used when a generic message should be returned to the requester. */ -public class GenericMessageResponse implements RestModel { - - @JsonProperty(required = true) - @JsonPropertyDescription("Enduser friendly translated message. Translation depends on the 'Accept-Language' header value") - private String message; +public class GenericMessageResponse extends MessageResponse implements RestModel { @JsonProperty(required = true) @JsonPropertyDescription("Internal developer friendly message") @@ -26,6 +23,7 @@ public class GenericMessageResponse implements RestModel { * Create a new generic message response POJO. */ public GenericMessageResponse() { + super(); } /** @@ -47,29 +45,10 @@ public GenericMessageResponse(String message) { * Internal message which may describe the message in a more technical fashion */ public GenericMessageResponse(String message, String internalMessage) { - this.message = message; + super(message); this.internalMessage = internalMessage; } - /** - * Return the message string. - * - * @return Message - */ - public String getMessage() { - return message; - } - - /** - * Set the message string. - * - * @param message - * Message - */ - public void setMessage(String message) { - this.message = message; - } - /** * Return the internal message. * diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/RestModel.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/RestModel.java index b1a89fa6c9..ee432ede4a 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/RestModel.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/RestModel.java @@ -9,22 +9,8 @@ * Marker interface for all rest models. */ @GenerateDocumentation -public interface RestModel extends Shareable { +public interface RestModel extends com.gentics.vertx.openapi.model.RestModel, Shareable { - /** - * Transforms the model into a JSON string, with pretty formatting. - * - * @return - */ - default String toJson() { - return toJson(true); - } - - /** - * Transforms the model into a JSON string. - * - * @return - */ default String toJson(boolean minify) { return JsonUtil.toJson(this, minify); } diff --git a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java index 9b495f6ab5..20a8df47ef 100644 --- a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java +++ b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java @@ -53,16 +53,16 @@ import com.gentics.mesh.json.deserializer.FieldDeserializer; import com.gentics.mesh.json.deserializer.FieldMapDeserializer; import com.gentics.mesh.json.deserializer.FieldSchemaDeserializer; -import com.gentics.mesh.json.deserializer.JsonArrayDeserializer; -import com.gentics.mesh.json.deserializer.JsonObjectDeserializer; import com.gentics.mesh.json.deserializer.NodeFieldListItemDeserializer; import com.gentics.mesh.json.deserializer.PermissionChangedEventModelDeserializer; import com.gentics.mesh.json.deserializer.RestExceptionDeserializer; import com.gentics.mesh.json.deserializer.UserNodeReferenceDeserializer; import com.gentics.mesh.json.serializer.BasicFieldSerializer; import com.gentics.mesh.json.serializer.FieldListSerializer; -import com.gentics.mesh.json.serializer.JsonArraySerializer; -import com.gentics.mesh.json.serializer.JsonObjectSerializer; +import com.gentics.vertx.openapi.model.serde.JsonArrayDeserializer; +import com.gentics.vertx.openapi.model.serde.JsonArraySerializer; +import com.gentics.vertx.openapi.model.serde.JsonObjectDeserializer; +import com.gentics.vertx.openapi.model.serde.JsonObjectSerializer; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; diff --git a/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonArrayDeserializer.java b/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonArrayDeserializer.java deleted file mode 100644 index 0d852818f3..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonArrayDeserializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gentics.mesh.json.deserializer; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; - -import io.vertx.core.json.JsonArray; - -/** - * Custom deserializer for Vert.x {@link JsonArray} - */ -public class JsonArrayDeserializer extends JsonDeserializer { - - @Override - public JsonArray deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectCodec oc = jsonParser.getCodec(); - JsonNode node = oc.readTree(jsonParser); - return new JsonArray(node.toString()); - } - -} diff --git a/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonObjectDeserializer.java b/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonObjectDeserializer.java deleted file mode 100644 index b0b09e10ef..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/json/deserializer/JsonObjectDeserializer.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.gentics.mesh.json.deserializer; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.ObjectCodec; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.JsonNode; - -import io.vertx.core.json.JsonObject; - -/** - * Custom deserializer for {@link JsonObject} - */ -public class JsonObjectDeserializer extends JsonDeserializer { - - @Override - public JsonObject deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException { - ObjectCodec oc = jsonParser.getCodec(); - JsonNode node = oc.readTree(jsonParser); - return new JsonObject(node.toString()); - } - -} diff --git a/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonArraySerializer.java b/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonArraySerializer.java deleted file mode 100644 index d7647e3515..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonArraySerializer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gentics.mesh.json.serializer; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import io.vertx.core.json.JsonArray; - -/** - * Custom JSON serializer for Vert.x {@link JsonArray}. - */ -public class JsonArraySerializer extends JsonSerializer { - - @Override - public void serialize(JsonArray value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeObject(value.getList()); - } -} diff --git a/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonObjectSerializer.java b/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonObjectSerializer.java deleted file mode 100644 index e6824245fe..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/json/serializer/JsonObjectSerializer.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.gentics.mesh.json.serializer; - -import java.io.IOException; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.JsonSerializer; -import com.fasterxml.jackson.databind.SerializerProvider; - -import io.vertx.core.json.JsonObject; - -/** - * Custom serializer for Vert.x {@link JsonObject} - */ -public class JsonObjectSerializer extends JsonSerializer { - - @Override - public void serialize(JsonObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException { - jgen.writeObject(value.getMap()); - } -} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java b/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java index 60368ba6f4..3d878ae7c0 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/ParameterProvider.java @@ -1,175 +1,8 @@ package com.gentics.mesh.parameter; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; - -import org.apache.commons.lang.BooleanUtils; -import org.raml.model.parameter.QueryParameter; - /** * Common interface for query parameters. */ -public interface ParameterProvider { - - /** - * * Validate the parameters and throw an exception when an invalid set of parameters has been detected. - */ - default void validate() { - } - - /** - * Return the RAML parameters for this provider. - * - * @return - */ - Map getRAMLParameters(); - - /** - * Set the query parameter. - * - * @param name - * Parameter name - * @param value - * Parameter value - */ - void setParameter(String name, String value); - - /** - * Set the multivalue query parameter - * @param value type - * @param name parameter name - * @param values parameter values - * @param converter function to convert the values into strings - */ - default void setMultivalueParameter(String name, Collection values, Function converter) { - if (values == null) { - setParameter(name, null); - } else { - setParameter(name, values.stream().map(converter::apply).collect(Collectors.joining(","))); - } - } - - /** - * Set the multivalue query parameter to string values - * @param name name - * @param values string values - */ - default void setMultivalueParameter(String name, Collection values) { - setMultivalueParameter(name, values, Function.identity()); - } - - /** - * Return the query parameter value for the given name. - * - * @param name - * Parameter name - * @return Loaded value or null - */ - String getParameter(String name); - - /** - * Return the query parameter value for the given name, or the default value. - * - * @param name - * Parameter name - * @param defaultValue - * Default value - * @return Loaded value or null - */ - default String getParameter(String name, String defaultValue) { - String parameter = getParameter(name); - if (parameter != null) { - return parameter; - } else { - return defaultValue; - } - } - - /** - * Return the query parameter values for the given name. - * @param value type - * @param name parameter name - * @param converter function that converts the stores String into the expected value - * @return Loaded values or empty set - */ - default Set getMultivalueParameter(String name, Function converter) { - String value = getParameter(name); - if (value == null || value.isEmpty()) { - return Collections.emptySet(); - } else { - return Arrays.asList(value.split(",")).stream().map(converter::apply).collect(Collectors.toSet()); - } - } - - /** - * Return the query parameter values for the given name as set of strings - * @param name parameter name - * @return Loaded values or empty set - */ - default Set getMultivalueParameter(String name) { - return getMultivalueParameter(name, Function.identity()); - } - - /** - * Return the query parameters as a map. - * - * @return - */ - Map getParameters(); - - /** - * Convert the provides object to a string representation. - * - * @param value - * @return String representation of value - */ - default String convertToStr(Object value) { - if (value instanceof String[]) { - String stringVal = ""; - String[] values = (String[]) value; - for (int i = 0; i < values.length; i++) { - stringVal += values[i]; - if (i != values.length - 1) { - stringVal += ','; - } - } - return stringVal; - } else if (value instanceof Integer) { - return Integer.toString((int) value); - } else if (value instanceof Boolean) { - return BooleanUtils.toStringTrueFalse((Boolean) value); - } else { - return value.toString(); - } - } - - /** - * Return the query parameters which do not include the the first & or ? character. - * - * @return Query string - */ - default String getQueryParameters() { - StringBuilder query = new StringBuilder(); - Map params = getParameters(); - for (Entry entry : params.entrySet()) { - String value = entry.getValue(); - if (value != null) { - if (query.length() != 0) { - query.append("&"); - } - // try { - query.append(entry.getKey() + "=" + value);// URLEncoder.encode(value, "UTF-8")); - // } catch (UnsupportedEncodingException e) { - // } - } - } - return query.toString(); - } +public interface ParameterProvider extends com.gentics.vertx.openapi.model.ParameterProvider { } From c68cb681f02e1ead46bc75e514ed30ddd15cf6d4 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 17 Feb 2026 17:15:55 +0100 Subject: [PATCH 20/75] Refactor --- .../core/endpoint/admin/AdminHandler.java | 26 ++++- .../generator/OpenAPIRuntimeGenerator.java | 103 ------------------ .../mesh/generator/OpenAPIGenerator.java | 5 + .../parameter/impl/SortingParametersImpl.java | 2 +- 4 files changed, 31 insertions(+), 105 deletions(-) delete mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java create mode 100644 doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index d2f1d5c321..fdee151758 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -64,8 +64,12 @@ import com.gentics.vertx.openapi.model.Format; import com.gentics.vertx.openapi.model.OpenAPIGenerationException; +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; import io.vertx.core.Vertx; /** @@ -292,7 +296,27 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "", "\\/api\\/\\{apiversion\\}[.]*")); // Make an instance with blacklist path patterns - OpenAPIv3Generator generator = new OpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + OpenAPIv3Generator generator = new OpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()) { + @Override + protected void addSecurity(OpenAPI openApi) { + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } + SecurityScheme securityBearerAuth = new SecurityScheme(); + securityBearerAuth.setScheme("bearer"); + securityBearerAuth.setType(SecurityScheme.Type.HTTP); + securityBearerAuth.setBearerFormat("JWT"); + components.addSecuritySchemes("bearerAuth", securityBearerAuth); + //TODO OAuth2 + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + openApi.addSecurityItem(reqBearerAuth); + } + }; // Generate... try { diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java deleted file mode 100644 index 37403b531e..0000000000 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.gentics.mesh.generator; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import com.gentics.mesh.rest.InternalEndpointRoute; -import com.github.jknack.handlebars.internal.lang3.StringUtils; - -import io.swagger.v3.core.util.Json; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.servers.Server; -import io.swagger.v3.oas.models.tags.Tag; -import io.vertx.ext.web.Router; - -public final class OpenAPIRuntimeGenerator { - - public static String fromRouter(Router router) { - OpenAPI openApi = new OpenAPI(); - Info info = new Info(); - List servers = new ArrayList<>(); - List tags = new ArrayList<>(); - Paths paths = new Paths(); - - info.setTitle("Gentics OpenAPI"); - - openApi.setInfo(info); - openApi.setServers(servers); - openApi.setPaths(paths); - openApi.setTags(tags); - - router(router, info, paths); - - return Json.pretty(openApi); - } - - private static final void router(Router router, Info info, Paths paths) { - router.getRoutes().stream() - .filter(r -> StringUtils.isNotBlank(r.getPath())) - .forEach(r -> { - String path = Arrays.stream(r.getPath().split("/")) - .map(segment -> segment.startsWith(":") ? ("{" + segment.substring(1) + "}") : segment) - .collect(Collectors.joining("/")); - - Operation o = new Operation(); - o.setParameters(Arrays.stream(r.getPath().split("/")) - .filter(segment -> segment.startsWith(":")) - .map(segment -> segment.substring(1)) - .map(segment -> { - Parameter p = new Parameter(); - p.setName(segment); - p.setRequired(true); - p.setAllowEmptyValue(false); - return p; - }).collect(Collectors.toList())); - PathItem i = new PathItem(); - r.methods().stream().forEach(m -> { - switch (m.name()) { - case "GET": - i.setGet(o); - break; - case "POST": - i.setPost(o); - break; - case "PUT": - i.setPut(o); - break; - case "DELETE": - i.setDelete(o); - break; - case "OPTIONS": - i.setOptions(o); - break; - case "PATCH": - i.setPatch(o); - break; - case "TRACE": - i.setTrace(o); - break; - case "HEAD": - i.setHead(o); - break; - } - }); - Optional.ofNullable(r.getMetadata(InternalEndpointRoute.class.getCanonicalName())) - .map(InternalEndpointRoute.class::cast) - .ifPresent(ie -> { - ie.getQueryParameters().entrySet().stream(); - }); - paths.put(path, i); - - Optional.ofNullable(r.getSubRouter()) - .ifPresent(s -> router(s, info, paths)); - }); - } -} diff --git a/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java b/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java new file mode 100644 index 0000000000..93dcbd2b27 --- /dev/null +++ b/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java @@ -0,0 +1,5 @@ +package com.gentics.mesh.generator; + +public class OpenAPIGenerator { + +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java index da97df93be..d7cdc1a8fd 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java @@ -62,7 +62,7 @@ public SortingParametersImpl(ActionContext ac) { sortOrderParameter.setExample(SortOrder.ASCENDING.name()); sortOrderParameter.setRequired(false); sortOrderParameter.setType(ParamType.STRING); - sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); + sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(e -> e.getValue().toLowerCase()).collect(Collectors.toList())); parameters.put(SortingParameters.SORT_ORDER_PARAMETER_KEY, sortOrderParameter); return parameters; From 29afff02aefd88f9fa3ed40263c55085eb12e6d2 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 19 Feb 2026 14:56:34 +0100 Subject: [PATCH 21/75] Compile time basic generation --- .../core/endpoint/admin/AdminHandler.java | 68 +++----------- .../mesh/dagger/module/MicrometerModule.java | 2 - .../generator/AbstractEndpointGenerator.java | 46 ++++++++++ .../generator/OpenAPIRuntimeGenerator.java | 89 +++++++++++++++++++ .../gentics/mesh/generator/RAMLGenerator.java | 27 ------ .../mesh/util/MeshOpenAPIv3Generator.java | 89 +++++++++++++++++++ .../generator/ExampleGeneratorRunner.java | 4 + .../mesh/generator/OpenAPIGenerator.java | 5 -- .../gentics/mesh/core/data/dao/JobDao.java | 1 - .../parameter/impl/SortingParametersImpl.java | 5 +- .../mesh/rest/InternalEndpointRoute.java | 2 +- .../mesh/parameter/OpenAPIParameters.java | 8 +- 12 files changed, 245 insertions(+), 101 deletions(-) create mode 100644 core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java create mode 100644 core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java delete mode 100644 doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index fdee151758..6835db0693 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -25,7 +25,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.reflections.Reflections; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,7 +42,6 @@ import com.gentics.mesh.core.rest.admin.cluster.coordinator.CoordinatorMasterResponse; import com.gentics.mesh.core.rest.admin.consistency.ConsistencyCheckResponse; import com.gentics.mesh.core.rest.admin.status.MeshStatusResponse; -import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; import com.gentics.mesh.core.verticle.handler.WriteLock; @@ -57,19 +55,12 @@ import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; import com.gentics.mesh.util.RxUtil; import com.gentics.mesh.util.UUIDUtil; -import com.gentics.vertx.openapi.OpenAPIv3Generator; -import com.gentics.vertx.openapi.OpenAPIv3Generator.InParameter; import com.gentics.vertx.openapi.model.Format; import com.gentics.vertx.openapi.model.OpenAPIGenerationException; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.media.Schema; -import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.security.SecurityRequirement; -import io.swagger.v3.oas.models.security.SecurityScheme; import io.vertx.core.Vertx; /** @@ -293,30 +284,10 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "", "\\/api\\/\\{apiversion\\}[.]*")); + blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*")); // Make an instance with blacklist path patterns - OpenAPIv3Generator generator = new OpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()) { - @Override - protected void addSecurity(OpenAPI openApi) { - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - SecurityScheme securityBearerAuth = new SecurityScheme(); - securityBearerAuth.setScheme("bearer"); - securityBearerAuth.setType(SecurityScheme.Type.HTTP); - securityBearerAuth.setBearerFormat("JWT"); - components.addSecuritySchemes("bearerAuth", securityBearerAuth); - //TODO OAuth2 - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - openApi.addSecurityItem(reqBearerAuth); - } - }; + MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); // Generate... try { @@ -325,38 +296,19 @@ protected void addSecurity(OpenAPI openApi) { //... from base root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), //... from generic project root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{projectName}")) + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), // ...with desired format Format.parse(format), // ...with desired pretty printing !httpServerConfig.isMinifyJson(), // ...with desired spec version - useVersion31, - // ...with a manipulator injecting a `projectName` path parameter, where unavailable - Optional.of((path, item) -> { - if (path.contains("/{projectName}/")) { - Parameter projectNameParam = new Parameter().name("projectName").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); - item.readOperations().stream() - .forEach(o -> o.getParameters().stream().filter(p -> "projectName".equals(p.getName())).findAny() - .ifPresentOrElse(present -> { - // already exists, no action - }, () -> o.addParametersItem(projectNameParam))); - } - return path; - }), - // ...with component model provider - Optional.of(() -> Collections.unmodifiableCollection( - new Reflections("com.gentics.mesh") - .getSubTypesOf(RestModel.class) - .stream() - .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) - .collect(Collectors.toSet())))), - OK, - "yaml".equalsIgnoreCase(format) - ? HttpConstants.APPLICATION_YAML_UTF8 - : HttpConstants.APPLICATION_JSON_UTF8 - ); + useVersion31), + OK, + "yaml".equalsIgnoreCase(format) + ? HttpConstants.APPLICATION_YAML_UTF8 + : HttpConstants.APPLICATION_JSON_UTF8 + ); } catch (OpenAPIGenerationException e) { ac.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); } diff --git a/core/src/main/java/com/gentics/mesh/dagger/module/MicrometerModule.java b/core/src/main/java/com/gentics/mesh/dagger/module/MicrometerModule.java index d236687205..08dd7d3101 100644 --- a/core/src/main/java/com/gentics/mesh/dagger/module/MicrometerModule.java +++ b/core/src/main/java/com/gentics/mesh/dagger/module/MicrometerModule.java @@ -90,8 +90,6 @@ private static Optional createTag(String key, String value) { * * @param options * Mesh options - * @param meterRegistry - * Reference to the to be used meter registry * @return */ @Provides diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java index 3e016ee008..6b11934e29 100644 --- a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -4,8 +4,12 @@ import java.io.File; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.FileUtils; import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; import com.gentics.mesh.core.endpoint.admin.AdminEndpointImpl; @@ -44,6 +48,8 @@ public abstract class AbstractEndpointGenerator extends AbstractGenerator { + private static final Logger log = LoggerFactory.getLogger(AbstractEndpointGenerator.class); + public AbstractEndpointGenerator() { } @@ -185,11 +191,51 @@ protected void addCoreEndpoints(T consumer) throws IOException { addEndpoints(coreBasePath, consumer, projectInfoEndpoint, false); } + /** + * Add any extra verticles to the consumer. + * + * @param resources + * @throws IOException + */ + protected void addExtraEndpoints(T consumer) throws IOException { + } + + /** + * Add an endpoint to the data consumer. + * + * @param coreBasePath + * @param consumer + * @param projectInfoEndpoint + * @param isProject + * @throws IOException + */ protected abstract void addEndpoints(String coreBasePath, T consumer, AbstractInternalEndpoint projectInfoEndpoint, boolean isProject) throws IOException; + /** + * Mock an endpoint with data, full enough for the generator to succeed. + * + * @param endpoint + */ protected void initEndpoint(AbstractInternalEndpoint endpoint) { Vertx vertx = mock(Vertx.class); Mockito.when(endpoint.getRouter()).thenReturn(Router.router(vertx)); endpoint.registerEndPoints(); } + + /** + * Save the string content to the given file in the output folder. + * + * @param filename + * Name of the file to be written to + * @param content + * Content to be written + * @throws IOException + */ + public void writeFile(String filename, String content) throws IOException { + if (outputFolder != null) { + File outputFile = new File(outputFolder, filename); + FileUtils.writeStringToFile(outputFile, content, StandardCharsets.UTF_8); + log.info("File saved to {" + outputFile.getPath() + "}"); + } + } } diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java new file mode 100644 index 0000000000..db74e02199 --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java @@ -0,0 +1,89 @@ +package com.gentics.mesh.generator; + +import static com.gentics.mesh.MeshVersion.CURRENT_API_VERSION; +import static org.apache.commons.lang3.StringUtils.isEmpty; + +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; +import com.gentics.vertx.openapi.OpenAPIv3Generator; +import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.OpenAPIGenerationException; + +import io.vertx.ext.web.Router; + +/** + * OpenAPI v3 API definition generator. Outputs JSON and YAML schemas. + * + * @author plyhun + * + */ +public class OpenAPIRuntimeGenerator extends AbstractEndpointGenerator { + + private static final Logger log = LoggerFactory.getLogger(OpenAPIRuntimeGenerator.class); + + private final MeshOpenAPIv3Generator generator; + private final Map routers = new HashMap<>(); + private final String fileName; + + public OpenAPIRuntimeGenerator() { + super(); + this.fileName = null; + this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), + Optional.empty(), Optional.empty()); + } + + public OpenAPIRuntimeGenerator(File outputFolder, String fileName, boolean cleanup) throws IOException { + super(new File(outputFolder, "api"), false); + this.fileName = fileName; + this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), + Optional.empty(), Optional.empty()); + } + + public OpenAPIRuntimeGenerator(File outputFolder, String fileName) throws IOException { + super(new File(outputFolder, "api"), false); + this.fileName = fileName; + this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), + Optional.empty(), Optional.empty()); + } + + public String generate(String format) throws IOException, OpenAPIGenerationException { + log.info("Starting OpenAPIv3 generation..."); + addCoreEndpoints(generator); + addProjectEndpoints(generator); + addExtraEndpoints(generator); + + return generator.generate(routers, Format.parse(format), true, false); + } + + @Override + protected void addEndpoints(String basePath, OpenAPIv3Generator consumer, AbstractInternalEndpoint verticle, + boolean isProject) throws IOException { + String fullPath = "/api/v" + CURRENT_API_VERSION + basePath + "/" + verticle.getBasePath(); + if (isEmpty(verticle.getBasePath())) { + fullPath = "/api/v" + CURRENT_API_VERSION + basePath; + } + routers.put(verticle.getRouter(), fullPath); + } + + /** + * Start the generator. + * + * @throws IOException + * @throws OpenAPIGenerationException + */ + public void run() throws IOException, OpenAPIGenerationException { + String yaml = generate("yaml"); + writeFile(fileName, yaml); + } +} diff --git a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java index c046c05172..ccdfdc2e9a 100644 --- a/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/RAMLGenerator.java @@ -11,7 +11,6 @@ import java.util.function.BiConsumer; import java.util.stream.Collectors; -import org.apache.commons.io.FileUtils; import org.raml.emitter.RamlEmitter; import org.raml.model.Action; import org.raml.model.ActionType; @@ -212,23 +211,6 @@ protected void addEndpoints(String basePath, Map resources, Ab } - /** - * Save the string content to the given file in the output folder. - * - * @param filename - * Name of the file to be written to - * @param content - * Content to be written - * @throws IOException - */ - public void writeFile(String filename, String content) throws IOException { - if (outputFolder != null) { - File outputFile = new File(outputFolder, filename); - FileUtils.writeStringToFile(outputFile, content); - log.info("File saved to {" + outputFile.getPath() + "}"); - } - } - /** * Convert the http method to a RAML action type. * @@ -239,15 +221,6 @@ protected ActionType getActionType(HttpMethod method) { return ActionType.valueOf(method.name()); } - /** - * Add any extra verticles to the map of RAML resources. - * - * @param resources - * @throws IOException - */ - protected void addExtraEndpoints(Map resources) throws IOException { - } - /** * Start the generator. * diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java new file mode 100644 index 0000000000..65d9c7e2fd --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -0,0 +1,89 @@ +package com.gentics.mesh.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import org.reflections.Reflections; + +import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.vertx.openapi.OpenAPIv3Generator; +import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.OpenAPIGenerationException; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.vertx.ext.web.Router; + +/** + * Overridden OpenAPI v3 generator, adding Mesh specific features. + */ +public class MeshOpenAPIv3Generator extends OpenAPIv3Generator { + + public MeshOpenAPIv3Generator(String version, List servers, + Optional> maybePathBlacklist, + Optional> maybePathWhitelist) { + super(version, servers, maybePathBlacklist, maybePathWhitelist); + } + + @Override + protected void addSecurity(OpenAPI openApi) { + Components components; + if (openApi.getComponents() == null) { + components = new Components(); + openApi.setComponents(components); + } else { + components = openApi.getComponents(); + } + SecurityScheme securityBearerAuth = new SecurityScheme(); + securityBearerAuth.setScheme("bearer"); + securityBearerAuth.setType(SecurityScheme.Type.HTTP); + securityBearerAuth.setBearerFormat("JWT"); + components.addSecuritySchemes("bearerAuth", securityBearerAuth); + //TODO OAuth2 + SecurityRequirement reqBearerAuth = new SecurityRequirement(); + reqBearerAuth.addList("bearerAuth"); + openApi.addSecurityItem(reqBearerAuth); + } + + /** + * Generate the specification using some functional shortcuts. + * + * @param routers + * @param format + * @param pretty + * @param useVersion31 + * @return + * @throws OpenAPIGenerationException + */ + public String generate(Map routers, Format format, boolean pretty, boolean useVersion31) throws OpenAPIGenerationException { + return generate(routers, format, pretty, useVersion31, + // transform project path item + Optional.of((path, item) -> { + if (path.contains("/{project}/")) { + Parameter projectNameParam = new Parameter().name("project").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); + item.readOperations().stream() + .forEach(o -> o.getParameters().stream().filter(p -> "project".equals(p.getName())).findAny() + .ifPresentOrElse(present -> { + // already exists, no action + }, () -> o.addParametersItem(projectNameParam))); + } + return path; + }), + // fill the component model + Optional.of(() -> Collections.unmodifiableCollection( + new Reflections("com.gentics.mesh") + .getSubTypesOf(RestModel.class) + .stream() + .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) + .collect(Collectors.toSet())))); + } +} diff --git a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java index 73cf23eaed..3ea9b4a65c 100644 --- a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java +++ b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java @@ -49,6 +49,10 @@ public static void main(String[] args) throws Exception { }, true); ramlGenerator.run(); + // Generate OpenAPI base spec + OpenAPIRuntimeGenerator openApiGenerator = new OpenAPIRuntimeGenerator(OUTPUT_ROOT_FOLDER, "openapi.yaml", false); + openApiGenerator.run(); + // Generate elasticsearch flattened models SearchModelGenerator searchModelGen = new SearchModelGenerator(OUTPUT_ROOT_FOLDER); searchModelGen.run(); diff --git a/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java b/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java deleted file mode 100644 index 93dcbd2b27..0000000000 --- a/doc/src/main/java/com/gentics/mesh/generator/OpenAPIGenerator.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.gentics.mesh.generator; - -public class OpenAPIGenerator { - -} diff --git a/mdm/api/src/main/java/com/gentics/mesh/core/data/dao/JobDao.java b/mdm/api/src/main/java/com/gentics/mesh/core/data/dao/JobDao.java index d0a2b0417d..c39d30f08e 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/core/data/dao/JobDao.java +++ b/mdm/api/src/main/java/com/gentics/mesh/core/data/dao/JobDao.java @@ -103,7 +103,6 @@ default Page findAllNoPerm(InternalActionContext ac, PagingPar * Delete the job. * * @param job - * @param bac */ void delete(HibJob job); diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java index d7cdc1a8fd..7e85bae2d6 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/SortingParametersImpl.java @@ -57,12 +57,11 @@ public SortingParametersImpl(ActionContext ac) { // sort order QueryParameter sortOrderParameter = new QueryParameter(); - sortOrderParameter.setDescription("Field order (ASC/DESC/UNSORTED) to sort the result by."); - sortOrderParameter.setDefaultValue(SortingParameters.DEFAULT_SORT_ORDER.name()); + sortOrderParameter.setDescription("Field order (ASC/DESC) to sort the result by."); sortOrderParameter.setExample(SortOrder.ASCENDING.name()); sortOrderParameter.setRequired(false); sortOrderParameter.setType(ParamType.STRING); - sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().map(e -> e.getValue().toLowerCase()).collect(Collectors.toList())); + sortOrderParameter.setEnumeration(Arrays.asList(SortOrder.values()).stream().filter(e -> SortOrder.UNSORTED != e).map(e -> e.getValue().toLowerCase()).collect(Collectors.toList())); parameters.put(SortingParameters.SORT_ORDER_PARAMETER_KEY, sortOrderParameter); return parameters; diff --git a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index 3a53b3dd37..8fe0b3db0b 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -34,7 +34,7 @@ public interface InternalEndpointRoute extends com.gentics.vertx.openapi.metadat InternalEndpointRoute events(MeshEvent... events); /** - * @see {@link com.gentics.vertx.openapi.metadata.InternalEndpointRoute#blockingHandler(Handler)} + * @see com.gentics.vertx.openapi.metadata.InternalEndpointRoute#blockingHandler(Handler) * * @deprecated since requests will only be "ordered" when running in the same http verticle */ diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java index f16054439f..4e5447f1bc 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java @@ -21,7 +21,7 @@ public interface OpenAPIParameters extends ParameterProvider { /** * Set the OpenAPI standard version * - * @param flag + * @param version The specification version * @return Fluent API */ default OpenAPIParameters setVersion(Version version) { @@ -32,7 +32,7 @@ default OpenAPIParameters setVersion(Version version) { /** * Check whether OpenAPI spec version * - * @return flag value + * @return value */ default Version getVersion() { return Version.parse(getParameter(VERSION_PARAMETER_KEY), Version.V30); @@ -41,7 +41,7 @@ default Version getVersion() { /** * Set the format * - * @param flag + * @param format the specification format * @return Fluent API */ default OpenAPIParameters setFormat(Format format) { @@ -52,7 +52,7 @@ default OpenAPIParameters setFormat(Format format) { /** * Check the spec format * - * @return flag value + * @return value */ default Format getFormat() { return Format.parse(getParameter(FORMAT_PARAMETER_KEY), Format.JSON); From edc6643ce459beb924bc455f81c31b3085a04f93 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 19 Feb 2026 14:56:45 +0100 Subject: [PATCH 22/75] Docs --- doc/src/main/hugo/content/docs/references.asciidoc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/src/main/hugo/content/docs/references.asciidoc b/doc/src/main/hugo/content/docs/references.asciidoc index c75a97d820..9703d974a4 100644 --- a/doc/src/main/hugo/content/docs/references.asciidoc +++ b/doc/src/main/hugo/content/docs/references.asciidoc @@ -19,6 +19,17 @@ There are many things you can do with the REST API. To name a few: NOTE: All REST API responses are available in JSON, only, except for binary data. +=== OpenAPI Specification + +The link:https://swagger.io/specification/[OpenAPI specification] is available for Gentics Mesh in two variants: + +* The generic link:https://spec.openapis.org/oas/v3.0.0.html[OpenAPI v3.0 specification], unrelated to any actual Gentics Mesh installation, can be checked right link:/docs/api/openapi.yaml[here] in the YAML format. +* The installation-aware specification, which is generated right in Mesh at runtime, and, in addition to the generic specification, contains also the endpoints of all the plugins currently running along, as well as some technical info of an installation (servers). It also allows choosing the right specification version, either 3.0 or 3.1. + +=== RAML Specification + +The link:https://github.com/raml-org/raml-spec/blob/master/versions/raml-08/raml-08.md[RAML v0.8 specification] provides the generic REST API information, and can be checked link:/docs/api/api.raml[here]. + === Query Parameters The REST API end points can be used in combination with a set of query parameters, all of which are documented for the specific end points in the link:/docs/api[Gentics Mesh REST API] reference. Following, we provide an overview of the most common query parameters. From 52bb1fc7372c00914812b6c146013c0599c64c9b Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 19 Feb 2026 17:59:55 +0100 Subject: [PATCH 23/75] Autogenerated test client base --- Jenkinsfile.split | 9 +- bom/pom.xml | 22 +- .../mesh/test/context/MeshTestContext.java | 19 +- tests/tests-core/pom.xml | 76 +- .../test/openapi/OpenAPIMeshRestClient.java | 1827 +++++++++++++++++ 5 files changed, 1936 insertions(+), 17 deletions(-) create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java diff --git a/Jenkinsfile.split b/Jenkinsfile.split index bde284048d..5103dff9b3 100644 --- a/Jenkinsfile.split +++ b/Jenkinsfile.split @@ -18,6 +18,7 @@ properties([ booleanParam(name: 'runUnstableTests', defaultValue: true, description: "Whether to run tests in failing group."), booleanParam(name: 'runPerformanceTests', defaultValue: false, description: "Whether to run performance tests."), booleanParam(name: 'runClusterTests', defaultValue: false, description: "Whether to run cluster tests."), + booleanParam(name: 'useOpenApiTestClient',defaultValue: false, description: "Whether to use an autogenerated OpenAPI OkHTTP 3 based REST client for the tests.") booleanParam(name: 'runDeploy', defaultValue: false, description: "Whether to run the deploy steps."), booleanParam(name: 'runDeployTesting', defaultValue: false, description: "Whether to run the testing deploy steps."), booleanParam(name: 'runDocker', defaultValue: false, description: "Whether to run the docker steps."), @@ -68,7 +69,7 @@ final def testPart(partName, current, branches) { // prior to starting the tests, start the docker containers with the db and the testdb-manager sh 'docker login -u $repoUsername -p $repoPassword docker.gentics.com' if (Boolean.valueOf(params.testMaria)) { - sh "mvn -pl :mesh-database-connector-mariadb docker:start -Dskip.mariadb.tests=${noMariadb} " + sh "mvn -pl :mesh-database-connector-mariadb docker:start -Dskip.mariadb.tests=${noMariadb} " } // run the tests withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1g -XX:MaxMetaspaceSize=128m "]) { @@ -77,10 +78,10 @@ final def testPart(partName, current, branches) { } finally { // finally stop the docker containers if (Boolean.valueOf(params.testMaria)) { - sh "mvn -pl :mesh-database-connector-mariadb docker:stop -Dskip.mariadb.tests=${noMariadb} " + sh "mvn -pl :mesh-database-connector-mariadb docker:stop -Dskip.mariadb.tests=${noMariadb} " + } } } - } stash name: "jacoco" + current, includes: "**/jacoco-partial.exec", allowEmpty: true } finally { step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/*.xml']) @@ -216,7 +217,7 @@ stage("Setup Build Environment") { } try { withCredentials([usernamePassword(credentialsId: 'docker.gentics.com', usernameVariable: 'repoUsername', passwordVariable: 'repoPassword'),usernamePassword(credentialsId: 'gentics.gpg', usernameVariable: 'gpgKeyName', passwordVariable: 'gpgKeyPass')]) { - withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false")]) { + withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { sh "mvn -fae -Dsurefire.excludedGroups=com.gentics.mesh.test.category.FailingTests,com.gentics.mesh.test.category.ClusterTests -Dmaven.javadoc.skip=true -Dskip.cluster.tests=true -Dmaven.test.failure.ignore=true -Dmesh.container.image.prefix=docker.gentics.com/ -Dskip.mariadb.tests=${noMariadb} -Dskip.hsqlmemory.tests=${noHsqldb} -B -U -e test" } } diff --git a/bom/pom.xml b/bom/pom.xml index 8944ebaade..b6ad3577bc 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -47,6 +47,7 @@ 2.7.3 1.15.5 0.0.1-SNAPSHOT + 5.3.0 @@ -138,7 +139,12 @@ com.squareup.okhttp3 okhttp-jvm - 5.3.0 + ${okhttp.version} + + + com.squareup.okhttp3 + logging-interceptor + ${okhttp.version} @@ -177,7 +183,7 @@ com.google.code.gson gson - 2.8.9 + 2.10.1 @@ -813,9 +819,9 @@ 3.2.0 - jakarta.persistence - jakarta.persistence-api - 3.2.0 + jakarta.persistence + jakarta.persistence-api + 3.2.0 com.squareup.inject @@ -838,9 +844,9 @@ ${hazelcast-hibernate.version} - org.hibernate.orm - hibernate-jcache - ${hibernate.version} + org.hibernate.orm + hibernate-jcache + ${hibernate.version} org.hibernate.orm diff --git a/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java b/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java index 074b2a0e59..7e027a5cdd 100644 --- a/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java +++ b/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java @@ -8,6 +8,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.nio.file.NoSuchFileException; import java.security.cert.CertificateException; import java.time.Duration; @@ -1020,6 +1021,20 @@ public Mesh getMesh() { return mesh; } + private MeshRestClient createRestClient(MeshRestClientConfig config) { + String customMeshRestClient = System.getenv("MESH_REST_CLIENT_CLASS"); + if (StringUtils.isNotBlank(customMeshRestClient)) { + try { + return (MeshRestClient) Class.forName(customMeshRestClient).getConstructor(MeshRestClientConfig.class, OkHttpClient.class).newInstance(config, okHttp); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException | NoSuchMethodException | SecurityException + | ClassNotFoundException e) { + LOG.error("Could not instantiate MeshRestClient from class " + customMeshRestClient, e); + } + } + return MeshRestClient.create(config, okHttp); + } + private void setupRestEndpoints(MeshTestSetting settings) throws Exception { mesh.getOptions().getUploadOptions().setByteLimit(Long.MAX_VALUE); @@ -1034,11 +1049,11 @@ private void setupRestEndpoints(MeshTestSetting settings) throws Exception { .setBasePath(CURRENT_API_BASE_PATH) .setSsl(false); - MeshRestClient httpClient = MeshRestClient.create(httpConfigBuilder.build(), okHttp); + MeshRestClient httpClient = createRestClient(httpConfigBuilder.build()); httpClient.setLogin(getData().user().getUsername(), getData().getUserInfo().getPassword()); httpClient.login().blockingGet(); clients.put("http_v" + CURRENT_API_VERSION, httpClient); - anonymousClients.put("http_v" + CURRENT_API_VERSION, MeshRestClient.create(httpConfigBuilder.build(), okHttp)); + anonymousClients.put("http_v" + CURRENT_API_VERSION, createRestClient(httpConfigBuilder.build())); // Setup SSL client if needed SSLTestMode ssl = settings.ssl(); diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index dc19ff8f1d..775906b18b 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -86,10 +86,28 @@ 4.4.13 - io.swagger - swagger-parser - 2.0.0-rc1 + io.swagger + swagger-parser + 2.0.0-rc1 + + jakarta.annotation + jakarta.annotation-api + 2.1.1 + + + com.google.code.gson + gson + + + io.gsonfire + gson-fire + 1.9.0 + + + com.squareup.okhttp3 + logging-interceptor + @@ -140,6 +158,58 @@ + + org.openapitools + openapi-generator-maven-plugin + 7.19.0 + + + generate-client + + generate + + process-resources + + ${basedir}/../../doc/src/main/docs/generated/api/openapi.yaml + ${project.build.directory}/generated-sources/openapi + java + false + false + true + false + + JsonObject=com.google.gson.JsonObject + + + true + false + true + / + + okhttp-gson + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-sources + generate-sources + + add-source + + + + ${project.build.directory}/generated-sources/openapi + ${project.build.directory}/generated-sources/annotations + + + + + diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java new file mode 100644 index 0000000000..75d1c05014 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -0,0 +1,1827 @@ +package com.gentics.mesh.test.openapi; + +import java.io.InputStream; + +import org.openapitools.client.ApiClient; +import org.openapitools.client.api.DefaultApi; +import org.openapitools.client.model.LoginRequest; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.gentics.mesh.core.rest.MeshServerInfoModel; +import com.gentics.mesh.core.rest.admin.cluster.ClusterStatusResponse; +import com.gentics.mesh.core.rest.admin.cluster.coordinator.CoordinatorMasterResponse; +import com.gentics.mesh.core.rest.admin.consistency.ConsistencyCheckResponse; +import com.gentics.mesh.core.rest.admin.localconfig.LocalConfigModel; +import com.gentics.mesh.core.rest.admin.status.MeshStatusResponse; +import com.gentics.mesh.core.rest.branch.BranchCreateRequest; +import com.gentics.mesh.core.rest.branch.BranchListResponse; +import com.gentics.mesh.core.rest.branch.BranchResponse; +import com.gentics.mesh.core.rest.branch.BranchUpdateRequest; +import com.gentics.mesh.core.rest.branch.info.BranchInfoMicroschemaList; +import com.gentics.mesh.core.rest.branch.info.BranchInfoSchemaList; +import com.gentics.mesh.core.rest.common.GenericMessageResponse; +import com.gentics.mesh.core.rest.common.ObjectPermissionGrantRequest; +import com.gentics.mesh.core.rest.common.ObjectPermissionResponse; +import com.gentics.mesh.core.rest.common.ObjectPermissionRevokeRequest; +import com.gentics.mesh.core.rest.common.RestModel; +import com.gentics.mesh.core.rest.graphql.GraphQLRequest; +import com.gentics.mesh.core.rest.graphql.GraphQLResponse; +import com.gentics.mesh.core.rest.group.GroupCreateRequest; +import com.gentics.mesh.core.rest.group.GroupListResponse; +import com.gentics.mesh.core.rest.group.GroupResponse; +import com.gentics.mesh.core.rest.group.GroupUpdateRequest; +import com.gentics.mesh.core.rest.job.JobListResponse; +import com.gentics.mesh.core.rest.job.JobResponse; +import com.gentics.mesh.core.rest.lang.LanguageListResponse; +import com.gentics.mesh.core.rest.lang.LanguageResponse; +import com.gentics.mesh.core.rest.microschema.impl.MicroschemaCreateRequest; +import com.gentics.mesh.core.rest.microschema.impl.MicroschemaResponse; +import com.gentics.mesh.core.rest.microschema.impl.MicroschemaUpdateRequest; +import com.gentics.mesh.core.rest.navigation.NavigationResponse; +import com.gentics.mesh.core.rest.node.NodeCreateRequest; +import com.gentics.mesh.core.rest.node.NodeListResponse; +import com.gentics.mesh.core.rest.node.NodeResponse; +import com.gentics.mesh.core.rest.node.NodeUpdateRequest; +import com.gentics.mesh.core.rest.node.NodeUpsertRequest; +import com.gentics.mesh.core.rest.node.PublishStatusModel; +import com.gentics.mesh.core.rest.node.PublishStatusResponse; +import com.gentics.mesh.core.rest.node.field.BinaryCheckStatus; +import com.gentics.mesh.core.rest.node.field.image.ImageManipulationRequest; +import com.gentics.mesh.core.rest.node.field.image.ImageVariantsResponse; +import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryMetadataRequest; +import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; +import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; +import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; +import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; +import com.gentics.mesh.core.rest.plugin.PluginListResponse; +import com.gentics.mesh.core.rest.plugin.PluginResponse; +import com.gentics.mesh.core.rest.project.ProjectCreateRequest; +import com.gentics.mesh.core.rest.project.ProjectListResponse; +import com.gentics.mesh.core.rest.project.ProjectResponse; +import com.gentics.mesh.core.rest.project.ProjectUpdateRequest; +import com.gentics.mesh.core.rest.role.RoleCreateRequest; +import com.gentics.mesh.core.rest.role.RoleListResponse; +import com.gentics.mesh.core.rest.role.RolePermissionRequest; +import com.gentics.mesh.core.rest.role.RolePermissionResponse; +import com.gentics.mesh.core.rest.role.RoleResponse; +import com.gentics.mesh.core.rest.role.RoleUpdateRequest; +import com.gentics.mesh.core.rest.schema.MicroschemaListResponse; +import com.gentics.mesh.core.rest.schema.MicroschemaModel; +import com.gentics.mesh.core.rest.schema.MicroschemaReference; +import com.gentics.mesh.core.rest.schema.SchemaListResponse; +import com.gentics.mesh.core.rest.schema.SchemaModel; +import com.gentics.mesh.core.rest.schema.SchemaReference; +import com.gentics.mesh.core.rest.schema.change.impl.SchemaChangesListModel; +import com.gentics.mesh.core.rest.schema.impl.SchemaCreateRequest; +import com.gentics.mesh.core.rest.schema.impl.SchemaResponse; +import com.gentics.mesh.core.rest.schema.impl.SchemaUpdateRequest; +import com.gentics.mesh.core.rest.search.SearchStatusResponse; +import com.gentics.mesh.core.rest.tag.TagCreateRequest; +import com.gentics.mesh.core.rest.tag.TagFamilyCreateRequest; +import com.gentics.mesh.core.rest.tag.TagFamilyListResponse; +import com.gentics.mesh.core.rest.tag.TagFamilyResponse; +import com.gentics.mesh.core.rest.tag.TagFamilyUpdateRequest; +import com.gentics.mesh.core.rest.tag.TagListResponse; +import com.gentics.mesh.core.rest.tag.TagListUpdateRequest; +import com.gentics.mesh.core.rest.tag.TagResponse; +import com.gentics.mesh.core.rest.tag.TagUpdateRequest; +import com.gentics.mesh.core.rest.user.UserAPITokenResponse; +import com.gentics.mesh.core.rest.user.UserCreateRequest; +import com.gentics.mesh.core.rest.user.UserListResponse; +import com.gentics.mesh.core.rest.user.UserPermissionResponse; +import com.gentics.mesh.core.rest.user.UserResetTokenResponse; +import com.gentics.mesh.core.rest.user.UserResponse; +import com.gentics.mesh.core.rest.user.UserUpdateRequest; +import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; +import com.gentics.mesh.parameter.BackupParameters; +import com.gentics.mesh.parameter.ImageManipulationParameters; +import com.gentics.mesh.parameter.PagingParameters; +import com.gentics.mesh.parameter.ParameterProvider; +import com.gentics.mesh.rest.JWTAuthentication; +import com.gentics.mesh.rest.client.MeshBinaryResponse; +import com.gentics.mesh.rest.client.MeshRequest; +import com.gentics.mesh.rest.client.MeshRestClient; +import com.gentics.mesh.rest.client.MeshRestClientConfig; +import com.gentics.mesh.rest.client.MeshWebrootFieldResponse; +import com.gentics.mesh.rest.client.MeshWebrootResponse; +import com.gentics.mesh.rest.client.MeshWebsocket; +import com.gentics.mesh.rest.client.impl.EmptyResponse; + +import io.reactivex.Single; +import io.vertx.core.json.JsonObject; +import okhttp3.OkHttpClient; + +public class OpenAPIMeshRestClient implements MeshRestClient { + + private final ApiClient apiClient; + private final DefaultApi api; + private final MeshRestClientConfig config; + private final JWTAuthentication authentication = new JWTAuthentication(); + + public OpenAPIMeshRestClient(MeshRestClientConfig config, OkHttpClient okHttp) { + this.apiClient = new ApiClient(okHttp).setBasePath(config.getBasePath()); + this.api = new DefaultApi(this.apiClient); + this.config = config; + } + + @Override + public MeshRequest findNodeByUuid(String projectName, String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createNode(String projectName, NodeCreateRequest nodeCreateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateNode(String projectName, String uuid, NodeUpdateRequest nodeUpdateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteNode(String projectName, String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteNode(String projectName, String uuid, String languageTag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findNodes(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findNodeChildren(String projectName, String parentNodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findNodesForTag(String projectName, String tagFamilyUuid, String tagUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest addTagToNode(String projectName, String nodeUuid, String tagUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest removeTagFromNode(String projectName, String nodeUuid, String tagUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest moveNode(String projectName, String nodeUuid, String targetFolderUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagsForNode(String projectName, String nodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateTagsForNode(String projectName, String nodeUuid, + TagListUpdateRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getNodePublishStatus(String projectName, String nodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getNodeLanguagePublishStatus(String projectName, String nodeUuid, + String languageTag, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest publishNode(String projectName, String nodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest publishNodeLanguage(String projectName, String nodeUuid, String languageTag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest takeNodeOffline(String projectName, String nodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest takeNodeLanguageOffline(String projectName, String nodeUuid, String languageTag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest listNodeVersions(String projectName, String nodeUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getNodeRolePermissions(String projectName, String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantNodeRolePermissions(String projectName, String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeNodeRolePermissions(String projectName, String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createTag(String projectName, String tagFamilyUuid, TagCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagByUuid(String projectName, String tagFamilyUuid, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, + TagUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createTag(String projectName, String tagFamilyUuid, String uuid, + TagCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteTag(String projectName, String tagFamilyUuid, String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTags(String projectName, String tagFamilyUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getTagRolePermissions(String projectName, String tagFamilyUuid, + String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantTagRolePermissions(String projectName, String tagFamilyUuid, + String uuid, ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeTagRolePermissions(String projectName, String tagFamilyUuid, + String uuid, ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findProjectByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findProjectByName(String name, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findProjects(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createProject(ProjectCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createProject(String uuid, ProjectCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateProject(String uuid, ProjectUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteProject(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest purgeProject(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getProjectRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantProjectRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeProjectRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagFamilyByUuid(String projectName, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagFamilies(String projectName, PagingParameters pagingInfo) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createTagFamily(String projectName, TagFamilyCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteTagFamily(String projectName, String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, + TagFamilyUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createTagFamily(String projectName, String tagFamilyUuid, + TagFamilyCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagFamilies(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getTagFamilyRolePermissions(String projectName, String tagFamilyUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantTagFamilyRolePermissions(String projectName, String tagFamilyUuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeTagFamilyRolePermissions(String projectName, + String tagFamilyUuid, ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webroot(String projectName, String path, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webroot(String projectName, String[] pathSegments, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootUpdate(String projectName, String path, NodeUpdateRequest nodeUpdateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootUpdate(String projectName, String[] pathSegments, + NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootCreate(String projectName, String path, NodeCreateRequest nodeCreateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootCreate(String projectName, String[] pathSegments, + NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createSchema(SchemaCreateRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createSchema(String uuid, SchemaCreateRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findSchemaByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest diffSchema(String uuid, SchemaModel request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteSchema(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findSchemas(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findMicroschemas(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest applyChangesToSchema(String uuid, SchemaChangesListModel changes) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignSchemaToProject(String projectName, String schemaUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest unassignSchemaFromProject(String projectName, String schemaUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findSchemas(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignMicroschemaToProject(String projectName, String microschemaUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest unassignMicroschemaFromProject(String projectName, String microschemaUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findMicroschemas(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getSchemaRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantSchemaRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeSchemaRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findGroupByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findGroups(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createGroup(GroupCreateRequest createRequest) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateGroup(String uuid, GroupUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteGroup(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest addUserToGroup(String groupUuid, String userUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest removeUserFromGroup(String groupUuid, String userUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest addRoleToGroup(String groupUuid, String roleUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest removeRoleFromGroup(String groupUuid, String roleUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getGroupRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantGroupRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeGroupRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findUserByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findUsers(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createUser(UserCreateRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createUser(String uuid, UserCreateRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateUser(String uuid, UserUpdateRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteUser(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findUsersOfGroup(String groupUuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest readUserPermissions(String uuid, String pathToElement) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getUserResetToken(String userUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest issueAPIToken(String userUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invalidateAPIToken(String userUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getUserRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantUserRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeUserRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findRoleByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findRoles(ParameterProvider... parameter) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createRole(RoleCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createRole(String uuid, RoleCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteRole(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findRolesForGroup(String groupUuid, ParameterProvider... parameter) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateRolePermissions(String roleUuid, String pathToElement, + RolePermissionRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest readRolePermissions(String roleUuid, String pathToElement) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getRoleRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantRoleRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeRoleRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Single login() { + return Single.fromCallable(() -> api.apiV2AuthLoginPost(new LoginRequest() + .username(authentication.getUsername()) + .password(authentication.getPassword()) + .newPassword(authentication.getNewPassword()))) + .map(tokenResponse -> { + authentication.setToken(tokenResponse.getToken()); + return new GenericMessageResponse("OK"); + }); + } + + @Override + public Single logout() { + return Single.fromCallable(() -> api.apiV2AuthLogoutGet()) + .map(response -> { + authentication.setToken(null); + return new GenericMessageResponse("OK"); + }); + } + + @Override + public MeshRequest me(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchNodes(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchNodesRaw(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchNodes(String projectName, String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchNodesRaw(String projectName, String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchUsers(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchUsersRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchGroups(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchGroupsRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchRoles(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchRolesRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchProjects(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchProjectsRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTags(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagsRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTags(String projectName, String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagsRaw(String projectName, String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagFamilies(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagFamiliesRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagFamilies(String projectName, String json, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchTagFamiliesRaw(String projectName, String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchSchemas(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchSchemasRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchMicroschemas(String json, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchMicroschemasRaw(String json) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeIndexClear(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeIndexSync(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest searchStatus() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest meshStatus() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest clusterStatus() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeBackup() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeBackup(BackupParameters parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeExport() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeRestore() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeImport() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest checkConsistency(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest repairConsistency(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest debugInfo(String... include) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest loadCoordinationMaster() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest setCoordinationMaster() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest clearCache() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deployPlugin(PluginDeploymentRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findPlugins(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findPlugin(String id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest undeployPlugin(String id) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createMicroschema(MicroschemaCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findMicroschemaByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteMicroschema(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest applyChangesToMicroschema(String uuid, SchemaChangesListModel changes) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest diffMicroschema(String uuid, MicroschemaModel request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getMicroschemaRolePermissions(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantMicroschemaRolePermissions(String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeMicroschemaRolePermissions(String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateNodeBinaryField(String projectName, String nodeUuid, String languageTag, + String nodeVersion, String fieldKey, InputStream fileData, long fileSize, String fileName, + String contentType, boolean publish, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest downloadBinaryField(String projectName, String nodeUuid, String languageTag, + String fieldKey, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest downloadBinaryField(String projectName, String nodeUuid, String languageTag, + String fieldKey, long from, long to, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest transformNodeBinaryField(String projectName, String nodeUuid, String languageTag, + String version, String fieldKey, ImageManipulationParameters imageManipulationParameter) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateNodeBinaryFieldCheckStatus(String projectName, String nodeUuid, + String languageTag, String nodeVersion, String fieldKey, String secret, String branchUuid, + BinaryCheckStatus status, String reason) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest upsertNodeBinaryFieldImageVariants(String projectName, String nodeUuid, + String fieldKey, ImageManipulationRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest clearNodeBinaryFieldImageVariants(String projectName, String nodeUuid, + String fieldKey, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getNodeBinaryFieldImageVariants(String projectName, String nodeUuid, + String fieldKey, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateNodeS3BinaryField(String projectName, String nodeUuid, String fieldKey, + S3BinaryUploadRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest extractMetadataNodeS3BinaryField(String projectName, String nodeUuid, + String fieldKey, S3BinaryMetadataRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest resolveLinks(String body, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest validateSchema(SchemaModel schema) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest validateMicroschema(MicroschemaModel microschemaModel) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest loadNavigation(String projectName, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest navroot(String projectName, String path, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshWebsocket eventbus() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createBranch(String projectName, BranchCreateRequest branchCreateRequest, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest createBranch(String projectName, String uuid, + BranchCreateRequest branchCreateRequest, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findBranchByUuid(String projectName, String branchUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findBranches(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateBranch(String projectName, String branchUuid, + BranchUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignBranchSchemaVersions(String projectName, String branchUuid, + BranchInfoSchemaList schemaVersionReferences) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignBranchSchemaVersions(String projectName, String branchUuid, + SchemaReference... schemaVersionReferences) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignBranchMicroschemaVersions(String projectName, String branchUuid, + BranchInfoMicroschemaList microschemaVersionReferences) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignBranchMicroschemaVersions(String projectName, String branchUuid, + MicroschemaReference... microschemaVersionReferences) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest migrateBranchSchemas(String projectName, String branchUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest migrateBranchMicroschemas(String projectName, String branchUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest setLatestBranch(String projectName, String branchUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest addTagToBranch(String projectName, String branchUuid, String tagUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest removeTagFromBranch(String projectName, String branchUuid, String tagUuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findTagsForBranch(String projectName, String branchUuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateTagsForBranch(String projectName, String branchUuid, + TagListUpdateRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getBranchRolePermissions(String projectName, String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest grantBranchRolePermissions(String projectName, String uuid, + ObjectPermissionGrantRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest revokeBranchRolePermissions(String projectName, String uuid, + ObjectPermissionRevokeRequest request) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getApiInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getRAML() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest getOpenAPI(Format format, Version version) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest graphql(String projectName, GraphQLRequest request, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findJobs(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findJobByUuid(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteJob(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest resetJob(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest processJob(String uuid) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest invokeJobProcessing() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest get(String path) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest get(String path, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest post(String path) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest post(String path, JsonObject body) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest post(String path, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest post(String path, T request, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest put(String path) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest put(String path, JsonObject body) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest put(String path, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest put(String path, T request, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest delete(String path) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest deleteEmpty(String path) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest delete(String path, Class responseClass) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest ready() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest live() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest writable() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest loadLocalConfig() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest updateLocalConfig(LocalConfigModel localConfigModel) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootField(String projectName, String fieldName, String path, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest webrootField(String projectName, String fieldName, + String[] pathSegments, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, + String path, ImageManipulationRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, + String[] pathSegments, ImageManipulationRequest request, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguageByUuid(String uuid, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguageByTag(String tag, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguages(ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguageByUuid(String projectName, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguageByTag(String projectName, String tag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest findLanguages(String projectName, ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignLanguageToProjectByUuid(String projectName, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest assignLanguageToProjectByTag(String projectName, String tag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest unassignLanguageFromProjectByUuid(String projectName, String uuid, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRequest unassignLanguageFromProjectByTag(String projectName, String tag, + ParameterProvider... parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRestClient setLogin(String username, String password) { + authentication.setLogin(username, password); + return this; + } + + @Override + public MeshRestClient setLogin(String username, String password, String newPassword) { + authentication.setLogin(username, password, newPassword); + return null; + } + + @Override + public void close() { + // TODO Auto-generated method stub + + } + + @Override + public MeshRestClient setAPIKey(String apiKey) { + authentication.setToken(apiKey); + return this; + } + + @Override + public String getAPIKey() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRestClient disableAnonymousAccess() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRestClient enableAnonymousAccess() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRestClient setAuthenticationProvider(JWTAuthentication authentication) { + // TODO Auto-generated method stub + return null; + } + + @Override + public JWTAuthentication getAuthentication() { + // TODO Auto-generated method stub + return null; + } + + @Override + public MeshRestClientConfig getConfig() { + // TODO Auto-generated method stub + return null; + } + +} From efca8cef55e924b945f1c5433ceb41e8b7ecd969 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 20 Feb 2026 18:18:20 +0100 Subject: [PATCH 24/75] Tests --- .../mesh/core/endpoint/node/NodeEndpoint.java | 31 ++- .../mesh/util/MeshOpenAPIv3Generator.java | 8 +- .../mesh/generator/DocGeneratorRunner.java | 16 +- .../parameter/impl/GenericParametersImpl.java | 1 + .../parameter/impl/JobParametersImpl.java | 13 +- .../parameter/impl/NodeParametersImpl.java | 4 + .../verticle/handler/HandlerUtilities.java | 31 +-- .../parameter/client/AbstractParameters.java | 3 +- .../client/impl/MeshRestHttpClientImpl.java | 1 - .../mesh/core/rest/common/PagingMetaInfo.java | 2 +- .../mesh/test/context/MeshTestContext.java | 1 - .../test/openapi/OpenAPIMeshRequestImpl.java | 72 +++++++ .../test/openapi/OpenAPIMeshRestClient.java | 170 ++++++++++------ .../mesh/test/openapi/UpgradedDefaultApi.java | 182 ++++++++++++++++++ 14 files changed, 440 insertions(+), 95 deletions(-) create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index 4bc7b674c8..995d75c854 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -26,6 +26,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -40,6 +41,7 @@ import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.core.rest.navigation.NavigationResponse; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.impl.BranchParametersImpl; import com.gentics.mesh.parameter.impl.DeleteParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; @@ -54,6 +56,7 @@ import com.gentics.mesh.rest.InternalEndpointRoute; import io.vertx.core.MultiMap; +import io.vertx.core.json.JsonObject; /** * The content verticle adds rest endpoints for manipulating nodes. @@ -176,6 +179,8 @@ private void addLanguageHandlers() { endpoint.addUriParameter("language", "Language tag of the content which should be deleted.", "en"); endpoint.method(DELETE); endpoint.produces(APPLICATION_JSON); + endpoint.addQueryParameters(DeleteParametersImpl.class); + endpoint.addQueryParameters(BranchParametersImpl.class); endpoint.description("Delete the language specific content of the node."); endpoint.exampleResponse(NO_CONTENT, "Language variation of the node has been deleted."); endpoint.exampleResponse(NOT_FOUND, miscExamples.createMessageResponse(), "The node could not be found."); @@ -571,13 +576,33 @@ private void addDeleteHandler() { // TODO use schema and only handle those i18n properties that were specified // within the schema. private void addUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.description("Update or create the node with the given uuid. " + InternalEndpointRoute postEndpoint = createRoute(); + postEndpoint.description("Update or create the node with the given uuid. " + "Mesh will automatically check for version conflicts if a version was specified in the request and return a 409 error if a conflict has been detected. " + "Additional conflict checks for WebRoot path conflicts will also be performed. The node is created if no node with the specified uuid could be found."); + postEndpoint.path("/:nodeUuid"); + postEndpoint.addUriParameter("nodeUuid", "Uuid of the node", NODE_DELOREAN_UUID); + postEndpoint.method(POST); + postEndpoint.consumes(APPLICATION_JSON); + postEndpoint.produces(APPLICATION_JSON); + postEndpoint.exampleRequest(new JsonObject(JsonUtil.toJson(nodeExamples.getNodeCreateRequest2()))); + postEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); + postEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); + postEndpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); + postEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("nodeUuid"); + ac.getVersioningParameters().setVersion("draft"); + crudHandler.handleUpdate(ac, uuid); + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute endpoint = createRoute(); + endpoint.description("Update the node with the given uuid. " + + "Mesh will automatically check for version conflicts if a version was specified in the request and return a 409 error if a conflict has been detected. " + + "Additional conflict checks for WebRoot path conflicts will also be performed."); endpoint.path("/:nodeUuid"); endpoint.addUriParameter("nodeUuid", "Uuid of the node", NODE_DELOREAN_UUID); - endpoint.method(POST); + endpoint.method(PUT); endpoint.consumes(APPLICATION_JSON); endpoint.produces(APPLICATION_JSON); endpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 65d9c7e2fd..77b3e67eb7 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -48,10 +48,10 @@ protected void addSecurity(OpenAPI openApi) { securityBearerAuth.setType(SecurityScheme.Type.HTTP); securityBearerAuth.setBearerFormat("JWT"); components.addSecuritySchemes("bearerAuth", securityBearerAuth); - //TODO OAuth2 - SecurityRequirement reqBearerAuth = new SecurityRequirement(); - reqBearerAuth.addList("bearerAuth"); - openApi.addSecurityItem(reqBearerAuth); +// TODO we do not need this globally, do we? +// SecurityRequirement reqBearerAuth = new SecurityRequirement(); +// reqBearerAuth.addList("bearerAuth"); +// openApi.addSecurityItem(reqBearerAuth); } /** diff --git a/doc/src/main/java/com/gentics/mesh/generator/DocGeneratorRunner.java b/doc/src/main/java/com/gentics/mesh/generator/DocGeneratorRunner.java index 1faad07af1..679beeb30f 100644 --- a/doc/src/main/java/com/gentics/mesh/generator/DocGeneratorRunner.java +++ b/doc/src/main/java/com/gentics/mesh/generator/DocGeneratorRunner.java @@ -1,12 +1,6 @@ package com.gentics.mesh.generator; -import com.gentics.mesh.etc.config.env.EnvironmentVariable; -import com.gentics.mesh.generator.TableGenerator; -import com.github.jknack.handlebars.Handlebars; -import com.github.jknack.handlebars.Template; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; -import org.reflections.Reflections; +import static org.reflections.scanners.Scanners.FieldsAnnotated; import java.io.File; import java.io.IOException; @@ -19,7 +13,13 @@ import java.util.Map; import java.util.Objects; -import static org.reflections.scanners.Scanners.FieldsAnnotated; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.reflections.Reflections; + +import com.gentics.mesh.etc.config.env.EnvironmentVariable; +import com.github.jknack.handlebars.Handlebars; +import com.github.jknack.handlebars.Template; /** * Class responsible for invoking all documentation generators. diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java index 14dd643be6..0668a261ca 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java @@ -41,6 +41,7 @@ public String getName() { fieldsParam.setDescription("Limit the output to certain fields. This is useful in order to reduce the response JSON overhead."); fieldsParam.setType(ParamType.STRING); fieldsParam.setDefaultValue(""); + fieldsParam.setExample("uuid,name"); parameters.put(FIELDS_PARAM_KEY, fieldsParam); QueryParameter etagParam = new QueryParameter(); diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java index fb9e361bf5..8887dcee07 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java @@ -1,11 +1,15 @@ package com.gentics.mesh.parameter.impl; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.ArrayUtils; import org.raml.model.ParamType; import org.raml.model.parameter.QueryParameter; +import com.gentics.mesh.core.rest.job.JobStatus; +import com.gentics.mesh.core.rest.job.JobType; import com.gentics.mesh.handler.ActionContext; import com.gentics.mesh.parameter.AbstractParameters; import com.gentics.mesh.parameter.JobParameters; @@ -34,9 +38,9 @@ public JobParametersImpl(ActionContext ac) { Map parameters = new HashMap<>(); parameters.put(STATUS_PARAMETER_KEY, createQueryParameter( - "Parameter for filtering jobs by their status. Multiple values can be given separated by commas.")); + "Parameter for filtering jobs by their status. Multiple values can be given separated by commas.", ArrayUtils.toStringArray(JobStatus.values()))); parameters.put(TYPE_PARAMETER_KEY, createQueryParameter( - "Parameter for filtering jobs by their type. Multiple values can be given separated by commas.")); + "Parameter for filtering jobs by their type. Multiple values can be given separated by commas.", ArrayUtils.toStringArray(JobType.values()))); parameters.put(BRANCH_NAME_PARAMETER_KEY, createQueryParameter( "Parameter for filtering jobs by the branch name. Multiple values can be given separated by commas.")); parameters.put(BRANCH_UUID_PARAMETER_KEY, createQueryParameter( @@ -67,11 +71,14 @@ public String getName() { * @param description parameter description * @return query parameter */ - protected QueryParameter createQueryParameter(String description) { + protected QueryParameter createQueryParameter(String description, String... enumValues) { QueryParameter param = new QueryParameter(); param.setDescription(description); param.setType(ParamType.STRING); param.setDefaultValue(""); + if (enumValues.length > 0) { + param.setEnumeration(Arrays.asList(enumValues)); + } return param; } } \ No newline at end of file diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java index 990d508c8b..60309d4ce5 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java @@ -1,13 +1,16 @@ package com.gentics.mesh.parameter.impl; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.stream.Collectors; import org.raml.model.ParamType; import org.raml.model.parameter.QueryParameter; import com.gentics.mesh.handler.ActionContext; import com.gentics.mesh.parameter.AbstractParameters; +import com.gentics.mesh.parameter.LinkType; import com.gentics.mesh.parameter.NodeParameters; /** @@ -53,6 +56,7 @@ public String getName() { resolveLinksParameter.setExample("medium"); resolveLinksParameter.setRequired(false); resolveLinksParameter.setType(ParamType.STRING); + resolveLinksParameter.setEnumeration(Arrays.asList(LinkType.values()).stream().map(e -> e.name().toLowerCase()).collect(Collectors.toList())); parameters.put(RESOLVE_LINKS_QUERY_PARAM_KEY, resolveLinksParameter); return parameters; diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java index c4ba95c066..096d62e05f 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java @@ -4,11 +4,7 @@ import static com.gentics.mesh.core.data.perm.InternalPermission.DELETE_PERM; import static com.gentics.mesh.core.rest.error.Errors.error; import static com.gentics.mesh.core.rest.event.EventCauseAction.DELETE; -import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; -import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpResponseStatus.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; @@ -21,6 +17,7 @@ import javax.inject.Provider; import javax.inject.Singleton; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,8 +40,10 @@ import com.gentics.mesh.core.db.TxEventAction0; import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.error.NotModifiedException; +import com.gentics.mesh.core.rest.schema.SchemaReferenceInfo; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.event.EventQueueBatch; +import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.util.UUIDUtil; import com.gentics.mesh.util.ValidationUtil; @@ -216,12 +215,22 @@ public , RM extends RestModel> void createOrUpdateE RM model = actions.transformToRestSync(tx, updateElement, ac, 0); return model; } else { - created.set(true); - T createdElement = actions.create(tx, ac, bac.batch(), uuid); - RM model = actions.transformToRestSync(tx, createdElement, ac, 0); - String path = actions.getAPIPath(tx, ac, createdElement); - ac.setLocation(path); - return model; + // the create request must have a schema info + SchemaReferenceInfo schemaInfo = JsonUtil.readValue(ac.getBodyAsString(), SchemaReferenceInfo.class); + boolean missingSchemaInfo = schemaInfo.getSchema() == null + || (StringUtils.isEmpty(schemaInfo.getSchema().getUuid()) + && StringUtils.isEmpty(schemaInfo.getSchema().getName())); + if (missingSchemaInfo && uuid != null) { + // otherwise it is a flawed update request + throw error(NOT_FOUND, "object_not_found_for_uuid", uuid); + } else { + created.set(true); + T createdElement = actions.create(tx, ac, bac.batch(), uuid); + RM model = actions.transformToRestSync(tx, createdElement, ac, 0); + String path = actions.getAPIPath(tx, ac, createdElement); + ac.setLocation(path); + return model; + } } }, model -> ac.send(model, created.get() ? CREATED : OK)); } diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/AbstractParameters.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/AbstractParameters.java index 4832753e2e..e9600b36c3 100644 --- a/rest-client/src/main/java/com/gentics/mesh/parameter/client/AbstractParameters.java +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/AbstractParameters.java @@ -5,11 +5,10 @@ import org.raml.model.parameter.QueryParameter; -import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; /** - * Common default implementation for {@link PagingParameters} + * Common default implementation for {@link ParameterProvider} */ public abstract class AbstractParameters implements ParameterProvider { diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 9b4a49f58d..d77b0b63c3 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -1,6 +1,5 @@ package com.gentics.mesh.rest.client.impl; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON_UTF8; import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML_UTF8; import static com.gentics.mesh.rest.client.impl.HttpMethod.DELETE; import static com.gentics.mesh.rest.client.impl.HttpMethod.GET; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java index a2e5f7bbd9..abb891e423 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/PagingMetaInfo.java @@ -12,7 +12,7 @@ public class PagingMetaInfo implements RestModel { @JsonPropertyDescription("Number of the current page.") private long currentPage; - @JsonProperty(required = true) + @JsonProperty(required = false) @JsonPropertyDescription("Number of elements which can be included in a single page.") private Long perPage; diff --git a/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java b/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java index 7e027a5cdd..eec8b39796 100644 --- a/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java +++ b/tests/common/src/main/java/com/gentics/mesh/test/context/MeshTestContext.java @@ -73,7 +73,6 @@ import com.gentics.mesh.search.TrackingSearchProvider; import com.gentics.mesh.search.TrackingSearchProviderImpl; import com.gentics.mesh.search.verticle.ElasticsearchProcessVerticle; -import com.gentics.mesh.test.ElasticsearchTestMode; import com.gentics.mesh.test.MeshCoreOptionChanger; import com.gentics.mesh.test.MeshInstanceProvider; import com.gentics.mesh.test.MeshTestActions; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java new file mode 100644 index 0000000000..3d6761dc63 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java @@ -0,0 +1,72 @@ +package com.gentics.mesh.test.openapi; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.openapitools.client.ApiResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gentics.mesh.json.JsonUtil; +import com.gentics.mesh.rest.client.MeshRequest; +import com.gentics.mesh.rest.client.MeshResponse; + +import io.reactivex.Single; + +class OpenAPIMeshRequestImpl implements MeshRequest { + + protected static final Logger log = LoggerFactory.getLogger(OpenAPIMeshRequestImpl.class); + + private final Callable> callable; + + public OpenAPIMeshRequestImpl(Callable> callable) { + this.callable = callable; + } + + protected Single> toApiSingle() { + return Single.fromCallable(callable); + } + + @Override + public Single toSingle() { + return toApiSingle().map(ApiResponse::getData); + } + + @Override + public void setHeader(String name, String value) { + log.warn("Setting custom headers is not supported. Call of " + name + "=" + value + "is ignored"); + } + + @Override + public Single> getResponse() { + return toApiSingle().map(apir -> { + return new MeshResponse() { + + @Override + public Map> getHeaders() { + return apir.getHeaders(); + } + + @Override + public int getStatusCode() { + return apir.getStatusCode(); + } + + @Override + public String getBodyAsString() { + return JsonUtil.toJson(apir.getData()); + } + + @Override + public T getBody() { + return apir.getData(); + } + + @Override + public void close() { + } + }; + }); + } +} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 75d1c05014..0af3c7817d 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -1,9 +1,9 @@ package com.gentics.mesh.test.openapi; import java.io.InputStream; +import java.util.Arrays; import org.openapitools.client.ApiClient; -import org.openapitools.client.api.DefaultApi; import org.openapitools.client.model.LoginRequest; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -95,10 +95,19 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; +import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.BackupParameters; +import com.gentics.mesh.parameter.BranchParameters; +import com.gentics.mesh.parameter.DeleteParameters; +import com.gentics.mesh.parameter.GenericParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; +import com.gentics.mesh.parameter.JobParameters; +import com.gentics.mesh.parameter.NodeParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; +import com.gentics.mesh.parameter.PublishParameters; +import com.gentics.mesh.parameter.RolePermissionParameters; +import com.gentics.mesh.parameter.VersioningParameters; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; import com.gentics.mesh.rest.client.MeshRequest; @@ -113,174 +122,207 @@ import io.vertx.core.json.JsonObject; import okhttp3.OkHttpClient; +@SuppressWarnings({"unchecked","rawtypes"}) public class OpenAPIMeshRestClient implements MeshRestClient { private final ApiClient apiClient; - private final DefaultApi api; + private final UpgradedDefaultApi api; private final MeshRestClientConfig config; private final JWTAuthentication authentication = new JWTAuthentication(); + private static final T findParameter(String key, ParameterProvider... parameters) { + return (T) Arrays.stream(parameters).map(p -> p.getParameter(key)).findAny().orElse(null); + } + public OpenAPIMeshRestClient(MeshRestClientConfig config, OkHttpClient okHttp) { - this.apiClient = new ApiClient(okHttp).setBasePath(config.getBasePath()); - this.api = new DefaultApi(this.apiClient); + this.apiClient = new ApiClient(okHttp).setBasePath("%s://%s:%d".formatted((config.isSsl() ? "https" : "http"), config.getHost(), config.getPort())); + this.api = new UpgradedDefaultApi(this.apiClient); this.config = config; } @Override public MeshRequest findNodeByUuid(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidGetWithHttpInfo( + uuid, projectName, + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters))); } @Override public MeshRequest createNode(String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesPostWithHttpInfo(projectName, + org.openapitools.client.model.NodeCreateRequest.fromJson(JsonUtil.toJson(nodeCreateRequest)))); } @Override public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeCreateRequest.toJson()))); } @Override public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeUpsertRequest.toJson()))); } @Override public MeshRequest updateNode(String projectName, String uuid, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPutWithHttpInfo(uuid, projectName, + org.openapitools.client.model.NodeUpdateRequest.fromJson(JsonUtil.toJson(nodeUpdateRequest)))); } @Override public MeshRequest deleteNode(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidDeleteWithHttpInfo(uuid, projectName, + findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), + findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters))); } @Override public MeshRequest deleteNode(String projectName, String uuid, String languageTag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguageDeleteWithHttpInfo( + languageTag, uuid, projectName, + findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), + findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters))); } @Override public MeshRequest findNodes(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesGetWithHttpInfo(projectName, + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters))); } @Override public MeshRequest findNodeChildren(String projectName, String parentNodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidChildrenGetWithHttpInfo( + parentNodeUuid, projectName, + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), + findParameter(PagingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters))); } @Override public MeshRequest findNodesForTag(String projectName, String tagFamilyUuid, String tagUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidGetWithHttpInfo( + tagFamilyUuid, tagUuid, projectName, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters))); } @Override public MeshRequest addTagToNode(String projectName, String nodeUuid, String tagUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidPostWithHttpInfo( + tagUuid, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters))); } @Override public MeshRequest removeTagFromNode(String projectName, String nodeUuid, String tagUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidDeleteWithHttpInfo(tagUuid, nodeUuid, projectName)); } @Override public MeshRequest moveNode(String projectName, String nodeUuid, String targetFolderUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidMoveToToUuidPostWithHttpInfo( + targetFolderUuid, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters))); } @Override public MeshRequest findTagsForNode(String projectName, String nodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsGetWithHttpInfo(nodeUuid, projectName, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters))); } @Override public MeshRequest updateTagsForNode(String projectName, String nodeUuid, TagListUpdateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsPostWithHttpInfo( + nodeUuid, projectName, + org.openapitools.client.model.TagListUpdateRequest.fromJson(JsonUtil.toJson(request)))); } @Override public MeshRequest getNodePublishStatus(String projectName, String nodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedGetWithHttpInfo(nodeUuid, projectName)); } @Override public MeshRequest getNodeLanguagePublishStatus(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedGetWithHttpInfo(languageTag, nodeUuid, projectName)); } @Override public MeshRequest publishNode(String projectName, String nodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedPostWithHttpInfo(nodeUuid, projectName, + findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters))); } @Override public MeshRequest publishNodeLanguage(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedPostWithHttpInfo(languageTag, nodeUuid, projectName)); } @Override public MeshRequest takeNodeOffline(String projectName, String nodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedDeleteWithHttpInfo(nodeUuid, projectName, + findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters))); } @Override public MeshRequest takeNodeLanguageOffline(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedDeleteWithHttpInfo(languageTag, nodeUuid, projectName)); } @Override public MeshRequest listNodeVersions(String projectName, String nodeUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidVersionsGetWithHttpInfo(nodeUuid, projectName, + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters))); } @Override public MeshRequest getNodeRolePermissions(String projectName, String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsGetWithHttpInfo(uuid, projectName)); } @Override @@ -907,6 +949,7 @@ public Single login() { .newPassword(authentication.getNewPassword()))) .map(tokenResponse -> { authentication.setToken(tokenResponse.getToken()); + apiClient.setBearerToken(tokenResponse.getToken()); return new GenericMessageResponse("OK"); }); } @@ -1529,37 +1572,42 @@ public MeshRequest graphql(String projectName, GraphQLRequest r @Override public MeshRequest findJobs(ParameterProvider... parameters) { // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsGetWithHttpInfo( + findParameter(JobParameters.FROM_VERSION_PARAMETER_KEY, parameters), + findParameter(JobParameters.MICROSCHEMA_NAME_PARAMETER_KEY, parameters), + findParameter(JobParameters.MICROSCHEMA_UUID_PARAMETER_KEY, parameters), + findParameter(JobParameters.BRANCH_NAME_PARAMETER_KEY, parameters), + findParameter(JobParameters.BRANCH_UUID_PARAMETER_KEY, parameters), + findParameter(JobParameters.TYPE_PARAMETER_KEY, parameters), + findParameter(JobParameters.SCHEMA_NAME_PARAMETER_KEY, parameters), + findParameter(JobParameters.STATUS_PARAMETER_KEY, parameters), + findParameter(JobParameters.SCHEMA_UUID_PARAMETER_KEY, parameters), + findParameter(JobParameters.TO_VERSION_PARAMETER_KEY, parameters))); } @Override public MeshRequest findJobByUuid(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidGetWithHttpInfo(uuid)); } @Override public MeshRequest deleteJob(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidDeleteWithHttpInfo(uuid)); } @Override public MeshRequest resetJob(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidErrorDeleteWithHttpInfo(uuid)); } @Override public MeshRequest processJob(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidProcessPostWithHttpInfo(uuid)); } @Override public MeshRequest invokeJobProcessing() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminProcessJobsPostWithHttpInfo()); } @Override diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java new file mode 100644 index 0000000000..3f57b2d158 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java @@ -0,0 +1,182 @@ +package com.gentics.mesh.test.openapi; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.openapitools.client.ApiCallback; +import org.openapitools.client.ApiClient; +import org.openapitools.client.ApiException; +import org.openapitools.client.ApiResponse; +import org.openapitools.client.Pair; +import org.openapitools.client.api.DefaultApi; +import org.openapitools.client.model.NodeResponse; + +import com.google.gson.JsonParser; +import com.google.gson.reflect.TypeToken; + +import io.vertx.core.json.JsonObject; + +/** + * An upgrade to the generated DefaultAPI, overriding its generation problems + */ +@SuppressWarnings("rawtypes") +public class UpgradedDefaultApi extends DefaultApi { + + public UpgradedDefaultApi() { + super(); + } + + public UpgradedDefaultApi(ApiClient apiClient) { + super(apiClient); + } + + /** + * Build call for apiV2ProjectNodesNodeUuidPost + * @param nodeUuid Uuid of the node (required) + * @param project (required) + * @param body Json body + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details + + + + + + +
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = Optional.ofNullable(body).map(json -> JsonParser.parseString(body.toString()).getAsJsonObject()); + + // create path and map variables + String localVarPath = "/api/v2/{project}/nodes/{nodeUuid}" + .replace("{" + "nodeUuid" + "}", getApiClient().escapeString(nodeUuid.toString())) + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/json", + "*/*" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + private okhttp3.Call apiV2ProjectNodesNodeUuidPostValidateBeforeCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { + // verify the required parameter 'nodeUuid' is set + if (nodeUuid == null) { + throw new ApiException("Missing the required parameter 'nodeUuid' when calling apiV2ProjectNodesNodeUuidPost(Async)"); + } + + // verify the required parameter 'project' is set + if (project == null) { + throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectNodesNodeUuidPost(Async)"); + } + + return apiV2ProjectNodesNodeUuidPostCall(nodeUuid, project, body, _callback); + } + + /** + * + * + * @param nodeUuid Uuid of the node (required) + * @param project (required) + * @return NodeResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + + +
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
+ */ + public NodeResponse apiV2ProjectNodesNodeUuidPost(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body) throws ApiException { + ApiResponse localVarResp = apiV2ProjectNodesNodeUuidPostWithHttpInfo(nodeUuid, project, body); + return localVarResp.getData(); + } + + /** + * + * + * @param nodeUuid Uuid of the node (required) + * @param project (required) + * @return ApiResponse<NodeResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + + +
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
+ */ + public ApiResponse apiV2ProjectNodesNodeUuidPostWithHttpInfo(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body) throws ApiException { + okhttp3.Call localVarCall = apiV2ProjectNodesNodeUuidPostValidateBeforeCall(nodeUuid, project, body, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return getApiClient().execute(localVarCall, localVarReturnType); + } + + /** + * (asynchronously) + * + * @param nodeUuid Uuid of the node (required) + * @param project (required) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details + + + + + + +
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectNodesNodeUuidPostAsync(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { + okhttp3.Call localVarCall = apiV2ProjectNodesNodeUuidPostValidateBeforeCall(nodeUuid, project, body, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } +} From 6d50af4b3248ed9128200c62f364105d75d3935b Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 24 Feb 2026 17:16:11 +0100 Subject: [PATCH 25/75] Test infra fixes. Endpoint definitions mismatch fixes. OpenAPI test client. --- .../core/endpoint/admin/AdminEndpoint.java | 2 +- .../mesh/util/MeshOpenAPIv3Generator.java | 1 + .../MeshRestClientMessageException.java | 2 +- .../rest/common/GenericMessageResponse.java | 2 +- .../mesh/core/rest/job/JobResponse.java | 6 +- .../java/com/gentics/mesh/json/JsonUtil.java | 13 ++- .../com/gentics/mesh/test/ClientHelper.java | 19 +++- tests/tests-core/pom.xml | 2 + .../test/openapi/OpenAPIMeshRequestImpl.java | 38 +++++-- .../test/openapi/OpenAPIMeshRestClient.java | 103 ++++++++++++------ 10 files changed, 136 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index cd3ff61a3f..1ec1513268 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -284,7 +284,7 @@ protected void addJobHandler() { processJob.path("/jobs/:jobUuid/process"); processJob.method(POST); processJob.description("Process the job. Failed jobs will be automatically reset and put in queued state."); - processJob.exampleResponse(OK, "Job has been queued for processing."); + processJob.exampleResponse(OK, jobExamples.createJobResponse(), "Job information."); processJob.addUriParameter("jobUuid", "Uuid of the job.", JOB_UUID); processJob.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 77b3e67eb7..44b11f8c54 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -13,6 +13,7 @@ import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.vertx.openapi.OpenAPIv3Generator; import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.InParameter; import com.gentics.vertx.openapi.model.OpenAPIGenerationException; import io.swagger.v3.oas.models.Components; diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java index d49a6e292c..82b54ac72e 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java @@ -33,7 +33,7 @@ public MeshRestClientMessageException(int statusCode, String statusMessage, Stri } public MeshRestClientMessageException(int statusCode, String statusMessage, GenericMessageResponse responseMessage, HttpMethod method, String uri) { - super("Error:" + statusCode + " in " + method.name() + " " + uri + " : " + statusMessage); + super("Error:" + statusCode + (method != null ? (" in " + method.name()) : "") + " at " + uri + " : " + statusMessage); this.responseMessage = responseMessage; this.statusCode = statusCode; this.uri = uri; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java index 7e5b36e66f..04cb493aa6 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/common/GenericMessageResponse.java @@ -11,7 +11,7 @@ */ public class GenericMessageResponse extends MessageResponse implements RestModel { - @JsonProperty(required = true) + @JsonProperty(required = false) @JsonPropertyDescription("Internal developer friendly message") private String internalMessage; diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/job/JobResponse.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/job/JobResponse.java index 668c05fa92..765fbf9ffa 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/job/JobResponse.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/job/JobResponse.java @@ -16,7 +16,7 @@ */ public class JobResponse extends AbstractResponse { - @JsonProperty(required = true) + @JsonProperty(required = false) @JsonPropertyDescription("User reference of the creator of the element.") private UserReference creator; @@ -44,11 +44,11 @@ public class JobResponse extends AbstractResponse { @JsonPropertyDescription("Properties of the job.") private Map properties = new HashMap<>(); - @JsonProperty(required = true) + @JsonProperty(required = false) @JsonPropertyDescription("The stop date of the job.") private String stopDate; - @JsonProperty(required = true) + @JsonProperty(required = false) @JsonPropertyDescription("The start date of the job.") private String startDate; diff --git a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java index 20a8df47ef..c00d7c0551 100644 --- a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java +++ b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java @@ -6,6 +6,8 @@ import java.io.IOException; import org.codehaus.jettison.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -19,9 +21,11 @@ import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; @@ -66,8 +70,6 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Main JSON Util which is used to register all custom JSON specific handlers and deserializers. @@ -91,8 +93,11 @@ public final class JsonUtil { private static void initDefaultMapper() { minifyingPrettyPrinter = new MinimalPrettyPrinter(); - defaultMapper = new ObjectMapper(new JsonFactoryBuilder() - .streamReadConstraints(StreamReadConstraints.builder().maxStringLength(Integer.MAX_VALUE).build()).build()); + defaultMapper = JsonMapper.builder(new JsonFactoryBuilder() + .streamReadConstraints(StreamReadConstraints.builder().maxStringLength(Integer.MAX_VALUE).build()) + .build()) + .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS) + .build(); defaultMapper.setDefaultPropertyInclusion(JsonInclude.Value.construct(Include.NON_NULL, Include.ALWAYS)); defaultMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); diff --git a/tests/common/src/main/java/com/gentics/mesh/test/ClientHelper.java b/tests/common/src/main/java/com/gentics/mesh/test/ClientHelper.java index 7b9b920cb6..3dc401bc24 100644 --- a/tests/common/src/main/java/com/gentics/mesh/test/ClientHelper.java +++ b/tests/common/src/main/java/com/gentics/mesh/test/ClientHelper.java @@ -10,6 +10,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.Locale; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -18,6 +20,7 @@ import com.gentics.mesh.core.data.i18n.I18NUtil; import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.core.rest.error.GenericRestException; +import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.client.MeshResponse; import com.gentics.mesh.rest.client.MeshRestClientMessageException; @@ -25,6 +28,7 @@ import com.gentics.mesh.test.context.ClientHandler; import com.gentics.mesh.test.util.TestUtils; import com.gentics.mesh.util.ETag; +import com.google.gson.JsonSyntaxException; import io.netty.handler.codec.http.HttpResponseStatus; import io.reactivex.Observable; @@ -159,6 +163,17 @@ public static MeshRestClientMessageException call(ClientHandler handler, Throwable cause = e.getCause(); if (cause instanceof MeshRestClientMessageException) { error = (MeshRestClientMessageException) e.getCause(); + } else if (cause.getClass().getCanonicalName().equals("org.openapitools.client.ApiException")) { + try { + String responseBody = (String) Arrays.stream(cause.getClass().getDeclaredMethods()).filter(m -> m.getName().equals("getResponseBody") && m.getParameterCount() == 0).findAny().orElseThrow(() -> new IllegalStateException(e)).invoke(cause); + int code = (int) Arrays.stream(cause.getClass().getDeclaredMethods()).filter(m -> m.getName().equals("getCode") && m.getParameterCount() == 0).findAny().orElseThrow(() -> new IllegalStateException(e)).invoke(cause); + String message = (String) Arrays.stream(cause.getClass().getDeclaredMethods()).filter(m -> m.getName().equals("getMessage") && m.getParameterCount() == 0).findAny().orElseThrow(() -> new IllegalStateException(e)).invoke(cause); + error = new MeshRestClientMessageException(code, message, JsonUtil.readValue(responseBody, GenericMessageResponse.class), null, null); + } catch (JsonSyntaxException | GenericRestException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException | IllegalStateException + | SecurityException e1) { + throw new RuntimeException(e1); + } } else { throw (RuntimeException) e; } @@ -170,9 +185,7 @@ public static MeshRestClientMessageException call(ClientHandler handler, } else { expectException(error, status, bodyMessageI18nKey, i18nParams); } - if (error instanceof MeshRestClientMessageException) { - return (MeshRestClientMessageException) e.getCause(); - } + return error; } catch (Exception e) { throw new RuntimeException(e); } diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index 775906b18b..493ae78ba4 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -181,10 +181,12 @@ JsonObject=com.google.gson.JsonObject + true true false true / + false okhttp-gson diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java index 3d6761dc63..1c1d8f9b35 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRequestImpl.java @@ -3,25 +3,30 @@ import java.util.List; import java.util.Map; import java.util.concurrent.Callable; +import java.util.function.Function; import org.openapitools.client.ApiResponse; +import org.openapitools.client.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.client.MeshResponse; +import com.gentics.mesh.rest.client.impl.EmptyResponse; import io.reactivex.Single; -class OpenAPIMeshRequestImpl implements MeshRequest { +class OpenAPIMeshRequestImpl implements MeshRequest { protected static final Logger log = LoggerFactory.getLogger(OpenAPIMeshRequestImpl.class); private final Callable> callable; + private final Class targetType; - public OpenAPIMeshRequestImpl(Callable> callable) { + public OpenAPIMeshRequestImpl(Callable> callable, Class targetType) { this.callable = callable; + this.targetType = targetType; } protected Single> toApiSingle() { @@ -29,8 +34,8 @@ protected Single> toApiSingle() { } @Override - public Single toSingle() { - return toApiSingle().map(ApiResponse::getData); + public Single toSingle() { + return toApiSingle().map(apir -> ifNull(apir.getData(), r -> adaptResponse(r))); } @Override @@ -39,9 +44,9 @@ public void setHeader(String name, String value) { } @Override - public Single> getResponse() { + public Single> getResponse() { return toApiSingle().map(apir -> { - return new MeshResponse() { + return new MeshResponse() { @Override public Map> getHeaders() { @@ -59,8 +64,8 @@ public String getBodyAsString() { } @Override - public T getBody() { - return apir.getData(); + public R getBody() { + return adaptResponse(apir.getData()); } @Override @@ -69,4 +74,21 @@ public void close() { }; }); } + + @SuppressWarnings("unchecked") + protected R ifNull(T t, Function ifNotNull) { + if (t == null) { + if (targetType.equals(EmptyResponse.class)) { + return (R) EmptyResponse.getInstance(); + } else { + return null; + } + } else { + return ifNotNull.apply(t); + } + } + + protected R adaptResponse(T t) { + return ifNull(t, r -> JsonUtil.readValue(JSON.getGson().toJson(t), targetType)); + } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 0af3c7817d..cd2e4fba34 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -4,6 +4,7 @@ import java.util.Arrays; import org.openapitools.client.ApiClient; +import org.openapitools.client.JSON; import org.openapitools.client.model.LoginRequest; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -117,11 +118,15 @@ import com.gentics.mesh.rest.client.MeshWebrootResponse; import com.gentics.mesh.rest.client.MeshWebsocket; import com.gentics.mesh.rest.client.impl.EmptyResponse; +import com.google.gson.JsonSyntaxException; import io.reactivex.Single; import io.vertx.core.json.JsonObject; import okhttp3.OkHttpClient; +/** + * Generated OpenAPI test client adapter. + */ @SuppressWarnings({"unchecked","rawtypes"}) public class OpenAPIMeshRestClient implements MeshRestClient { @@ -130,10 +135,39 @@ public class OpenAPIMeshRestClient implements MeshRestClient { private final MeshRestClientConfig config; private final JWTAuthentication authentication = new JWTAuthentication(); + /** + * Find the parameter in the providers. + * + * @param + * @param key + * @param parameters + * @return + */ private static final T findParameter(String key, ParameterProvider... parameters) { return (T) Arrays.stream(parameters).map(p -> p.getParameter(key)).findAny().orElse(null); } + /** + * Adapt the request of Mesh REST model to an OpenAPI model. + * + * @param + * @param + * @param r + * @return + */ + private static O adaptRequest(R r) { + if (r == null) { + return null; + } + try { + String modelName = r.getClass().getSimpleName(); + Class openApiModelClass = (Class) Class.forName("org.openapitools.client.model." + modelName); + return JSON.getGson().fromJson(r.toJson(), openApiModelClass); + } catch (JsonSyntaxException | ClassNotFoundException e) { + throw new IllegalArgumentException(e); + } + } + public OpenAPIMeshRestClient(MeshRestClientConfig config, OkHttpClient okHttp) { this.apiClient = new ApiClient(okHttp).setBasePath("%s://%s:%d".formatted((config.isSsl() ? "https" : "http"), config.getHost(), config.getPort())); this.api = new UpgradedDefaultApi(this.apiClient); @@ -149,40 +183,42 @@ public MeshRequest findNodeByUuid(String projectName, String uuid, findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), - findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters))); + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters)), NodeResponse.class); } @Override public MeshRequest createNode(String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesPostWithHttpInfo(projectName, - org.openapitools.client.model.NodeCreateRequest.fromJson(JsonUtil.toJson(nodeCreateRequest)))); + org.openapitools.client.model.NodeCreateRequest.fromJson(JsonUtil.toJson(nodeCreateRequest))), NodeResponse.class); } @Override public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeCreateRequest.toJson()))); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeCreateRequest.toJson())), + NodeResponse.class); } @Override public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeUpsertRequest.toJson()))); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeUpsertRequest.toJson())), + NodeResponse.class); } @Override public MeshRequest updateNode(String projectName, String uuid, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPutWithHttpInfo(uuid, projectName, - org.openapitools.client.model.NodeUpdateRequest.fromJson(JsonUtil.toJson(nodeUpdateRequest)))); + org.openapitools.client.model.NodeUpdateRequest.fromJson(JsonUtil.toJson(nodeUpdateRequest))), NodeResponse.class); } @Override public MeshRequest deleteNode(String projectName, String uuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidDeleteWithHttpInfo(uuid, projectName, findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), - findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters))); + findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters)), EmptyResponse.class); } @Override @@ -191,7 +227,7 @@ public MeshRequest deleteNode(String projectName, String uuid, St return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguageDeleteWithHttpInfo( languageTag, uuid, projectName, findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), - findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters))); + findParameter(DeleteParameters.RECURSIVE_PARAMETER_KEY, parameters)), EmptyResponse.class); } @Override @@ -206,7 +242,7 @@ public MeshRequest findNodes(String projectName, ParameterProv findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), - findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters))); + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @Override @@ -222,7 +258,7 @@ public MeshRequest findNodeChildren(String projectName, String findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), - findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters))); + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @Override @@ -232,7 +268,7 @@ public MeshRequest findNodesForTag(String projectName, String return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidGetWithHttpInfo( tagFamilyUuid, tagUuid, projectName, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(GenericParameters.ETAG_PARAM_KEY, parameters))); + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), NodeListResponse.class); } @Override @@ -240,13 +276,13 @@ public MeshRequest addTagToNode(String projectName, String nodeUui ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidPostWithHttpInfo( tagUuid, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters))); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), NodeResponse.class); } @Override public MeshRequest removeTagFromNode(String projectName, String nodeUuid, String tagUuid, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidDeleteWithHttpInfo(tagUuid, nodeUuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidDeleteWithHttpInfo(tagUuid, nodeUuid, projectName), EmptyResponse.class); } @Override @@ -254,7 +290,7 @@ public MeshRequest moveNode(String projectName, String nodeUuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidMoveToToUuidPostWithHttpInfo( targetFolderUuid, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters))); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), EmptyResponse.class); } @Override @@ -263,7 +299,7 @@ public MeshRequest findTagsForNode(String projectName, String n return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsGetWithHttpInfo(nodeUuid, projectName, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), - findParameter(GenericParameters.ETAG_PARAM_KEY, parameters))); + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), TagListResponse.class); } @Override @@ -271,45 +307,48 @@ public MeshRequest updateTagsForNode(String projectName, String TagListUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsPostWithHttpInfo( nodeUuid, projectName, - org.openapitools.client.model.TagListUpdateRequest.fromJson(JsonUtil.toJson(request)))); + org.openapitools.client.model.TagListUpdateRequest.fromJson(JsonUtil.toJson(request))), TagListResponse.class); } @Override public MeshRequest getNodePublishStatus(String projectName, String nodeUuid, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedGetWithHttpInfo(nodeUuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedGetWithHttpInfo(nodeUuid, projectName), PublishStatusResponse.class); } @Override public MeshRequest getNodeLanguagePublishStatus(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedGetWithHttpInfo(languageTag, nodeUuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedGetWithHttpInfo(languageTag, nodeUuid, projectName), + PublishStatusModel.class); } @Override public MeshRequest publishNode(String projectName, String nodeUuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedPostWithHttpInfo(nodeUuid, projectName, - findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters))); + findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters)), PublishStatusResponse.class); } @Override public MeshRequest publishNodeLanguage(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedPostWithHttpInfo(languageTag, nodeUuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedPostWithHttpInfo(languageTag, nodeUuid, projectName), + PublishStatusModel.class); } @Override public MeshRequest takeNodeOffline(String projectName, String nodeUuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPublishedDeleteWithHttpInfo(nodeUuid, projectName, - findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters))); + findParameter(PublishParameters.RECURSIVE_PARAMETER_KEY, parameters)), EmptyResponse.class); } @Override public MeshRequest takeNodeLanguageOffline(String projectName, String nodeUuid, String languageTag, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedDeleteWithHttpInfo(languageTag, nodeUuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidLanguagesLanguagePublishedDeleteWithHttpInfo(languageTag, nodeUuid, projectName), + EmptyResponse.class); } @Override @@ -317,12 +356,13 @@ public MeshRequest listNodeVersions(String projectName, St ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidVersionsGetWithHttpInfo(nodeUuid, projectName, findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), - findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters))); + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters)), + NodeVersionsResponse.class); } @Override public MeshRequest getNodeRolePermissions(String projectName, String uuid) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsGetWithHttpInfo(uuid, projectName)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsGetWithHttpInfo(uuid, projectName), ObjectPermissionResponse.class); } @Override @@ -608,8 +648,9 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid @Override public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, + null, uuid, null, + adaptRequest(request)), GenericMessageResponse.class); } @Override @@ -1582,32 +1623,32 @@ public MeshRequest findJobs(ParameterProvider... parameters) { findParameter(JobParameters.SCHEMA_NAME_PARAMETER_KEY, parameters), findParameter(JobParameters.STATUS_PARAMETER_KEY, parameters), findParameter(JobParameters.SCHEMA_UUID_PARAMETER_KEY, parameters), - findParameter(JobParameters.TO_VERSION_PARAMETER_KEY, parameters))); + findParameter(JobParameters.TO_VERSION_PARAMETER_KEY, parameters)), JobListResponse.class); } @Override public MeshRequest findJobByUuid(String uuid) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidGetWithHttpInfo(uuid)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidGetWithHttpInfo(uuid), JobResponse.class); } @Override public MeshRequest deleteJob(String uuid) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidDeleteWithHttpInfo(uuid)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest resetJob(String uuid) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidErrorDeleteWithHttpInfo(uuid)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidErrorDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest processJob(String uuid) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidProcessPostWithHttpInfo(uuid)); + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsJobUuidProcessPostWithHttpInfo(uuid), JobResponse.class); } @Override public MeshRequest invokeJobProcessing() { - return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminProcessJobsPostWithHttpInfo()); + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminProcessJobsPostWithHttpInfo(), GenericMessageResponse.class); } @Override From 3ce3eae5a297130e3e9ff06d566e036652a614d8 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 25 Feb 2026 15:35:31 +0100 Subject: [PATCH 26/75] Security refactored --- .../rest/impl/InternalEndpointRouteImpl.java | 12 ++++++ .../mesh/util/MeshOpenAPIv3Generator.java | 39 +++++++------------ .../mesh/rest/InternalEndpointRoute.java | 15 +++++++ 3 files changed, 40 insertions(+), 26 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index 241c3b15de..f3220e8c35 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -3,6 +3,7 @@ import static com.gentics.mesh.core.rest.error.Errors.error; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; @@ -39,6 +40,7 @@ public class InternalEndpointRouteImpl extends com.gentics.vertx.openapi.metadat */ public InternalEndpointRouteImpl(Router router, LocalConfigApi localConfigApi, Database db) { super(router); + setInsecure(false); ReadOnlyHandler readOnlyHandler = new ReadOnlyHandler(localConfigApi, db); route.handler(readOnlyHandler); } @@ -72,6 +74,16 @@ public InternalEndpointRoute exampleRequest(JSONObject jsonObject) { return this; } + @Override + public InternalEndpointRoute setInsecure(boolean insecure) { + if (insecure) { + setSecuritySchemes(Collections.emptyList()); + } else { + setSecuritySchemes(Collections.singletonList("bearerAuth")); + } + return this; + } + private class ReadOnlyHandler implements PlatformHandler { private final LocalConfigApi localConfigApi; diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 44b11f8c54..d9d8599a1c 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -12,15 +12,13 @@ import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.vertx.openapi.OpenAPIv3Generator; +import com.gentics.vertx.openapi.model.ExtendedSecurityScheme; import com.gentics.vertx.openapi.model.Format; import com.gentics.vertx.openapi.model.InParameter; import com.gentics.vertx.openapi.model.OpenAPIGenerationException; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; import io.vertx.ext.web.Router; @@ -29,30 +27,19 @@ */ public class MeshOpenAPIv3Generator extends OpenAPIv3Generator { - public MeshOpenAPIv3Generator(String version, List servers, - Optional> maybePathBlacklist, - Optional> maybePathWhitelist) { - super(version, servers, maybePathBlacklist, maybePathWhitelist); + protected static ExtendedSecurityScheme securityBearerAuth; + + static { + securityBearerAuth = new ExtendedSecurityScheme(false); + securityBearerAuth.getScheme().setScheme("bearer"); + securityBearerAuth.getScheme().setType(SecurityScheme.Type.HTTP); + securityBearerAuth.getScheme().setBearerFormat("JWT"); } - @Override - protected void addSecurity(OpenAPI openApi) { - Components components; - if (openApi.getComponents() == null) { - components = new Components(); - openApi.setComponents(components); - } else { - components = openApi.getComponents(); - } - SecurityScheme securityBearerAuth = new SecurityScheme(); - securityBearerAuth.setScheme("bearer"); - securityBearerAuth.setType(SecurityScheme.Type.HTTP); - securityBearerAuth.setBearerFormat("JWT"); - components.addSecuritySchemes("bearerAuth", securityBearerAuth); -// TODO we do not need this globally, do we? -// SecurityRequirement reqBearerAuth = new SecurityRequirement(); -// reqBearerAuth.addList("bearerAuth"); -// openApi.addSecurityItem(reqBearerAuth); + public MeshOpenAPIv3Generator(String version, List servers, + Optional> maybePathBlacklist, + Optional> maybePathWhitelist) { + super(version, servers, Collections.singletonMap("bearerAuth", securityBearerAuth), maybePathBlacklist, maybePathWhitelist); } /** @@ -66,7 +53,7 @@ protected void addSecurity(OpenAPI openApi) { * @throws OpenAPIGenerationException */ public String generate(Map routers, Format format, boolean pretty, boolean useVersion31) throws OpenAPIGenerationException { - return generate(routers, format, pretty, useVersion31, + return generate("Gentics Mesh REST API", routers, format, pretty, useVersion31, // transform project path item Optional.of((path, item) -> { if (path.contains("/{project}/")) { diff --git a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index 8fe0b3db0b..426f692b4e 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -42,6 +42,21 @@ public interface InternalEndpointRoute extends com.gentics.vertx.openapi.metadat @Deprecated InternalEndpointRoute blockingHandler(Handler requestHandler); + /** + * If true, the endpoint can be used with no authentication. + * + * @return + */ + boolean isInsecure(); + + /** + * Set the endpoint to omit the secure token requirement. + * + * @param insecure + * @return + */ + InternalEndpointRoute setInsecure(boolean insecure); + @Override default com.gentics.vertx.openapi.metadata.InternalEndpointRoute exampleRequest(JsonObject jsonObject) { try { From 2f7c6db7487323db2853c70b49c8de51d90c3e69 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 26 Feb 2026 18:16:45 +0100 Subject: [PATCH 27/75] Add missing APIs --- .../endpoint/project/LanguageEndpoint.java | 5 ++ .../generator/AbstractEndpointGenerator.java | 10 +++ .../test/openapi/OpenAPIMeshRestClient.java | 65 +++++++++---------- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/project/LanguageEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/project/LanguageEndpoint.java index 1fc220464f..851b67ede2 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/project/LanguageEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/project/LanguageEndpoint.java @@ -25,6 +25,11 @@ public class LanguageEndpoint extends AbstractInternalEndpoint { private final LanguageCrudHandler crudHandler; + public LanguageEndpoint() { + super("languages", null, null, null, null); + this.crudHandler = null; + } + @Inject public LanguageEndpoint(MeshAuthChain chain, LocalConfigApi localConfigApi, Database db, MeshOptions options, LanguageCrudHandler crudHandler) { super("languages", chain, localConfigApi, db, options); diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java index 6b11934e29..cac16da7ff 100644 --- a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -23,8 +23,10 @@ import com.gentics.mesh.core.endpoint.microschema.ProjectMicroschemaEndpoint; import com.gentics.mesh.core.endpoint.navroot.NavRootEndpoint; import com.gentics.mesh.core.endpoint.node.NodeEndpoint; +import com.gentics.mesh.core.endpoint.project.LanguageEndpoint; import com.gentics.mesh.core.endpoint.project.ProjectEndpoint; import com.gentics.mesh.core.endpoint.project.ProjectInfoEndpoint; +import com.gentics.mesh.core.endpoint.project.ProjectLanguageEndpoint; import com.gentics.mesh.core.endpoint.role.RoleEndpoint; import com.gentics.mesh.core.endpoint.schema.ProjectSchemaEndpoint; import com.gentics.mesh.core.endpoint.schema.SchemaEndpoint; @@ -113,6 +115,10 @@ protected void addProjectEndpoints(T consumer) throws IOException { ProjectMicroschemaEndpoint projectMicroschemaEndpoint = Mockito.spy(new ProjectMicroschemaEndpoint()); initEndpoint(projectMicroschemaEndpoint); addEndpoints(projectBasePath, consumer, projectMicroschemaEndpoint, true); + + ProjectLanguageEndpoint projectLanguageEndpoint = Mockito.spy(new ProjectLanguageEndpoint()); + initEndpoint(projectLanguageEndpoint); + addEndpoints(projectBasePath, consumer, projectLanguageEndpoint, true); } /** @@ -189,6 +195,10 @@ protected void addCoreEndpoints(T consumer) throws IOException { ProjectInfoEndpoint projectInfoEndpoint = Mockito.spy(new ProjectInfoEndpoint()); initEndpoint(projectInfoEndpoint); addEndpoints(coreBasePath, consumer, projectInfoEndpoint, false); + + LanguageEndpoint languageEndpoint = Mockito.spy(new LanguageEndpoint()); + initEndpoint(languageEndpoint); + addEndpoints(coreBasePath, consumer, languageEndpoint, false); } /** diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index cd2e4fba34..5eaf18fc8b 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -106,6 +106,7 @@ import com.gentics.mesh.parameter.NodeParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; +import com.gentics.mesh.parameter.ProjectLoadParameters; import com.gentics.mesh.parameter.PublishParameters; import com.gentics.mesh.parameter.RolePermissionParameters; import com.gentics.mesh.parameter.VersioningParameters; @@ -133,7 +134,7 @@ public class OpenAPIMeshRestClient implements MeshRestClient { private final ApiClient apiClient; private final UpgradedDefaultApi api; private final MeshRestClientConfig config; - private final JWTAuthentication authentication = new JWTAuthentication(); + private JWTAuthentication authentication = new JWTAuthentication(); /** * Find the parameter in the providers. @@ -1783,74 +1784,74 @@ public MeshRequest upsertWebrootFieldImageVariants(String @Override public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, String[] pathSegments, ImageManipulationRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub return null; } @Override public MeshRequest findLanguageByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2LanguagesLanguageUuidGetWithHttpInfo(uuid), LanguageResponse.class); } @Override public MeshRequest findLanguageByTag(String tag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2LanguagesTagLanguageTagGetWithHttpInfo(tag), LanguageResponse.class); } @Override public MeshRequest findLanguages(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2LanguagesGetWithHttpInfo(findParameter(PagingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), LanguageListResponse.class); } @Override public MeshRequest findLanguageByUuid(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesLanguageUuidGetWithHttpInfo(uuid, projectName), LanguageResponse.class); } @Override public MeshRequest findLanguageByTag(String projectName, String tag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesTagLanguageTagGetWithHttpInfo(tag, projectName), LanguageResponse.class); } @Override public MeshRequest findLanguages(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesGet(projectName, + findParameter(PagingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), LanguageListResponse.class); } @Override public MeshRequest assignLanguageToProjectByUuid(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesLanguageUuidPostWithHttpInfo(uuid, projectName, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), + ProjectResponse.class); } @Override public MeshRequest assignLanguageToProjectByTag(String projectName, String tag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesTagLanguageTagPostWithHttpInfo(tag, projectName, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), + ProjectResponse.class); } @Override public MeshRequest unassignLanguageFromProjectByUuid(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesLanguageUuidDeleteWithHttpInfo(uuid, projectName, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), + ProjectResponse.class); } @Override public MeshRequest unassignLanguageFromProjectByTag(String projectName, String tag, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectLanguagesTagLanguageTagDeleteWithHttpInfo(tag, projectName, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), + ProjectResponse.class); } @Override @@ -1867,8 +1868,6 @@ public MeshRestClient setLogin(String username, String password, String newPassw @Override public void close() { - // TODO Auto-generated method stub - } @Override @@ -1879,38 +1878,34 @@ public MeshRestClient setAPIKey(String apiKey) { @Override public String getAPIKey() { - // TODO Auto-generated method stub - return null; + return authentication.getToken(); } @Override public MeshRestClient disableAnonymousAccess() { // TODO Auto-generated method stub - return null; + return this; } @Override public MeshRestClient enableAnonymousAccess() { // TODO Auto-generated method stub - return null; + return this; } @Override public MeshRestClient setAuthenticationProvider(JWTAuthentication authentication) { - // TODO Auto-generated method stub - return null; + this.authentication = authentication; + return this; } @Override public JWTAuthentication getAuthentication() { - // TODO Auto-generated method stub - return null; + return authentication; } @Override public MeshRestClientConfig getConfig() { - // TODO Auto-generated method stub - return null; + return config; } - } From b100562e9d61d49283262d65c82477b7a96ca504 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 2 Mar 2026 13:12:16 +0100 Subject: [PATCH 28/75] Fixes --- .../gentics/mesh/etc/config/MeshOptions.java | 16 ++++++++++++++ bom/pom.xml | 7 ++++--- .../core/endpoint/admin/AdminHandler.java | 21 +++++++++++++++---- .../mesh/util/MeshOpenAPIv3Generator.java | 2 +- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index 32c56ddafc..c35cbbba9e 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -31,6 +31,7 @@ public abstract class MeshOptions implements Option { public static final String MESH_UPDATECHECK_ENV = "MESH_UPDATECHECK"; public static final String MESH_TEMP_DIR_ENV = "MESH_TEMP_DIR"; public static final String MESH_PLUGIN_DIR_ENV = "MESH_PLUGIN_DIR"; + public static final String MESH_OPENAPI_INCLUDE_PLUGINS_ENV = "MESH_OPENAPI_INCLUDE_PLUGINS"; public static final String MESH_PLUGIN_USE_HTTP2_ENV = "MESH_PLUGIN_USE_HTTP2"; public static final String MESH_PLUGIN_TIMEOUT_ENV = "MESH_PLUGIN_TIMEOUT"; public static final String MESH_NODE_NAME_ENV = "MESH_NODE_NAME"; @@ -161,6 +162,11 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_PLUGIN_USE_HTTP2_ENV, description = "Override the HTTP/2 usage flag for plugins.") private boolean pluginUseHttp2 = false; + @JsonProperty(required = false) + @JsonPropertyDescription("Should the endpoints of the included plugins be served in the OpenAPI specification generation requests. Default is false.") + @EnvironmentVariable(name = MESH_OPENAPI_INCLUDE_PLUGINS_ENV, description = "Override the flag to include endpoints of the plugins be served in the OpenAPI specification generation requests") + private boolean openapiIncludePlugins = false; + /* EXTRA Command Line Arguments */ @JsonIgnore @EnvironmentVariable(name = MESH_CLUSTER_INIT_ENV, description = "Enable or disable the initial cluster database setup. This is useful for testing.") @@ -544,6 +550,16 @@ public MeshOptions setPluginUseHttp2(boolean pluginUseHttp2) { return this; } + public boolean isOpenapiIncludePlugins() { + return openapiIncludePlugins; + } + + @Setter + public MeshOptions setOpenapiIncludePlugins(boolean openapiIncludePlugins) { + this.openapiIncludePlugins = openapiIncludePlugins; + return this; + } + @JsonIgnore public abstract NativeQueryFiltering getNativeQueryFiltering(); diff --git a/bom/pom.xml b/bom/pom.xml index b6ad3577bc..3814e336af 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -27,8 +27,9 @@ 6.3.5 1.1.4 5.5.0 - 2.17.0 - 2.17.0 + 2.21.1 + 2.21 + 2.21.1 4.2.7.Final 3.2.2 2.16.83 @@ -239,7 +240,7 @@ com.fasterxml.jackson.core jackson-annotations - ${jackson.version} + ${jackson-annotations.version} com.fasterxml.jackson.module diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 6835db0693..e1ebd2b55a 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -18,6 +18,7 @@ import java.util.Set; import java.util.UUID; import java.util.function.Function; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -270,9 +271,16 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest // Collect available servers HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - List servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(() -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort()))); + Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + List servers; + try { + servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(noClusterServerSupplier); + } catch (Throwable e) { + log.error("Could not retrieve the server list out of Hazelcast", e); + servers = noClusterServerSupplier.get(); + } /* * Blacklist @@ -285,6 +293,9 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*")); + if (!options.isOpenapiIncludePlugins()) { + //blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/[.]*")); + } // Make an instance with blacklist path patterns MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); @@ -296,7 +307,9 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest //... from base root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), //... from generic project root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")) + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")), + //... from generic plugin root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), // ...with desired format Format.parse(format), diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index d9d8599a1c..4baf7dd83f 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -57,7 +57,7 @@ public String generate(Map routers, Format format, boolean prett // transform project path item Optional.of((path, item) -> { if (path.contains("/{project}/")) { - Parameter projectNameParam = new Parameter().name("project").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Uuid of the related project")); + Parameter projectNameParam = new Parameter().name("project").in(InParameter.PATH.toString()).schema(new Schema().type("string").description("Name of the related project")); item.readOperations().stream() .forEach(o -> o.getParameters().stream().filter(p -> "project".equals(p.getName())).findAny() .ifPresentOrElse(present -> { From 73f3f0950dacf71e46f38066aa8c585eaa78835a Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 13:35:05 +0100 Subject: [PATCH 29/75] Refactoring. Binary handler tests work again --- .../mesh/core/endpoint/admin/AdminHandler.java | 2 +- .../mesh/core/endpoint/node/NodeEndpoint.java | 12 ++++++++---- .../mesh/parameter/impl/JobParametersImpl.java | 3 ++- .../mesh/parameter/impl/OpenAPIParametersImpl.java | 4 ++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index e1ebd2b55a..c81d51b9c4 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -292,7 +292,7 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*")); + blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); if (!options.isOpenapiIncludePlugins()) { //blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/[.]*")); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index 995d75c854..f121cf14b3 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -16,7 +16,6 @@ import static com.gentics.mesh.example.ExampleUuids.TAG_RED_UUID; import static com.gentics.mesh.example.ExampleUuids.UUID_1; import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_OCTET_STREAM; import static com.gentics.mesh.http.HttpConstants.MULTIPART_FORM_DATA; import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; @@ -31,7 +30,9 @@ import javax.inject.Inject; import org.apache.commons.lang3.StringUtils; +import org.raml.model.ParamType; import org.raml.model.Resource; +import org.raml.model.parameter.QueryParameter; import com.gentics.mesh.auth.MeshAuthChain; import com.gentics.mesh.cli.BootstrapInitializer; @@ -194,11 +195,16 @@ private void addLanguageHandlers() { } private void addBinaryHandlers() { + QueryParameter publishQueryParameter = new QueryParameter(); + publishQueryParameter.setDescription("Should the saved data be published at once"); + publishQueryParameter.setDefaultValue("false"); + publishQueryParameter.setType(ParamType.BOOLEAN); + InternalEndpointRoute fieldUpdate = createRoute(); fieldUpdate.path("/:nodeUuid/binary/:fieldName"); fieldUpdate.addUriParameter("nodeUuid", "Uuid of the node.", NODE_DELOREAN_UUID); fieldUpdate.addUriParameter("fieldName", "Name of the field which should be created.", "stringField"); - fieldUpdate.addQueryParameter("publish", "Whether the node shall be published after updating the binary field", "true"); + fieldUpdate.addQueryParameter("publish", publishQueryParameter); fieldUpdate.method(POST); fieldUpdate.consumes(MULTIPART_FORM_DATA); fieldUpdate.produces(APPLICATION_JSON); @@ -258,8 +264,6 @@ private void addBinaryHandlers() { fieldGet.addQueryParameters(ImageManipulationParametersImpl.class); fieldGet.addQueryParameters(VersioningParametersImpl.class); fieldGet.method(GET); - fieldGet.produces(APPLICATION_OCTET_STREAM); - fieldGet.exampleResponse(OK, "Binary data"); fieldGet.description( "Download the binary field with the given name. You can use image query parameters for crop and resize if the binary data represents an image."); fieldGet.blockingHandler(rc -> { diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java index 8887dcee07..e90b0bccaf 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/JobParametersImpl.java @@ -75,9 +75,10 @@ protected QueryParameter createQueryParameter(String description, String... enum QueryParameter param = new QueryParameter(); param.setDescription(description); param.setType(ParamType.STRING); - param.setDefaultValue(""); if (enumValues.length > 0) { param.setEnumeration(Arrays.asList(enumValues)); + } else { + param.setDefaultValue(""); } return param; } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java index f15aa929cc..646d04c1b5 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java @@ -39,7 +39,7 @@ public OpenAPIParametersImpl() { // version QueryParameter versionParameter = new QueryParameter(); - versionParameter.setDefaultValue(Version.V30.name()); + versionParameter.setDefaultValue(Version.V30.name().toLowerCase()); versionParameter.setDescription("Specify, whether the OpenAPI standard version 3.1 should be generated. If false or unset, a version 3.0 of standard will be used."); versionParameter.setExample(Version.V31.name()); versionParameter.setRequired(false); @@ -49,7 +49,7 @@ public OpenAPIParametersImpl() { // format QueryParameter formatParameter = new QueryParameter(); - formatParameter.setDefaultValue(Format.JSON.name()); + formatParameter.setDefaultValue(Format.JSON.name().toLowerCase()); formatParameter.setDescription("Specify the output format. Default is JSON."); formatParameter.setExample(Format.YAML.name()); formatParameter.setRequired(false); From ffa52d32602c6ad8f1ab1c5a964950862a90d468 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 14:03:03 +0100 Subject: [PATCH 30/75] Flip the plugins endpoint inclusion option --- .../com/gentics/mesh/etc/config/MeshOptions.java | 16 ++++++++-------- .../mesh/core/endpoint/admin/AdminHandler.java | 7 ++++--- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index c35cbbba9e..7f5d98ef87 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -31,7 +31,7 @@ public abstract class MeshOptions implements Option { public static final String MESH_UPDATECHECK_ENV = "MESH_UPDATECHECK"; public static final String MESH_TEMP_DIR_ENV = "MESH_TEMP_DIR"; public static final String MESH_PLUGIN_DIR_ENV = "MESH_PLUGIN_DIR"; - public static final String MESH_OPENAPI_INCLUDE_PLUGINS_ENV = "MESH_OPENAPI_INCLUDE_PLUGINS"; + public static final String MESH_OPENAPI_EXCLUDE_PLUGINS_ENV = "MESH_OPENAPI_EXCLUDE_PLUGINS"; public static final String MESH_PLUGIN_USE_HTTP2_ENV = "MESH_PLUGIN_USE_HTTP2"; public static final String MESH_PLUGIN_TIMEOUT_ENV = "MESH_PLUGIN_TIMEOUT"; public static final String MESH_NODE_NAME_ENV = "MESH_NODE_NAME"; @@ -163,9 +163,9 @@ public abstract class MeshOptions implements Option { private boolean pluginUseHttp2 = false; @JsonProperty(required = false) - @JsonPropertyDescription("Should the endpoints of the included plugins be served in the OpenAPI specification generation requests. Default is false.") - @EnvironmentVariable(name = MESH_OPENAPI_INCLUDE_PLUGINS_ENV, description = "Override the flag to include endpoints of the plugins be served in the OpenAPI specification generation requests") - private boolean openapiIncludePlugins = false; + @JsonPropertyDescription("Should the endpoints of the included plugins be excluded from the OpenAPI specification generation requests. Default is true.") + @EnvironmentVariable(name = MESH_OPENAPI_EXCLUDE_PLUGINS_ENV, description = "Override the flag to exclude endpoints of the plugins from the OpenAPI specification generation requests") + private boolean openapiExcludePlugins = true; /* EXTRA Command Line Arguments */ @JsonIgnore @@ -550,13 +550,13 @@ public MeshOptions setPluginUseHttp2(boolean pluginUseHttp2) { return this; } - public boolean isOpenapiIncludePlugins() { - return openapiIncludePlugins; + public boolean isOpenapiExcludePlugins() { + return openapiExcludePlugins; } @Setter - public MeshOptions setOpenapiIncludePlugins(boolean openapiIncludePlugins) { - this.openapiIncludePlugins = openapiIncludePlugins; + public MeshOptions setOpenapiExcludePlugins(boolean openapiExcludePlugins) { + this.openapiExcludePlugins = openapiExcludePlugins; return this; } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index c81d51b9c4..66d7a53298 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -63,6 +63,7 @@ import com.gentics.vertx.openapi.model.OpenAPIGenerationException; import io.vertx.core.Vertx; +import io.vertx.ext.web.Router; /** * Handler for admin request methods. @@ -293,8 +294,8 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); - if (!options.isOpenapiIncludePlugins()) { - //blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/[.]*")); + if (options.isOpenapiExcludePlugins()) { + blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/[.]*")); } // Make an instance with blacklist path patterns @@ -309,7 +310,7 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest //... from generic project root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")), //... from generic plugin root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")) + options.isOpenapiExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), // ...with desired format Format.parse(format), From 795e27fe65a1db3bb459b620f25f200c23404887 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 14:13:45 +0100 Subject: [PATCH 31/75] Changelog --- .../src/changelog/entries/2026/03/8685.GPU-2196.enhancement | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement diff --git a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement new file mode 100644 index 0000000000..8521aaf32f --- /dev/null +++ b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement @@ -0,0 +1,2 @@ +REST API: Along with existing RAML API definition, it is now possible to generate an OpenAPI (aka Swagger) specification for the Mesh REST API, which can optionally include the API for all the currently running Mesh Plugins. + For that, three new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`v3.0` or `v3.1`) query parameters, and two v3.0 shortcuts, `/openapi.yaml` and `openapi.json`. \ No newline at end of file From 257ebc0c3245c7cf009c2532b64c4c84c41c3c59 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 15:36:40 +0100 Subject: [PATCH 32/75] Hide the violating endpoint from the generator --- .../mesh/core/endpoint/RolePermissionHandlingEndpoint.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java index e04cccf7f1..015d6b3318 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java @@ -76,6 +76,7 @@ protected void addRolePermissionHandler(String uuidParameterName, String uuidPar revokePermissionsEndpoint.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); revokePermissionsEndpoint.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); revokePermissionsEndpoint.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpoint.setHidden(true); revokePermissionsEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = rc.request().getParam(uuidParameterName); From 19ab5570344bda5e901a46bb54e3ef3f01b68c93 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 15:58:20 +0100 Subject: [PATCH 33/75] Cleanup --- CHANGELOG.adoc | 2 -- .../entries/2026/03/8685.GPU-2196.enhancement | 2 +- ...timeGenerator.java => OpenAPIMockAPIGenerator.java} | 10 +++++----- .../gentics/mesh/generator/ExampleGeneratorRunner.java | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) rename core/src/main/java/com/gentics/mesh/generator/{OpenAPIRuntimeGenerator.java => OpenAPIMockAPIGenerator.java} (91%) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index c9be0f8417..b1a6a44bd2 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -213,8 +213,6 @@ enforced in the UI, and is now enforced in the REST API. The check is not perfor icon:check[] Core: When a project was deleted, its associated version purge jobs were not. This has been fixed. -icon:check[] Rest: New OpenAPI secure endpoints `/openapi.yaml` and `/openapi.json` have been added. - [[v1.8.6]] == 1.8.6 (06.07.2022) diff --git a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement index 8521aaf32f..d1c302ac35 100644 --- a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement +++ b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement @@ -1,2 +1,2 @@ REST API: Along with existing RAML API definition, it is now possible to generate an OpenAPI (aka Swagger) specification for the Mesh REST API, which can optionally include the API for all the currently running Mesh Plugins. - For that, three new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`v3.0` or `v3.1`) query parameters, and two v3.0 shortcuts, `/openapi.yaml` and `openapi.json`. \ No newline at end of file + For that, three new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`3.0` or `3.1`) query parameters, and two v3.0 shortcuts, `/openapi.yaml` and `openapi.json`. \ No newline at end of file diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java similarity index 91% rename from core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java rename to core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java index db74e02199..cb012d4e41 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIRuntimeGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java @@ -28,29 +28,29 @@ * @author plyhun * */ -public class OpenAPIRuntimeGenerator extends AbstractEndpointGenerator { +public class OpenAPIMockAPIGenerator extends AbstractEndpointGenerator { - private static final Logger log = LoggerFactory.getLogger(OpenAPIRuntimeGenerator.class); + private static final Logger log = LoggerFactory.getLogger(OpenAPIMockAPIGenerator.class); private final MeshOpenAPIv3Generator generator; private final Map routers = new HashMap<>(); private final String fileName; - public OpenAPIRuntimeGenerator() { + public OpenAPIMockAPIGenerator() { super(); this.fileName = null; this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), Optional.empty(), Optional.empty()); } - public OpenAPIRuntimeGenerator(File outputFolder, String fileName, boolean cleanup) throws IOException { + public OpenAPIMockAPIGenerator(File outputFolder, String fileName, boolean cleanup) throws IOException { super(new File(outputFolder, "api"), false); this.fileName = fileName; this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), Optional.empty(), Optional.empty()); } - public OpenAPIRuntimeGenerator(File outputFolder, String fileName) throws IOException { + public OpenAPIMockAPIGenerator(File outputFolder, String fileName) throws IOException { super(new File(outputFolder, "api"), false); this.fileName = fileName; this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), diff --git a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java index 3ea9b4a65c..2162a4be67 100644 --- a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java +++ b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java @@ -50,7 +50,7 @@ public static void main(String[] args) throws Exception { ramlGenerator.run(); // Generate OpenAPI base spec - OpenAPIRuntimeGenerator openApiGenerator = new OpenAPIRuntimeGenerator(OUTPUT_ROOT_FOLDER, "openapi.yaml", false); + OpenAPIMockAPIGenerator openApiGenerator = new OpenAPIMockAPIGenerator(OUTPUT_ROOT_FOLDER, "openapi.yaml", false); openApiGenerator.run(); // Generate elasticsearch flattened models From 49dda94b5e57d924b033a5ffc66428a5fb3ee28e Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 3 Mar 2026 17:30:22 +0100 Subject: [PATCH 34/75] Generated client fixes --- .../RolePermissionHandlingEndpoint.java | 34 +++++++------- ...RolePermissionHandlingProjectEndpoint.java | 40 ++++++++++++----- .../endpoint/tagfamily/TagFamilyEndpoint.java | 44 ++++++++++++++----- .../test/openapi/OpenAPIMeshRestClient.java | 39 ++++++++-------- .../statichandler/StaticHandlerUtilsTest.java | 4 +- 5 files changed, 98 insertions(+), 63 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java index 015d6b3318..a4d63afed2 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingEndpoint.java @@ -66,23 +66,6 @@ protected void addRolePermissionHandler(String uuidParameterName, String uuidPar crudHandler.handleGrantPermissions(ac, uuid); }, isOrderedBlockingHandlers()); - InternalEndpointRoute revokePermissionsEndpoint = createRoute(); - revokePermissionsEndpoint.path(path); - revokePermissionsEndpoint.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); - revokePermissionsEndpoint.method(DELETE); - revokePermissionsEndpoint.description("Revoke permissions on the " + typeDescription + " from multiple roles."); - revokePermissionsEndpoint.consumes(APPLICATION_JSON); - revokePermissionsEndpoint.produces(APPLICATION_JSON); - revokePermissionsEndpoint.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); - revokePermissionsEndpoint.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); - revokePermissionsEndpoint.events(ROLE_PERMISSIONS_CHANGED); - revokePermissionsEndpoint.setHidden(true); - revokePermissionsEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = rc.request().getParam(uuidParameterName); - crudHandler.handleRevokePermissions(ac, uuid); - }, isOrderedBlockingHandlers()); - InternalEndpointRoute revokePermissionsEndpointStandard = createRoute(); revokePermissionsEndpointStandard.path(path); revokePermissionsEndpointStandard.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); @@ -98,5 +81,22 @@ protected void addRolePermissionHandler(String uuidParameterName, String uuidPar String uuid = rc.request().getParam(uuidParameterName); crudHandler.handleRevokePermissions(ac, uuid); }, isOrderedBlockingHandlers()); + + InternalEndpointRoute revokePermissionsEndpointNonStandard = createRoute(); + revokePermissionsEndpointNonStandard.path(path); + revokePermissionsEndpointNonStandard.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); + revokePermissionsEndpointNonStandard.method(DELETE); + revokePermissionsEndpointNonStandard.description("Revoke permissions on the " + typeDescription + " from multiple roles."); + revokePermissionsEndpointNonStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); + revokePermissionsEndpointNonStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); + revokePermissionsEndpointNonStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointNonStandard.setHidden(true); + revokePermissionsEndpointNonStandard.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = rc.request().getParam(uuidParameterName); + crudHandler.handleRevokePermissions(ac, uuid); + }, isOrderedBlockingHandlers()); } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingProjectEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingProjectEndpoint.java index 7f0205317c..4ba89bc66f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingProjectEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/RolePermissionHandlingProjectEndpoint.java @@ -6,6 +6,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import com.gentics.mesh.auth.MeshAuthChain; import com.gentics.mesh.cli.BootstrapInitializer; @@ -67,17 +68,34 @@ protected void addRolePermissionHandler(String uuidParameterName, String uuidPar crudHandler.handleGrantPermissions(ac, uuid); }, isOrderedBlockingHandlers()); - InternalEndpointRoute revokePermissionsEndpoint = createRoute(); - revokePermissionsEndpoint.path(path); - revokePermissionsEndpoint.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); - revokePermissionsEndpoint.method(DELETE); - revokePermissionsEndpoint.description("Revoke permissions on the " + typeDescription + " from multiple roles."); - revokePermissionsEndpoint.consumes(APPLICATION_JSON); - revokePermissionsEndpoint.produces(APPLICATION_JSON); - revokePermissionsEndpoint.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); - revokePermissionsEndpoint.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); - revokePermissionsEndpoint.events(ROLE_PERMISSIONS_CHANGED); - revokePermissionsEndpoint.blockingHandler(rc -> { + InternalEndpointRoute revokePermissionsEndpointNonStandard = createRoute(); + revokePermissionsEndpointNonStandard.path(path); + revokePermissionsEndpointNonStandard.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); + revokePermissionsEndpointNonStandard.method(DELETE); + revokePermissionsEndpointNonStandard.setHidden(true); + revokePermissionsEndpointNonStandard.description("Revoke permissions on the " + typeDescription + " from multiple roles."); + revokePermissionsEndpointNonStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); + revokePermissionsEndpointNonStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); + revokePermissionsEndpointNonStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointNonStandard.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = rc.request().getParam(uuidParameterName); + crudHandler.handleRevokePermissions(ac, uuid); + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute revokePermissionsEndpointStandard = createRoute(); + revokePermissionsEndpointStandard.path(path); + revokePermissionsEndpointStandard.addUriParameter(uuidParameterName, "Uuid of the " + typeDescription, uuidParameterExample); + revokePermissionsEndpointStandard.method(PUT); + revokePermissionsEndpointStandard.description("Revoke permissions on the " + typeDescription + " from multiple roles."); + revokePermissionsEndpointStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(includePublishPermissions)); + revokePermissionsEndpointStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(includePublishPermissions), "Updated permissions."); + revokePermissionsEndpointStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointStandard.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = rc.request().getParam(uuidParameterName); crudHandler.handleRevokePermissions(ac, uuid); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index f2756a380b..ccd3ca94b5 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -17,6 +17,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -215,18 +216,37 @@ private void addTagRolePermissionHandler() { tagCrudHandler.handleGrantPermissions(ac, tagFamilyUuid, uuid); }, isOrderedBlockingHandlers()); - InternalEndpointRoute revokePermissionsEndpoint = createRoute(); - revokePermissionsEndpoint.path("/:tagFamilyUuid/tags/:tagUuid/rolePermissions"); - revokePermissionsEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - revokePermissionsEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); - revokePermissionsEndpoint.method(DELETE); - revokePermissionsEndpoint.description("Revoke permissions on the tag from multiple roles."); - revokePermissionsEndpoint.consumes(APPLICATION_JSON); - revokePermissionsEndpoint.produces(APPLICATION_JSON); - revokePermissionsEndpoint.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(false)); - revokePermissionsEndpoint.exampleResponse(OK, roleExamples.getObjectPermissionResponse(false), "Updated permissions."); - revokePermissionsEndpoint.events(ROLE_PERMISSIONS_CHANGED); - revokePermissionsEndpoint.blockingHandler(rc -> { + InternalEndpointRoute revokePermissionsEndpointNonStandard = createRoute(); + revokePermissionsEndpointNonStandard.path("/:tagFamilyUuid/tags/:tagUuid/rolePermissions"); + revokePermissionsEndpointNonStandard.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + revokePermissionsEndpointNonStandard.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + revokePermissionsEndpointNonStandard.method(DELETE); + revokePermissionsEndpointNonStandard.description("Revoke permissions on the tag from multiple roles."); + revokePermissionsEndpointNonStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointNonStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(false)); + revokePermissionsEndpointNonStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(false), "Updated permissions."); + revokePermissionsEndpointNonStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointNonStandard.setHidden(true); + revokePermissionsEndpointNonStandard.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); + String uuid = PathParameters.getTagUuid(rc); + tagCrudHandler.handleRevokePermissions(ac, tagFamilyUuid, uuid); + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute revokePermissionsEndpointStandard = createRoute(); + revokePermissionsEndpointStandard.path("/:tagFamilyUuid/tags/:tagUuid/rolePermissions"); + revokePermissionsEndpointStandard.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + revokePermissionsEndpointStandard.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + revokePermissionsEndpointStandard.method(PUT); + revokePermissionsEndpointStandard.description("Revoke permissions on the tag from multiple roles."); + revokePermissionsEndpointStandard.consumes(APPLICATION_JSON); + revokePermissionsEndpointStandard.produces(APPLICATION_JSON); + revokePermissionsEndpointStandard.exampleRequest(roleExamples.getObjectPermissionRevokeRequest(false)); + revokePermissionsEndpointStandard.exampleResponse(OK, roleExamples.getObjectPermissionResponse(false), "Updated permissions."); + revokePermissionsEndpointStandard.events(ROLE_PERMISSIONS_CHANGED); + revokePermissionsEndpointStandard.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); String uuid = PathParameters.getTagUuid(rc); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 5eaf18fc8b..eb2eec2e3a 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -109,6 +109,7 @@ import com.gentics.mesh.parameter.ProjectLoadParameters; import com.gentics.mesh.parameter.PublishParameters; import com.gentics.mesh.parameter.RolePermissionParameters; +import com.gentics.mesh.parameter.SortingParameters; import com.gentics.mesh.parameter.VersioningParameters; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; @@ -369,75 +370,71 @@ public MeshRequest getNodeRolePermissions(String proje @Override public MeshRequest grantNodeRolePermissions(String projectName, String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPostWithHttpInfo(uuid, projectName, org.openapitools.client.model.ObjectPermissionGrantRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); } @Override public MeshRequest revokeNodeRolePermissions(String projectName, String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPutWithHttpInfo(uuid, projectName, org.openapitools.client.model.ObjectPermissionRevokeRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); } @Override public MeshRequest createTag(String projectName, String tagFamilyUuid, TagCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, org.openapitools.client.model.TagCreateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); } @Override public MeshRequest findTagByUuid(String projectName, String tagFamilyUuid, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidGetWithHttpInfo(tagFamilyUuid, uuid, projectName, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), TagResponse.class); } @Override public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, org.openapitools.client.model.TagUpdateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); } @Override public MeshRequest createTag(String projectName, String tagFamilyUuid, String uuid, TagCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, org.openapitools.client.model.TagCreateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); } @Override public MeshRequest deleteTag(String projectName, String tagFamilyUuid, String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidDeleteWithHttpInfo(tagFamilyUuid, uuid, projectName), EmptyResponse.class); } @Override public MeshRequest findTags(String projectName, String tagFamilyUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsGetWithHttpInfo(tagFamilyUuid, projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), TagListResponse.class); } @Override public MeshRequest getTagRolePermissions(String projectName, String tagFamilyUuid, String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissionsGetWithHttpInfo(tagFamilyUuid, uuid, projectName), ObjectPermissionResponse.class); } @Override public MeshRequest grantTagRolePermissions(String projectName, String tagFamilyUuid, String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissionsPostWithHttpInfo(tagFamilyUuid, uuid, projectName, org.openapitools.client.model.ObjectPermissionGrantRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); } @Override public MeshRequest revokeTagRolePermissions(String projectName, String tagFamilyUuid, String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub + // api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissions return null; } diff --git a/tests/tests-plugin-api/src/main/java/com/gentics/mesh/plugin/statichandler/StaticHandlerUtilsTest.java b/tests/tests-plugin-api/src/main/java/com/gentics/mesh/plugin/statichandler/StaticHandlerUtilsTest.java index 8959fd5343..e66398cb21 100644 --- a/tests/tests-plugin-api/src/main/java/com/gentics/mesh/plugin/statichandler/StaticHandlerUtilsTest.java +++ b/tests/tests-plugin-api/src/main/java/com/gentics/mesh/plugin/statichandler/StaticHandlerUtilsTest.java @@ -64,7 +64,7 @@ public void testFolderRequestReturns404() throws Exception { Handler handler = handlerCaptor.getAllValues().get(0); //get the prehandler RoutingContext rc = mock(RoutingContext.class); - when(rc.normalisedPath()).thenReturn("/"); + when(rc.normalizedPath()).thenReturn("/"); CountDownLatch latch = new CountDownLatch(1); doAnswer(inv -> { @@ -96,7 +96,7 @@ public void testFileRequestCallsNext() throws Exception { RoutingContext rc = mock(RoutingContext.class); when(rc.currentRoute()).thenReturn(currentRoute); when(rc.mountPoint()).thenReturn(null); - when(rc.normalisedPath()).thenReturn("/static/StaticHandlerUtilsTest.class"); + when(rc.normalizedPath()).thenReturn("/static/StaticHandlerUtilsTest.class"); CountDownLatch nextLatch = new CountDownLatch(1); doAnswer(inv -> { From da23a957f915513064b76b4e9c33d27a541ccee0 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 4 Mar 2026 12:38:35 +0100 Subject: [PATCH 35/75] More OpenAPI test client Impls and API definition fixes --- .../endpoint/project/ProjectEndpoint.java | 22 ++++- .../endpoint/tagfamily/TagFamilyEndpoint.java | 40 +++++--- .../mesh/rest/MeshLocalClientImpl.java | 10 +- .../parameter/ParameterProviderContext.java | 5 + .../parameter/impl/EtagParametersImpl.java | 43 +++++++++ .../parameter/impl/GenericParametersImpl.java | 12 +-- .../verticle/handler/HandlerUtilities.java | 2 +- .../parameter/client/EtagParametersImpl.java | 10 ++ .../client/GenericParametersImpl.java | 2 +- .../client/impl/MeshRestHttpClientImpl.java | 8 +- .../client/method/ProjectClientMethods.java | 4 +- .../mesh/parameter/EtagParameters.java | 36 +++++++ .../mesh/parameter/GenericParameters.java | 29 +----- tests/tests-core/pom.xml | 4 +- .../test/openapi/OpenAPIMeshRestClient.java | 93 +++++++++---------- 15 files changed, 204 insertions(+), 116 deletions(-) create mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/EtagParametersImpl.java create mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/EtagParametersImpl.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/EtagParameters.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java index a4cc6f5deb..629e05de3d 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java @@ -13,6 +13,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -70,9 +71,9 @@ private void addUpdateHandler() { InternalEndpointRoute updateEndpoint = createRoute(); updateEndpoint.path("/:projectUuid"); updateEndpoint - .description("Update the project with the given uuid. The project is created if no project with the specified uuid could be found."); + .description("Update the project with the given uuid."); updateEndpoint.addUriParameter("projectUuid", "Uuid of the project.", PROJECT_DEMO_UUID); - updateEndpoint.method(POST); + updateEndpoint.method(PUT); updateEndpoint.consumes(APPLICATION_JSON); updateEndpoint.produces(APPLICATION_JSON); updateEndpoint.exampleRequest(projectExamples.getProjectUpdateRequest("New project name")); @@ -83,6 +84,23 @@ private void addUpdateHandler() { String uuid = ac.getParameter("projectUuid"); crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); + + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:projectUuid"); + upsertEndpoint + .description("Update the project with the given uuid. The project is created if no project with the specified uuid could be found."); + upsertEndpoint.addUriParameter("projectUuid", "Uuid of the project.", PROJECT_DEMO_UUID); + upsertEndpoint.method(POST); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.exampleRequest(projectExamples.getProjectCreateRequest("New project name")); + upsertEndpoint.exampleResponse(OK, projectExamples.getProjectResponse("New project name"), "Updated project."); + upsertEndpoint.events(PROJECT_UPDATED); + upsertEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("projectUuid"); + crudHandler.handleUpdate(ac, uuid); + }, isOrderedBlockingHandlers()); } // TODO when the root tag is not saved the project can't be saved. Unfortunately this did not show up as an http error. We must handle those diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index ccd3ca94b5..2aa1854b1f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -34,6 +34,7 @@ import com.gentics.mesh.core.endpoint.tag.TagCrudHandler; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.BranchParametersImpl; +import com.gentics.mesh.parameter.impl.EtagParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -294,6 +295,7 @@ private void addTagFamilyReadHandler() { readOne.path("/:tagFamilyUuid"); readOne.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); readOne.method(GET); + readOne.addQueryParameters(EtagParametersImpl.class); readOne.description("Read the tag family with the given uuid."); readOne.produces(APPLICATION_JSON); readOne.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Colors"), "Loaded tag family."); @@ -333,17 +335,33 @@ private void addTagFamilyCreateHandler() { } private void addTagFamilyUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:tagFamilyUuid"); - endpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - endpoint.method(POST); - endpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.exampleRequest(tagFamilyExamples.getTagFamilyUpdateRequest("Nicer colors")); - endpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated tag family."); - endpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:tagFamilyUuid"); + updateEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + updateEndpoint.method(PUT); + updateEndpoint.description("Update the tag family with the given uuid."); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyUpdateRequest("Nicer colors")); + updateEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated tag family."); + updateEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); + updateEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:tagFamilyUuid"); + upsertEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + upsertEndpoint.method(POST); + upsertEndpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyCreateRequest("Nicer colors")); + upsertEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or created tag family."); + upsertEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); + upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index c7e3d0ca53..8cd7d671e7 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -470,15 +470,13 @@ public MeshRequest findProjects(ParameterProvider... parame @Override @Deprecated - public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid) { - return assignLanguageToProjectByUuid(projectUuid, languageUuid); + public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return assignLanguageToProjectByUuid(projectUuid, languageUuid, parameters); } @Override - public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid) { - LocalActionContextImpl ac = createContext(ProjectResponse.class); - // TODO add implementation - return new MeshLocalRequestImpl<>(ac.getFuture()); + public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return unassignLanguageFromProjectByUuid(projectUuid, languageUuid, parameters); } @Override diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index 059667acb2..70a7e5236c 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -6,6 +6,7 @@ import com.gentics.mesh.parameter.impl.ConsistencyCheckParametersImpl; import com.gentics.mesh.parameter.impl.DeleteParametersImpl; import com.gentics.mesh.parameter.impl.DisplayParametersImpl; +import com.gentics.mesh.parameter.impl.EtagParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.ImageManipulationParametersImpl; import com.gentics.mesh.parameter.impl.ImageManipulationRetrievalParametersImpl; @@ -81,6 +82,10 @@ default GenericParameters getGenericParameters() { return new GenericParametersImpl(this); } + default EtagParameters getEtagParameters() { + return new EtagParametersImpl(this); + } + default ImageManipulationRetrievalParameters getImageManipulationRetrievalParameters() { return new ImageManipulationRetrievalParametersImpl(this); } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/EtagParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/EtagParametersImpl.java new file mode 100644 index 0000000000..c84c4552b8 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/EtagParametersImpl.java @@ -0,0 +1,43 @@ +package com.gentics.mesh.parameter.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.raml.model.ParamType; +import org.raml.model.parameter.QueryParameter; + +import com.gentics.mesh.handler.ActionContext; +import com.gentics.mesh.parameter.AbstractParameters; +import com.gentics.mesh.parameter.EtagParameters; + +/** + * @see EtagParameters + */ +public class EtagParametersImpl extends AbstractParameters implements EtagParameters { + + public EtagParametersImpl(ActionContext ac) { + super(ac); + } + + public EtagParametersImpl() { + } + + @Override + public String getName() { + return "ETAG REST query parameters"; + } + + @Override + public Map getRAMLParameters() { + Map parameters = new HashMap<>(); + + QueryParameter etagParam = new QueryParameter(); + etagParam.setDescription( + "Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed."); + etagParam.setType(ParamType.BOOLEAN); + etagParam.setDefaultValue("true"); + parameters.put(ETAG_PARAM_KEY, etagParam); + + return parameters; + } +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java index 0668a261ca..80bf255abb 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/GenericParametersImpl.java @@ -7,13 +7,12 @@ import org.raml.model.parameter.QueryParameter; import com.gentics.mesh.handler.ActionContext; -import com.gentics.mesh.parameter.AbstractParameters; import com.gentics.mesh.parameter.GenericParameters; /** * @see GenericParameters */ -public class GenericParametersImpl extends AbstractParameters implements GenericParameters { +public class GenericParametersImpl extends EtagParametersImpl implements GenericParameters { public GenericParametersImpl(ActionContext ac) { super(ac); @@ -35,7 +34,7 @@ public String getName() { @Override public Map getRAMLParameters() { - Map parameters = new HashMap<>(); + Map parameters = new HashMap<>(super.getRAMLParameters()); QueryParameter fieldsParam = new QueryParameter(); fieldsParam.setDescription("Limit the output to certain fields. This is useful in order to reduce the response JSON overhead."); @@ -44,13 +43,6 @@ public String getName() { fieldsParam.setExample("uuid,name"); parameters.put(FIELDS_PARAM_KEY, fieldsParam); - QueryParameter etagParam = new QueryParameter(); - etagParam.setDescription( - "Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed."); - etagParam.setType(ParamType.BOOLEAN); - etagParam.setDefaultValue("true"); - parameters.put(ETAG_PARAM_KEY, etagParam); - return parameters; } diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java index 096d62e05f..e58393a863 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java @@ -275,7 +275,7 @@ public , RM extends RestModel> void readElement(Int T element = actions.loadByUuid(context(tx, ac, parent), uuid, perm, true); // Handle etag - if (ac.getGenericParameters().getETag()) { + if (ac.getEtagParameters().getETag()) { String etag = actions.getETag(tx, ac, element); ac.setEtag(etag, true); if (ac.matches(etag, true)) { diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/EtagParametersImpl.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/EtagParametersImpl.java new file mode 100644 index 0000000000..73d6d6f456 --- /dev/null +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/EtagParametersImpl.java @@ -0,0 +1,10 @@ +package com.gentics.mesh.parameter.client; + +import com.gentics.mesh.parameter.EtagParameters; + +/** + * @see EtagParameters + */ +public class EtagParametersImpl extends AbstractParameters implements EtagParameters { + +} diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/GenericParametersImpl.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/GenericParametersImpl.java index 86226f30a2..43a290e512 100644 --- a/rest-client/src/main/java/com/gentics/mesh/parameter/client/GenericParametersImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/GenericParametersImpl.java @@ -5,6 +5,6 @@ /** * @see GenericParameters */ -public class GenericParametersImpl extends AbstractParameters implements GenericParameters { +public class GenericParametersImpl extends EtagParametersImpl implements GenericParameters { } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index d77b0b63c3..e7590f1401 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -414,13 +414,13 @@ public MeshRequest findProjects(ParameterProvider... parame } @Override - public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid) { - return assignLanguageToProjectByUuid(projectUuid, languageUuid); + public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return assignLanguageToProjectByUuid(projectUuid, languageUuid, parameters); } @Override - public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid) { - return unassignLanguageFromProjectByUuid(projectUuid, languageUuid); + public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return unassignLanguageFromProjectByUuid(projectUuid, languageUuid, parameters); } @Override diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java index 9277f4f9a2..fcb36c7c64 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java @@ -51,7 +51,7 @@ public interface ProjectClientMethods { * @param languageUuid * @return */ - MeshRequest assignLanguageToProject(String projectUuid, String languageUuid); + MeshRequest assignLanguageToProject(String projectUuid, String languageUuid, ParameterProvider... parameters); /** * Unassign the given language from the project. @@ -60,7 +60,7 @@ public interface ProjectClientMethods { * @param languageUuid * @return */ - MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid); + MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid, ParameterProvider... parameters); /** * Create a new project. diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/EtagParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/EtagParameters.java new file mode 100644 index 0000000000..b73c457940 --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/EtagParameters.java @@ -0,0 +1,36 @@ +package com.gentics.mesh.parameter; + +/** + * ETAG parameters + */ +public interface EtagParameters extends ParameterProvider { + + /** + * Query parameter key: {@value #ETAG_PARAM_KEY} + */ + public static final String ETAG_PARAM_KEY = "etag"; + + /** + * Return whether the etag should be omitted or included. + * + * @return + */ + default boolean getETag() { + String value = getParameter(ETAG_PARAM_KEY); + if (value != null) { + return Boolean.valueOf(value); + } else { + return true; + } + } + + /** + * Set the etag inclusion flag. + * + * @param includeEtag + */ + default EtagParameters setETag(boolean includeEtag) { + setParameter(ETAG_PARAM_KEY, String.valueOf(includeEtag)); + return this; + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/GenericParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/GenericParameters.java index a7ca8afa60..a926091f17 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/GenericParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/GenericParameters.java @@ -6,18 +6,13 @@ /** * Interface for generic query parameters. */ -public interface GenericParameters extends ParameterProvider { +public interface GenericParameters extends EtagParameters { /** * Query parameter key: {@value #FIELDS_PARAM_KEY} */ public static final String FIELDS_PARAM_KEY = "fields"; - /** - * Query parameter key: {@value #ETAG_PARAM_KEY} - */ - public static final String ETAG_PARAM_KEY = "etag"; - /** * Return the fields which should be included in the response. * @@ -43,27 +38,9 @@ default GenericParameters setFields(String... fields) { return this; } - /** - * Return whether the etag should be omitted or included. - * - * @return - */ - default boolean getETag() { - String value = getParameter(ETAG_PARAM_KEY); - if (value != null) { - return Boolean.valueOf(value); - } else { - return true; - } - } - - /** - * Set the etag inclusion flag. - * - * @param includeEtag - */ + @Override default GenericParameters setETag(boolean includeEtag) { - setParameter(ETAG_PARAM_KEY, String.valueOf(includeEtag)); + EtagParameters.super.setETag(includeEtag); return this; } } diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index 493ae78ba4..cf04d594de 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -177,9 +177,7 @@ false true false - - JsonObject=com.google.gson.JsonObject - + JsonObject=com.google.gson.JsonObject true true diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index eb2eec2e3a..01706d55c2 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -100,6 +100,7 @@ import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.BranchParameters; import com.gentics.mesh.parameter.DeleteParameters; +import com.gentics.mesh.parameter.EtagParameters; import com.gentics.mesh.parameter.GenericParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.JobParameters; @@ -145,7 +146,7 @@ public class OpenAPIMeshRestClient implements MeshRestClient { * @param parameters * @return */ - private static final T findParameter(String key, ParameterProvider... parameters) { + protected static final T findParameter(String key, ParameterProvider... parameters) { return (T) Arrays.stream(parameters).map(p -> p.getParameter(key)).findAny().orElse(null); } @@ -157,7 +158,7 @@ private static final T findParameter(String key, ParameterProvider... parame * @param r * @return */ - private static O adaptRequest(R r) { + protected static O adaptRequest(R r) { if (r == null) { return null; } @@ -370,36 +371,38 @@ public MeshRequest getNodeRolePermissions(String proje @Override public MeshRequest grantNodeRolePermissions(String projectName, String uuid, ObjectPermissionGrantRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPostWithHttpInfo(uuid, projectName, org.openapitools.client.model.ObjectPermissionGrantRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPostWithHttpInfo(uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeNodeRolePermissions(String projectName, String uuid, ObjectPermissionRevokeRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPutWithHttpInfo(uuid, projectName, org.openapitools.client.model.ObjectPermissionRevokeRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidRolePermissionsPutWithHttpInfo(uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest createTag(String projectName, String tagFamilyUuid, TagCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, org.openapitools.client.model.TagCreateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, adaptRequest(request)), TagResponse.class); } @Override public MeshRequest findTagByUuid(String projectName, String tagFamilyUuid, String uuid, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidGetWithHttpInfo(tagFamilyUuid, uuid, projectName, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), TagResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidGetWithHttpInfo(tagFamilyUuid, uuid, projectName, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), TagResponse.class); } @Override public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, org.openapitools.client.model.TagUpdateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, adaptRequest(request)), TagResponse.class); } @Override public MeshRequest createTag(String projectName, String tagFamilyUuid, String uuid, TagCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, org.openapitools.client.model.TagCreateRequest.fromJson(JsonUtil.toJson(request))), TagResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsPostWithHttpInfo(tagFamilyUuid, projectName, adaptRequest(request)), TagResponse.class); } @Override @@ -428,126 +431,116 @@ public MeshRequest getTagRolePermissions(String projec @Override public MeshRequest grantTagRolePermissions(String projectName, String tagFamilyUuid, String uuid, ObjectPermissionGrantRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissionsPostWithHttpInfo(tagFamilyUuid, uuid, projectName, org.openapitools.client.model.ObjectPermissionGrantRequest.fromJson(JsonUtil.toJson(request))), ObjectPermissionResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissionsPostWithHttpInfo(tagFamilyUuid, uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeTagRolePermissions(String projectName, String tagFamilyUuid, String uuid, ObjectPermissionRevokeRequest request) { - // api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissions - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidRolePermissionsPutWithHttpInfo(tagFamilyUuid, uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest findProjectByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidGetWithHttpInfo(uuid, findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters)), ProjectResponse.class); } @Override public MeshRequest findProjectByName(String name, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectGetWithHttpInfo(name), ProjectResponse.class); } @Override public MeshRequest findProjects(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsGetWithHttpInfo( + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), ProjectListResponse.class); } @Override - public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid) { - // TODO Auto-generated method stub - return null; + public MeshRequest assignLanguageToProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectLanguagesLanguageUuidPostWithHttpInfo(languageUuid, projectUuid, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), ProjectResponse.class); } @Override - public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid) { - // TODO Auto-generated method stub - return null; + public MeshRequest unassignLanguageFromProject(String projectUuid, String languageUuid, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectLanguagesLanguageUuidDeleteWithHttpInfo(languageUuid, projectUuid, findParameter(ProjectLoadParameters.LANGS_QUERY_PARAM_KEY, parameters)), ProjectResponse.class); } @Override public MeshRequest createProject(ProjectCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsPostWithHttpInfo(adaptRequest(request)), ProjectResponse.class); } @Override public MeshRequest createProject(String uuid, ProjectCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPutWithHttpInfo(uuid, adaptRequest(request)), ProjectResponse.class); } @Override public MeshRequest updateProject(String uuid, ProjectUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, adaptRequest(request)), ProjectResponse.class); } @Override public MeshRequest deleteProject(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest purgeProject(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidDeleteWithHttpInfo(uuid), GenericMessageResponse.class); } @Override public MeshRequest getProjectRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidRolePermissionsGetWithHttpInfo(uuid), ObjectPermissionResponse.class); } @Override public MeshRequest grantProjectRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionGrantRequest.class); } @Override public MeshRequest revokeProjectRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionGrantRequest.class); } @Override public MeshRequest findTagFamilyByUuid(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidGetWithHttpInfo(uuid, projectName, findParameter(EtagParameters.ETAG_PARAM_KEY, parameters)), TagFamilyResponse.class); } @Override - public MeshRequest findTagFamilies(String projectName, PagingParameters pagingInfo) { - // TODO Auto-generated method stub - return null; + public MeshRequest findTagFamilies(String projectName, PagingParameters parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesGetWithHttpInfo(projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), TagFamilyListResponse.class); } @Override public MeshRequest createTagFamily(String projectName, TagFamilyCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesPostWithHttpInfo(projectName, adaptRequest(request)), TagFamilyResponse.class); } @Override public MeshRequest deleteTagFamily(String projectName, String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidDeleteWithHttpInfo(uuid, projectName), EmptyResponse.class); } @Override public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); } @Override From eb2d4b838ddf8c44b0535239e0b89376127a46e2 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 4 Mar 2026 16:11:45 +0100 Subject: [PATCH 36/75] Give each plugin an own OpenAPI --- .../com/gentics/mesh/etc/config}/Format.java | 23 ++++- .../gentics/mesh/etc/config/MeshOptions.java | 38 +++++---- .../mesh/etc/config/OpenAPIOptions.java | 82 ++++++++++++++++++ .../com/gentics/mesh/etc/config}/Version.java | 38 ++++++++- .../entries/2026/03/8685.GPU-2196.enhancement | 3 +- .../route/AbstractInternalEndpoint.java | 4 + .../core/endpoint/admin/AdminHandler.java | 62 +++----------- .../core/endpoint/admin/RestInfoEndpoint.java | 27 ++---- .../generator/AbstractEndpointGenerator.java | 5 ++ .../plugin/registry/RestPluginRegistry.java | 83 ++++++++++++++++--- .../mesh/rest/MeshLocalClientImpl.java | 4 +- .../mesh/util/MeshOpenAPIv3Generator.java | 18 +++- .../parameter/impl/OpenAPIParametersImpl.java | 4 +- .../dagger/AdminEndpointProviderModule.java | 6 +- .../com/gentics/mesh/dagger/UtilModule.java | 57 +++++++++++++ .../mesh/endpoint/admin/HibAdminHandler.java | 6 +- .../client/impl/MeshRestHttpClientImpl.java | 4 +- .../client/method/ApiInfoClientMethods.java | 4 +- .../mesh/parameter/OpenAPIParameters.java | 4 +- .../core/openapi/OpenAPIEndpointTest.java | 4 +- .../test/openapi/OpenAPIMeshRestClient.java | 4 +- 21 files changed, 354 insertions(+), 126 deletions(-) rename {rest-model/src/main/java/com/gentics/mesh/core/rest/openapi => api/src/main/java/com/gentics/mesh/etc/config}/Format.java (59%) create mode 100644 api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java rename {rest-model/src/main/java/com/gentics/mesh/core/rest/openapi => api/src/main/java/com/gentics/mesh/etc/config}/Version.java (50%) diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java b/api/src/main/java/com/gentics/mesh/etc/config/Format.java similarity index 59% rename from rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java rename to api/src/main/java/com/gentics/mesh/etc/config/Format.java index 5bfa4f288c..1733c377a5 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Format.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/Format.java @@ -1,6 +1,6 @@ -package com.gentics.mesh.core.rest.openapi; +package com.gentics.mesh.etc.config; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; /** * OpenAPI output format @@ -9,11 +9,26 @@ public enum Format { /** * Yaml */ - YAML, + YAML(0), /** * JSON */ - JSON; + JSON(1); + + private final int level; + + private Format(int level) { + this.level = level; + } + + /** + * Get the filtering int value. + * + * @return + */ + public int getLevel() { + return level; + } /** * Safe parse string value diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index 7f5d98ef87..6207366b67 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -31,7 +31,6 @@ public abstract class MeshOptions implements Option { public static final String MESH_UPDATECHECK_ENV = "MESH_UPDATECHECK"; public static final String MESH_TEMP_DIR_ENV = "MESH_TEMP_DIR"; public static final String MESH_PLUGIN_DIR_ENV = "MESH_PLUGIN_DIR"; - public static final String MESH_OPENAPI_EXCLUDE_PLUGINS_ENV = "MESH_OPENAPI_EXCLUDE_PLUGINS"; public static final String MESH_PLUGIN_USE_HTTP2_ENV = "MESH_PLUGIN_USE_HTTP2"; public static final String MESH_PLUGIN_TIMEOUT_ENV = "MESH_PLUGIN_TIMEOUT"; public static final String MESH_NODE_NAME_ENV = "MESH_NODE_NAME"; @@ -153,6 +152,10 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_MIGRATION_TRIGGER_INTERVAL, description = "Override the migration trigger interval") private long migrationTriggerInterval = DEFAULT_MIGRATION_TRIGGER_INTERVAL; + @JsonProperty(required = true) + @JsonPropertyDescription("OpenAPI options.") + private OpenAPIOptions openApiOptions = new OpenAPIOptions(); + @JsonProperty(required = true) @JsonPropertyDescription("GraphQL options.") private GraphQLOptions graphQLOptions = new GraphQLOptions(); @@ -162,11 +165,6 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_PLUGIN_USE_HTTP2_ENV, description = "Override the HTTP/2 usage flag for plugins.") private boolean pluginUseHttp2 = false; - @JsonProperty(required = false) - @JsonPropertyDescription("Should the endpoints of the included plugins be excluded from the OpenAPI specification generation requests. Default is true.") - @EnvironmentVariable(name = MESH_OPENAPI_EXCLUDE_PLUGINS_ENV, description = "Override the flag to exclude endpoints of the plugins from the OpenAPI specification generation requests") - private boolean openapiExcludePlugins = true; - /* EXTRA Command Line Arguments */ @JsonIgnore @EnvironmentVariable(name = MESH_CLUSTER_INIT_ENV, description = "Enable or disable the initial cluster database setup. This is useful for testing.") @@ -278,6 +276,24 @@ public MeshOptions setMonitoringOptions(MonitoringConfig monitoringOptions) { return this; } + /** + * Get the OpenAPI options + * @return options + */ + @JsonProperty("openApi") + public OpenAPIOptions getOpenAPIOptions() { + return openApiOptions; + } + + /** + * Set the OpenAPI options + * @param openApiOptions options + * @return fluent API + */ + public MeshOptions setOpenAPIOptions(OpenAPIOptions openApiOptions) { + this.openApiOptions = openApiOptions; + return this; + } /** * Get the graphql options @@ -550,16 +566,6 @@ public MeshOptions setPluginUseHttp2(boolean pluginUseHttp2) { return this; } - public boolean isOpenapiExcludePlugins() { - return openapiExcludePlugins; - } - - @Setter - public MeshOptions setOpenapiExcludePlugins(boolean openapiExcludePlugins) { - this.openapiExcludePlugins = openapiExcludePlugins; - return this; - } - @JsonIgnore public abstract NativeQueryFiltering getNativeQueryFiltering(); diff --git a/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java new file mode 100644 index 0000000000..8f15b97a3f --- /dev/null +++ b/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java @@ -0,0 +1,82 @@ +package com.gentics.mesh.etc.config; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyDescription; +import com.gentics.mesh.annotation.Setter; +import com.gentics.mesh.etc.config.env.EnvironmentVariable; +import com.gentics.mesh.etc.config.env.Option; + +/** + * OpenAPI related options + */ +public class OpenAPIOptions implements Option { + + public static final String MESH_OPENAPI_EXCLUDE_PLUGINS_ENV = "MESH_OPENAPI_EXCLUDE_PLUGINS"; + public static final String MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS_ENV = "MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS"; + public static final String MESH_OPENAPI_DEFAULT_VERSION_ENV = "MESH_OPENAPI_DEFAULT_VERSION"; + public static final String MESH_OPENAPI_DEFAULT_FORMAT_ENV = "MESH_OPENAPI_DEFAULT_FORMAT"; + + public static final String DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS = "ALL"; + public static final Version DEFAULT_MESH_OPENAPI_DEFAULT_VERSION = Version.V30; + public static final Format DEFAULT_MESH_OPENAPI_DEFAULT_FORMAT = Format.YAML; + + @JsonProperty(required = false) + @JsonPropertyDescription("Should the endpoints of the included plugins be excluded from the OpenAPI specification generation requests. Default is true.") + @EnvironmentVariable(name = MESH_OPENAPI_EXCLUDE_PLUGINS_ENV, description = "Override the flag to exclude endpoints of the plugins from the OpenAPI specification generation requests") + private boolean excludePlugins = true; + + @JsonProperty(required = false) + @JsonPropertyDescription("Should each of the running Mesh Plugin serve its own /openapi.yaml endpoint? Possible values: ALL, NONE, or comma-separated plugin IDs. Default is " + DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS) + @EnvironmentVariable(name = MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS_ENV, description = "Override the list of Mesh Plugins with own OpenAPI endpoints") + private String pluginsWithOwnEndpoints = DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS; + + @JsonProperty(required = false) + @JsonPropertyDescription("Default OpenAPI specification version. Default value: V30") + @EnvironmentVariable(name = MESH_OPENAPI_DEFAULT_VERSION_ENV, description = "Override the default OpenAPI specification version") + private Version defaultVersion = DEFAULT_MESH_OPENAPI_DEFAULT_VERSION; + + @JsonProperty(required = false) + @JsonPropertyDescription("Default OpenAPI specification format. Default value: YAML") + @EnvironmentVariable(name = MESH_OPENAPI_DEFAULT_FORMAT_ENV, description = "Override the default OpenAPI specification format") + private Format defaultFormat = DEFAULT_MESH_OPENAPI_DEFAULT_FORMAT; + + public boolean isExcludePlugins() { + return excludePlugins; + } + + @Setter + public OpenAPIOptions setExcludePlugins(boolean openapiExcludePlugins) { + this.excludePlugins = openapiExcludePlugins; + return this; + } + + public String getPluginsWithOwnEndpoints() { + return pluginsWithOwnEndpoints; + } + + @Setter + public OpenAPIOptions setPluginsWithOwnEndpoints(String openapiPluginsWithOwnEndpoints) { + this.pluginsWithOwnEndpoints = openapiPluginsWithOwnEndpoints; + return this; + } + + public Version getDefaultVersion() { + return defaultVersion; + } + + @Setter + public OpenAPIOptions setDefaultVersion(Version defaultVersion) { + this.defaultVersion = defaultVersion; + return this; + } + + public Format getDefaultFormat() { + return defaultFormat; + } + + @Setter + public OpenAPIOptions setDefaultFormat(Format defaultFormat) { + this.defaultFormat = defaultFormat; + return this; + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java b/api/src/main/java/com/gentics/mesh/etc/config/Version.java similarity index 50% rename from rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java rename to api/src/main/java/com/gentics/mesh/etc/config/Version.java index 9bb3d7156f..79cab7a007 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/openapi/Version.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/Version.java @@ -1,6 +1,6 @@ -package com.gentics.mesh.core.rest.openapi; +package com.gentics.mesh.etc.config; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; /** * OpenAPI specification version @@ -10,11 +10,26 @@ public enum Version { /** * v3.0 */ - V30, + V30(0), /** * v3.1 */ - V31; + V31(1); + + private final int level; + + private Version(int level) { + this.level = level; + } + + /** + * Get the filtering int value. + * + * @return + */ + public int getLevel() { + return level; + } /** * Safe parse string value @@ -36,4 +51,19 @@ public static Version parse(String version, Version defaultValue) { } return defaultValue; } + + /** + * Pretty print the version + * + * @return + */ + public String pretty() { + switch(this) { + case V30: + return "3.0"; + case V31: + return "3.1"; + } + return null; + } } diff --git a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement index d1c302ac35..78b6889035 100644 --- a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement +++ b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement @@ -1,2 +1,3 @@ REST API: Along with existing RAML API definition, it is now possible to generate an OpenAPI (aka Swagger) specification for the Mesh REST API, which can optionally include the API for all the currently running Mesh Plugins. - For that, three new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`3.0` or `3.1`) query parameters, and two v3.0 shortcuts, `/openapi.yaml` and `openapi.json`. \ No newline at end of file + For that, two new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`3.0` or `3.1`) query parameters, and a shortcut, `/openapi.yaml` or `openapi.json`, depending on the options. + Also, every Mesh Plugin can optionally have an own `/openapi.yaml` definition endpoint. \ No newline at end of file diff --git a/common/src/main/java/com/gentics/mesh/router/route/AbstractInternalEndpoint.java b/common/src/main/java/com/gentics/mesh/router/route/AbstractInternalEndpoint.java index 223224662c..8fa06a1937 100644 --- a/common/src/main/java/com/gentics/mesh/router/route/AbstractInternalEndpoint.java +++ b/common/src/main/java/com/gentics/mesh/router/route/AbstractInternalEndpoint.java @@ -141,4 +141,8 @@ protected boolean isOrderedBlockingHandlers() { return Optional.ofNullable(options).map(MeshOptions::getVertxOptions) .map(VertxOptions::isOrderedBlockingHandlers).orElse(true); } + + public MeshOptions getOptions() { + return options; + } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 66d7a53298..8a022a8a34 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -11,17 +11,10 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.UUID; import java.util.function.Function; -import java.util.function.Supplier; -import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; @@ -36,20 +29,18 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.endpoint.handler.AbstractHandler; import com.gentics.mesh.core.rest.MeshServerInfoModel; import com.gentics.mesh.core.rest.admin.cluster.coordinator.CoordinatorMasterResponse; import com.gentics.mesh.core.rest.admin.consistency.ConsistencyCheckResponse; import com.gentics.mesh.core.rest.admin.status.MeshStatusResponse; -import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; -import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.generator.RAMLGenerator; import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.parameter.BackupParameters; @@ -96,12 +87,12 @@ public abstract class AdminHandler extends AbstractHandler { protected final CacheRegistry cacheRegistry; - protected final ClusterManager clusterManager; + protected final MeshOpenAPIv3Generator generator; protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ClusterManager clusterManager) { + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, MeshOpenAPIv3Generator generator) { this.vertx = vertx; this.db = db; this.routerStorage = routerStorage; @@ -114,7 +105,7 @@ protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage this.writeLock = writeLock; this.consistencyCheckHandler = consistencyCheckHandler; this.cacheRegistry = cacheRegistry; - this.clusterManager = clusterManager; + this.generator = generator; } /** @@ -257,7 +248,7 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { * @param ac internal action context. */ public void handleOpenAPIv3(InternalActionContext ac) { - handleOpenAPIv3(ac, ac.getOpenAPIParameters().getFormat()); + handleOpenAPIv3(ac, ac.getOpenAPIParameters().getFormat(), ac.getOpenAPIParameters().getVersion()); } /** @@ -266,40 +257,9 @@ public void handleOpenAPIv3(InternalActionContext ac) { * @param ac internal action context * @param format2 manually picked specification format */ - public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest.openapi.Format format2) { - boolean useVersion31 = ac.getOpenAPIParameters().getVersion() == Version.V31; - String format = Optional.ofNullable(format2).orElse(com.gentics.mesh.core.rest.openapi.Format.JSON).name().toLowerCase(); - - // Collect available servers - HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - List servers; - try { - servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(noClusterServerSupplier); - } catch (Throwable e) { - log.error("Could not retrieve the server list out of Hazelcast", e); - servers = noClusterServerSupplier.get(); - } - - /* - * Blacklist - * a) all the actual project roots - * b) old api version roots - * c) an `apiversion` selector parameter root - */ - Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() - .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) - .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); - blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); - if (options.isOpenapiExcludePlugins()) { - blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/[.]*")); - } - - // Make an instance with blacklist path patterns - MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.etc.config.Format format2, com.gentics.mesh.etc.config.Version version) { + boolean useVersion31 = Optional.ofNullable(version).orElse(options.getOpenAPIOptions().getDefaultVersion()) == Version.V31; + String format = Optional.ofNullable(format2).orElse(options.getOpenAPIOptions().getDefaultFormat()).name().toLowerCase(); // Generate... try { @@ -309,13 +269,15 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.core.rest routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), //... from generic project root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")), + //... from project plugin root + options.getOpenAPIOptions().isExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")), //... from generic plugin root - options.isOpenapiExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")) + options.getOpenAPIOptions().isExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), // ...with desired format Format.parse(format), // ...with desired pretty printing - !httpServerConfig.isMinifyJson(), + !options.getHttpServerOptions().isMinifyJson(), // ...with desired spec version useVersion31), OK, diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index 26356b53ab..6fa03d4a53 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -11,7 +11,7 @@ import com.gentics.mesh.auth.MeshAuthChain; import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.rest.openapi.Format; +import com.gentics.mesh.etc.config.Format; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.example.RestInfoExamples; import com.gentics.mesh.parameter.impl.OpenAPIParametersImpl; @@ -84,30 +84,19 @@ public void registerEndPoints() { adminHandler.handleOpenAPIv3(ac); }, false); - secure("/openapi.yaml"); + + MeshOptions options = getOptions(); + secure("/openapi." + options.getOpenAPIOptions().getDefaultFormat().name().toLowerCase()); InternalEndpointRoute openapiYml = createRoute(); - openapiYml.path("/openapi.yaml"); + openapiYml.path("/openapi." + options.getOpenAPIOptions().getDefaultFormat().name().toLowerCase()); openapiYml.method(GET); - openapiYml.description("Endpoint which provides a OpenAPIv3.0 YAML document for all registed endpoints."); + openapiYml.description("Endpoint which provides a OpenAPI v" + options.getOpenAPIOptions().getDefaultVersion().pretty() + " " + options.getOpenAPIOptions().getDefaultFormat().name() + " document for all registed endpoints."); openapiYml.displayName("OpenAPI specification"); openapiYml.exampleResponse(OK, "Not yet specified"); - openapiYml.produces(APPLICATION_YAML); + openapiYml.produces(options.getOpenAPIOptions().getDefaultFormat() == Format.JSON ? APPLICATION_JSON : APPLICATION_YAML); openapiYml.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac, Format.YAML); - }, false); - - secure("/openapi.json"); - InternalEndpointRoute openapiJson = createRoute(); - openapiJson.path("/openapi.json"); - openapiJson.method(GET); - openapiJson.description("Endpoint which provides a OpenAPIv3.0 JSON document for all registed endpoints."); - openapiJson.displayName("OpenAPI specification"); - openapiJson.exampleResponse(OK, "Not yet specified"); - openapiJson.produces(APPLICATION_JSON); - openapiJson.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac, Format.JSON); + adminHandler.handleOpenAPIv3(ac, options.getOpenAPIOptions().getDefaultFormat(), options.getOpenAPIOptions().getDefaultVersion()); }, false); secure("/"); diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java index cac16da7ff..f2a699f3e0 100644 --- a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -35,6 +35,8 @@ import com.gentics.mesh.core.endpoint.utility.UtilityEndpoint; import com.gentics.mesh.core.endpoint.webroot.WebRootEndpoint; import com.gentics.mesh.core.endpoint.webrootfield.WebRootFieldEndpoint; +import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.etc.config.OpenAPIOptions; import com.gentics.mesh.graphql.GraphQLEndpoint; import com.gentics.mesh.router.APIRouterImpl; import com.gentics.mesh.router.RootRouterImpl; @@ -228,7 +230,10 @@ protected void addExtraEndpoints(T consumer) throws IOException { */ protected void initEndpoint(AbstractInternalEndpoint endpoint) { Vertx vertx = mock(Vertx.class); + MeshOptions options = mock(MeshOptions.class); Mockito.when(endpoint.getRouter()).thenReturn(Router.router(vertx)); + Mockito.when(endpoint.getOptions()).thenReturn(options); + Mockito.when(options.getOpenAPIOptions()).thenReturn(new OpenAPIOptions()); endpoint.registerEndPoints(); } diff --git a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java index d71fc9058b..15cf6596c8 100644 --- a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java +++ b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java @@ -1,27 +1,45 @@ package com.gentics.mesh.plugin.registry; import static com.gentics.mesh.core.rest.error.Errors.error; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; +import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.vertx.core.http.HttpMethod.GET; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Singleton; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gentics.mesh.MeshVersion; import com.gentics.mesh.core.rest.error.GenericRestException; +import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.plugin.MeshPlugin; import com.gentics.mesh.plugin.RestPlugin; import com.gentics.mesh.router.PluginRouter; -import com.gentics.mesh.router.PluginRouterImpl; import com.gentics.mesh.router.RouterStorage; -import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; +import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.OpenAPIGenerationException; +import com.gentics.vertx.openapi.route.InternalEndpointBuilder; import io.reactivex.Completable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import io.vertx.core.Vertx; import io.vertx.ext.web.Router; /** @@ -39,9 +57,15 @@ public class RestPluginRegistry implements PluginRegistry { private final RouterStorageRegistryImpl routerStorageRegistry; + private final MeshOptions options; + + private final MeshOpenAPIv3Generator openApiV3Generator; + @Inject - public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry) { + public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry, MeshOptions options, MeshOpenAPIv3Generator openAPIv3Generator) { this.routerStorageRegistry = routerStorageRegistry; + this.options = options; + this.openApiV3Generator = openAPIv3Generator; } @Override @@ -54,22 +78,59 @@ public Completable register(MeshPlugin plugin) { String apiName = restPlugin.restApiName(); log.info("Registering rest plugin {" + name + "} with id {" + plugin.id() + "}"); for (RouterStorage rs : routerStorageRegistry.getInstances()) { + String pluginsWithOpenAPI = options.getOpenAPIOptions().getPluginsWithOwnEndpoints(); PluginRouter globalPluginRouter = rs.root().apiRouter().pluginRouter(); PluginRouter projectPluginRouter = rs.root().apiRouter().projectsRouter().projectRouter().pluginRouter(); - Router globalRouter = restPlugin.createGlobalRouter(); - if (globalRouter != null) { - globalPluginRouter.addRouter(apiName, globalRouter); - log.info("Registering REST API Plugin {" + name + "} for globally"); - } Router projectRouter = restPlugin.createProjectRouter(); if (projectRouter != null) { projectPluginRouter.addRouter(apiName, projectRouter); log.info("Registering REST API Plugin {" + name + "} for projects"); } + Router globalRouter = restPlugin.createGlobalRouter(); + if (globalRouter == null && !"NONE".equals(pluginsWithOpenAPI)) { + globalRouter = Router.router(Vertx.vertx()); + } + if (globalRouter != null) { + if (StringUtils.isNotBlank(pluginsWithOpenAPI) + && ("ALL".equals(pluginsWithOpenAPI) + || Arrays.stream(pluginsWithOpenAPI.split(",")).anyMatch(id -> id.equals(restPlugin.id())))) { + Version defaultVersion = options.getOpenAPIOptions().getDefaultVersion(); + com.gentics.mesh.etc.config.Format defaultFormat = options.getOpenAPIOptions().getDefaultFormat(); + Pair globalRouterPair = Pair.of(globalRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/"); + InternalEndpointBuilder.wrap(globalRouter) + .withPath("/openapi." + defaultFormat.name().toLowerCase()) + .withMethod(GET) + .withDescription("Endpoint which provides a OpenAPI v" + defaultVersion.pretty() + " " + defaultFormat.name() + " document for all registered endpoints of " + restPlugin.name()) + .withDisplayName("OpenAPI specification") + .withExampleResponse(OK, "Not yet specified") + .produces(defaultFormat == com.gentics.mesh.etc.config.Format.JSON ? APPLICATION_JSON : APPLICATION_YAML) + .withBlockingHandler(rc -> { + try { + rc.response().send(openApiV3Generator.generate( + restPlugin.name() + " OpenAPI v" + defaultVersion.pretty() + " specification", + projectRouter != null + ? Stream.of( + //... from plugin project root + Pair.of(projectRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/" + restPlugin.restApiName()), + //... from generic plugin root + globalRouterPair + ).collect(Collectors.toMap(Pair::getKey, Pair::getValue)) + : Map.of(globalRouterPair.getKey(), globalRouterPair.getValue()), + defaultFormat == com.gentics.mesh.etc.config.Format.JSON ? Format.JSON : Format.YAML, + true, + defaultVersion == Version.V31)); + } catch (OpenAPIGenerationException e) { + rc.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); + } + }, false) + .build(); + } + globalPluginRouter.addRouter(apiName, globalRouter); + log.info("Registering REST API Plugin {" + name + "} for globally"); + } } } - sub.onComplete(); }).doOnError(error -> { if (error instanceof GenericRestException) { diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 8cd7d671e7..5eb144a054 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -82,8 +82,6 @@ import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; import com.gentics.mesh.core.rest.plugin.PluginListResponse; import com.gentics.mesh.core.rest.plugin.PluginResponse; @@ -125,6 +123,8 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 4baf7dd83f..5e658d9cfe 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -45,6 +45,7 @@ public MeshOpenAPIv3Generator(String version, List servers, /** * Generate the specification using some functional shortcuts. * + * @param name * @param routers * @param format * @param pretty @@ -53,7 +54,22 @@ public MeshOpenAPIv3Generator(String version, List servers, * @throws OpenAPIGenerationException */ public String generate(Map routers, Format format, boolean pretty, boolean useVersion31) throws OpenAPIGenerationException { - return generate("Gentics Mesh REST API", routers, format, pretty, useVersion31, + return generate("Gentics Mesh REST API", routers, format, pretty, useVersion31); + } + + /** + * Generate the specification using some functional shortcuts and an API name. + * + * @param name + * @param routers + * @param format + * @param pretty + * @param useVersion31 + * @return + * @throws OpenAPIGenerationException + */ + public String generate(String name, Map routers, Format format, boolean pretty, boolean useVersion31) throws OpenAPIGenerationException { + return generate(name, routers, format, pretty, useVersion31, // transform project path item Optional.of((path, item) -> { if (path.contains("/{project}/")) { diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java index 646d04c1b5..784f9e4da1 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java @@ -8,8 +8,8 @@ import org.raml.model.ParamType; import org.raml.model.parameter.QueryParameter; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.handler.ActionContext; import com.gentics.mesh.parameter.AbstractParameters; import com.gentics.mesh.parameter.OpenAPIParameters; diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java index 9a4dce7e0d..cabce1e188 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java @@ -7,7 +7,6 @@ import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.contentoperation.ContentCachedStorage; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; import com.gentics.mesh.core.endpoint.admin.AdminEndpointImpl; import com.gentics.mesh.core.endpoint.admin.AdminHandler; @@ -26,6 +25,7 @@ import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; import dagger.Module; import dagger.Provides; @@ -48,7 +48,7 @@ public static AdminEndpoint provideAdminEndpoint(MeshAuthChain chain, AdminHandl public static AdminHandler provideAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { - return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache, clusterManager); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, MeshOpenAPIv3Generator generator) { + return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache, generator); } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java index 5bb12714fb..eb7a11a266 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java @@ -1,10 +1,28 @@ package com.gentics.mesh.dagger; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; import java.util.Random; +import java.util.Set; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; import javax.inject.Singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.core.db.cluster.ClusterManager; +import com.gentics.mesh.etc.config.HttpServerConfig; +import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.hibernate.util.UuidGenerator; +import com.gentics.mesh.router.RouterStorageRegistry; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; import dagger.Module; import dagger.Provides; @@ -22,4 +40,43 @@ public class UtilModule { public UuidGenerator uuidGenerator() { return new UuidGenerator(new Random()); } + + + @Provides + @Singleton + public static MeshOpenAPIv3Generator provideOpenAPIv3Generator(MeshOptions options, ClusterManager clusterManager, RouterStorageRegistry routerStorageRegistry) { + // Collect available servers + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + List servers; + try { + servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(noClusterServerSupplier); + } catch (Throwable e) { + Logger log = LoggerFactory.getLogger(AdminEndpointProviderModule.class); + log.error("Could not retrieve the server list out of Hazelcast", e); + servers = noClusterServerSupplier.get(); + } + + /* + * Blacklist + * a) all the actual project roots + * b) old api version roots + * c) an `apiversion` selector parameter root + * d) plugins root, if forbidden + * e) eventbus + */ + Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() + .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) + .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); + blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); + blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); + if (options.getOpenAPIOptions().isExcludePlugins()) { + blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/[a-zA-Z0-9\\-\\._@$%!]+\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/\\{project\\}\\/plugins[.]*")); + } + + // Make an instance with blacklist path patterns + return new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java index 681b1c310e..7c6c494419 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java @@ -12,7 +12,6 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminHandler; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; @@ -22,6 +21,7 @@ import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; import io.vertx.core.Vertx; @@ -38,8 +38,8 @@ public class HibAdminHandler extends AdminHandler { public HibAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { - super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, clusterManager); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, MeshOpenAPIv3Generator meshOpenAPIv3Generator) { + super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, meshOpenAPIv3Generator); this.contentCache = contentCache; } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index e7590f1401..2dc8321816 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -64,8 +64,6 @@ import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; import com.gentics.mesh.core.rest.plugin.PluginListResponse; import com.gentics.mesh.core.rest.plugin.PluginResponse; @@ -107,6 +105,8 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java index a5c0e39512..754c7d3e37 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java @@ -1,8 +1,8 @@ package com.gentics.mesh.rest.client.method; import com.gentics.mesh.core.rest.MeshServerInfoModel; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.monitoring.MonitoringRestClient; diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java index 4e5447f1bc..025a8c58c2 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java @@ -1,7 +1,7 @@ package com.gentics.mesh.parameter; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; /** * Parameters for an OpenAPI definition request diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java index 41a9735b86..7df38ca74f 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java @@ -15,8 +15,8 @@ import org.junit.runners.Parameterized.Parameters; import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 01706d55c2..efecacdac9 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -53,8 +53,6 @@ import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.field.s3binary.S3RestResponse; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; -import com.gentics.mesh.core.rest.openapi.Format; -import com.gentics.mesh.core.rest.openapi.Version; import com.gentics.mesh.core.rest.plugin.PluginDeploymentRequest; import com.gentics.mesh.core.rest.plugin.PluginListResponse; import com.gentics.mesh.core.rest.plugin.PluginResponse; @@ -96,6 +94,8 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; +import com.gentics.mesh.etc.config.Format; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.BranchParameters; From fcf0cd93849d316f924bf837a0134449a9a2261e Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 5 Mar 2026 09:40:46 +0100 Subject: [PATCH 37/75] Fix the project blacklist --- .../core/endpoint/admin/AdminHandler.java | 50 ++++++++++++++-- .../mesh/core/endpoint/node/NodeEndpoint.java | 1 + .../plugin/registry/RestPluginRegistry.java | 33 +++++++++-- .../mesh/util/MeshOpenAPIv3Generator.java | 2 - .../dagger/AdminEndpointProviderModule.java | 6 +- .../com/gentics/mesh/dagger/UtilModule.java | 57 ------------------- .../mesh/endpoint/admin/HibAdminHandler.java | 6 +- 7 files changed, 80 insertions(+), 75 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 8a022a8a34..a543a46c07 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -11,10 +11,17 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.apache.commons.lang.StringUtils; @@ -29,6 +36,7 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.endpoint.handler.AbstractHandler; import com.gentics.mesh.core.rest.MeshServerInfoModel; @@ -39,6 +47,7 @@ import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; +import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.generator.RAMLGenerator; @@ -87,12 +96,12 @@ public abstract class AdminHandler extends AbstractHandler { protected final CacheRegistry cacheRegistry; - protected final MeshOpenAPIv3Generator generator; + protected final ClusterManager clusterManager; protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, MeshOpenAPIv3Generator generator) { + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ClusterManager clusterManager) { this.vertx = vertx; this.db = db; this.routerStorage = routerStorage; @@ -105,7 +114,7 @@ protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage this.writeLock = writeLock; this.consistencyCheckHandler = consistencyCheckHandler; this.cacheRegistry = cacheRegistry; - this.generator = generator; + this.clusterManager = clusterManager; } /** @@ -261,9 +270,42 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.etc.confi boolean useVersion31 = Optional.ofNullable(version).orElse(options.getOpenAPIOptions().getDefaultVersion()) == Version.V31; String format = Optional.ofNullable(format2).orElse(options.getOpenAPIOptions().getDefaultFormat()).name().toLowerCase(); + // Collect available servers + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + List servers; + try { + servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(noClusterServerSupplier); + } catch (Throwable e) { + log.error("Could not retrieve the server list out of Hazelcast", e); + servers = noClusterServerSupplier.get(); + } + + /* + * Blacklist + * a) all the actual project roots + * b) old api version roots + * c) an `apiversion` selector parameter root + * d) plugin paths, optionally + */ + Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() + .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) + .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); + blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); + blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); + if (options.getOpenAPIOptions().isExcludePlugins()) { + blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/\\{project\\}\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/plugins[.]*")); + } + + // Make an instance with blacklist path patterns + MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + // Generate... try { ac.send(generator.generate( + "Gentics Mesh REST API", Stream.of( //... from base root routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), @@ -275,7 +317,7 @@ public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.etc.confi options.getOpenAPIOptions().isExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/")) ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), // ...with desired format - Format.parse(format), + Format.parse(format), // ...with desired pretty printing !options.getHttpServerOptions().isMinifyJson(), // ...with desired spec version diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index f121cf14b3..90730bd6d8 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -264,6 +264,7 @@ private void addBinaryHandlers() { fieldGet.addQueryParameters(ImageManipulationParametersImpl.class); fieldGet.addQueryParameters(VersioningParametersImpl.class); fieldGet.method(GET); + fieldGet.produces("*/*"); fieldGet.description( "Download the binary field with the given name. You can use image query parameters for crop and resize if the binary data represents an image."); fieldGet.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java index 15cf6596c8..5f7241db62 100644 --- a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java +++ b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java @@ -11,8 +11,11 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -25,7 +28,9 @@ import org.slf4j.LoggerFactory; import com.gentics.mesh.MeshVersion; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.rest.error.GenericRestException; +import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.plugin.MeshPlugin; @@ -59,13 +64,13 @@ public class RestPluginRegistry implements PluginRegistry { private final MeshOptions options; - private final MeshOpenAPIv3Generator openApiV3Generator; + private final ClusterManager clusterManager; @Inject - public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry, MeshOptions options, MeshOpenAPIv3Generator openAPIv3Generator) { + public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry, MeshOptions options, ClusterManager clustterManager) { this.routerStorageRegistry = routerStorageRegistry; this.options = options; - this.openApiV3Generator = openAPIv3Generator; + this.clusterManager = clustterManager; } @Override @@ -94,10 +99,10 @@ public Completable register(MeshPlugin plugin) { if (globalRouter != null) { if (StringUtils.isNotBlank(pluginsWithOpenAPI) && ("ALL".equals(pluginsWithOpenAPI) - || Arrays.stream(pluginsWithOpenAPI.split(",")).anyMatch(id -> id.equals(restPlugin.id())))) { + || Arrays.stream(pluginsWithOpenAPI.split(",")).map(String::trim).anyMatch(id -> id.equals(restPlugin.id())))) { Version defaultVersion = options.getOpenAPIOptions().getDefaultVersion(); com.gentics.mesh.etc.config.Format defaultFormat = options.getOpenAPIOptions().getDefaultFormat(); - Pair globalRouterPair = Pair.of(globalRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/"); + Pair globalRouterPair = Pair.of(globalRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/" + restPlugin.restApiName()); InternalEndpointBuilder.wrap(globalRouter) .withPath("/openapi." + defaultFormat.name().toLowerCase()) .withMethod(GET) @@ -107,12 +112,28 @@ public Completable register(MeshPlugin plugin) { .produces(defaultFormat == com.gentics.mesh.etc.config.Format.JSON ? APPLICATION_JSON : APPLICATION_YAML) .withBlockingHandler(rc -> { try { + // Collect available servers + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + List servers; + try { + servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(noClusterServerSupplier); + } catch (Throwable e) { + log.error("Could not retrieve the server list out of Hazelcast", e); + servers = noClusterServerSupplier.get(); + } + + // Make an instance with blacklist path patterns + MeshOpenAPIv3Generator openApiV3Generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.empty(), Optional.empty()); + rc.response().send(openApiV3Generator.generate( restPlugin.name() + " OpenAPI v" + defaultVersion.pretty() + " specification", projectRouter != null ? Stream.of( //... from plugin project root - Pair.of(projectRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/" + restPlugin.restApiName()), + Pair.of(projectRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/" + restPlugin.restApiName()), //... from generic plugin root globalRouterPair ).collect(Collectors.toMap(Pair::getKey, Pair::getValue)) diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 5e658d9cfe..38a857c103 100644 --- a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -45,7 +45,6 @@ public MeshOpenAPIv3Generator(String version, List servers, /** * Generate the specification using some functional shortcuts. * - * @param name * @param routers * @param format * @param pretty @@ -60,7 +59,6 @@ public String generate(Map routers, Format format, boolean prett /** * Generate the specification using some functional shortcuts and an API name. * - * @param name * @param routers * @param format * @param pretty diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java index cabce1e188..9a4dce7e0d 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java @@ -7,6 +7,7 @@ import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.contentoperation.ContentCachedStorage; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; import com.gentics.mesh.core.endpoint.admin.AdminEndpointImpl; import com.gentics.mesh.core.endpoint.admin.AdminHandler; @@ -25,7 +26,6 @@ import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; -import com.gentics.mesh.util.MeshOpenAPIv3Generator; import dagger.Module; import dagger.Provides; @@ -48,7 +48,7 @@ public static AdminEndpoint provideAdminEndpoint(MeshAuthChain chain, AdminHandl public static AdminHandler provideAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, MeshOpenAPIv3Generator generator) { - return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache, generator); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { + return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache, clusterManager); } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java index eb7a11a266..5bb12714fb 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/UtilModule.java @@ -1,28 +1,10 @@ package com.gentics.mesh.dagger; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; import java.util.Random; -import java.util.Set; -import java.util.function.Supplier; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.IntStream; import javax.inject.Singleton; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.core.db.cluster.ClusterManager; -import com.gentics.mesh.etc.config.HttpServerConfig; -import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.hibernate.util.UuidGenerator; -import com.gentics.mesh.router.RouterStorageRegistry; -import com.gentics.mesh.util.MeshOpenAPIv3Generator; import dagger.Module; import dagger.Provides; @@ -40,43 +22,4 @@ public class UtilModule { public UuidGenerator uuidGenerator() { return new UuidGenerator(new Random()); } - - - @Provides - @Singleton - public static MeshOpenAPIv3Generator provideOpenAPIv3Generator(MeshOptions options, ClusterManager clusterManager, RouterStorageRegistry routerStorageRegistry) { - // Collect available servers - HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - List servers; - try { - servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(noClusterServerSupplier); - } catch (Throwable e) { - Logger log = LoggerFactory.getLogger(AdminEndpointProviderModule.class); - log.error("Could not retrieve the server list out of Hazelcast", e); - servers = noClusterServerSupplier.get(); - } - - /* - * Blacklist - * a) all the actual project roots - * b) old api version roots - * c) an `apiversion` selector parameter root - * d) plugins root, if forbidden - * e) eventbus - */ - Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() - .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) - .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); - blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); - if (options.getOpenAPIOptions().isExcludePlugins()) { - blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/[a-zA-Z0-9\\-\\._@$%!]+\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/\\{project\\}\\/plugins[.]*")); - } - - // Make an instance with blacklist path patterns - return new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); - } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java index 7c6c494419..681b1c310e 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java @@ -12,6 +12,7 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; +import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminHandler; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; @@ -21,7 +22,6 @@ import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; -import com.gentics.mesh.util.MeshOpenAPIv3Generator; import io.vertx.core.Vertx; @@ -38,8 +38,8 @@ public class HibAdminHandler extends AdminHandler { public HibAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, MeshOpenAPIv3Generator meshOpenAPIv3Generator) { - super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, meshOpenAPIv3Generator); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { + super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, clusterManager); this.contentCache = contentCache; } From 0e1561541e08be50b67f9191fbe092af762d36da Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 5 Mar 2026 17:39:42 +0100 Subject: [PATCH 38/75] Move plugin apis out --- .../mesh/util/MeshOpenAPIv3Generator.java | 0 .../plugin/registry/RestPluginRegistry.java | 98 ++----------------- .../com/gentics/mesh/plugin/RestPlugin.java | 11 +++ 3 files changed, 19 insertions(+), 90 deletions(-) rename {core => common}/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java (100%) diff --git a/core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java similarity index 100% rename from core/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java rename to common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java diff --git a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java index 5f7241db62..286eec9c48 100644 --- a/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java +++ b/core/src/main/java/com/gentics/mesh/plugin/registry/RestPluginRegistry.java @@ -1,50 +1,27 @@ package com.gentics.mesh.plugin.registry; import static com.gentics.mesh.core.rest.error.Errors.error; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; -import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; -import static io.netty.handler.codec.http.HttpResponseStatus.OK; -import static io.vertx.core.http.HttpMethod.GET; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.inject.Inject; import javax.inject.Singleton; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.rest.error.GenericRestException; -import com.gentics.mesh.etc.config.HttpServerConfig; -import com.gentics.mesh.etc.config.MeshOptions; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.plugin.MeshPlugin; import com.gentics.mesh.plugin.RestPlugin; import com.gentics.mesh.router.PluginRouter; import com.gentics.mesh.router.RouterStorage; import com.gentics.mesh.router.RouterStorageRegistryImpl; -import com.gentics.mesh.util.MeshOpenAPIv3Generator; -import com.gentics.vertx.openapi.model.Format; -import com.gentics.vertx.openapi.model.OpenAPIGenerationException; -import com.gentics.vertx.openapi.route.InternalEndpointBuilder; import io.reactivex.Completable; -import io.vertx.core.Vertx; import io.vertx.ext.web.Router; /** @@ -62,15 +39,9 @@ public class RestPluginRegistry implements PluginRegistry { private final RouterStorageRegistryImpl routerStorageRegistry; - private final MeshOptions options; - - private final ClusterManager clusterManager; - @Inject - public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry, MeshOptions options, ClusterManager clustterManager) { + public RestPluginRegistry(RouterStorageRegistryImpl routerStorageRegistry) { this.routerStorageRegistry = routerStorageRegistry; - this.options = options; - this.clusterManager = clustterManager; } @Override @@ -83,73 +54,20 @@ public Completable register(MeshPlugin plugin) { String apiName = restPlugin.restApiName(); log.info("Registering rest plugin {" + name + "} with id {" + plugin.id() + "}"); for (RouterStorage rs : routerStorageRegistry.getInstances()) { - String pluginsWithOpenAPI = options.getOpenAPIOptions().getPluginsWithOwnEndpoints(); PluginRouter globalPluginRouter = rs.root().apiRouter().pluginRouter(); PluginRouter projectPluginRouter = rs.root().apiRouter().projectsRouter().projectRouter().pluginRouter(); - Router projectRouter = restPlugin.createProjectRouter(); - if (projectRouter != null) { - projectPluginRouter.addRouter(apiName, projectRouter); - log.info("Registering REST API Plugin {" + name + "} for projects"); - } - Router globalRouter = restPlugin.createGlobalRouter(); - if (globalRouter == null && !"NONE".equals(pluginsWithOpenAPI)) { - globalRouter = Router.router(Vertx.vertx()); - } + Pair routers = restPlugin.createRouters(); + Router globalRouter = routers.getLeft(); if (globalRouter != null) { - if (StringUtils.isNotBlank(pluginsWithOpenAPI) - && ("ALL".equals(pluginsWithOpenAPI) - || Arrays.stream(pluginsWithOpenAPI.split(",")).map(String::trim).anyMatch(id -> id.equals(restPlugin.id())))) { - Version defaultVersion = options.getOpenAPIOptions().getDefaultVersion(); - com.gentics.mesh.etc.config.Format defaultFormat = options.getOpenAPIOptions().getDefaultFormat(); - Pair globalRouterPair = Pair.of(globalRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/" + restPlugin.restApiName()); - InternalEndpointBuilder.wrap(globalRouter) - .withPath("/openapi." + defaultFormat.name().toLowerCase()) - .withMethod(GET) - .withDescription("Endpoint which provides a OpenAPI v" + defaultVersion.pretty() + " " + defaultFormat.name() + " document for all registered endpoints of " + restPlugin.name()) - .withDisplayName("OpenAPI specification") - .withExampleResponse(OK, "Not yet specified") - .produces(defaultFormat == com.gentics.mesh.etc.config.Format.JSON ? APPLICATION_JSON : APPLICATION_YAML) - .withBlockingHandler(rc -> { - try { - // Collect available servers - HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - List servers; - try { - servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(noClusterServerSupplier); - } catch (Throwable e) { - log.error("Could not retrieve the server list out of Hazelcast", e); - servers = noClusterServerSupplier.get(); - } - - // Make an instance with blacklist path patterns - MeshOpenAPIv3Generator openApiV3Generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.empty(), Optional.empty()); - - rc.response().send(openApiV3Generator.generate( - restPlugin.name() + " OpenAPI v" + defaultVersion.pretty() + " specification", - projectRouter != null - ? Stream.of( - //... from plugin project root - Pair.of(projectRouter, "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/" + restPlugin.restApiName()), - //... from generic plugin root - globalRouterPair - ).collect(Collectors.toMap(Pair::getKey, Pair::getValue)) - : Map.of(globalRouterPair.getKey(), globalRouterPair.getValue()), - defaultFormat == com.gentics.mesh.etc.config.Format.JSON ? Format.JSON : Format.YAML, - true, - defaultVersion == Version.V31)); - } catch (OpenAPIGenerationException e) { - rc.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); - } - }, false) - .build(); - } globalPluginRouter.addRouter(apiName, globalRouter); log.info("Registering REST API Plugin {" + name + "} for globally"); } + Router projectRouter = routers.getRight(); + if (projectRouter != null) { + projectPluginRouter.addRouter(apiName, projectRouter); + log.info("Registering REST API Plugin {" + name + "} for projects"); + } } } sub.onComplete(); diff --git a/plugin-api/src/main/java/com/gentics/mesh/plugin/RestPlugin.java b/plugin-api/src/main/java/com/gentics/mesh/plugin/RestPlugin.java index 095fc4f70f..a28e91f05a 100644 --- a/plugin-api/src/main/java/com/gentics/mesh/plugin/RestPlugin.java +++ b/plugin-api/src/main/java/com/gentics/mesh/plugin/RestPlugin.java @@ -8,6 +8,8 @@ import java.util.Optional; import java.util.concurrent.Callable; +import org.apache.commons.lang3.tuple.Pair; + import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.etc.config.VertxOptions; @@ -45,6 +47,15 @@ default Router createProjectRouter() { return null; } + /** + * Create a pair of routers, global and project. Note that this method will be invoked multiple times in order to register the endpoints to all REST verticles. + * + * @return + */ + default Pair createRouters() { + return Pair.of(createGlobalRouter(), createProjectRouter()); + } + /** * Return a wrapped routing context. * From 1895d80a9d824846176786b030b17290966eb9f8 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 6 Mar 2026 14:36:49 +0100 Subject: [PATCH 39/75] Fix Jenkins --- Jenkinsfile.split | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile.split b/Jenkinsfile.split index 5103dff9b3..97d20589bf 100644 --- a/Jenkinsfile.split +++ b/Jenkinsfile.split @@ -18,7 +18,7 @@ properties([ booleanParam(name: 'runUnstableTests', defaultValue: true, description: "Whether to run tests in failing group."), booleanParam(name: 'runPerformanceTests', defaultValue: false, description: "Whether to run performance tests."), booleanParam(name: 'runClusterTests', defaultValue: false, description: "Whether to run cluster tests."), - booleanParam(name: 'useOpenApiTestClient',defaultValue: false, description: "Whether to use an autogenerated OpenAPI OkHTTP 3 based REST client for the tests.") + booleanParam(name: 'useOpenApiTestClient',defaultValue: false, description: "Whether to use an autogenerated OpenAPI OkHTTP 3 based REST client for the tests."), booleanParam(name: 'runDeploy', defaultValue: false, description: "Whether to run the deploy steps."), booleanParam(name: 'runDeployTesting', defaultValue: false, description: "Whether to run the testing deploy steps."), booleanParam(name: 'runDocker', defaultValue: false, description: "Whether to run the docker steps."), @@ -219,7 +219,7 @@ stage("Setup Build Environment") { withCredentials([usernamePassword(credentialsId: 'docker.gentics.com', usernameVariable: 'repoUsername', passwordVariable: 'repoPassword'),usernamePassword(credentialsId: 'gentics.gpg', usernameVariable: 'gpgKeyName', passwordVariable: 'gpgKeyPass')]) { withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { sh "mvn -fae -Dsurefire.excludedGroups=com.gentics.mesh.test.category.FailingTests,com.gentics.mesh.test.category.ClusterTests -Dmaven.javadoc.skip=true -Dskip.cluster.tests=true -Dmaven.test.failure.ignore=true -Dmesh.container.image.prefix=docker.gentics.com/ -Dskip.mariadb.tests=${noMariadb} -Dskip.hsqlmemory.tests=${noHsqldb} -B -U -e test" - } + } } } finally { step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/*.xml']) From 835e8e3109c80a46030d8cffb366eb0f8c81673b Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 6 Mar 2026 14:40:31 +0100 Subject: [PATCH 40/75] Properly add rest client test class key --- Jenkinsfile.split | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.split b/Jenkinsfile.split index 97d20589bf..bae2205498 100644 --- a/Jenkinsfile.split +++ b/Jenkinsfile.split @@ -72,7 +72,7 @@ final def testPart(partName, current, branches) { sh "mvn -pl :mesh-database-connector-mariadb docker:start -Dskip.mariadb.tests=${noMariadb} " } // run the tests - withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1g -XX:MaxMetaspaceSize=128m "]) { + withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1g -XX:MaxMetaspaceSize=128m ", "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { sh ".jenkins/run-splits.sh includes-${postfix} ${jacoco} ${partName} ${partId}" } } finally { From 83ed9c1bb7fd964bf5f2ebedd8a7c414459081b1 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 6 Mar 2026 15:15:29 +0100 Subject: [PATCH 41/75] CI build fix --- .../com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index efecacdac9..d40d301ede 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -110,6 +110,7 @@ import com.gentics.mesh.parameter.ProjectLoadParameters; import com.gentics.mesh.parameter.PublishParameters; import com.gentics.mesh.parameter.RolePermissionParameters; +import com.gentics.mesh.parameter.SchemaUpdateParameters; import com.gentics.mesh.parameter.SortingParameters; import com.gentics.mesh.parameter.VersioningParameters; import com.gentics.mesh.rest.JWTAuthentication; @@ -640,7 +641,9 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, - null, uuid, null, + findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), + findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), + findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), adaptRequest(request)), GenericMessageResponse.class); } From 686a40a193a135935e80c492f6f85b5567946133 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 6 Mar 2026 16:24:16 +0100 Subject: [PATCH 42/75] Get back the old upsert logic --- .../verticle/handler/HandlerUtilities.java | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java index e58393a863..462f6cf88a 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java @@ -4,7 +4,11 @@ import static com.gentics.mesh.core.data.perm.InternalPermission.DELETE_PERM; import static com.gentics.mesh.core.rest.error.Errors.error; import static com.gentics.mesh.core.rest.event.EventCauseAction.DELETE; -import static io.netty.handler.codec.http.HttpResponseStatus.*; +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; +import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; @@ -17,7 +21,6 @@ import javax.inject.Provider; import javax.inject.Singleton; -import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,10 +43,8 @@ import com.gentics.mesh.core.db.TxEventAction0; import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.mesh.core.rest.error.NotModifiedException; -import com.gentics.mesh.core.rest.schema.SchemaReferenceInfo; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.event.EventQueueBatch; -import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.util.UUIDUtil; import com.gentics.mesh.util.ValidationUtil; @@ -215,22 +216,12 @@ public , RM extends RestModel> void createOrUpdateE RM model = actions.transformToRestSync(tx, updateElement, ac, 0); return model; } else { - // the create request must have a schema info - SchemaReferenceInfo schemaInfo = JsonUtil.readValue(ac.getBodyAsString(), SchemaReferenceInfo.class); - boolean missingSchemaInfo = schemaInfo.getSchema() == null - || (StringUtils.isEmpty(schemaInfo.getSchema().getUuid()) - && StringUtils.isEmpty(schemaInfo.getSchema().getName())); - if (missingSchemaInfo && uuid != null) { - // otherwise it is a flawed update request - throw error(NOT_FOUND, "object_not_found_for_uuid", uuid); - } else { - created.set(true); - T createdElement = actions.create(tx, ac, bac.batch(), uuid); - RM model = actions.transformToRestSync(tx, createdElement, ac, 0); - String path = actions.getAPIPath(tx, ac, createdElement); - ac.setLocation(path); - return model; - } + created.set(true); + T createdElement = actions.create(tx, ac, bac.batch(), uuid); + RM model = actions.transformToRestSync(tx, createdElement, ac, 0); + String path = actions.getAPIPath(tx, ac, createdElement); + ac.setLocation(path); + return model; } }, model -> ac.send(model, created.get() ? CREATED : OK)); } From 4c8808b49f88e4992b240f0c2d6c4a86d3b4af72 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 6 Mar 2026 17:13:38 +0100 Subject: [PATCH 43/75] Regression fixes --- .../com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java | 2 +- .../mesh/rest/client/MeshRestClientMessageException.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index 1ec1513268..b4ebc5ba54 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -309,7 +309,7 @@ protected void addDebugInfoHandler() { InternalEndpointRoute route = createRoute(); route.path("/debuginfo"); route.method(GET); - route.produces(APPLICATION_OCTET_STREAM); + route.produces("*/*"); route.exampleResponse(OK, "ZIP file"); route.description("Downloads a zip file of various [debug information](/docs/administration-guide/#debuginfo) files."); route.addQueryParameter("include", "Information to include. See the [documentation](/docs/administration-guide/#debuginfo) for possible usages.", "-log,consistencyCheck"); diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java index 82b54ac72e..63b5503589 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/MeshRestClientMessageException.java @@ -33,7 +33,7 @@ public MeshRestClientMessageException(int statusCode, String statusMessage, Stri } public MeshRestClientMessageException(int statusCode, String statusMessage, GenericMessageResponse responseMessage, HttpMethod method, String uri) { - super("Error:" + statusCode + (method != null ? (" in " + method.name()) : "") + " at " + uri + " : " + statusMessage); + super("Error:" + statusCode + (method != null ? (" in " + method.name()) : "") + " " + uri + " : " + statusMessage); this.responseMessage = responseMessage; this.statusCode = statusCode; this.uri = uri; From b45d7bfb5ce445f7d6f639d0a096af08c14941d9 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 9 Mar 2026 17:13:20 +0100 Subject: [PATCH 44/75] Simplify/cleanup the API --- .../gentics/mesh/etc/config/MeshOptions.java | 42 ++++---- .../mesh/etc/config/OpenAPIOptions.java | 82 --------------- .../mesh/util/MeshOpenAPIv3Generator.java | 10 ++ .../core/endpoint/admin/AdminEndpoint.java | 1 - .../core/endpoint/admin/AdminHandler.java | 99 +------------------ .../core/endpoint/admin/RestInfoEndpoint.java | 36 ++----- .../generator/AbstractEndpointGenerator.java | 6 +- .../mesh/rest/MeshLocalClientImpl.java | 4 +- .../parameter/ParameterProviderContext.java | 5 - .../parameter/impl/OpenAPIParametersImpl.java | 67 ------------- .../mesh/etc/config/HibernateMeshOptions.java | 18 ++++ ...odule.java => EndpointProviderModule.java} | 18 +++- .../mesh/dagger/HibernateMeshComponent.java | 2 +- .../gentics/mesh/dagger/HibernateModule.java | 11 --- .../mesh/endpoint/admin/HibAdminHandler.java | 84 ++++++++++++++++ .../client/OpenAPIParametersImpl.java | 10 -- .../client/impl/MeshRestHttpClientImpl.java | 7 +- .../client/method/ApiInfoClientMethods.java | 6 +- .../mesh/parameter/OpenAPIParameters.java | 60 ----------- .../core/openapi/OpenAPIEndpointTest.java | 25 ++--- .../test/openapi/OpenAPIMeshRestClient.java | 4 +- 21 files changed, 176 insertions(+), 421 deletions(-) delete mode 100644 api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java delete mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java rename mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/{AdminEndpointProviderModule.java => EndpointProviderModule.java} (79%) delete mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index 6207366b67..cdf1f47334 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -152,9 +152,6 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_MIGRATION_TRIGGER_INTERVAL, description = "Override the migration trigger interval") private long migrationTriggerInterval = DEFAULT_MIGRATION_TRIGGER_INTERVAL; - @JsonProperty(required = true) - @JsonPropertyDescription("OpenAPI options.") - private OpenAPIOptions openApiOptions = new OpenAPIOptions(); @JsonProperty(required = true) @JsonPropertyDescription("GraphQL options.") @@ -276,25 +273,6 @@ public MeshOptions setMonitoringOptions(MonitoringConfig monitoringOptions) { return this; } - /** - * Get the OpenAPI options - * @return options - */ - @JsonProperty("openApi") - public OpenAPIOptions getOpenAPIOptions() { - return openApiOptions; - } - - /** - * Set the OpenAPI options - * @param openApiOptions options - * @return fluent API - */ - public MeshOptions setOpenAPIOptions(OpenAPIOptions openApiOptions) { - this.openApiOptions = openApiOptions; - return this; - } - /** * Get the graphql options * @return graphql options @@ -630,4 +608,24 @@ public void validate(MeshOptions options) { } public abstract boolean hasDatabaseLevelCache(); + + /** + * Get default OpenAPI spec version + * + * @return + */ + public abstract Version getDefaultOpenAPIVersion(); + + /** + * Get default OpenAPI spec format + * + * @return + */ + public abstract Format getDefaultOpenAPIFormat(); + + /** + * Get the comma separated list of plugin, that should provide no API info. Can be null or empty. + * @return + */ + public abstract String getNoApiInfoPlugins(); } diff --git a/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java deleted file mode 100644 index 8f15b97a3f..0000000000 --- a/api/src/main/java/com/gentics/mesh/etc/config/OpenAPIOptions.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.gentics.mesh.etc.config; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import com.gentics.mesh.annotation.Setter; -import com.gentics.mesh.etc.config.env.EnvironmentVariable; -import com.gentics.mesh.etc.config.env.Option; - -/** - * OpenAPI related options - */ -public class OpenAPIOptions implements Option { - - public static final String MESH_OPENAPI_EXCLUDE_PLUGINS_ENV = "MESH_OPENAPI_EXCLUDE_PLUGINS"; - public static final String MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS_ENV = "MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS"; - public static final String MESH_OPENAPI_DEFAULT_VERSION_ENV = "MESH_OPENAPI_DEFAULT_VERSION"; - public static final String MESH_OPENAPI_DEFAULT_FORMAT_ENV = "MESH_OPENAPI_DEFAULT_FORMAT"; - - public static final String DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS = "ALL"; - public static final Version DEFAULT_MESH_OPENAPI_DEFAULT_VERSION = Version.V30; - public static final Format DEFAULT_MESH_OPENAPI_DEFAULT_FORMAT = Format.YAML; - - @JsonProperty(required = false) - @JsonPropertyDescription("Should the endpoints of the included plugins be excluded from the OpenAPI specification generation requests. Default is true.") - @EnvironmentVariable(name = MESH_OPENAPI_EXCLUDE_PLUGINS_ENV, description = "Override the flag to exclude endpoints of the plugins from the OpenAPI specification generation requests") - private boolean excludePlugins = true; - - @JsonProperty(required = false) - @JsonPropertyDescription("Should each of the running Mesh Plugin serve its own /openapi.yaml endpoint? Possible values: ALL, NONE, or comma-separated plugin IDs. Default is " + DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS) - @EnvironmentVariable(name = MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS_ENV, description = "Override the list of Mesh Plugins with own OpenAPI endpoints") - private String pluginsWithOwnEndpoints = DEFAULT_MESH_OPENAPI_PLUGINS_WITH_OWN_ENDPOINTS; - - @JsonProperty(required = false) - @JsonPropertyDescription("Default OpenAPI specification version. Default value: V30") - @EnvironmentVariable(name = MESH_OPENAPI_DEFAULT_VERSION_ENV, description = "Override the default OpenAPI specification version") - private Version defaultVersion = DEFAULT_MESH_OPENAPI_DEFAULT_VERSION; - - @JsonProperty(required = false) - @JsonPropertyDescription("Default OpenAPI specification format. Default value: YAML") - @EnvironmentVariable(name = MESH_OPENAPI_DEFAULT_FORMAT_ENV, description = "Override the default OpenAPI specification format") - private Format defaultFormat = DEFAULT_MESH_OPENAPI_DEFAULT_FORMAT; - - public boolean isExcludePlugins() { - return excludePlugins; - } - - @Setter - public OpenAPIOptions setExcludePlugins(boolean openapiExcludePlugins) { - this.excludePlugins = openapiExcludePlugins; - return this; - } - - public String getPluginsWithOwnEndpoints() { - return pluginsWithOwnEndpoints; - } - - @Setter - public OpenAPIOptions setPluginsWithOwnEndpoints(String openapiPluginsWithOwnEndpoints) { - this.pluginsWithOwnEndpoints = openapiPluginsWithOwnEndpoints; - return this; - } - - public Version getDefaultVersion() { - return defaultVersion; - } - - @Setter - public OpenAPIOptions setDefaultVersion(Version defaultVersion) { - this.defaultVersion = defaultVersion; - return this; - } - - public Format getDefaultFormat() { - return defaultFormat; - } - - @Setter - public OpenAPIOptions setDefaultFormat(Format defaultFormat) { - this.defaultFormat = defaultFormat; - return this; - } -} diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 38a857c103..2f4fa2e2b0 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -88,4 +88,14 @@ public String generate(String name, Map routers, Format format, .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) .collect(Collectors.toSet())))); } + + @Override + protected String getComponentName(Class cls) { + String componentName = super.getComponentName(cls); + + if (componentName.endsWith("Impl")) { + componentName = componentName.substring(0, componentName.length()-4); + } + return componentName; + } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index b4ebc5ba54..dded5263b5 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -9,7 +9,6 @@ import static com.gentics.mesh.example.ExampleUuids.JOB_UUID; import static com.gentics.mesh.example.ExampleUuids.PLUGIN_1_ID; import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; -import static com.gentics.mesh.http.HttpConstants.APPLICATION_OCTET_STREAM; import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.DELETE; diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index a543a46c07..ec60a2bc86 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -11,26 +11,12 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpResponseStatus.SERVICE_UNAVAILABLE; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; import java.util.UUID; -import java.util.function.Function; -import java.util.function.Supplier; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.IntStream; -import java.util.stream.Stream; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang3.tuple.Pair; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gentics.mesh.Mesh; -import com.gentics.mesh.MeshVersion; import com.gentics.mesh.cache.CacheRegistry; import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.context.InternalActionContext; @@ -47,23 +33,16 @@ import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; -import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.generator.RAMLGenerator; -import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; -import com.gentics.mesh.util.MeshOpenAPIv3Generator; import com.gentics.mesh.util.RxUtil; import com.gentics.mesh.util.UUIDUtil; -import com.gentics.vertx.openapi.model.Format; -import com.gentics.vertx.openapi.model.OpenAPIGenerationException; import io.vertx.core.Vertx; -import io.vertx.ext.web.Router; /** * Handler for admin request methods. @@ -256,81 +235,7 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { * * @param ac internal action context. */ - public void handleOpenAPIv3(InternalActionContext ac) { - handleOpenAPIv3(ac, ac.getOpenAPIParameters().getFormat(), ac.getOpenAPIParameters().getVersion()); - } - - /** - * Handle a request of generating OpenAPI spec for all the routes. - * - * @param ac internal action context - * @param format2 manually picked specification format - */ - public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.etc.config.Format format2, com.gentics.mesh.etc.config.Version version) { - boolean useVersion31 = Optional.ofNullable(version).orElse(options.getOpenAPIOptions().getDefaultVersion()) == Version.V31; - String format = Optional.ofNullable(format2).orElse(options.getOpenAPIOptions().getDefaultFormat()).name().toLowerCase(); - - // Collect available servers - HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - List servers; - try { - servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(noClusterServerSupplier); - } catch (Throwable e) { - log.error("Could not retrieve the server list out of Hazelcast", e); - servers = noClusterServerSupplier.get(); - } - - /* - * Blacklist - * a) all the actual project roots - * b) old api version roots - * c) an `apiversion` selector parameter root - * d) plugin paths, optionally - */ - Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() - .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) - .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); - blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); - blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); - if (options.getOpenAPIOptions().isExcludePlugins()) { - blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/\\{project\\}\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/plugins[.]*")); - } - - // Make an instance with blacklist path patterns - MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); - - // Generate... - try { - ac.send(generator.generate( - "Gentics Mesh REST API", - Stream.of( - //... from base root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), - //... from generic project root - routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")), - //... from project plugin root - options.getOpenAPIOptions().isExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}/plugins/")), - //... from generic plugin root - options.getOpenAPIOptions().isExcludePlugins() ? Stream.>empty() : routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().pluginRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/plugins/")) - ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), - // ...with desired format - Format.parse(format), - // ...with desired pretty printing - !options.getHttpServerOptions().isMinifyJson(), - // ...with desired spec version - useVersion31), - OK, - "yaml".equalsIgnoreCase(format) - ? HttpConstants.APPLICATION_YAML_UTF8 - : HttpConstants.APPLICATION_JSON_UTF8 - ); - } catch (OpenAPIGenerationException e) { - ac.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); - } - } + public abstract void handleOpenAPIv3(InternalActionContext ac); /** * Generate and return the RAML of the server. diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index 6fa03d4a53..c0ffa4b4a4 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -2,19 +2,15 @@ import static com.gentics.mesh.http.HttpConstants.APPLICATION_JSON; import static com.gentics.mesh.http.HttpConstants.APPLICATION_YAML; -import static com.gentics.mesh.http.HttpConstants.TEXT_PLAIN_UTF8; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.GET; -import javax.inject.Inject; - import com.gentics.mesh.auth.MeshAuthChain; import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.db.Database; import com.gentics.mesh.etc.config.Format; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.example.RestInfoExamples; -import com.gentics.mesh.parameter.impl.OpenAPIParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.RouterStorage; import com.gentics.mesh.router.route.AbstractInternalEndpoint; @@ -27,13 +23,12 @@ */ public class RestInfoEndpoint extends AbstractInternalEndpoint { - private RestInfoExamples examples = new RestInfoExamples(); + protected RestInfoExamples examples = new RestInfoExamples(); - private AdminHandler adminHandler; + protected AdminHandler adminHandler; - private RouterStorage routerStorage; + protected RouterStorage routerStorage; - @Inject public RestInfoEndpoint(MeshAuthChain chain, AdminHandler adminHandler, LocalConfigApi localConfigApi, Database db, MeshOptions options) { super(null, chain, localConfigApi, db, options); this.adminHandler = adminHandler; @@ -70,33 +65,18 @@ public void registerEndPoints() { adminHandler.handleRAML(ac); }, false); - secure("/openapi"); - InternalEndpointRoute openapi = createRoute(); - openapi.path("/openapi"); - openapi.method(GET); - openapi.description("Endpoint which provides a OpenAPIv3 document for all registed endpoints."); - openapi.displayName("OpenAPI specification"); - openapi.exampleResponse(OK, "Not yet specified"); - openapi.produces(TEXT_PLAIN_UTF8); - openapi.addQueryParameters(OpenAPIParametersImpl.class); - openapi.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac); - }, false); - - MeshOptions options = getOptions(); - secure("/openapi." + options.getOpenAPIOptions().getDefaultFormat().name().toLowerCase()); + secure("/openapi." + options.getDefaultOpenAPIFormat().name().toLowerCase()); InternalEndpointRoute openapiYml = createRoute(); - openapiYml.path("/openapi." + options.getOpenAPIOptions().getDefaultFormat().name().toLowerCase()); + openapiYml.path("/openapi." + options.getDefaultOpenAPIFormat().name().toLowerCase()); openapiYml.method(GET); - openapiYml.description("Endpoint which provides a OpenAPI v" + options.getOpenAPIOptions().getDefaultVersion().pretty() + " " + options.getOpenAPIOptions().getDefaultFormat().name() + " document for all registed endpoints."); + openapiYml.description("Endpoint which provides an OpenAPI v" + options.getDefaultOpenAPIVersion().pretty() + " " + options.getDefaultOpenAPIFormat().name() + " document for all registed endpoints."); openapiYml.displayName("OpenAPI specification"); openapiYml.exampleResponse(OK, "Not yet specified"); - openapiYml.produces(options.getOpenAPIOptions().getDefaultFormat() == Format.JSON ? APPLICATION_JSON : APPLICATION_YAML); + openapiYml.produces(options.getDefaultOpenAPIFormat() == Format.JSON ? APPLICATION_JSON : APPLICATION_YAML); openapiYml.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac, options.getOpenAPIOptions().getDefaultFormat(), options.getOpenAPIOptions().getDefaultVersion()); + adminHandler.handleOpenAPIv3(ac); }, false); secure("/"); diff --git a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java index f2a699f3e0..ebeba53916 100644 --- a/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/AbstractEndpointGenerator.java @@ -35,8 +35,9 @@ import com.gentics.mesh.core.endpoint.utility.UtilityEndpoint; import com.gentics.mesh.core.endpoint.webroot.WebRootEndpoint; import com.gentics.mesh.core.endpoint.webrootfield.WebRootFieldEndpoint; +import com.gentics.mesh.etc.config.Format; import com.gentics.mesh.etc.config.MeshOptions; -import com.gentics.mesh.etc.config.OpenAPIOptions; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.graphql.GraphQLEndpoint; import com.gentics.mesh.router.APIRouterImpl; import com.gentics.mesh.router.RootRouterImpl; @@ -233,7 +234,8 @@ protected void initEndpoint(AbstractInternalEndpoint endpoint) { MeshOptions options = mock(MeshOptions.class); Mockito.when(endpoint.getRouter()).thenReturn(Router.router(vertx)); Mockito.when(endpoint.getOptions()).thenReturn(options); - Mockito.when(options.getOpenAPIOptions()).thenReturn(new OpenAPIOptions()); + Mockito.when(options.getDefaultOpenAPIFormat()).thenReturn(Format.YAML); + Mockito.when(options.getDefaultOpenAPIVersion()).thenReturn(Version.V30); endpoint.registerEndPoints(); } diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 5eb144a054..a5a5ecd9b5 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -123,8 +123,6 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; @@ -1622,7 +1620,7 @@ public MeshRequest getRAML() { } @Override - public MeshRequest getOpenAPI(Format format, Version version) { + public MeshRequest getOpenAPI() { // TODO Auto-generated method stub return null; } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index 70a7e5236c..86d6559e2f 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -13,7 +13,6 @@ import com.gentics.mesh.parameter.impl.IndexMaintenanceParametersImpl; import com.gentics.mesh.parameter.impl.JobParametersImpl; import com.gentics.mesh.parameter.impl.NodeParametersImpl; -import com.gentics.mesh.parameter.impl.OpenAPIParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.ProjectLoadParametersImpl; import com.gentics.mesh.parameter.impl.ProjectPurgeParametersImpl; @@ -117,8 +116,4 @@ default DisplayParameters getDisplayParameters() { default ConsistencyCheckParameters getConsistencyCheckParameters() { return new ConsistencyCheckParametersImpl(this); } - - default OpenAPIParameters getOpenAPIParameters() { - return new OpenAPIParametersImpl(this); - } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java deleted file mode 100644 index 784f9e4da1..0000000000 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/OpenAPIParametersImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.gentics.mesh.parameter.impl; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; - -import org.raml.model.ParamType; -import org.raml.model.parameter.QueryParameter; - -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; -import com.gentics.mesh.handler.ActionContext; -import com.gentics.mesh.parameter.AbstractParameters; -import com.gentics.mesh.parameter.OpenAPIParameters; - -/** - * Parameters implementation of {@link OpenAPIParameters} - */ -public class OpenAPIParametersImpl extends AbstractParameters implements OpenAPIParameters { - - /** - * Create instance on given action context - * @param ac action context - */ - public OpenAPIParametersImpl(ActionContext ac) { - super(ac); - } - - /** - * Create empty instance - */ - public OpenAPIParametersImpl() { - } - - @Override - public Map getRAMLParameters() { - Map parameters = new HashMap<>(); - - // version - QueryParameter versionParameter = new QueryParameter(); - versionParameter.setDefaultValue(Version.V30.name().toLowerCase()); - versionParameter.setDescription("Specify, whether the OpenAPI standard version 3.1 should be generated. If false or unset, a version 3.0 of standard will be used."); - versionParameter.setExample(Version.V31.name()); - versionParameter.setRequired(false); - versionParameter.setEnumeration(Arrays.asList(Version.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); - versionParameter.setType(ParamType.STRING); - parameters.put(VERSION_PARAMETER_KEY, versionParameter); - - // format - QueryParameter formatParameter = new QueryParameter(); - formatParameter.setDefaultValue(Format.JSON.name().toLowerCase()); - formatParameter.setDescription("Specify the output format. Default is JSON."); - formatParameter.setExample(Format.YAML.name()); - formatParameter.setRequired(false); - formatParameter.setEnumeration(Arrays.asList(Format.values()).stream().map(e -> e.toString().toLowerCase()).collect(Collectors.toList())); - formatParameter.setType(ParamType.STRING); - parameters.put(FORMAT_PARAMETER_KEY, formatParameter); - - return parameters; - } - - @Override - public String getName() { - return "OpenAPI request parameters"; - } -} diff --git a/mdm/hibernate-api/src/main/java/com/gentics/mesh/etc/config/HibernateMeshOptions.java b/mdm/hibernate-api/src/main/java/com/gentics/mesh/etc/config/HibernateMeshOptions.java index 49e934b403..ab4bd05d07 100644 --- a/mdm/hibernate-api/src/main/java/com/gentics/mesh/etc/config/HibernateMeshOptions.java +++ b/mdm/hibernate-api/src/main/java/com/gentics/mesh/etc/config/HibernateMeshOptions.java @@ -123,4 +123,22 @@ public void setLicenseKeyPath(String licenseKeyPath) { public boolean hasDatabaseLevelCache() { return true; } + + @Override + @JsonIgnore + public Version getDefaultOpenAPIVersion() { + return Version.V30; + } + + @Override + @JsonIgnore + public Format getDefaultOpenAPIFormat() { + return Format.YAML; + } + + @Override + @JsonIgnore + public String getNoApiInfoPlugins() { + return null; + } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java similarity index 79% rename from mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java rename to mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java index 9a4dce7e0d..79ed5cad45 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/AdminEndpointProviderModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java @@ -14,6 +14,7 @@ import com.gentics.mesh.core.endpoint.admin.JobHandler; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.core.endpoint.admin.LocalConfigHandler; +import com.gentics.mesh.core.endpoint.admin.RestInfoEndpoint; import com.gentics.mesh.core.endpoint.admin.ShutdownHandler; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.endpoint.admin.debuginfo.DebugInfoHandler; @@ -22,6 +23,7 @@ import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.endpoint.admin.HibAdminHandler; +import com.gentics.mesh.etc.config.HibernateMeshOptions; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; @@ -32,7 +34,21 @@ import io.vertx.core.Vertx; @Module -public class AdminEndpointProviderModule { +public class EndpointProviderModule { + + @Provides + public static HibernateMeshOptions hibernateMeshOptions(MeshOptions meshOptions) { + if (meshOptions instanceof HibernateMeshOptions) { + return (HibernateMeshOptions) meshOptions; + } else { + throw new IllegalArgumentException("Unsupported MeshOptions class:" + meshOptions.getClass().getCanonicalName()); + } + } + + @Provides + public static RestInfoEndpoint provideRestInfoEndpoint(MeshAuthChain chain, AdminHandler adminHandler, LocalConfigApi localConfigApi, Database db, MeshOptions options) { + return new RestInfoEndpoint(chain, adminHandler, localConfigApi, db, options); + } @Provides public static AdminEndpoint provideAdminEndpoint(MeshAuthChain chain, AdminHandler adminHandler, JobHandler jobHandler, diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateMeshComponent.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateMeshComponent.java index bb25bc406e..98157a48b7 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateMeshComponent.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateMeshComponent.java @@ -22,7 +22,7 @@ * Central dagger mesh component which will expose dependencies. */ @Singleton -@Component(modules = { CommonModule.class, HibernateModule.class, SearchWaitUtilProviderModule.class, AdminEndpointProviderModule.class, DatabaseConnectorModule.class, AuthChainProviderModule.class }) +@Component(modules = { CommonModule.class, HibernateModule.class, SearchWaitUtilProviderModule.class, EndpointProviderModule.class, DatabaseConnectorModule.class, AuthChainProviderModule.class }) public interface HibernateMeshComponent extends MeshComponent { @Getter diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateModule.java index 785dd0e446..0a7f7b6913 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/HibernateModule.java @@ -93,8 +93,6 @@ import com.gentics.mesh.database.cluster.HibClusterManager; import com.gentics.mesh.distributed.MasterInfoProvider; import com.gentics.mesh.distributed.MasterInfoProviderImpl; -import com.gentics.mesh.etc.config.HibernateMeshOptions; -import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.hibernate.HibernateRootResolver; import com.gentics.mesh.hibernate.data.binary.impl.HibBinariesImpl; import com.gentics.mesh.hibernate.data.dao.BinaryDaoImpl; @@ -292,15 +290,6 @@ public abstract class HibernateModule { @Binds abstract S3Binaries bindS3Binaries(S3HibBinariesImpl e); - @Provides - public static HibernateMeshOptions hibernateMeshOptions(MeshOptions meshOptions) { - if (meshOptions instanceof HibernateMeshOptions) { - return (HibernateMeshOptions) meshOptions; - } else { - throw new IllegalArgumentException("Unsupported MeshOptions class:" + meshOptions.getClass().getCanonicalName()); - } - } - @Provides @Singleton public static HazelcastInstance hazelcast(HibClusterManager clusterManager) { diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java index 681b1c310e..058da08912 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java @@ -4,8 +4,25 @@ import static com.gentics.mesh.core.rest.error.Errors.error; import static com.gentics.mesh.rest.Messages.message; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import com.gentics.mesh.MeshVersion; import com.gentics.mesh.cache.CacheRegistry; import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.contentoperation.ContentCachedStorage; @@ -18,10 +35,16 @@ import com.gentics.mesh.core.verticle.handler.HandlerUtilities; import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; +import com.gentics.mesh.etc.config.HttpServerConfig; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.etc.config.Version; +import com.gentics.mesh.http.HttpConstants; import com.gentics.mesh.router.RouterStorageImpl; import com.gentics.mesh.router.RouterStorageRegistryImpl; import com.gentics.mesh.search.SearchProvider; +import com.gentics.mesh.util.MeshOpenAPIv3Generator; +import com.gentics.vertx.openapi.model.Format; +import com.gentics.vertx.openapi.model.OpenAPIGenerationException; import io.vertx.core.Vertx; @@ -58,4 +81,65 @@ public void handleCacheClear(InternalActionContext ac) { return message(ac, "cache_clear_invoked"); }, model -> ac.send(model, OK)); } + + @Override + public void handleOpenAPIv3(InternalActionContext ac) { + boolean useVersion31 = options.getDefaultOpenAPIVersion() == Version.V31; + String format = options.getDefaultOpenAPIFormat().name().toLowerCase(); + + // Collect available servers + HttpServerConfig httpServerConfig = options.getHttpServerOptions(); + Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); + List servers; + try { + servers = Optional.ofNullable(clusterManager.getHazelcast()) + .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) + .orElseGet(noClusterServerSupplier); + } catch (Throwable e) { + log.error("Could not retrieve the server list out of Hazelcast", e); + servers = noClusterServerSupplier.get(); + } + + /* + * Blacklist + * a) all the actual project roots + * b) old api version roots + * c) an `apiversion` selector parameter root + * d) plugin paths + */ + Set blacklistedRouteRegex = new HashSet<>(routerStorageRegistry.getInstances().stream() + .flatMap(rr -> rr.root().apiRouter().projectsRouter().getProjectRouters().keySet().stream()) + .map(project -> "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/" + project + "[.]*").collect(Collectors.toSet())); + blacklistedRouteRegex.addAll(IntStream.range(1, MeshVersion.CURRENT_API_VERSION).mapToObj(v -> "\\/api\\/v" + v + "[.]*").collect(Collectors.toList())); + blacklistedRouteRegex.addAll(List.of("\\/api\\/\\{apiversion\\}[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/eventbus\\/")); + blacklistedRouteRegex.addAll(List.of("\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/\\{project\\}\\/plugins[.]*", "\\/api\\/v" + MeshVersion.CURRENT_API_VERSION + "\\/plugins[.]*")); + + // Make an instance with blacklist path patterns + MeshOpenAPIv3Generator generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), servers, Optional.of(blacklistedRouteRegex.stream().map(Pattern::compile).collect(Collectors.toList())), Optional.empty()); + + // Generate... + try { + ac.send(generator.generate( + "Gentics Mesh REST API", + Stream.of( + //... from base root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().getRouter(), StringUtils.EMPTY)), + //... from generic project root + routerStorageRegistry.getInstances().stream().map(rr -> Pair.of(rr.root().apiRouter().projectsRouter().projectRouter().getRouter(), "/api/v" + MeshVersion.CURRENT_API_VERSION + "/{project}")) + ).flatMap(Function.identity()).collect(Collectors.toMap(Pair::getKey, Pair::getValue)), + // ...with desired format + Format.parse(format), + // ...with desired pretty printing + !options.getHttpServerOptions().isMinifyJson(), + // ...with desired spec version + useVersion31), + OK, + "yaml".equalsIgnoreCase(format) + ? HttpConstants.APPLICATION_YAML_UTF8 + : HttpConstants.APPLICATION_JSON_UTF8 + ); + } catch (OpenAPIGenerationException e) { + ac.fail(error(INTERNAL_SERVER_ERROR, "error_internal", e)); + } + } } diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java deleted file mode 100644 index fa41f68d3e..0000000000 --- a/rest-client/src/main/java/com/gentics/mesh/parameter/client/OpenAPIParametersImpl.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.gentics.mesh.parameter.client; - -import com.gentics.mesh.parameter.OpenAPIParameters; - -/** - * Implemetation of the OpenAPI parameters - */ -public class OpenAPIParametersImpl extends AbstractParameters implements OpenAPIParameters { - -} diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 2dc8321816..9526fd5738 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -105,15 +105,12 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; import com.gentics.mesh.parameter.client.BinaryCheckParametersImpl; import com.gentics.mesh.parameter.client.NodeParametersImpl; -import com.gentics.mesh.parameter.client.OpenAPIParametersImpl; import com.gentics.mesh.parameter.client.VersioningParametersImpl; import com.gentics.mesh.rest.client.AbstractMeshRestHttpClient; import com.gentics.mesh.rest.client.MeshBinaryResponse; @@ -1382,8 +1379,8 @@ public MeshRequest getRAML() { } @Override - public MeshRequest getOpenAPI(Format format, Version version) { - MeshRequest request = prepareRequest(GET, "/openapi" + getQuery(getConfig(), new OpenAPIParametersImpl().setFormat(format).setVersion(version)), String.class); + public MeshRequest getOpenAPI() { + MeshRequest request = prepareRequest(GET, "/openapi.yaml" + getQuery(getConfig()), String.class); request.setHeader("Accept", "*/*"); return request; } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java index 754c7d3e37..1b8dd4d790 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ApiInfoClientMethods.java @@ -1,8 +1,6 @@ package com.gentics.mesh.rest.client.method; import com.gentics.mesh.core.rest.MeshServerInfoModel; -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.monitoring.MonitoringRestClient; @@ -28,9 +26,9 @@ public interface ApiInfoClientMethods { MeshRequest getRAML(); /** - * Generate an OpenAPI + * Generate an OpenAPI specification * * @return */ - MeshRequest getOpenAPI(Format format, Version version); + MeshRequest getOpenAPI(); } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java deleted file mode 100644 index 025a8c58c2..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/OpenAPIParameters.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.gentics.mesh.parameter; - -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; - -/** - * Parameters for an OpenAPI definition request - */ -public interface OpenAPIParameters extends ParameterProvider { - - /** - * Key of the "version" parameter - */ - public static final String VERSION_PARAMETER_KEY = "version"; - - /** - * Key of the "format" parameter - */ - public static final String FORMAT_PARAMETER_KEY = "format"; - - /** - * Set the OpenAPI standard version - * - * @param version The specification version - * @return Fluent API - */ - default OpenAPIParameters setVersion(Version version) { - setParameter(VERSION_PARAMETER_KEY, String.valueOf(version)); - return this; - } - - /** - * Check whether OpenAPI spec version - * - * @return value - */ - default Version getVersion() { - return Version.parse(getParameter(VERSION_PARAMETER_KEY), Version.V30); - } - - /** - * Set the format - * - * @param format the specification format - * @return Fluent API - */ - default OpenAPIParameters setFormat(Format format) { - setParameter(FORMAT_PARAMETER_KEY, String.valueOf(format)); - return this; - } - - /** - * Check the spec format - * - * @return value - */ - default Format getFormat() { - return Format.parse(getParameter(FORMAT_PARAMETER_KEY), Format.JSON); - } -} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java index 7df38ca74f..e46446f324 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java @@ -4,6 +4,7 @@ import static com.gentics.mesh.test.TestSize.FULL; import static org.assertj.core.api.Assertions.assertThat; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -15,8 +16,6 @@ import org.junit.runners.Parameterized.Parameters; import com.gentics.mesh.MeshVersion; -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -28,15 +27,11 @@ @RunWith(Parameterized.class) public class OpenAPIEndpointTest extends AbstractMeshTest { - @Parameters(name = "{index}: no Mesh Client = {0}, format = {1}, version = {2}") + @Parameters(name = "{index}: no Mesh Client = {0}") public static Collection parameters() throws Exception { List params = new ArrayList<>(); for (boolean noClient : new Boolean[] {false, true}) { - for (Format format : Format.values()) { - for (Version version : Version.values()) { - params.add(new Object[] {noClient, format, version}); - } - } + params.add(new Object[] {noClient}); } return params; } @@ -44,25 +39,17 @@ public static Collection parameters() throws Exception { @Parameter(0) public boolean noClient; - @Parameter(1) - public Format format; - - @Parameter(2) - public Version version; - @Test - public void testOpenAPI() { + public void testOpenAPI() throws IOException { String input = null; if (noClient) { input = "http://localhost:" + options().getHttpServerOptions().getPort() + MeshVersion.CURRENT_API_BASE_PATH - + "/openapi?format=" - + format.name().toLowerCase() - + "&version=" + version.name().toLowerCase(); + + "/openapi.yaml"; } else { grantAdmin(); - input = call(() -> client().getOpenAPI(format, version)); + input = call(() -> client().getOpenAPI()); } assertNoErrors(input); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index d40d301ede..e0eae2aaf7 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -94,8 +94,6 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.etc.config.Format; -import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.BranchParameters; @@ -1592,7 +1590,7 @@ public MeshRequest getRAML() { } @Override - public MeshRequest getOpenAPI(Format format, Version version) { + public MeshRequest getOpenAPI() { // TODO Auto-generated method stub return null; } From d9efe01abe0e73489028e2348a19d11dac70b20a Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 10 Mar 2026 12:17:04 +0100 Subject: [PATCH 45/75] Support overridden mesh options --- .../AbstractHibernateOptionsProvider.java | 21 +++++++++++++++++-- .../core/HibernateMeshOptionsProvider.java | 5 +++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/AbstractHibernateOptionsProvider.java b/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/AbstractHibernateOptionsProvider.java index 288ceaa13f..bbbcdc2f83 100644 --- a/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/AbstractHibernateOptionsProvider.java +++ b/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/AbstractHibernateOptionsProvider.java @@ -1,5 +1,11 @@ package com.gentics.mesh.core; +import java.util.Optional; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.databind.ObjectMapper; import com.gentics.mesh.OptionsLoader; import com.gentics.mesh.etc.config.HibernateMeshOptions; @@ -7,10 +13,21 @@ public abstract class AbstractHibernateOptionsProvider implements HibernateMeshOptionsProvider { + protected static final Logger log = LoggerFactory.getLogger(AbstractHibernateOptionsProvider.class); + protected final HibernateMeshOptions meshOptions; + @SuppressWarnings("unchecked") public AbstractHibernateOptionsProvider() { - meshOptions = OptionsLoader.createOrloadOptions(HibernateMeshOptions.class, OptionsLoader.generateDefaultConfig(HibernateMeshOptions.class, null)); + Class optionsClass = Optional.ofNullable(System.getProperty(ENV_OPTIONS_CLASS, StringUtils.EMPTY)).filter(StringUtils::isNotBlank).map(cls -> { + try { + return (Class) Class.forName(cls); + } catch (ClassNotFoundException e) { + log.error("Could not instantiate custom options class: " + cls, e); + return HibernateMeshOptions.class; + } + }).orElse(HibernateMeshOptions.class); + meshOptions = OptionsLoader.createOrloadOptions(optionsClass, OptionsLoader.generateDefaultConfig(optionsClass, null)); } @Override @@ -24,7 +41,7 @@ public MeshOptions getClone() throws Exception { ObjectMapper mapper = OptionsLoader.getYAMLMapper(); String optionsAsString = mapper.writeValueAsString(meshOptions); - HibernateMeshOptions clonedMeshOptions = mapper.readValue(optionsAsString, HibernateMeshOptions.class); + HibernateMeshOptions clonedMeshOptions = mapper.readValue(optionsAsString, meshOptions.getClass()); // by actually sharing the storage options, we make sure that if the database connection settings are changed in the original mesh options, // all instances will get the updated settings diff --git a/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/HibernateMeshOptionsProvider.java b/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/HibernateMeshOptionsProvider.java index 88d5717245..36fc9d8a68 100644 --- a/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/HibernateMeshOptionsProvider.java +++ b/tests/tests-hibernate/src/main/java/com/gentics/mesh/core/HibernateMeshOptionsProvider.java @@ -3,7 +3,12 @@ import com.gentics.mesh.etc.config.HibernateMeshOptions; import com.gentics.mesh.test.MeshOptionsProvider; +/** + * An extension to the {@link MeshOptionsProvider} to customize options class and instance. + */ public interface HibernateMeshOptionsProvider extends MeshOptionsProvider { + + public static final String ENV_OPTIONS_CLASS = "optionsClass"; void fillMeshOptions(HibernateMeshOptions meshOptions); } From 028246b454e6dcc48e0d4b9bee0fdc12642d564d Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 10 Mar 2026 13:35:09 +0100 Subject: [PATCH 46/75] No extra models --- .../mesh/util/MeshOpenAPIv3Generator.java | 11 +---------- .../mesh/core/endpoint/admin/AdminHandler.java | 6 +----- .../mesh/dagger/EndpointProviderModule.java | 5 ++--- .../mesh/endpoint/admin/HibAdminHandler.java | 17 +++-------------- 4 files changed, 7 insertions(+), 32 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 2f4fa2e2b0..b590af6f66 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -6,11 +6,7 @@ import java.util.Map; import java.util.Optional; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import org.reflections.Reflections; - -import com.gentics.mesh.core.rest.common.RestModel; import com.gentics.vertx.openapi.OpenAPIv3Generator; import com.gentics.vertx.openapi.model.ExtendedSecurityScheme; import com.gentics.vertx.openapi.model.Format; @@ -81,12 +77,7 @@ public String generate(String name, Map routers, Format format, return path; }), // fill the component model - Optional.of(() -> Collections.unmodifiableCollection( - new Reflections("com.gentics.mesh") - .getSubTypesOf(RestModel.class) - .stream() - .filter(ty -> ty.getPackageName().startsWith("com.gentics.mesh")) - .collect(Collectors.toSet())))); + Optional.empty()); } @Override diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index ec60a2bc86..0a60c66580 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -22,7 +22,6 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.endpoint.handler.AbstractHandler; import com.gentics.mesh.core.rest.MeshServerInfoModel; @@ -75,12 +74,10 @@ public abstract class AdminHandler extends AbstractHandler { protected final CacheRegistry cacheRegistry; - protected final ClusterManager clusterManager; - protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ClusterManager clusterManager) { + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry) { this.vertx = vertx; this.db = db; this.routerStorage = routerStorage; @@ -93,7 +90,6 @@ protected AdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage this.writeLock = writeLock; this.consistencyCheckHandler = consistencyCheckHandler; this.cacheRegistry = cacheRegistry; - this.clusterManager = clusterManager; } /** diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java index 79ed5cad45..3fb64ab83f 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/dagger/EndpointProviderModule.java @@ -7,7 +7,6 @@ import com.gentics.mesh.cli.BootstrapInitializer; import com.gentics.mesh.contentoperation.ContentCachedStorage; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminEndpoint; import com.gentics.mesh.core.endpoint.admin.AdminEndpointImpl; import com.gentics.mesh.core.endpoint.admin.AdminHandler; @@ -64,7 +63,7 @@ public static AdminEndpoint provideAdminEndpoint(MeshAuthChain chain, AdminHandl public static AdminHandler provideAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { - return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache, clusterManager); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache) { + return new HibAdminHandler(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, contentCache); } } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java index 058da08912..d1f768548a 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java @@ -13,7 +13,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -29,7 +28,6 @@ import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.user.HibUser; import com.gentics.mesh.core.db.Database; -import com.gentics.mesh.core.db.cluster.ClusterManager; import com.gentics.mesh.core.endpoint.admin.AdminHandler; import com.gentics.mesh.core.endpoint.admin.consistency.ConsistencyCheckHandler; import com.gentics.mesh.core.verticle.handler.HandlerUtilities; @@ -61,8 +59,8 @@ public class HibAdminHandler extends AdminHandler { public HibAdminHandler(Vertx vertx, Database db, RouterStorageImpl routerStorage, BootstrapInitializer boot, SearchProvider searchProvider, HandlerUtilities utils, MeshOptions options, RouterStorageRegistryImpl routerStorageRegistry, Coordinator coordinator, WriteLock writeLock, - ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache, ClusterManager clusterManager) { - super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry, clusterManager); + ConsistencyCheckHandler consistencyCheckHandler, CacheRegistry cacheRegistry, ContentCachedStorage contentCache) { + super(vertx, db, routerStorage, boot, searchProvider, utils, options, routerStorageRegistry, coordinator, writeLock, consistencyCheckHandler, cacheRegistry); this.contentCache = contentCache; } @@ -89,16 +87,7 @@ public void handleOpenAPIv3(InternalActionContext ac) { // Collect available servers HttpServerConfig httpServerConfig = options.getHttpServerOptions(); - Supplier> noClusterServerSupplier = () -> Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); - List servers; - try { - servers = Optional.ofNullable(clusterManager.getHazelcast()) - .map(hz -> hz.getCluster().getMembers().stream().map(m -> m.getAddress().getHost() + ":" + m.getAddress().getPort()).collect(Collectors.toList())) - .orElseGet(noClusterServerSupplier); - } catch (Throwable e) { - log.error("Could not retrieve the server list out of Hazelcast", e); - servers = noClusterServerSupplier.get(); - } + List servers = Collections.singletonList((httpServerConfig.isSsl() ? "https://" : "http://") + httpServerConfig.getHost() + ":" + (httpServerConfig.isSsl() ? httpServerConfig.getSslPort() : httpServerConfig.getPort())); /* * Blacklist From 3e08e97627306111cbaa35aa7192d89b3d5218fe Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 11 Mar 2026 11:53:09 +0100 Subject: [PATCH 47/75] Update usages of OpenAPI generator --- .../rest/impl/InternalEndpointRouteImpl.java | 10 ++++-- .../mesh/util/MeshOpenAPIv3Generator.java | 10 ------ .../generator/OpenAPIMockAPIGenerator.java | 7 ++-- core/src/main/resources/logback.xml | 2 +- .../java/com/gentics/mesh/json/JsonUtil.java | 32 +++++++++++++++++-- 5 files changed, 40 insertions(+), 21 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index f3220e8c35..1a875532eb 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -11,6 +11,7 @@ import org.codehaus.jettison.json.JSONObject; import org.raml.model.MimeType; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.core.rest.MeshEvent; @@ -53,8 +54,13 @@ public InternalEndpointRoute blockingHandler(Handler requestHand } @Override - protected String getJsonSchema(Class clazz) { - return JsonUtil.getJsonSchema(clazz); + protected String getJsonSchema(JsonSchema schema) { + return JsonUtil.getJsonSchema(schema); + } + + @Override + protected JsonSchema getJsonSchemaObject(Class clazz) { + return JsonUtil.getJsonSchemaObject(clazz); } @Override diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index b590af6f66..319b5f7859 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -79,14 +79,4 @@ public String generate(String name, Map routers, Format format, // fill the component model Optional.empty()); } - - @Override - protected String getComponentName(Class cls) { - String componentName = super.getComponentName(cls); - - if (componentName.endsWith("Impl")) { - componentName = componentName.substring(0, componentName.length()-4); - } - return componentName; - } } diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java index cb012d4e41..f0bf02f552 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java @@ -44,17 +44,14 @@ public OpenAPIMockAPIGenerator() { } public OpenAPIMockAPIGenerator(File outputFolder, String fileName, boolean cleanup) throws IOException { - super(new File(outputFolder, "api"), false); + super(new File(outputFolder, "api"), cleanup); this.fileName = fileName; this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), Optional.empty(), Optional.empty()); } public OpenAPIMockAPIGenerator(File outputFolder, String fileName) throws IOException { - super(new File(outputFolder, "api"), false); - this.fileName = fileName; - this.generator = new MeshOpenAPIv3Generator(MeshVersion.getPlainVersion(), Collections.emptyList(), - Optional.empty(), Optional.empty()); + this(outputFolder, fileName, false); } public String generate(String format) throws IOException, OpenAPIGenerationException { diff --git a/core/src/main/resources/logback.xml b/core/src/main/resources/logback.xml index 3159433ef1..ad969a6c4a 100644 --- a/core/src/main/resources/logback.xml +++ b/core/src/main/resources/logback.xml @@ -19,7 +19,7 @@ - + diff --git a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java index c00d7c0551..65be61a627 100644 --- a/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java +++ b/rest-model/src/main/java/com/gentics/mesh/json/JsonUtil.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.json.JsonMapper; import com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver; import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; import com.gentics.mesh.core.rest.error.AbstractRestException; import com.gentics.mesh.core.rest.error.GenericRestException; @@ -243,21 +244,46 @@ public static T readValue(String content, Class valueType) throws Generic } /** - * Generate the JSON schema for the given model class. + * Generate the JSON string for the given model schema. * * @param clazz * Model class * @return */ - public static String getJsonSchema(Class clazz) { + public static String getJsonSchema(JsonSchema schema) { try { - com.fasterxml.jackson.module.jsonSchema.JsonSchema schema = schemaGen.generateSchema(clazz); return defaultMapper.writerWithDefaultPrettyPrinter().writeValueAsString(schema); } catch (Exception e) { throw new GenericRestException(INTERNAL_SERVER_ERROR, "error_internal", e); } } + /** + * Generate the JSON schema model for the given model class. + * + * @param clazz + * Model class + * @return + */ + public static String getJsonSchema(Class clazz) { + return getJsonSchema(getJsonSchemaObject(clazz)); + } + + /** + * Generate the JSON schema for the given model class. + * + * @param clazz + * Model class + * @return + */ + public static JsonSchema getJsonSchemaObject(Class clazz) { + try { + return schemaGen.generateSchema(clazz); + } catch (Exception e) { + throw new GenericRestException(INTERNAL_SERVER_ERROR, "error_internal", e); + } + } + /** * Return the JSON object mapper. * From 10a716504b05e5d17723ea73179165ac3b711976 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 11 Mar 2026 13:20:45 +0100 Subject: [PATCH 48/75] OpenAPI killswitch --- .../gentics/mesh/etc/config/MeshOptions.java | 13 ++++++++ .../core/endpoint/admin/RestInfoEndpoint.java | 7 +++- .../core/openapi/OpenAPIEndpointTest.java | 32 ++++++++++++++++--- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index cdf1f47334..fb1414691e 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -37,6 +37,7 @@ public abstract class MeshOptions implements Option { public static final String MESH_CLUSTER_INIT_ENV = "MESH_CLUSTER_INIT"; public static final String MESH_LOCK_PATH_ENV = "MESH_LOCK_PATH"; public static final String MESH_LIVE_PATH_ENV = "MESH_LIVE_PATH"; + public static final String MESH_SERVE_OPENAPI_ENV = "MESH_SERVE_OPENAPI"; public static final String MESH_START_IN_READ_ONLY_ENV = "MESH_START_IN_READ_ONLY"; public static final String MESH_INITIAL_ADMIN_PASSWORD_ENV = "MESH_INITIAL_ADMIN_PASSWORD"; public static final String MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET_ENV = "MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET"; @@ -152,6 +153,9 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_MIGRATION_TRIGGER_INTERVAL, description = "Override the migration trigger interval") private long migrationTriggerInterval = DEFAULT_MIGRATION_TRIGGER_INTERVAL; + @JsonProperty(required = false) + @EnvironmentVariable(name = MESH_SERVE_OPENAPI_ENV, description = "Serve OpenAPI specification under `/openapi*` endpoints. Default: true") + private boolean serveOpenApi = true; @JsonProperty(required = true) @JsonPropertyDescription("GraphQL options.") @@ -544,6 +548,15 @@ public MeshOptions setPluginUseHttp2(boolean pluginUseHttp2) { return this; } + public boolean isServeOpenApi() { + return serveOpenApi; + } + + @Setter + public void setServeOpenApi(boolean serveOpenApi) { + this.serveOpenApi = serveOpenApi; + } + @JsonIgnore public abstract NativeQueryFiltering getNativeQueryFiltering(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java index c0ffa4b4a4..233abd9a9a 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/RestInfoEndpoint.java @@ -15,6 +15,7 @@ import com.gentics.mesh.router.RouterStorage; import com.gentics.mesh.router.route.AbstractInternalEndpoint; +import io.netty.handler.codec.http.HttpResponseStatus; import io.vertx.core.Vertx; import io.vertx.ext.web.Router; @@ -76,7 +77,11 @@ public void registerEndPoints() { openapiYml.produces(options.getDefaultOpenAPIFormat() == Format.JSON ? APPLICATION_JSON : APPLICATION_YAML); openapiYml.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - adminHandler.handleOpenAPIv3(ac); + if (getOptions().isServeOpenApi()) { + adminHandler.handleOpenAPIv3(ac); + } else { + ac.send(HttpResponseStatus.FORBIDDEN); + } }, false); secure("/"); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java index e46446f324..bac06c8d39 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -19,6 +20,7 @@ import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; +import io.netty.handler.codec.http.HttpResponseStatus; import io.swagger.parser.OpenAPIParser; import io.swagger.parser.models.ParseOptions; import io.swagger.parser.models.SwaggerParseResult; @@ -27,11 +29,13 @@ @RunWith(Parameterized.class) public class OpenAPIEndpointTest extends AbstractMeshTest { - @Parameters(name = "{index}: no Mesh Client = {0}") + @Parameters(name = "{index}: no Mesh Client = {0}, OpenAPI disabled: {1}") public static Collection parameters() throws Exception { List params = new ArrayList<>(); for (boolean noClient : new Boolean[] {false, true}) { - params.add(new Object[] {noClient}); + for (boolean apiDisabled : new Boolean[] {false, true}) { + params.add(new Object[] {noClient, apiDisabled}); + } } return params; } @@ -39,6 +43,14 @@ public static Collection parameters() throws Exception { @Parameter(0) public boolean noClient; + @Parameter(1) + public boolean apiDisabled; + + @Before + public void setup() { + testContext.getOptions().setServeOpenApi(!apiDisabled); + } + @Test public void testOpenAPI() throws IOException { String input = null; @@ -49,7 +61,12 @@ public void testOpenAPI() throws IOException { + "/openapi.yaml"; } else { grantAdmin(); - input = call(() -> client().getOpenAPI()); + if (apiDisabled) { + call(() -> client().getOpenAPI(), HttpResponseStatus.FORBIDDEN); + return; + } else { + input = call(() -> client().getOpenAPI()); + } } assertNoErrors(input); } @@ -63,7 +80,12 @@ protected void assertNoErrors(String input) { ? parser.readLocation(input, null, null) : parser.readContents(input, null, null); - assertThat(result.getOpenAPI()).as("Parsed API").isNotNull(); - assertThat(result.getMessages()).as("Error messages").isNullOrEmpty(); + if (apiDisabled) { + assertThat(result.getOpenAPI()).as("Parsed API").isNull(); + assertThat(result.getMessages()).as("Error messages").isNotEmpty(); + } else { + assertThat(result.getOpenAPI()).as("Parsed API").isNotNull(); + assertThat(result.getMessages()).as("Error messages").isNullOrEmpty(); + } } } From d31583247100e0fc7183c381dd073004f3120bc4 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 11 Mar 2026 18:00:49 +0100 Subject: [PATCH 49/75] Plugin routes are secured by default --- .../mesh/util/MeshOpenAPIv3Generator.java | 18 +++++++++++------- .../generator/OpenAPIMockAPIGenerator.java | 6 ++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 319b5f7859..855b621f9f 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -23,19 +23,23 @@ */ public class MeshOpenAPIv3Generator extends OpenAPIv3Generator { - protected static ExtendedSecurityScheme securityBearerAuth; + protected final ExtendedSecurityScheme securityBearerAuth; - static { - securityBearerAuth = new ExtendedSecurityScheme(false); - securityBearerAuth.getScheme().setScheme("bearer"); - securityBearerAuth.getScheme().setType(SecurityScheme.Type.HTTP); - securityBearerAuth.getScheme().setBearerFormat("JWT"); + public MeshOpenAPIv3Generator(String version, List servers, + Optional> maybePathBlacklist, + Optional> maybePathWhitelist) { + this(version, servers, false, maybePathBlacklist, maybePathWhitelist); } public MeshOpenAPIv3Generator(String version, List servers, + boolean secureByDefault, Optional> maybePathBlacklist, Optional> maybePathWhitelist) { - super(version, servers, Collections.singletonMap("bearerAuth", securityBearerAuth), maybePathBlacklist, maybePathWhitelist); + super(version, servers, Collections.singletonMap("bearerAuth", new ExtendedSecurityScheme(secureByDefault)), maybePathBlacklist, maybePathWhitelist); + securityBearerAuth = security.get("bearerAuth"); + securityBearerAuth.getScheme().setScheme("bearer"); + securityBearerAuth.getScheme().setType(SecurityScheme.Type.HTTP); + securityBearerAuth.getScheme().setBearerFormat("JWT"); } /** diff --git a/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java index f0bf02f552..aeba46c75b 100644 --- a/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java +++ b/core/src/main/java/com/gentics/mesh/generator/OpenAPIMockAPIGenerator.java @@ -39,15 +39,13 @@ public class OpenAPIMockAPIGenerator extends AbstractEndpointGenerator Date: Thu, 12 Mar 2026 12:09:22 +0100 Subject: [PATCH 50/75] Allow running skipping doc --- .../runner/OpenAPISpecGeneratorRunner.java | 38 +++++++++++++++++++ .../generator/ExampleGeneratorRunner.java | 5 ++- tests/tests-core/pom.xml | 24 +++++++++++- 3 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/com/gentics/mesh/generator/runner/OpenAPISpecGeneratorRunner.java diff --git a/core/src/main/java/com/gentics/mesh/generator/runner/OpenAPISpecGeneratorRunner.java b/core/src/main/java/com/gentics/mesh/generator/runner/OpenAPISpecGeneratorRunner.java new file mode 100644 index 0000000000..2aa205574b --- /dev/null +++ b/core/src/main/java/com/gentics/mesh/generator/runner/OpenAPISpecGeneratorRunner.java @@ -0,0 +1,38 @@ +package com.gentics.mesh.generator.runner; + +import java.io.File; + +import com.gentics.mesh.generator.OpenAPIMockAPIGenerator; + +public class OpenAPISpecGeneratorRunner { + + private static final long SPEC_AGE_MILLIS = 1000 * 60 * 5; + + /** + * Start the generator. + * + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + if (args == null || args.length < 1) { + System.err.println("OpenAPISpecGeneratorRunner: no output folder provided"); + return; + } + final File outputFolder = new File(args[0]); + + if (!outputFolder.exists()) { + outputFolder.mkdirs(); + } else { + File openapiYaml = new File(outputFolder, "openapi.yaml"); + if (openapiYaml.exists() && (openapiYaml.lastModified() - System.currentTimeMillis()) < SPEC_AGE_MILLIS) { + System.out.println("OpenAPISpecGeneratorRunner: openapi.yaml is fresh enough, no generation required"); + return; + } + } + + // Generate OpenAPI base spec + OpenAPIMockAPIGenerator openApiGenerator = new OpenAPIMockAPIGenerator(outputFolder, "openapi.yaml", false); + openApiGenerator.run(); + } +} diff --git a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java index 2162a4be67..9050b015a1 100644 --- a/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java +++ b/doc/src/main/java/com/gentics/mesh/generator/ExampleGeneratorRunner.java @@ -6,6 +6,8 @@ import java.io.File; import java.io.IOException; +import com.gentics.mesh.generator.runner.OpenAPISpecGeneratorRunner; + /** * Runner for various example generators. */ @@ -50,8 +52,7 @@ public static void main(String[] args) throws Exception { ramlGenerator.run(); // Generate OpenAPI base spec - OpenAPIMockAPIGenerator openApiGenerator = new OpenAPIMockAPIGenerator(OUTPUT_ROOT_FOLDER, "openapi.yaml", false); - openApiGenerator.run(); + OpenAPISpecGeneratorRunner.main(new String[] { OUTPUT_ROOT_FOLDER.getPath() }); // Generate elasticsearch flattened models SearchModelGenerator searchModelGen = new SearchModelGenerator(OUTPUT_ROOT_FOLDER); diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index cf04d594de..0cbafd416e 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -126,6 +126,29 @@ + + org.codehaus.mojo + exec-maven-plugin + 1.5.0 + + + Generate OpenAPI + + exec + + generate-resources + + ${env.JAVA_HOME}/bin/java + + -classpath + + com.gentics.mesh.generator.runner.OpenAPISpecGeneratorRunner + ${basedir}/../../doc/src/main/docs/generated + + + + + maven-invoker-plugin 3.1.0 @@ -177,7 +200,6 @@ false true false - JsonObject=com.google.gson.JsonObject true true From 6d1acf31fc23a5e8808d6b274d8172d5134f6a99 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 13 Mar 2026 11:33:20 +0100 Subject: [PATCH 51/75] Changelog actualized --- .../src/changelog/entries/2026/03/8685.GPU-2196.enhancement | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement index 78b6889035..1a360ad8e5 100644 --- a/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement +++ b/changelog/src/changelog/entries/2026/03/8685.GPU-2196.enhancement @@ -1,3 +1 @@ -REST API: Along with existing RAML API definition, it is now possible to generate an OpenAPI (aka Swagger) specification for the Mesh REST API, which can optionally include the API for all the currently running Mesh Plugins. - For that, two new endpoints are provided, `/openapi`, which can be configured with `format` (`yaml or `json`) and/or `version` (`3.0` or `3.1`) query parameters, and a shortcut, `/openapi.yaml` or `openapi.json`, depending on the options. - Also, every Mesh Plugin can optionally have an own `/openapi.yaml` definition endpoint. \ No newline at end of file +REST API: Along with existing RAML API definition, it is now possible to generate an OpenAPI (aka Swagger) specification for the Mesh REST API. For that, an endpoint `/openapi.yaml` has been provided. \ No newline at end of file From e434beeb8184868834d298bad78b2769c168ba42 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 13 Mar 2026 16:04:07 +0100 Subject: [PATCH 52/75] Make eventbus completely hidden to the OpenAPI --- .../gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java index 3293bbdfd0..95ac0a36de 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/eventbus/EventbusEndpoint.java @@ -55,6 +55,7 @@ private void addEventBusHandler() { secureAll(); InternalEndpointRoute endpoint = createRoute(); endpoint.setRAMLPath("/"); + endpoint.setHidden(true); endpoint.description("This endpoint provides a sockjs compliant websocket which can be used to interface with the vert.x eventbus."); if (!isSpecGeneratorContext()) { From 120daa14d26668844e1fda54037aab50fe3701a3 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 Mar 2026 11:04:22 +0100 Subject: [PATCH 53/75] Make the test assertion overridable --- .../core/openapi/OpenAPIEndpointTest.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java index bac06c8d39..e5ce5e7299 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/openapi/OpenAPIEndpointTest.java @@ -21,9 +21,10 @@ import com.gentics.mesh.test.context.AbstractMeshTest; import io.netty.handler.codec.http.HttpResponseStatus; -import io.swagger.parser.OpenAPIParser; -import io.swagger.parser.models.ParseOptions; -import io.swagger.parser.models.SwaggerParseResult; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.ParseOptions; +import io.swagger.v3.parser.core.models.SwaggerParseResult; @MeshTestSetting(testSize = FULL, startServer = true) @RunWith(Parameterized.class) @@ -71,14 +72,15 @@ public void testOpenAPI() throws IOException { assertNoErrors(input); } - protected void assertNoErrors(String input) { - OpenAPIParser parser = new OpenAPIParser(); + protected OpenAPI assertNoErrors(String input) { + OpenAPIV3Parser parser = new OpenAPIV3Parser(); ParseOptions options = new ParseOptions(); options.setResolve(true); options.setResolveFully(true); + SwaggerParseResult result = noClient - ? parser.readLocation(input, null, null) - : parser.readContents(input, null, null); + ? parser.readLocation(input, null, options) + : parser.readContents(input, null, options); if (apiDisabled) { assertThat(result.getOpenAPI()).as("Parsed API").isNull(); @@ -86,6 +88,7 @@ protected void assertNoErrors(String input) { } else { assertThat(result.getOpenAPI()).as("Parsed API").isNotNull(); assertThat(result.getMessages()).as("Error messages").isNullOrEmpty(); - } + } + return result.getOpenAPI(); } } From 641a8d1b932b1ea1860f94f656b594b7deec5e4d Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 Mar 2026 11:16:12 +0100 Subject: [PATCH 54/75] Missing dependency --- tests/tests-core/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index 0cbafd416e..8be2f6beda 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -86,9 +86,9 @@ 4.4.13 - io.swagger + io.swagger.parser.v3 swagger-parser - 2.0.0-rc1 + 2.1.39 jakarta.annotation From 2ee36eae0b50460774491485e767091d650925c7 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 16 Mar 2026 15:43:00 +0100 Subject: [PATCH 55/75] Spect validation fixes --- .../core/endpoint/admin/AdminHandler.java | 13 +- .../core/endpoint/group/GroupEndpoint.java | 38 ++-- .../ProjectMicroschemaEndpoint.java | 4 + .../schema/ProjectSchemaEndpoint.java | 4 + .../core/endpoint/schema/SchemaEndpoint.java | 45 +++-- .../endpoint/webroot/WebRootEndpoint.java | 44 +++-- .../mesh/endpoint/admin/HibAdminHandler.java | 6 +- .../test/openapi/OpenAPIMeshRestClient.java | 180 ++++++++++-------- 8 files changed, 209 insertions(+), 125 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java index 0a60c66580..eaf0d1da2a 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminHandler.java @@ -32,7 +32,9 @@ import com.gentics.mesh.core.verticle.handler.WriteLock; import com.gentics.mesh.distributed.coordinator.Coordinator; import com.gentics.mesh.distributed.coordinator.MasterServer; +import com.gentics.mesh.etc.config.Format; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.etc.config.Version; import com.gentics.mesh.generator.RAMLGenerator; import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.router.RouterStorageImpl; @@ -231,7 +233,16 @@ public MeshServerInfoModel getMeshServerInfoModel(InternalActionContext ac) { * * @param ac internal action context. */ - public abstract void handleOpenAPIv3(InternalActionContext ac); + public void handleOpenAPIv3(InternalActionContext ac) { + handleOpenAPIv3(ac, options.getDefaultOpenAPIFormat(), options.getDefaultOpenAPIVersion()); + } + + /** + * Handle a request of generating OpenAPI spec for all the routes, using the configured format and version. + * + * @param ac internal action context. + */ + public abstract void handleOpenAPIv3(InternalActionContext ac, Format format, Version version); /** * Generate and return the RAML of the server. diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java index 95dff5296c..46678cf695 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java @@ -19,6 +19,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -183,22 +184,37 @@ private void addDeleteHandler() { // TODO Determine what we should do about conflicting group names. Should we let neo4j handle those cases? // TODO update timestamps private void addUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:groupUuid"); - endpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); - endpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); - endpoint.method(POST); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.exampleRequest(groupExamples.getGroupUpdateRequest("New group name")); - endpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated group."); - endpoint.events(GROUP_UPDATED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:groupUuid"); + updateEndpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); + updateEndpoint.description("Update the group with the given uuid."); + updateEndpoint.method(PUT); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.exampleRequest(groupExamples.getGroupUpdateRequest("Group name")); + updateEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("Group name"), "Updated group."); + updateEndpoint.events(GROUP_UPDATED); + updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("groupUuid"); crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:groupUuid"); + upsertEndpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); + upsertEndpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); + upsertEndpoint.method(POST); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.exampleRequest(groupExamples.getGroupCreateRequest("New group name")); + upsertEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); + upsertEndpoint.events(GROUP_CREATED, GROUP_UPDATED); + upsertEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("groupUuid"); + crudHandler.handleUpdate(ac, uuid); + }, isOrderedBlockingHandlers()); } private void addReadHandler() { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/ProjectMicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/ProjectMicroschemaEndpoint.java index 8cc0e1824a..47e90e2bda 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/ProjectMicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/ProjectMicroschemaEndpoint.java @@ -18,6 +18,8 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.parameter.impl.GenericParametersImpl; +import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractProjectEndpoint; @@ -56,6 +58,8 @@ private void addReadHandlers() { endpoint.path("/"); endpoint.method(GET); endpoint.produces(APPLICATION_JSON); + endpoint.addQueryParameters(PagingParametersImpl.class); + endpoint.addQueryParameters(GenericParametersImpl.class); endpoint.description("Read all microschemas which are assigned to the project."); endpoint.exampleResponse(OK, microschemaExamples.getMicroschemaListResponse(), "List of assigned microschemas."); endpoint.handler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/ProjectSchemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/ProjectSchemaEndpoint.java index 72906b4f05..9343e5a8ea 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/ProjectSchemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/ProjectSchemaEndpoint.java @@ -20,6 +20,8 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.parameter.impl.GenericParametersImpl; +import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractProjectEndpoint; @@ -75,6 +77,8 @@ private void addReadHandlers() { readAll.path("/"); readAll.method(GET); readAll.description("Read multiple schemas and return a paged list response."); + readAll.addQueryParameters(PagingParametersImpl.class); + readAll.addQueryParameters(GenericParametersImpl.class); readAll.exampleResponse(OK, schemaExamples.getSchemaListResponse(), "Loaded list of schemas."); readAll.produces(APPLICATION_JSON); readAll.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java index 0a51b7c0b9..4dd48c1d67 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java @@ -14,6 +14,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -133,18 +134,38 @@ private void addDiffHandler() { } private void addUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:schemaUuid"); - endpoint.addUriParameter("schemaUuid", "Uuid of the schema.", SCHEMA_VEHICLE_UUID); - endpoint.method(POST); - endpoint.description("Update the schema."); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.addQueryParameters(SchemaUpdateParametersImpl.class); - endpoint.exampleRequest(schemaExamples.getSchemaUpdateRequest()); - endpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated schema."); - endpoint.events(SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:schemaUuid"); + updateEndpoint.addUriParameter("schemaUuid", "Uuid of the schema.", SCHEMA_VEHICLE_UUID); + updateEndpoint.method(PUT); + updateEndpoint.description("Update the schema."); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.addQueryParameters(SchemaUpdateParametersImpl.class); + updateEndpoint.exampleRequest(schemaExamples.getSchemaUpdateRequest()); + updateEndpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated schema."); + updateEndpoint.events(SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); + updateEndpoint.blockingHandler(rc -> { + // Update operations should always be executed sequentially - never in parallel + synchronized (schemaLock.mutex()) { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("schemaUuid"); + crudHandler.handleUpdate(ac, uuid); + } + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:schemaUuid"); + upsertEndpoint.addUriParameter("schemaUuid", "Uuid of the schema.", SCHEMA_VEHICLE_UUID); + upsertEndpoint.method(POST); + upsertEndpoint.description("Update or create the schema."); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(SchemaUpdateParametersImpl.class); + upsertEndpoint.exampleRequest(schemaExamples.getSchemaCreateRequest()); + upsertEndpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated or new schema."); + upsertEndpoint.events(SCHEMA_CREATED, SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); + upsertEndpoint.blockingHandler(rc -> { // Update operations should always be executed sequentially - never in parallel synchronized (schemaLock.mutex()) { InternalActionContext ac = wrap(rc); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java index f2f3f61bac..7914ef82e2 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java @@ -6,7 +6,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.vertx.core.http.HttpMethod.GET; -import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.*; import javax.inject.Inject; @@ -69,21 +69,37 @@ private void addPathReadHandler() { } private void addPathUpdateCreateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.pathRegex("\\/(.*)"); - endpoint.setRAMLPath("/{path}"); - endpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); - endpoint.method(POST); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.pathRegex("\\/(.*)"); + updateEndpoint.setRAMLPath("/{path}"); + updateEndpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); + updateEndpoint.method(PUT); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); - endpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); - endpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "Updated node."); - endpoint.exampleResponse(CREATED, nodeExamples.getNodeResponse2(), "Created node."); - endpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); + updateEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); + updateEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "Updated node."); + updateEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); - endpoint.description("Update or create a node for the given path."); - endpoint.blockingHandler(rc -> { + updateEndpoint.description("Update a node for the given path."); + updateEndpoint.blockingHandler(rc -> { + handler.handleUpdateCreatePath(rc, POST); + }, isOrderedBlockingHandlers()); + + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.pathRegex("\\/(.*)"); + upsertEndpoint.setRAMLPath("/{path}"); + upsertEndpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); + upsertEndpoint.method(POST); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + + upsertEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); + upsertEndpoint.exampleResponse(CREATED, nodeExamples.getNodeResponse2(), "Created node."); + upsertEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); + + upsertEndpoint.description("Update or create a node for the given path."); + upsertEndpoint.blockingHandler(rc -> { handler.handleUpdateCreatePath(rc, POST); }, isOrderedBlockingHandlers()); } diff --git a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java index d1f768548a..4cf6afbdce 100644 --- a/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java +++ b/mdm/hibernate-core/src/main/java/com/gentics/mesh/endpoint/admin/HibAdminHandler.java @@ -81,9 +81,9 @@ public void handleCacheClear(InternalActionContext ac) { } @Override - public void handleOpenAPIv3(InternalActionContext ac) { - boolean useVersion31 = options.getDefaultOpenAPIVersion() == Version.V31; - String format = options.getDefaultOpenAPIFormat().name().toLowerCase(); + public void handleOpenAPIv3(InternalActionContext ac, com.gentics.mesh.etc.config.Format formatConf, Version version) { + boolean useVersion31 = version == Version.V31; + String format = formatConf.name().toLowerCase(); // Collect available servers HttpServerConfig httpServerConfig = options.getHttpServerOptions(); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index e0eae2aaf7..0e6073d0cf 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -2,6 +2,7 @@ import java.io.InputStream; import java.util.Arrays; +import java.util.stream.Collectors; import org.openapitools.client.ApiClient; import org.openapitools.client.JSON; @@ -539,106 +540,112 @@ public MeshRequest deleteTagFamily(String projectName, String uui @Override public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPut(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); } @Override public MeshRequest createTagFamily(String projectName, String tagFamilyUuid, TagFamilyCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); } @Override public MeshRequest findTagFamilies(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesGetWithHttpInfo(projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), TagFamilyListResponse.class); } @Override public MeshRequest getTagFamilyRolePermissions(String projectName, String tagFamilyUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidRolePermissionsGetWithHttpInfo(tagFamilyUuid, projectName), ObjectPermissionResponse.class); } @Override public MeshRequest grantTagFamilyRolePermissions(String projectName, String tagFamilyUuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidRolePermissionsPostWithHttpInfo(tagFamilyUuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeTagFamilyRolePermissions(String projectName, String tagFamilyUuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidRolePermissionsPutWithHttpInfo(tagFamilyUuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest webroot(String projectName, String path, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathGetWithHttpInfo(path, projectName, + findParameter(ImageManipulationParameters.FOCAL_POINT_Z_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RECT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters)), MeshWebrootResponse.class); } @Override public MeshRequest webroot(String projectName, String[] pathSegments, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return webroot(projectName, Arrays.stream(pathSegments).collect(Collectors.joining("/")), parameters); } @Override public MeshRequest webrootUpdate(String projectName, String path, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathPutWithHttpInfo(path, projectName, adaptRequest(nodeUpdateRequest)), NodeResponse.class); } @Override public MeshRequest webrootUpdate(String projectName, String[] pathSegments, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return webrootUpdate(projectName, Arrays.stream(pathSegments).collect(Collectors.joining("/")), nodeUpdateRequest, parameters); } @Override public MeshRequest webrootCreate(String projectName, String path, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathPostWithHttpInfo(path, projectName, adaptRequest(nodeCreateRequest)), NodeResponse.class); } @Override public MeshRequest webrootCreate(String projectName, String[] pathSegments, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return webrootCreate(projectName, Arrays.stream(pathSegments).collect(Collectors.joining("/")), nodeCreateRequest, parameters); } @Override public MeshRequest createSchema(SchemaCreateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasPostWithHttpInfo(adaptRequest(request)), SchemaResponse.class); } @Override public MeshRequest createSchema(String uuid, SchemaCreateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, + findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), + findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), + findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), + adaptRequest(request)), SchemaResponse.class); } @Override public MeshRequest findSchemaByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidGetWithHttpInfo(uuid, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), SchemaResponse.class); } @Override public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPutWithHttpInfo(uuid, findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), @@ -647,168 +654,173 @@ public MeshRequest updateSchema(String uuid, SchemaUpdat @Override public MeshRequest diffSchema(String uuid, SchemaModel request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidDiffPostWithHttpInfo(uuid, adaptRequest(request)), SchemaChangesListModel.class); } @Override public MeshRequest deleteSchema(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest findSchemas(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasGetWithHttpInfo( + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), SchemaListResponse.class); } @Override public MeshRequest findMicroschemas(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasGetWithHttpInfo( + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), MicroschemaListResponse.class); } @Override public MeshRequest applyChangesToSchema(String uuid, SchemaChangesListModel changes) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidChangesPostWithHttpInfo(uuid, adaptRequest(changes)), GenericMessageResponse.class); } @Override public MeshRequest assignSchemaToProject(String projectName, String schemaUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectSchemasSchemaUuidPostWithHttpInfo(schemaUuid, projectName), SchemaResponse.class); } @Override public MeshRequest unassignSchemaFromProject(String projectName, String schemaUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectSchemasSchemaUuidDeleteWithHttpInfo(schemaUuid, projectName), EmptyResponse.class); } @Override public MeshRequest findSchemas(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectSchemasGetWithHttpInfo(projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), SchemaListResponse.class); } @Override public MeshRequest assignMicroschemaToProject(String projectName, String microschemaUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectMicroschemasMicroschemaUuidPostWithHttpInfo(microschemaUuid, projectName), MicroschemaResponse.class); } @Override public MeshRequest unassignMicroschemaFromProject(String projectName, String microschemaUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectMicroschemasMicroschemaUuidDeleteWithHttpInfo(microschemaUuid, projectName), EmptyResponse.class); } @Override public MeshRequest findMicroschemas(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectMicroschemasGetWithHttpInfo(projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), MicroschemaListResponse.class); } @Override public MeshRequest getSchemaRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidRolePermissionsGetWithHttpInfo(uuid), ObjectPermissionResponse.class); } @Override public MeshRequest grantSchemaRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeSchemaRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest findGroupByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidGetWithHttpInfo(uuid, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), GroupResponse.class); } @Override public MeshRequest findGroups(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2GroupsGetWithHttpInfo( + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), GroupListResponse.class); } @Override public MeshRequest createGroup(GroupCreateRequest createRequest) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsPostWithHttpInfo(adaptRequest(createRequest)), GroupResponse.class); } @Override public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, adaptRequest(createRequest)), GroupResponse.class); } @Override public MeshRequest updateGroup(String uuid, GroupUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPutWithHttpInfo(uuid, adaptRequest(request)), GroupResponse.class); } @Override public MeshRequest deleteGroup(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest addUserToGroup(String groupUuid, String userUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidUsersUserUuidPostWithHttpInfo(groupUuid, userUuid), GroupResponse.class); } @Override public MeshRequest removeUserFromGroup(String groupUuid, String userUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidUsersUserUuidDeleteWithHttpInfo(groupUuid, userUuid), EmptyResponse.class); } @Override public MeshRequest addRoleToGroup(String groupUuid, String roleUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolesRoleUuidPostWithHttpInfo(groupUuid, roleUuid), GroupResponse.class); } @Override public MeshRequest removeRoleFromGroup(String groupUuid, String roleUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolesRoleUuidDeleteWithHttpInfo(groupUuid, roleUuid), EmptyResponse.class); } @Override public MeshRequest getGroupRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolePermissionsGetWithHttpInfo(uuid), ObjectPermissionResponse.class); } @Override public MeshRequest grantGroupRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeGroupRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override From 3aec435d0a564bd5e1827320e19214e5b2f1faaa Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 19 Mar 2026 13:12:06 +0100 Subject: [PATCH 56/75] Tell apart update and upsert endpoints, as OpenAPI spec forbids it --- .../core/endpoint/branch/BranchEndpoint.java | 25 +++++++-- .../core/endpoint/group/GroupEndpoint.java | 2 +- .../endpoint/handler/AbstractCrudHandler.java | 7 ++- .../microschema/MicroschemaEndpoint.java | 40 ++++++++++---- .../mesh/core/endpoint/node/NodeEndpoint.java | 4 +- .../endpoint/project/ProjectEndpoint.java | 4 +- .../mesh/core/endpoint/role/RoleEndpoint.java | 36 +++++++++---- .../core/endpoint/schema/SchemaEndpoint.java | 2 +- .../core/endpoint/tag/TagCrudHandler.java | 5 +- .../endpoint/tagfamily/TagFamilyEndpoint.java | 45 +++++++++++----- .../mesh/core/endpoint/user/UserEndpoint.java | 52 ++++++++++++++----- .../endpoint/webroot/WebRootEndpoint.java | 4 +- .../core/endpoint/webroot/WebRootHandler.java | 6 ++- .../mesh/rest/MeshLocalClientImpl.java | 4 +- .../core/endpoint/handler/CrudHandler.java | 10 ++++ .../verticle/handler/HandlerUtilities.java | 24 +++++++-- .../client/impl/MeshRestHttpClientImpl.java | 20 +++---- .../mesh/core/branch/BranchEndpointTest.java | 3 +- .../mesh/core/group/GroupEndpointTest.java | 3 +- .../mesh/core/node/NodeEndpointTest.java | 3 +- .../core/project/ProjectEndpointTest.java | 3 +- .../mesh/core/role/RoleEndpointTest.java | 4 +- .../core/schema/MicroschemaEndpointTest.java | 3 +- .../mesh/core/schema/SchemaEndpointTest.java | 3 +- .../mesh/core/tag/TagEndpointTest.java | 4 +- .../core/tagfamily/TagFamilyEndpointTest.java | 5 +- .../mesh/core/user/UserEndpointTest.java | 3 +- .../test/openapi/OpenAPIMeshRestClient.java | 31 +++++++---- 28 files changed, 255 insertions(+), 100 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java index ad32b10715..ce3616d004 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java @@ -20,6 +20,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -237,12 +238,30 @@ private void addUpdateHandler() { crudHandler.handleSetLatest(ac, uuid); }, isOrderedBlockingHandlers()); + InternalEndpointRoute upsertBranch = createRoute(); + upsertBranch.path("/:branchUuid"); + upsertBranch.addUriParameter("branchUuid", "Uuid of the branch", BRANCH_UUID); + upsertBranch + .description("Update the branch with the given uuid. The branch is created if no branch with the specified uuid could be found."); + upsertBranch.method(POST); + upsertBranch.setMutating(true); + upsertBranch.consumes(APPLICATION_JSON); + upsertBranch.produces(APPLICATION_JSON); + upsertBranch.exampleRequest(versioningExamples.createBranchUpdateRequest("Winter Collection Branch")); + upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated branch"); + upsertBranch.events(BRANCH_CREATED, BRANCH_UPDATED); + upsertBranch.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = rc.request().params().get("branchUuid"); + crudHandler.handleUpdate(ac, uuid); + }, isOrderedBlockingHandlers()); + InternalEndpointRoute updateBranch = createRoute(); updateBranch.path("/:branchUuid"); updateBranch.addUriParameter("branchUuid", "Uuid of the branch", BRANCH_UUID); updateBranch - .description("Update the branch with the given uuid. The branch is created if no branch with the specified uuid could be found."); - updateBranch.method(POST); + .description("Update the branch with the given uuid."); + updateBranch.method(PUT); updateBranch.setMutating(true); updateBranch.consumes(APPLICATION_JSON); updateBranch.produces(APPLICATION_JSON); @@ -252,7 +271,7 @@ private void addUpdateHandler() { updateBranch.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = rc.request().params().get("branchUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, false); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java index 46678cf695..59a27666c8 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java @@ -197,7 +197,7 @@ private void addUpdateHandler() { updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("groupUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, false); }, isOrderedBlockingHandlers()); InternalEndpointRoute upsertEndpoint = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java index e3f14aff5f..3da8079137 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java @@ -85,8 +85,13 @@ public void handleRead(InternalActionContext ac, String uuid) { @Override public void handleUpdate(InternalActionContext ac, String uuid) { + handleUpdate(ac, uuid, true); + } + + @Override + public void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting) { validateParameter(uuid, "uuid"); - utils.updateElement(ac, uuid, crudActions()); + utils.updateElement(ac, uuid, crudActions(), createInexisting); } @Override diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java index 7120c2efa7..af6d32614c 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java @@ -14,6 +14,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -162,22 +163,39 @@ private void addDeleteHandler() { } private void addUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:microschemaUuid"); - endpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); - endpoint.method(POST); - endpoint.produces(APPLICATION_JSON); - endpoint.consumes(APPLICATION_JSON); - endpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); + InternalEndpointRoute upserEndpoint = createRoute(); + upserEndpoint.path("/:microschemaUuid"); + upserEndpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); + upserEndpoint.method(POST); + upserEndpoint.produces(APPLICATION_JSON); + upserEndpoint.consumes(APPLICATION_JSON); + upserEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); - endpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); - endpoint.description("Update the microschema with the given uuid."); - endpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); - endpoint.blockingHandler(rc -> { + upserEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); + upserEndpoint.description("Update or create the microschema with the given uuid."); + upserEndpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_CREATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); + upserEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("microschemaUuid"); crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); + + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:microschemaUuid"); + updateEndpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); + updateEndpoint.method(PUT); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); + // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); + updateEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); + updateEndpoint.description("Update the microschema with the given uuid."); + updateEndpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); + updateEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("microschemaUuid"); + crudHandler.handleUpdate(ac, uuid, false); + }, isOrderedBlockingHandlers()); } private void addCreateHandler() { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index 90730bd6d8..eef9fbe5be 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -614,12 +614,12 @@ private void addUpdateHandler() { endpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "Updated node."); endpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); endpoint.exampleResponse(NOT_FOUND, miscExamples.createMessageResponse(), "The node could not be found."); - endpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); + endpoint.events(NODE_UPDATED); endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("nodeUuid"); ac.getVersioningParameters().setVersion("draft"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, false); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java index 629e05de3d..a33f48d0b9 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java @@ -82,7 +82,7 @@ private void addUpdateHandler() { updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("projectUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, false); }, isOrderedBlockingHandlers()); InternalEndpointRoute upsertEndpoint = createRoute(); @@ -95,7 +95,7 @@ private void addUpdateHandler() { upsertEndpoint.produces(APPLICATION_JSON); upsertEndpoint.exampleRequest(projectExamples.getProjectCreateRequest("New project name")); upsertEndpoint.exampleResponse(OK, projectExamples.getProjectResponse("New project name"), "Updated project."); - upsertEndpoint.events(PROJECT_UPDATED); + upsertEndpoint.events(PROJECT_CREATED, PROJECT_UPDATED); upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("projectUuid"); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java index b1b1e06a11..6eafb0c573 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java @@ -14,6 +14,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -117,20 +118,35 @@ private void addDeleteHandler() { } private void addUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:roleUuid"); - endpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); - endpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); - endpoint.method(POST); - endpoint.consumes(APPLICATION_JSON); - endpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); - endpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated role."); - endpoint.events(ROLE_UPDATED, ROLE_CREATED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:roleUuid"); + upsertEndpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); + upsertEndpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); + upsertEndpoint.method(POST); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); + upsertEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated or new role."); + upsertEndpoint.events(ROLE_UPDATED, ROLE_CREATED); + upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("roleUuid"); crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); + + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:roleUuid"); + updateEndpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); + updateEndpoint.description("Update the role with the given uuid."); + updateEndpoint.method(PUT); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); + updateEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated role."); + updateEndpoint.events(ROLE_UPDATED); + updateEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("roleUuid"); + crudHandler.handleUpdate(ac, uuid, false); + }, isOrderedBlockingHandlers()); } private void addReadHandler() { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java index 4dd48c1d67..9dd1da86d3 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java @@ -150,7 +150,7 @@ private void addUpdateHandler() { synchronized (schemaLock.mutex()) { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("schemaUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, false); } }, isOrderedBlockingHandlers()); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java index 875d9510ef..b8152cce72 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java @@ -157,15 +157,16 @@ public void handleCreate(InternalActionContext ac, String tagFamilyUuid) { * The tags tagfamily uuid * @param tagUuid * Uuid of the tag which should be deleted + * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. */ - public void handleUpdate(InternalActionContext ac, String tagFamilyUuid, String tagUuid) { + public void handleUpdate(InternalActionContext ac, String tagFamilyUuid, String tagUuid, boolean createInexisting) { validateParameter(tagFamilyUuid, "tagFamilyUuid"); validateParameter(tagUuid, "tagUuid"); Function tagFamilyLoader = tx -> { return tx.tagFamilyActions().loadByUuid(context(tx, ac), tagFamilyUuid, READ_PERM, true); }; - utils.createOrUpdateElement(ac, tagFamilyLoader, tagUuid, tagActions); + utils.createOrUpdateElement(ac, tagFamilyLoader, tagUuid, tagActions, createInexisting); } /** diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index 2aa1854b1f..5722fdf7dd 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -95,24 +95,41 @@ public void registerEndPoints() { } private void addTagUpdateHandler() { - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:tagFamilyUuid/tags/:tagUuid"); - endpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - endpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); - endpoint.method(POST); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.description("Update the specified tag. The tag is created if no tag with the specified uuid could be found."); - endpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); - endpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated tag."); - endpoint.events(TAG_UPDATED, TAG_CREATED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:tagFamilyUuid/tags/:tagUuid"); + upsertEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + upsertEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + upsertEndpoint.method(POST); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.description("Update the specified tag. The tag is created if no tag with the specified uuid could be found."); + upsertEndpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); + upsertEndpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated tag."); + upsertEndpoint.events(TAG_UPDATED, TAG_CREATED); + upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); String uuid = PathParameters.getTagUuid(rc); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, true); }, isOrderedBlockingHandlers()); + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:tagFamilyUuid/tags/:tagUuid"); + updateEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + updateEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + updateEndpoint.method(PUT); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.description("Update the specified tag."); + updateEndpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); + updateEndpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated tag."); + updateEndpoint.events(TAG_UPDATED); + updateEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); + String uuid = PathParameters.getTagUuid(rc); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, false); + }, isOrderedBlockingHandlers()); } private void addTagCreateHandler() { @@ -348,7 +365,7 @@ private void addTagFamilyUpdateHandler() { updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, false); }, isOrderedBlockingHandlers()); InternalEndpointRoute upsertEndpoint = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java index 583af10605..943bd20dc6 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java @@ -11,6 +11,7 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; +import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -196,6 +197,31 @@ private void addDeleteHandler() { private void addUpdateHandler() { + // Add the user token handler first in order to allow for recovery token handling + getRouter().route("/:userUuid").method(PUT).handler(userTokenHandler); + // Chain the regular auth handler afterwards in order to handle non-token code requests + if (chain != null) { + chain.secure(getRouter().route("/:userUuid").method(PUT)); + } + + InternalEndpointRoute updateEndpoint = createRoute(); + updateEndpoint.path("/:userUuid"); + updateEndpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); + updateEndpoint.description("Update the user with the given uuid."); + updateEndpoint.method(PUT); + updateEndpoint.setMutating(true); + updateEndpoint.consumes(APPLICATION_JSON); + updateEndpoint.produces(APPLICATION_JSON); + updateEndpoint.addQueryParameters(UserParametersImpl.class); + updateEndpoint.exampleRequest(userExamples.getUserUpdateRequest("jdoe42")); + updateEndpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated user response."); + updateEndpoint.events(USER_UPDATED); + updateEndpoint.blockingHandler(rc -> { + InternalActionContext ac = wrap(rc); + String uuid = ac.getParameter("userUuid"); + crudHandler.handleUpdate(ac, uuid, false); + }, isOrderedBlockingHandlers()); + // Add the user token handler first in order to allow for recovery token handling getRouter().route("/:userUuid").method(POST).handler(userTokenHandler); // Chain the regular auth handler afterwards in order to handle non-token code requests @@ -203,19 +229,19 @@ private void addUpdateHandler() { chain.secure(getRouter().route("/:userUuid").method(POST)); } - InternalEndpointRoute endpoint = createRoute(); - endpoint.path("/:userUuid"); - endpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); - endpoint.description("Update the user with the given uuid. The user is created if no user with the specified uuid could be found."); - endpoint.method(POST); - endpoint.setMutating(true); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.addQueryParameters(UserParametersImpl.class); - endpoint.exampleRequest(userExamples.getUserUpdateRequest("jdoe42")); - endpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated user response."); - endpoint.events(USER_UPDATED); - endpoint.blockingHandler(rc -> { + InternalEndpointRoute upsertEndpoint = createRoute(); + upsertEndpoint.path("/:userUuid"); + upsertEndpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); + upsertEndpoint.description("Update the user with the given uuid. The user is created if no user with the specified uuid could be found."); + upsertEndpoint.method(POST); + upsertEndpoint.setMutating(true); + upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UserParametersImpl.class); + upsertEndpoint.exampleRequest(userExamples.getUserCreateRequest("jdoe42")); + upsertEndpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated or created user response."); + upsertEndpoint.events(USER_CREATED, USER_UPDATED); + upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("userUuid"); crudHandler.handleUpdate(ac, uuid); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java index 7914ef82e2..449800b186 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java @@ -83,7 +83,7 @@ private void addPathUpdateCreateHandler() { updateEndpoint.description("Update a node for the given path."); updateEndpoint.blockingHandler(rc -> { - handler.handleUpdateCreatePath(rc, POST); + handler.handleUpdateCreatePath(rc, POST, false); }, isOrderedBlockingHandlers()); InternalEndpointRoute upsertEndpoint = createRoute(); @@ -100,7 +100,7 @@ private void addPathUpdateCreateHandler() { upsertEndpoint.description("Update or create a node for the given path."); upsertEndpoint.blockingHandler(rc -> { - handler.handleUpdateCreatePath(rc, POST); + handler.handleUpdateCreatePath(rc, POST, true); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java index 3f0a175441..837e9f676f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java @@ -141,7 +141,7 @@ public void handleGetPath(RoutingContext rc) { } - public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method) { + public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method, boolean createInexisting) { InternalActionContext ac = new InternalRoutingActionContextImpl(rc); String path = rc.request().path().substring( rc.mountPoint().length()); @@ -179,7 +179,7 @@ public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method) { } ac.setBody(request); return contentDao.getNode(container).getUuid(); - } else { + } else if (createInexisting) { int diff = nodePath.getInitialStack().size() - nodePath.getSegments().size(); if (diff > 1) { String resolvedPath = nodePath.getResolvedPath(); @@ -206,6 +206,8 @@ public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method) { ac.put(WEBROOT_LAST_SEGMENT, nodePath.getInitialStack().firstElement()); ac.setBody(request); return null; + } else { + throw error(NOT_FOUND, "node_not_found_for_path", decodeSegment(nodePath.getTargetPath())); } }); } diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index a5a5ecd9b5..2c087050dc 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -411,7 +411,7 @@ public MeshRequest updateTag(String projectName, String tagFamilyUu ac.setPayloadObject(request); ac.setParameter("tagUuid", uuid); ac.setParameter("tagFamilyUuid", tagFamilyUuid); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, false); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -422,7 +422,7 @@ public MeshRequest createTag(String projectName, String tagFamilyUu ac.setPayloadObject(request); ac.setParameter("tagUuid", uuid); ac.setParameter("tagFamilyUuid", tagFamilyUuid); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java index 6b5d628d40..6c209acc04 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java @@ -41,6 +41,16 @@ public interface CrudHandler { */ void handleUpdate(InternalActionContext ac, String uuid); + /** + * Handle update requests. + * + * @param ac + * @param uuid + * Uuid of the element which should be updated + * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. + */ + void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting); + /** * Handle read list requests. * diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java index 462f6cf88a..ff5d21beb5 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java @@ -7,6 +7,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; import static io.netty.handler.codec.http.HttpResponseStatus.OK; @@ -161,6 +162,20 @@ public , RM extends RestModel> void updateElement(I createOrUpdateElement(ac, uuid, actions); } + /** + * Locate and update or create the element using the action context data. + * + * @param ac + * @param uuid + * Uuid of the element which should be updated + * @param actions + * Handler which provides the root vertex which should be used when loading the element + */ + public , RM extends RestModel> void updateElement(InternalActionContext ac, String uuid, + DAOActions actions, boolean createInexisting) { + createOrUpdateElement(ac, null, uuid, actions, createInexisting); + } + /** * Handle a create/update of the given element by uuid * @@ -177,7 +192,7 @@ public , RM extends RestModel> void updateElement(I */ public , RM extends RestModel> void createOrUpdateElement(InternalActionContext ac, String uuid, DAOActions actions) { - createOrUpdateElement(ac, null, uuid, actions); + createOrUpdateElement(ac, null, uuid, actions, true); } /** @@ -189,9 +204,10 @@ public , RM extends RestModel> void createOrUpdateE * @param uuid * Uuid of the element to create or update. If null, an element will be created with random Uuid * @param actions + * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. */ public , RM extends RestModel> void createOrUpdateElement(InternalActionContext ac, Function parentLoader, - String uuid, DAOActions actions) { + String uuid, DAOActions actions, boolean createInexisting) { ac.setHttpServerConfig(meshOptions.getHttpServerOptions()); try (WriteLock lock = writeLock.lock(ac)) { AtomicBoolean created = new AtomicBoolean(false); @@ -215,13 +231,15 @@ public , RM extends RestModel> void createOrUpdateE actions.update(tx, updateElement, ac, bac.batch()); RM model = actions.transformToRestSync(tx, updateElement, ac, 0); return model; - } else { + } else if (createInexisting) { created.set(true); T createdElement = actions.create(tx, ac, bac.batch(), uuid); RM model = actions.transformToRestSync(tx, createdElement, ac, 0); String path = actions.getAPIPath(tx, ac, createdElement); ac.setLocation(path); return model; + } else { + throw error(NOT_FOUND, "object_not_found_for_uuid", uuid); } }, model -> ac.send(model, created.get() ? CREATED : OK)); } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 9526fd5738..fe31977c67 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -176,7 +176,7 @@ public MeshRequest updateNode(String projectName, String uuid, Nod ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(nodeUpdateRequest, "nodeUpdateRequest must not be null"); - return prepareRequest(POST, "/" + encodeSegment(projectName) + "/nodes/" + uuid + getQuery(getConfig(), parameters), NodeResponse.class, + return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/nodes/" + uuid + getQuery(getConfig(), parameters), NodeResponse.class, nodeUpdateRequest); } @@ -280,7 +280,7 @@ public MeshRequest findTagByUuid(String projectName, String tagFami public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest tagUpdateRequest) { Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(tagUpdateRequest, "tagUpdateRequest must not be null"); - return prepareRequest(POST, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid + "/tags/" + uuid, TagResponse.class, + return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid + "/tags/" + uuid, TagResponse.class, tagUpdateRequest); } @@ -437,7 +437,7 @@ public MeshRequest createProject(String uuid, ProjectCreateRequ public MeshRequest updateProject(String uuid, ProjectUpdateRequest projectUpdateRequest) { Objects.requireNonNull(uuid, "uuid must not be null"); Objects.requireNonNull(projectUpdateRequest, "projectUpdateRequest must not be null"); - return prepareRequest(POST, "/projects/" + uuid, ProjectResponse.class, projectUpdateRequest); + return prepareRequest(PUT, "/projects/" + uuid, ProjectResponse.class, projectUpdateRequest); } @Override @@ -522,7 +522,7 @@ public MeshRequest updateTagFamily(String projectName, String Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(tagFamilyUuid, "tagFamilyUuid must not be null"); Objects.requireNonNull(tagFamilyUpdateRequest, "tagFamilyUpdateRequest must not be null"); - return prepareRequest(POST, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid, TagFamilyResponse.class, + return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid, TagFamilyResponse.class, tagFamilyUpdateRequest); } @@ -563,7 +563,7 @@ public MeshRequest createGroup(String uuid, GroupCreateRequest gr public MeshRequest updateGroup(String uuid, GroupUpdateRequest groupUpdateRequest) { Objects.requireNonNull(uuid, "uuid must not be null"); Objects.requireNonNull(groupUpdateRequest, "groupUpdateRequest must not be null"); - return prepareRequest(POST, "/groups/" + uuid, GroupResponse.class, groupUpdateRequest); + return prepareRequest(PUT, "/groups/" + uuid, GroupResponse.class, groupUpdateRequest); } @Override @@ -600,7 +600,7 @@ public MeshRequest createUser(String uuid, UserCreateRequest userC public MeshRequest updateUser(String uuid, UserUpdateRequest userUpdateRequest, ParameterProvider... parameters) { Objects.requireNonNull(uuid, "uuid must not be null"); Objects.requireNonNull(userUpdateRequest, "userUpdateRequest must not be null"); - return prepareRequest(POST, "/users/" + uuid + getQuery(getConfig(), parameters), UserResponse.class, userUpdateRequest); + return prepareRequest(PUT, "/users/" + uuid + getQuery(getConfig(), parameters), UserResponse.class, userUpdateRequest); } @Override @@ -784,7 +784,7 @@ public MeshRequest createSchema(String uuid, SchemaCreateRequest @Override public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { - return prepareRequest(POST, "/roles/" + uuid, RoleResponse.class, restRole); + return prepareRequest(PUT, "/roles/" + uuid, RoleResponse.class, restRole); } @Override @@ -796,7 +796,7 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid @Override public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { Objects.requireNonNull(uuid, "uuid must not be null"); - return prepareRequest(POST, "/schemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); + return prepareRequest(PUT, "/schemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); } @Override @@ -1295,7 +1295,7 @@ public MeshRequest findMicroschemas(ParameterProvider.. @Override public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { Objects.requireNonNull(uuid, "uuid must not be null"); - return prepareRequest(POST, "/microschemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); + return prepareRequest(PUT, "/microschemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); } @Override @@ -1363,7 +1363,7 @@ public MeshRequest updateBranch(String projectName, String branc Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(branchUuid, "branchUuid must not be null"); - return prepareRequest(POST, "/" + encodeSegment(projectName) + "/branches/" + branchUuid, BranchResponse.class, request); + return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/branches/" + branchUuid, BranchResponse.class, request); } @Override diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java index 5470617212..8f03aea6b8 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java @@ -734,7 +734,8 @@ public void testUpdateByUUIDWithoutPerm() throws Exception { public void testUpdateWithBogusUuid() throws GenericRestException, Exception { BranchUpdateRequest request = new BranchUpdateRequest(); // request.setActive(false); - call(() -> client().updateBranch(PROJECT_NAME, "bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateBranch(PROJECT_NAME, uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java index c0c29ad56b..6df9772555 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java @@ -504,7 +504,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { final String name = "New Name"; GroupUpdateRequest request = new GroupUpdateRequest(); request.setName(name); - call(() -> client().updateGroup("bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateGroup(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java index b13324face..ebc5caf481 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java @@ -1895,7 +1895,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { NodeParametersImpl parameters = new NodeParametersImpl(); parameters.setLanguages("en", "de"); - call(() -> client().updateNode(PROJECT_NAME, "bogus", request, parameters), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateNode(PROJECT_NAME, uuid, request, parameters), NOT_FOUND, "object_not_found_for_uuid", uuid); } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java index 4892b6327b..83e296be68 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java @@ -656,7 +656,8 @@ public void testUpdate() throws Exception { public void testUpdateWithBogusUuid() throws GenericRestException, Exception { ProjectUpdateRequest request = new ProjectUpdateRequest(); request.setName("new Name"); - call(() -> client().updateProject("bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateProject(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java index 31af95d39a..b32fd8d387 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java @@ -18,6 +18,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -462,7 +463,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { RoleUpdateRequest request = new RoleUpdateRequest(); request.setName("renamed role"); - call(() -> client().updateRole("bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateRole(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java index 0d279656be..f2da141c61 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java @@ -340,7 +340,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { MicroschemaUpdateRequest request = new MicroschemaUpdateRequest(); request.setName("new-name"); - call(() -> client().updateMicroschema("bogus", request), NOT_FOUND, "object_not_found_for_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateMicroschema(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); HibMicroschema reloaded = tx.microschemaDao().findByUuid(microschema.getUuid()); assertEquals("The name should not have been changed.", oldName, reloaded.getName()); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java index 433befaa22..efa913eb1a 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java @@ -611,7 +611,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { SchemaUpdateRequest request = new SchemaUpdateRequest(); request.setName("new-name"); - call(() -> client().updateSchema("bogus", request), NOT_FOUND, "object_not_found_for_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateSchema(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); HibSchema reloaded = schemaDao.findByUuid(schema.getUuid()); assertEquals("The name should not have been changed.", oldName, reloaded.getName()); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java index b9ca098a86..f59b5235e4 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java @@ -24,6 +24,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -671,9 +672,10 @@ public void testReadByUUIDWithMissingPermission() throws Exception { public void testUpdateWithBogusUuid() throws GenericRestException, Exception { TagUpdateRequest request = new TagUpdateRequest(); request.setName("newName"); + String uuid = UUIDUtil.randomUUID(); try (Tx tx = tx()) { HibTagFamily parentTagFamily = tagFamily("colors"); - call(() -> client().updateTag(PROJECT_NAME, parentTagFamily.getUuid(), "bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + call(() -> client().updateTag(PROJECT_NAME, parentTagFamily.getUuid(), uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java index 3df5271333..12c6026577 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java @@ -22,6 +22,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; +import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -529,8 +530,8 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { TagFamilyUpdateRequest request = new TagFamilyUpdateRequest(); request.setName("new Name"); - call(() -> client().updateTagFamily(PROJECT_NAME, "bogus", request), BAD_REQUEST, "error_illegal_uuid", - "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateTagFamily(PROJECT_NAME, uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java index 8437bea72e..9b29c0e502 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java @@ -696,7 +696,8 @@ public void testUpdateWithSpecialCharacters() throws Exception { public void testUpdateWithBogusUuid() throws GenericRestException, Exception { UserUpdateRequest request = new UserUpdateRequest(); request.setUsername("New Name"); - call(() -> client().updateUser("bogus", request), BAD_REQUEST, "error_illegal_uuid", "bogus"); + String uuid = UUIDUtil.randomUUID(); + call(() -> client().updateUser(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 0e6073d0cf..538d79ce53 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -825,34 +825,45 @@ public MeshRequest revokeGroupRolePermissions(String u @Override public MeshRequest findUserByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidGetWithHttpInfo(uuid, + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), UserResponse.class); } @Override public MeshRequest findUsers(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersGetWithHttpInfo( + findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), UserListResponse.class); } @Override public MeshRequest createUser(UserCreateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersPostWithHttpInfo(adaptRequest(request)), UserResponse.class); } @Override public MeshRequest createUser(String uuid, UserCreateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, getAPIKey(), adaptRequest(request)), UserResponse.class); } @Override public MeshRequest updateUser(String uuid, UserUpdateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPutWithHttpInfo(uuid, getAPIKey(), adaptRequest(request)), UserResponse.class); } @Override From 80b96abb22db598dc38f9e564f81ef5af59b2d42 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 19 Mar 2026 16:40:47 +0100 Subject: [PATCH 57/75] Regression --- .../gentics/mesh/core/endpoint/branch/BranchEndpoint.java | 2 +- .../core/endpoint/microschema/MicroschemaCrudHandler.java | 4 ++-- .../gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java | 4 ++-- .../com/gentics/mesh/core/branch/BranchEndpointTest.java | 3 ++- .../java/com/gentics/mesh/core/group/GroupEndpointTest.java | 2 +- .../java/com/gentics/mesh/core/node/NodeEndpointTest.java | 5 ----- .../com/gentics/mesh/core/schema/SchemaEndpointTest.java | 3 ++- .../java/com/gentics/mesh/core/user/UserEndpointTest.java | 5 ----- 8 files changed, 10 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java index ce3616d004..4ebd7c099e 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java @@ -248,7 +248,7 @@ private void addUpdateHandler() { upsertBranch.consumes(APPLICATION_JSON); upsertBranch.produces(APPLICATION_JSON); upsertBranch.exampleRequest(versioningExamples.createBranchUpdateRequest("Winter Collection Branch")); - upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated branch"); + upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated or new branch"); upsertBranch.events(BRANCH_CREATED, BRANCH_UPDATED); upsertBranch.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java index 44c1ad2701..b255103510 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java @@ -62,7 +62,7 @@ public MicroschemaCrudHandler(Database db, MicroschemaComparatorImpl comparator, } @Override - public void handleUpdate(InternalActionContext ac, String uuid) { + public void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting) { validateParameter(uuid, "uuid"); try (WriteLock lock = writeLock.lock(ac)) { @@ -78,7 +78,7 @@ public void handleUpdate(InternalActionContext ac, String uuid) { } MicroschemaDao microschemaDao = tx.microschemaDao(); HibMicroschema microschema = microschemaDao.findByUuid(uuid); - return microschema == null; + return microschema == null && createInexisting; }); // Delegate to handle update which will create the microschema diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java index a19cf16472..64b1f66a6f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java @@ -77,7 +77,7 @@ public SchemaCrudHandler(Database db, SchemaComparatorImpl comparator, Lazy client().findBranchByUuid(PROJECT_NAME, "bogus"), NOT_FOUND, "object_not_found_for_uuid", "bogus"); + String bogusUuid = UUIDUtil.randomUUID(); + call(() -> client().findBranchByUuid(PROJECT_NAME, bogusUuid), NOT_FOUND, "object_not_found_for_uuid", bogusUuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java index 6df9772555..6ad3699cb8 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java @@ -363,7 +363,7 @@ public void testReadByUUIDWithMissingPermission() throws Exception { @Test public void testReadGroupWithBogusUUID() throws Exception { - final String bogusUuid = "sadgasdasdg"; + final String bogusUuid = UUIDUtil.randomUUID(); call(() -> client().findGroupByUuid(bogusUuid), NOT_FOUND, "object_not_found_for_uuid", bogusUuid); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java index ebc5caf481..b4ad375b00 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java @@ -1695,11 +1695,6 @@ public void testReadByUUIDWithMissingPermission() throws Exception { } - @Test - public void testReadNodeByBogusUUID() throws Exception { - call(() -> client().findNodeByUuid(PROJECT_NAME, "bogusUUID"), NOT_FOUND, "object_not_found_for_uuid", "bogusUUID"); - } - @Test public void testReadNodeByInvalidUUID() throws Exception { String uuid = "dde8ba06bb7211e4897631a9ce2772f5"; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java index efa913eb1a..14f95195e8 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java @@ -403,7 +403,8 @@ public void testReadByUUIDWithMissingPermission() throws Exception { @Test public void testReadSchemaByInvalidUUID() throws Exception { - call(() -> client().findSchemaByUuid("bogus"), NOT_FOUND, "object_not_found_for_uuid", "bogus"); + String bogusUuid = UUIDUtil.randomUUID(); + call(() -> client().findSchemaByUuid(bogusUuid), NOT_FOUND, "object_not_found_for_uuid", bogusUuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java index 9b29c0e502..3cac86b568 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java @@ -1515,11 +1515,6 @@ public void testDeleteByUUIDWithNoPermission() throws Exception { } } - @Test(expected = NullPointerException.class) - public void testDeleteWithUuidNull() throws Exception { - call(() -> client().deleteUser(null), NOT_FOUND, "object_not_found_for_uuid", "null"); - } - @Test @Ignore public void testReadOwnCreatedUser() { From 97ffd76f031031572465d26a85c7222be6678130 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 20 Mar 2026 17:03:03 +0100 Subject: [PATCH 58/75] Regression --- .../core/endpoint/schema/SchemaCrudHandler.java | 2 +- tests/tests-core/pom.xml | 17 +++++++++++++++++ .../mesh/core/group/GroupEndpointTest.java | 4 ++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java index 64b1f66a6f..22ad9a4cd8 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java @@ -98,7 +98,7 @@ public void handleUpdate(InternalActionContext ac, String uuid, boolean createIn // Delegate to handle update which will create the schema if (delegateToCreate) { ac.skipWriteLock(); - super.handleUpdate(ac, uuid); + super.handleUpdate(ac, uuid, createInexisting); return; } diff --git a/tests/tests-core/pom.xml b/tests/tests-core/pom.xml index 8be2f6beda..1f95f9ec7e 100644 --- a/tests/tests-core/pom.xml +++ b/tests/tests-core/pom.xml @@ -24,6 +24,12 @@ com.gentics.mesh tests-mesh-common + + + com.github.java-json-tools + json-schema-validator + + com.gentics.mesh @@ -79,6 +85,17 @@ org.mock-server mockserver-junit-rule 5.9.0 + + + com.github.java-json-tools + json-schema-validator + + + + + com.github.java-json-tools + json-schema-validator + 2.2.14 org.apache.httpcomponents diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java index 6ad3699cb8..eb48608f70 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java @@ -113,9 +113,9 @@ public void testCreateWithUuid() throws Exception { final String name = "New Name"; String uuid = UUIDUtil.randomUUID(); - GroupUpdateRequest request = new GroupUpdateRequest(); + GroupCreateRequest request = new GroupCreateRequest(); request.setName(name); - GroupResponse restGroup = call(() -> client().updateGroup(uuid, request)); + GroupResponse restGroup = call(() -> client().createGroup(uuid, request)); waitForSearchIdleEvent(); From ae1ccb5f626aa1920a93226f2bf3e4fa467ff463 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 23 Mar 2026 10:25:51 +0100 Subject: [PATCH 59/75] Regression --- .../mesh/core/endpoint/microschema/MicroschemaCrudHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java index b255103510..a97e464ce6 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java @@ -84,7 +84,7 @@ public void handleUpdate(InternalActionContext ac, String uuid, boolean createIn // Delegate to handle update which will create the microschema if (delegateToCreate) { ac.skipWriteLock(); - super.handleUpdate(ac, uuid); + super.handleUpdate(ac, uuid, createInexisting); return; } From 44bb34672dbad9563c446c20515f4bb365d8ca36 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 23 Mar 2026 17:57:53 +0100 Subject: [PATCH 60/75] Remove dead APIs. Minor fixes. Client impl. --- .../rest/impl/InternalEndpointRouteImpl.java | 15 - .../core/endpoint/admin/AdminEndpoint.java | 10 +- .../microschema/MicroschemaEndpoint.java | 2 +- .../mesh/core/endpoint/role/RoleEndpoint.java | 2 +- .../mesh/rest/MeshLocalClientImpl.java | 35 +- .../mesh/rest/InternalEndpointRoute.java | 24 - .../gentics/mesh/example/MiscExamples.java | 21 +- .../client/impl/MeshRestHttpClientImpl.java | 30 +- .../client/method/AdminClientMethods.java | 38 - .../rest/client/method/RoleClientMethods.java | 2 +- .../core/node/NodeConflictEndpointTest.java | 4 +- .../com/gentics/mesh/plugin/BackupPlugin.java | 101 --- .../test/openapi/OpenAPIMeshRestClient.java | 224 +++--- .../mesh/test/openapi/UpgradedDefaultApi.java | 649 ++++++++++++++++++ 14 files changed, 766 insertions(+), 391 deletions(-) delete mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/plugin/BackupPlugin.java diff --git a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java index 1a875532eb..5ba44c649d 100644 --- a/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java +++ b/common/src/main/java/com/gentics/mesh/rest/impl/InternalEndpointRouteImpl.java @@ -4,13 +4,9 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Set; -import org.codehaus.jettison.json.JSONObject; -import org.raml.model.MimeType; - import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; @@ -69,17 +65,6 @@ public InternalEndpointRoute events(MeshEvent... events) { return this; } - @Override - public InternalEndpointRoute exampleRequest(JSONObject jsonObject) { - HashMap bodyMap = new HashMap<>(); - MimeType mimeType = new MimeType(); - String json = jsonObject.toString(); - mimeType.setExample(json); - bodyMap.put("application/json", mimeType); - this.exampleRequestMap = bodyMap; - return this; - } - @Override public InternalEndpointRoute setInsecure(boolean insecure) { if (insecure) { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index dded5263b5..1f96677865 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -29,6 +29,7 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.ConsistencyCheckParametersImpl; import com.gentics.mesh.parameter.impl.JobParametersImpl; +import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; import com.gentics.mesh.router.route.AbstractInternalEndpoint; @@ -139,16 +140,16 @@ protected void addPluginHandler() { }, isOrderedBlockingHandlers()); InternalEndpointRoute readEndpoint = createRoute(); - readEndpoint.path("/plugins/:uuid"); + readEndpoint.path("/plugins/:id"); readEndpoint.method(GET); readEndpoint.description("Loads deployment information for the plugin with the given id."); readEndpoint.produces(APPLICATION_JSON); - readEndpoint.addUriParameter("uuid", "Uuid of the plugin.", PLUGIN_1_ID); + readEndpoint.addUriParameter("id", "Id of the plugin.", PLUGIN_1_ID); readEndpoint.exampleResponse(OK, adminExamples.createHelloWorldPluginResponse(), "Plugin response."); readEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("uuid"); - pluginHandler.handleRead(ac, uuid); + String id = ac.getParameter("id"); + pluginHandler.handleRead(ac, id); }, false); InternalEndpointRoute readAllEndpoint = createRoute(); @@ -157,6 +158,7 @@ protected void addPluginHandler() { readAllEndpoint.description("Loads deployment information for all deployed plugins."); readAllEndpoint.produces(APPLICATION_JSON); readAllEndpoint.exampleResponse(OK, adminExamples.createPluginListResponse(), "Plugin list response."); + readAllEndpoint.addQueryParameters(PagingParametersImpl.class); readAllEndpoint.blockingHandler(rc -> { pluginHandler.handleReadList(wrap(rc)); }, false); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java index af6d32614c..78714680d8 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java @@ -169,7 +169,7 @@ private void addUpdateHandler() { upserEndpoint.method(POST); upserEndpoint.produces(APPLICATION_JSON); upserEndpoint.consumes(APPLICATION_JSON); - upserEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); + upserEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaCreateRequest()); // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); upserEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); upserEndpoint.description("Update or create the microschema with the given uuid."); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java index 6eafb0c573..865c06ad89 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java @@ -124,7 +124,7 @@ private void addUpdateHandler() { upsertEndpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); upsertEndpoint.method(POST); upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); + upsertEndpoint.exampleRequest(roleExamples.getRoleCreateRequest("New role name")); upsertEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated or new role."); upsertEndpoint.events(ROLE_UPDATED, ROLE_CREATED); upsertEndpoint.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 2c087050dc..ba400ea884 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -123,7 +123,6 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; @@ -1631,36 +1630,6 @@ public MeshRequest graphql(String projectName, GraphQLRequest r return null; } - @Override - public MeshRequest invokeBackup() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeBackup(BackupParameters backupParameters) { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeExport() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeRestore() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeImport() { - // TODO Auto-generated method stub - return null; - } - @Override public MeshRequest issueAPIToken(String userUuid) { // TODO Auto-generated method stub @@ -2032,8 +2001,8 @@ public MeshRequest revokeProjectRolePermissions(String } @Override - public MeshRequest getRoleRolePermissions(String uuid) { - LocalActionContextImpl ac = createContext(ObjectPermissionResponse.class); + public MeshRequest getRoleRolePermissions(String uuid, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectPermissionResponse.class, parameters); roleCrudHandler.handleReadPermissions(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } diff --git a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java index 426f692b4e..3cdd4a3607 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java +++ b/mdm/api/src/main/java/com/gentics/mesh/rest/InternalEndpointRoute.java @@ -1,15 +1,8 @@ package com.gentics.mesh.rest; -import static com.gentics.mesh.core.rest.error.Errors.error; -import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; - -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; - import com.gentics.mesh.core.rest.MeshEvent; import io.vertx.core.Handler; -import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; /** @@ -17,14 +10,6 @@ */ public interface InternalEndpointRoute extends com.gentics.vertx.openapi.metadata.InternalEndpointRoute { - /** - * Set the endpoint json example request via the provided json object. The JSON schema will not be generated. - * - * @param jsonObject - * @return Fluent API - */ - InternalEndpointRoute exampleRequest(JSONObject jsonObject); - /** * Set the events which are emitted by the action of the endpoint. * @@ -56,13 +41,4 @@ public interface InternalEndpointRoute extends com.gentics.vertx.openapi.metadat * @return */ InternalEndpointRoute setInsecure(boolean insecure); - - @Override - default com.gentics.vertx.openapi.metadata.InternalEndpointRoute exampleRequest(JsonObject jsonObject) { - try { - return exampleRequest(new JSONObject(jsonObject.encode())); - } catch (JSONException e) { - throw error(INTERNAL_SERVER_ERROR, "error_internal", e); - } - } } diff --git a/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java b/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java index af44589b25..405e53a27a 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java +++ b/mdm/common/src/main/java/com/gentics/mesh/example/MiscExamples.java @@ -4,9 +4,6 @@ import java.util.Map; import java.util.stream.Stream; -import org.codehaus.jettison.json.JSONException; -import org.codehaus.jettison.json.JSONObject; - import com.gentics.mesh.core.rest.auth.LoginRequest; import com.gentics.mesh.core.rest.auth.TokenResponse; import com.gentics.mesh.core.rest.common.GenericMessageResponse; @@ -63,17 +60,13 @@ public GenericMessageResponse createMessageResponse() { return message; } - public JSONObject getSearchQueryExample() { - JSONObject node = new JSONObject(); - try { - JSONObject query = new JSONObject(); - JSONObject queryString = new JSONObject(); - queryString.put("query", "some name"); - query.put("query_string", queryString); - node.put("query", query); - } catch (JSONException e) { - e.printStackTrace(); - } + public JsonObject getSearchQueryExample() { + JsonObject node = new JsonObject(); + JsonObject query = new JsonObject(); + JsonObject queryString = new JsonObject(); + queryString.put("query", "some name"); + query.put("query_string", queryString); + node.put("query", query); return node; } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 45b2643376..1ea7fa1703 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -105,7 +105,6 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; @@ -1117,31 +1116,6 @@ public MeshRequest searchStatus() { return prepareRequest(GET, "/search/status", SearchStatusResponse.class); } - @Override - public MeshRequest invokeBackup() { - return prepareRequest(POST, "/admin/graphdb/backup", GenericMessageResponse.class); - } - - @Override - public MeshRequest invokeBackup(BackupParameters parameters) { - return prepareRequest(POST, "/admin/graphdb/backup" + getQuery(getConfig(), parameters), GenericMessageResponse.class); - } - - @Override - public MeshRequest invokeExport() { - return prepareRequest(POST, "/admin/graphdb/export", GenericMessageResponse.class); - } - - @Override - public MeshRequest invokeImport() { - return prepareRequest(POST, "/admin/graphdb/import", GenericMessageResponse.class); - } - - @Override - public MeshRequest invokeRestore() { - return prepareRequest(POST, "/admin/graphdb/restore", GenericMessageResponse.class); - } - @Override public MeshRequest checkConsistency(ParameterProvider... parameters) { return prepareRequest(GET, "/admin/consistency/check" + getQuery(getConfig(), parameters), ConsistencyCheckResponse.class); @@ -1928,9 +1902,9 @@ public MeshRequest revokeProjectRolePermissions(String } @Override - public MeshRequest getRoleRolePermissions(String uuid) { + public MeshRequest getRoleRolePermissions(String uuid, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); - return prepareRequest(GET, "/roles/" + uuid + "/rolePermissions", ObjectPermissionResponse.class); + return prepareRequest(GET, "/roles/" + uuid + "/rolePermissions" + getQuery(getConfig(), parameters), ObjectPermissionResponse.class); } @Override diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/AdminClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/AdminClientMethods.java index 18fd0956f8..e219515282 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/AdminClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/AdminClientMethods.java @@ -34,44 +34,6 @@ public interface AdminClientMethods { @Deprecated MeshRequest clusterStatus(); - /** - * Invoke a graph database backup. - * - * @return - */ - MeshRequest invokeBackup(); - - /** - * Invoke a graph database backup. - * @return - */ - MeshRequest invokeBackup(BackupParameters parameters); - - /** - * Invoke a graph database export. - * - * @return - * @deprecated Endpoint currently not supported - */ - @Deprecated - MeshRequest invokeExport(); - - /** - * Invoke a graph database restore. - * - * @return - */ - MeshRequest invokeRestore(); - - /** - * Invoke a graph database import. - * - * @return - * @deprecated Endpoint currently not supported - */ - @Deprecated - MeshRequest invokeImport(); - /** * Invoke a consistency check of the graph database. * diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java index b65478a8ad..3eb73c30f0 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java @@ -110,7 +110,7 @@ public interface RoleClientMethods { * Uuid of the role * @return request */ - MeshRequest getRoleRolePermissions(String uuid); + MeshRequest getRoleRolePermissions(String uuid, ParameterProvider... parameters); /** * Grant permissions on the role to roles diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java index 9f7da55672..35f9b2b908 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java @@ -119,7 +119,7 @@ public void testConflictDetection() { assertThat((List) conflictException.getResponseMessage().getProperty("conflicts")).hasSize(1).containsExactly("teaser"); assertThat(conflictException.getStatusCode()).isEqualTo(CONFLICT.code()); - assertThat(conflictException.getMessage()).isEqualTo("Error:409 in POST " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + assertThat(conflictException.getMessage()).isEqualTo("Error:409 in PUT " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + "?lang=en,de : Conflict Info: " + I18NUtil.get(Locale.ENGLISH, "node_error_conflict_detected")); assertThat(conflictException.getResponseMessage().getProperty("oldVersion")).isEqualTo("1.0"); assertThat(conflictException.getResponseMessage().getProperty("newVersion")).isEqualTo("1.2"); @@ -357,7 +357,7 @@ public void testConflictInMicronode() { assertThat(((List) conflictException.getResponseMessage().getProperty("conflicts"))).hasSize(2).containsExactly("micronode.firstName", "micronode.lastName"); assertThat(conflictException.getStatusCode()).isEqualTo(CONFLICT.code()); - assertThat(conflictException.getMessage()).isEqualTo("Error:409 in POST " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + assertThat(conflictException.getMessage()).isEqualTo("Error:409 in PUT " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + "?lang=en,de : Conflict Info: " + I18NUtil.get(Locale.ENGLISH, "node_error_conflict_detected")); assertThat(conflictException.getResponseMessage().getProperty("oldVersion")).isEqualTo("1.1"); assertThat(conflictException.getResponseMessage().getProperty("newVersion")).isEqualTo("1.2"); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/plugin/BackupPlugin.java b/tests/tests-core/src/main/java/com/gentics/mesh/plugin/BackupPlugin.java deleted file mode 100644 index 776e08d59e..0000000000 --- a/tests/tests-core/src/main/java/com/gentics/mesh/plugin/BackupPlugin.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.gentics.mesh.plugin; - -import static com.gentics.mesh.core.rest.MeshEvent.CLUSTER_DATABASE_CHANGE_STATUS; - -import java.time.Duration; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.pf4j.PluginWrapper; - -import com.gentics.mesh.RestAPIVersion; -import com.gentics.mesh.plugin.env.PluginEnvironment; -import com.gentics.mesh.rest.client.MeshRestClient; -import com.gentics.mesh.rest.client.MeshRestClientConfig; - -import io.reactivex.Completable; -import io.vertx.core.eventbus.MessageConsumer; -import okhttp3.OkHttpClient; - -/** - * Test plugin which will - *
    - *
  1. Invoke the db backup and fail immediately, when initialized the first time
  2. - *
  3. Succeed initialization for every further attempt
  4. - *
- */ -public class BackupPlugin extends AbstractPlugin { - protected static AtomicBoolean firstStart = new AtomicBoolean(true); - - /** - * Create instance - * @param wrapper plugin wrapper - * @param env environment - */ - public BackupPlugin(PluginWrapper wrapper, PluginEnvironment env) { - super(wrapper, env); - } - - @Override - public Completable initialize() { - if (firstStart.getAndSet(false)) { - return invokeBackupAndFail(); - } else { - return Completable.complete(); - } - } - - /** - * Invoke the db back (using a client with timeout set to 1 ms), wait for the CLUSTER_DATABASE_CHANGE_STATUS event and then fail - * @return failing completable - */ - protected Completable invokeBackupAndFail() { - OkHttpClient okHttpClient = new OkHttpClient.Builder() - .callTimeout(Duration.ofMillis(1)) - .connectTimeout(Duration.ofMillis(1)) - .writeTimeout(Duration.ofMillis(1)) - .readTimeout(Duration.ofMillis(1)) - .build(); - - int port = environment().options().getHttpServerOptions().getPort(); - String host = "127.0.0.1"; - MeshRestClient client = MeshRestClient.create(MeshRestClientConfig.newConfig() - .setPort(port) - .setHost(host) - .setBasePath(RestAPIVersion.V1.getBasePath()) - .build(), okHttpClient); - - client.setAPIKey(environment().adminToken()); - - return client.invokeBackup().toCompletable().onErrorResumeNext(t -> waitForEvent(10_000)) - .andThen(Completable.error(new RuntimeException())); - } - - /** - * Wait for the event CLUSTER_DATABASE_CHANGE_STATUS - * @param timeoutMs timeout - * @return completable - */ - protected Completable waitForEvent(int timeoutMs) { - return Completable.fromAction(() -> { - CountDownLatch latch = new CountDownLatch(1); - MessageConsumer consumer = vertx().eventBus().consumer(CLUSTER_DATABASE_CHANGE_STATUS.address); - consumer.handler(msg -> latch.countDown()); - // The completion handler will be invoked once the consumer has been registered - consumer.completion().andThen(res -> { - if (res.failed()) { - throw new RuntimeException("Could not listen to event", res.cause()); - } - }); - try { - if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) { - throw new RuntimeException("Timeout while waiting for event"); - } - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - consumer.unregister(); - }); - } -} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 538d79ce53..ab37a01f41 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -96,12 +96,13 @@ import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; import com.gentics.mesh.json.JsonUtil; -import com.gentics.mesh.parameter.BackupParameters; import com.gentics.mesh.parameter.BranchParameters; +import com.gentics.mesh.parameter.ConsistencyCheckParameters; import com.gentics.mesh.parameter.DeleteParameters; import com.gentics.mesh.parameter.EtagParameters; import com.gentics.mesh.parameter.GenericParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; +import com.gentics.mesh.parameter.IndexMaintenanceParameters; import com.gentics.mesh.parameter.JobParameters; import com.gentics.mesh.parameter.NodeParameters; import com.gentics.mesh.parameter.PagingParameters; @@ -110,8 +111,10 @@ import com.gentics.mesh.parameter.PublishParameters; import com.gentics.mesh.parameter.RolePermissionParameters; import com.gentics.mesh.parameter.SchemaUpdateParameters; +import com.gentics.mesh.parameter.SearchParameters; import com.gentics.mesh.parameter.SortingParameters; import com.gentics.mesh.parameter.VersioningParameters; +import com.gentics.mesh.parameter.impl.ConsistencyCheckParametersImpl; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; import com.gentics.mesh.rest.client.MeshRequest; @@ -868,133 +871,127 @@ public MeshRequest updateUser(String uuid, UserUpdateRequest reque @Override public MeshRequest deleteUser(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest findUsersOfGroup(String groupUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidUsersGetWithHttpInfo(groupUuid, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), UserListResponse.class); } @Override public MeshRequest readUserPermissions(String uuid, String pathToElement) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPermissionsPathGetWithHttpInfo(pathToElement, uuid), UserPermissionResponse.class); } @Override public MeshRequest getUserResetToken(String userUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidResetTokenPostWithHttpInfo(userUuid), UserResetTokenResponse.class); } @Override public MeshRequest issueAPIToken(String userUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidTokenPostWithHttpInfo(userUuid), UserAPITokenResponse.class); } @Override public MeshRequest invalidateAPIToken(String userUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidTokenDeleteWithHttpInfo(userUuid), GenericMessageResponse.class); } @Override public MeshRequest getUserRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidRolePermissionsGetWithHttpInfo(uuid), ObjectPermissionResponse.class); } @Override public MeshRequest grantUserRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeUserRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest findRoleByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidGetWithHttpInfo(uuid, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), RoleResponse.class); } @Override - public MeshRequest findRoles(ParameterProvider... parameter) { - // TODO Auto-generated method stub - return null; + public MeshRequest findRoles(ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesGetWithHttpInfo( + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), RoleListResponse.class); } @Override public MeshRequest createRole(RoleCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesPostWithHttpInfo(adaptRequest(request)), RoleResponse.class); } @Override public MeshRequest createRole(String uuid, RoleCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, adaptRequest(request)), RoleResponse.class); } @Override public MeshRequest deleteRole(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest findRolesForGroup(String groupUuid, ParameterProvider... parameter) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidRolesGetWithHttpInfo(groupUuid, + groupUuid, null, null, groupUuid, groupUuid), RoleListResponse.class); } @Override public MeshRequest updateRolePermissions(String roleUuid, String pathToElement, RolePermissionRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPermissionsPathPostWithHttpInfo(pathToElement, roleUuid, adaptRequest(request)), GenericMessageResponse.class); } @Override public MeshRequest readRolePermissions(String roleUuid, String pathToElement) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPermissionsPathGetWithHttpInfo(pathToElement, roleUuid), RolePermissionResponse.class); } @Override public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, adaptRequest(restRole)), RoleResponse.class); } @Override - public MeshRequest getRoleRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + public MeshRequest getRoleRolePermissions(String uuid, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidGetWithHttpInfo(uuid, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), ObjectPermissionResponse.class); } @Override public MeshRequest grantRoleRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeRoleRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override @@ -1021,32 +1018,44 @@ public Single logout() { @Override public MeshRequest me(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AuthMeGetWithHttpInfo(), UserResponse.class); } @Override public MeshRequest searchNodes(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SearchNodesPostWithHttpInfo(json, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @Override public MeshRequest searchNodesRaw(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2RawSearchNodesPost(json, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters)), ObjectNode.class); } @Override public MeshRequest searchNodes(String projectName, String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectSearchNodesPostWithHttpInfo(json, projectName, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @Override public MeshRequest searchNodesRaw(String projectName, String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectRawSearchNodesPostWithHttpInfo(json, projectName, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters)), ObjectNode.class); } @Override @@ -1172,165 +1181,122 @@ public MeshRequest searchMicroschemasRaw(String json) { @Override public MeshRequest invokeIndexClear(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2SearchClearPostWithHttpInfo( + findParameter(IndexMaintenanceParameters.INDEX_PARAMETER_KEY, parameters)), GenericMessageResponse.class); } @Override public MeshRequest invokeIndexSync(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2SearchSyncPostWithHttpInfo( + findParameter(IndexMaintenanceParameters.INDEX_PARAMETER_KEY, parameters)), GenericMessageResponse.class); } @Override public MeshRequest searchStatus() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SearchStatusGetWithHttpInfo(), SearchStatusResponse.class); } @Override public MeshRequest meshStatus() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminStatusGetWithHttpInfo(), MeshStatusResponse.class); } @Override public MeshRequest clusterStatus() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeBackup() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeBackup(BackupParameters parameters) { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeExport() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeRestore() { - // TODO Auto-generated method stub - return null; - } - - @Override - public MeshRequest invokeImport() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminClusterStatusGetWithHttpInfo(), ClusterStatusResponse.class); } @Override public MeshRequest checkConsistency(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminConsistencyCheckGetWithHttpInfo( + findParameter(ConsistencyCheckParameters.ASYNC_PARAMETER_KEY, parameters)), ConsistencyCheckResponse.class); } @Override public MeshRequest repairConsistency(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminConsistencyRepairPostWithHttpInfo( + findParameter(ConsistencyCheckParameters.ASYNC_PARAMETER_KEY, parameters)), ConsistencyCheckResponse.class); } @Override public MeshRequest debugInfo(String... include) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminDebuginfoGetWithHttpInfo(Arrays.stream(include).collect(Collectors.joining(","))), MeshBinaryResponse.class); } @Override public MeshRequest loadCoordinationMaster() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminCoordinatorMasterGetWithHttpInfo(), CoordinatorMasterResponse.class); } @Override public MeshRequest setCoordinationMaster() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminCoordinatorMasterPostWithHttpInfo(), GenericMessageResponse.class); } @Override public MeshRequest clearCache() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminCacheDeleteWithHttpInfo(), GenericMessageResponse.class); } @Override public MeshRequest deployPlugin(PluginDeploymentRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminPluginsPostWithHttpInfo(adaptRequest(request)), PluginResponse.class); } @Override public MeshRequest findPlugins(ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminPluginsGetWithHttpInfo( + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), PluginListResponse.class); } @Override public MeshRequest findPlugin(String id) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminPluginsIdGetWithHttpInfo(id), PluginResponse.class); } @Override public MeshRequest undeployPlugin(String id) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminPluginsIdDeleteWithHttpInfo(id), GenericMessageResponse.class); } @Override public MeshRequest createMicroschema(MicroschemaCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasPostWithHttpInfo(adaptRequest(request)), MicroschemaResponse.class); } @Override public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, adaptRequest(request)), MicroschemaResponse.class); } @Override public MeshRequest findMicroschemaByUuid(String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidGetWithHttpInfo(uuid, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), MicroschemaResponse.class); } @Override public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPutWithHttpInfo(uuid, adaptRequest(request)), GenericMessageResponse.class); } @Override public MeshRequest deleteMicroschema(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidDeleteWithHttpInfo(uuid), EmptyResponse.class); } @Override public MeshRequest applyChangesToMicroschema(String uuid, SchemaChangesListModel changes) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidChangesPostWithHttpInfo(uuid, adaptRequest(changes)), GenericMessageResponse.class); } @Override public MeshRequest diffMicroschema(String uuid, MicroschemaModel request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidDiffPostWithHttpInfo(uuid, adaptRequest(request)), SchemaChangesListModel.class); } @Override diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java index 3f57b2d158..61a040c790 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java @@ -13,6 +13,7 @@ import org.openapitools.client.ApiResponse; import org.openapitools.client.Pair; import org.openapitools.client.api.DefaultApi; +import org.openapitools.client.model.NodeListResponse; import org.openapitools.client.model.NodeResponse; import com.google.gson.JsonParser; @@ -179,4 +180,652 @@ public okhttp3.Call apiV2ProjectNodesNodeUuidPostAsync(@jakarta.annotation.Nonnu getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; } + + // Missing JSON body fix + /** + * Build call for apiV2SearchNodesPost + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
+ */ + public okhttp3.Call apiV2SearchNodesPostCall(String jsonbody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(jsonbody); + + // create path and map variables + String localVarPath = "/api/v2/search/nodes"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + if (perPage != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("perPage", perPage)); + } + + if (sortBy != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("sortBy", sortBy)); + } + + if (etag != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("etag", etag)); + } + + if (page != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("page", page)); + } + + if (fields != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fields", fields)); + } + + if (branch != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("branch", branch)); + } + + if (order != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call apiV2SearchNodesPostValidateBeforeCall(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + return apiV2SearchNodesPostCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, _callback); + + } + + /** + * + * Invoke a search query for nodes and return a paged list response. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @return NodeListResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
+ */ + public NodeListResponse apiV2SearchNodesPost(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order) throws ApiException { + ApiResponse localVarResp = apiV2SearchNodesPostWithHttpInfo(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order); + return localVarResp.getData(); + } + + /** + * + * Invoke a search query for nodes and return a paged list response. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @return ApiResponse<NodeListResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
+ */ + public ApiResponse apiV2SearchNodesPostWithHttpInfo(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order) throws ApiException { + okhttp3.Call localVarCall = apiV2SearchNodesPostValidateBeforeCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return getApiClient().execute(localVarCall, localVarReturnType); + } + + /** + * (asynchronously) + * Invoke a search query for nodes and return a paged list response. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
+ */ + public okhttp3.Call apiV2SearchNodesPostAsync(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = apiV2SearchNodesPostValidateBeforeCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for apiV2RawSearchNodesPost + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public okhttp3.Call apiV2RawSearchNodesPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/rawSearch/nodes"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call apiV2RawSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + return apiV2RawSearchNodesPostCall(json, wait, _callback); + } + + /** + * + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @return Object + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public Object apiV2RawSearchNodesPost(String json, @jakarta.annotation.Nullable Boolean wait) throws ApiException { + ApiResponse localVarResp = apiV2RawSearchNodesPostWithHttpInfo(json, wait); + return localVarResp.getData(); + } + + /** + * + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @return ApiResponse<Object> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public ApiResponse apiV2RawSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nullable Boolean wait) throws ApiException { + okhttp3.Call localVarCall = apiV2RawSearchNodesPostValidateBeforeCall(json, wait, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return getApiClient().execute(localVarCall, localVarReturnType); + } + + /** + * (asynchronously) + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public okhttp3.Call apiV2RawSearchNodesPostAsync(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = apiV2RawSearchNodesPostValidateBeforeCall(json, wait, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for apiV2ProjectRawSearchNodesPost + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/rawSearch/nodes" + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call apiV2ProjectRawSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + // verify the required parameter 'project' is set + if (project == null) { + throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectRawSearchNodesPost(Async)"); + } + + return apiV2ProjectRawSearchNodesPostCall(json, project, wait, _callback); + + } + + /** + * + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @return Object + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public Object apiV2ProjectRawSearchNodesPost(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait) throws ApiException { + ApiResponse localVarResp = apiV2ProjectRawSearchNodesPostWithHttpInfo(json, project, wait); + return localVarResp.getData(); + } + + /** + * + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @return ApiResponse<Object> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public ApiResponse apiV2ProjectRawSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait) throws ApiException { + okhttp3.Call localVarCall = apiV2ProjectRawSearchNodesPostValidateBeforeCall(json, project, wait, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return getApiClient().execute(localVarCall, localVarReturnType); + } + + /** + * (asynchronously) + * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectRawSearchNodesPostAsync(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = apiV2ProjectRawSearchNodesPostValidateBeforeCall(json, project, wait, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } + + /** + * Build call for apiV2ProjectSearchNodesPost + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @param _callback Callback for upload/download progress + * @return Call to execute + * @throws ApiException If fail to serialize the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/search/nodes" + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + if (perPage != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("perPage", perPage)); + } + + if (sortBy != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("sortBy", sortBy)); + } + + if (etag != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("etag", etag)); + } + + if (page != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("page", page)); + } + + if (fields != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fields", fields)); + } + + if (order != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + @SuppressWarnings("rawtypes") + private okhttp3.Call apiV2ProjectSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + // verify the required parameter 'project' is set + if (project == null) { + throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectSearchNodesPost(Async)"); + } + + return apiV2ProjectSearchNodesPostCall(json, project, wait, perPage, sortBy, etag, page, fields, order, _callback); + + } + + /** + * + * Invoke a search query for nodes and return a paged list response. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @return NodeListResponse + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
+ */ + public NodeListResponse apiV2ProjectSearchNodesPost(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order) throws ApiException { + ApiResponse localVarResp = apiV2ProjectSearchNodesPostWithHttpInfo(json, project, wait, perPage, sortBy, etag, page, fields, order); + return localVarResp.getData(); + } + + /** + * + * Invoke a search query for nodes and return a paged list response. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @return ApiResponse<NodeListResponse> + * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
+ */ + public ApiResponse apiV2ProjectSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order) throws ApiException { + okhttp3.Call localVarCall = apiV2ProjectSearchNodesPostValidateBeforeCall(json, project, wait, perPage, sortBy, etag, page, fields, order, null); + Type localVarReturnType = new TypeToken(){}.getType(); + return getApiClient().execute(localVarCall, localVarReturnType); + } + + /** + * (asynchronously) + * Invoke a search query for nodes and return a paged list response. + * @param project (required) + * @param wait Specify whether search should wait for the search to be idle before responding. (optional) + * @param perPage Number of elements per page. (optional) + * @param sortBy Field name to sort the result by. (optional) + * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) + * @param page Number of page to be loaded. (optional) + * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) + * @param order Field order (ASC/DESC) to sort the result by. (optional) + * @param _callback The callback to be executed when the API call finishes + * @return The request call + * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @http.response.details + + + + + +
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
+ */ + public okhttp3.Call apiV2ProjectSearchNodesPostAsync(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = apiV2ProjectSearchNodesPostValidateBeforeCall(json, project, wait, perPage, sortBy, etag, page, fields, order, _callback); + Type localVarReturnType = new TypeToken(){}.getType(); + getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); + return localVarCall; + } } From bc56fc5039ca8c628ae340843737f5c8af1101ce Mon Sep 17 00:00:00 2001 From: lukas-smk Date: Tue, 24 Mar 2026 12:30:38 +0100 Subject: [PATCH 61/75] Add backOfficeAuth/cleanupSecret security schemes and noApiInfoPlugins config --- .../gentics/mesh/etc/config/MeshOptions.java | 21 ++++++++++++---- .../mesh/util/MeshOpenAPIv3Generator.java | 25 +++++++++++++++++-- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index fb1414691e..bac788c47f 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -38,6 +38,7 @@ public abstract class MeshOptions implements Option { public static final String MESH_LOCK_PATH_ENV = "MESH_LOCK_PATH"; public static final String MESH_LIVE_PATH_ENV = "MESH_LIVE_PATH"; public static final String MESH_SERVE_OPENAPI_ENV = "MESH_SERVE_OPENAPI"; + public static final String MESH_NO_API_INFO_PLUGINS_ENV = "MESH_NO_API_INFO_PLUGINS"; public static final String MESH_START_IN_READ_ONLY_ENV = "MESH_START_IN_READ_ONLY"; public static final String MESH_INITIAL_ADMIN_PASSWORD_ENV = "MESH_INITIAL_ADMIN_PASSWORD"; public static final String MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET_ENV = "MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET"; @@ -157,6 +158,11 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_SERVE_OPENAPI_ENV, description = "Serve OpenAPI specification under `/openapi*` endpoints. Default: true") private boolean serveOpenApi = true; + @JsonProperty(required = false) + @JsonPropertyDescription("Comma-separated list of plugin names for which no OpenAPI info should be included. Use '*' to disable for all plugins.") + @EnvironmentVariable(name = MESH_NO_API_INFO_PLUGINS_ENV, description = "Override the list of plugins excluded from OpenAPI info.") + private String noApiInfoPlugins; + @JsonProperty(required = true) @JsonPropertyDescription("GraphQL options.") private GraphQLOptions graphQLOptions = new GraphQLOptions(); @@ -557,6 +563,16 @@ public void setServeOpenApi(boolean serveOpenApi) { this.serveOpenApi = serveOpenApi; } + public String getNoApiInfoPlugins() { + return noApiInfoPlugins; + } + + @Setter + public MeshOptions setNoApiInfoPlugins(String noApiInfoPlugins) { + this.noApiInfoPlugins = noApiInfoPlugins; + return this; + } + @JsonIgnore public abstract NativeQueryFiltering getNativeQueryFiltering(); @@ -636,9 +652,4 @@ public void validate(MeshOptions options) { */ public abstract Format getDefaultOpenAPIFormat(); - /** - * Get the comma separated list of plugin, that should provide no API info. Can be null or empty. - * @return - */ - public abstract String getNoApiInfoPlugins(); } diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 855b621f9f..43befde0f2 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -2,6 +2,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -34,12 +35,32 @@ public MeshOpenAPIv3Generator(String version, List servers, public MeshOpenAPIv3Generator(String version, List servers, boolean secureByDefault, Optional> maybePathBlacklist, - Optional> maybePathWhitelist) { - super(version, servers, Collections.singletonMap("bearerAuth", new ExtendedSecurityScheme(secureByDefault)), maybePathBlacklist, maybePathWhitelist); + Optional> maybePathWhitelist) { + super(version, servers, buildSecurity(secureByDefault), maybePathBlacklist, maybePathWhitelist); securityBearerAuth = security.get("bearerAuth"); securityBearerAuth.getScheme().setScheme("bearer"); securityBearerAuth.getScheme().setType(SecurityScheme.Type.HTTP); securityBearerAuth.getScheme().setBearerFormat("JWT"); + + ExtendedSecurityScheme backOffice = security.get("backOfficeAuth"); + backOffice.getScheme().setType(SecurityScheme.Type.APIKEY); + backOffice.getScheme().setIn(SecurityScheme.In.HEADER); + backOffice.getScheme().setName("X-Authorization"); + backOffice.getScheme().setDescription("BackOffice JWT passed in a configurable request header (header name is defined in the server configuration, e.g. X-Authorization). Only required when BackOffice JWT authentication is enabled."); + + ExtendedSecurityScheme cleanup = security.get("cleanupSecret"); + cleanup.getScheme().setType(SecurityScheme.Type.APIKEY); + cleanup.getScheme().setIn(SecurityScheme.In.QUERY); + cleanup.getScheme().setName("secret"); + cleanup.getScheme().setDescription("Shared secret required to authorize cleanup operations."); + } + + private static Map buildSecurity(boolean secureByDefault) { + Map map = new HashMap<>(); + map.put("bearerAuth", new ExtendedSecurityScheme(secureByDefault)); + map.put("backOfficeAuth", new ExtendedSecurityScheme(false)); + map.put("cleanupSecret", new ExtendedSecurityScheme(false)); + return map; } /** From b71cf8ecd5d64b6e202a111346f98bde013a4d24 Mon Sep 17 00:00:00 2001 From: lukas-smk Date: Tue, 24 Mar 2026 13:54:08 +0100 Subject: [PATCH 62/75] Revert "Add backOfficeAuth/cleanupSecret security schemes and noApiInfoPlugins config" This reverts commit bc56fc5039ca8c628ae340843737f5c8af1101ce. --- .../gentics/mesh/etc/config/MeshOptions.java | 21 ++++------------ .../mesh/util/MeshOpenAPIv3Generator.java | 25 ++----------------- 2 files changed, 7 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java index bac788c47f..fb1414691e 100644 --- a/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java +++ b/api/src/main/java/com/gentics/mesh/etc/config/MeshOptions.java @@ -38,7 +38,6 @@ public abstract class MeshOptions implements Option { public static final String MESH_LOCK_PATH_ENV = "MESH_LOCK_PATH"; public static final String MESH_LIVE_PATH_ENV = "MESH_LIVE_PATH"; public static final String MESH_SERVE_OPENAPI_ENV = "MESH_SERVE_OPENAPI"; - public static final String MESH_NO_API_INFO_PLUGINS_ENV = "MESH_NO_API_INFO_PLUGINS"; public static final String MESH_START_IN_READ_ONLY_ENV = "MESH_START_IN_READ_ONLY"; public static final String MESH_INITIAL_ADMIN_PASSWORD_ENV = "MESH_INITIAL_ADMIN_PASSWORD"; public static final String MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET_ENV = "MESH_INITIAL_ADMIN_PASSWORD_FORCE_RESET"; @@ -158,11 +157,6 @@ public abstract class MeshOptions implements Option { @EnvironmentVariable(name = MESH_SERVE_OPENAPI_ENV, description = "Serve OpenAPI specification under `/openapi*` endpoints. Default: true") private boolean serveOpenApi = true; - @JsonProperty(required = false) - @JsonPropertyDescription("Comma-separated list of plugin names for which no OpenAPI info should be included. Use '*' to disable for all plugins.") - @EnvironmentVariable(name = MESH_NO_API_INFO_PLUGINS_ENV, description = "Override the list of plugins excluded from OpenAPI info.") - private String noApiInfoPlugins; - @JsonProperty(required = true) @JsonPropertyDescription("GraphQL options.") private GraphQLOptions graphQLOptions = new GraphQLOptions(); @@ -563,16 +557,6 @@ public void setServeOpenApi(boolean serveOpenApi) { this.serveOpenApi = serveOpenApi; } - public String getNoApiInfoPlugins() { - return noApiInfoPlugins; - } - - @Setter - public MeshOptions setNoApiInfoPlugins(String noApiInfoPlugins) { - this.noApiInfoPlugins = noApiInfoPlugins; - return this; - } - @JsonIgnore public abstract NativeQueryFiltering getNativeQueryFiltering(); @@ -652,4 +636,9 @@ public void validate(MeshOptions options) { */ public abstract Format getDefaultOpenAPIFormat(); + /** + * Get the comma separated list of plugin, that should provide no API info. Can be null or empty. + * @return + */ + public abstract String getNoApiInfoPlugins(); } diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 43befde0f2..855b621f9f 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -2,7 +2,6 @@ import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -35,32 +34,12 @@ public MeshOpenAPIv3Generator(String version, List servers, public MeshOpenAPIv3Generator(String version, List servers, boolean secureByDefault, Optional> maybePathBlacklist, - Optional> maybePathWhitelist) { - super(version, servers, buildSecurity(secureByDefault), maybePathBlacklist, maybePathWhitelist); + Optional> maybePathWhitelist) { + super(version, servers, Collections.singletonMap("bearerAuth", new ExtendedSecurityScheme(secureByDefault)), maybePathBlacklist, maybePathWhitelist); securityBearerAuth = security.get("bearerAuth"); securityBearerAuth.getScheme().setScheme("bearer"); securityBearerAuth.getScheme().setType(SecurityScheme.Type.HTTP); securityBearerAuth.getScheme().setBearerFormat("JWT"); - - ExtendedSecurityScheme backOffice = security.get("backOfficeAuth"); - backOffice.getScheme().setType(SecurityScheme.Type.APIKEY); - backOffice.getScheme().setIn(SecurityScheme.In.HEADER); - backOffice.getScheme().setName("X-Authorization"); - backOffice.getScheme().setDescription("BackOffice JWT passed in a configurable request header (header name is defined in the server configuration, e.g. X-Authorization). Only required when BackOffice JWT authentication is enabled."); - - ExtendedSecurityScheme cleanup = security.get("cleanupSecret"); - cleanup.getScheme().setType(SecurityScheme.Type.APIKEY); - cleanup.getScheme().setIn(SecurityScheme.In.QUERY); - cleanup.getScheme().setName("secret"); - cleanup.getScheme().setDescription("Shared secret required to authorize cleanup operations."); - } - - private static Map buildSecurity(boolean secureByDefault) { - Map map = new HashMap<>(); - map.put("bearerAuth", new ExtendedSecurityScheme(secureByDefault)); - map.put("backOfficeAuth", new ExtendedSecurityScheme(false)); - map.put("cleanupSecret", new ExtendedSecurityScheme(false)); - return map; } /** From aaa6c8ce74bdf1ac6252e797b72d5864d9a99118 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 24 Mar 2026 15:13:48 +0100 Subject: [PATCH 63/75] Test client almost done --- .../core/endpoint/admin/AdminEndpoint.java | 2 + .../core/endpoint/branch/BranchEndpoint.java | 2 +- .../node/AbstractBinaryUploadHandler.java | 17 +- .../mesh/core/endpoint/node/NodeEndpoint.java | 12 +- .../mesh/rest/MeshLocalClientImpl.java | 4 +- .../parameter/ParameterProviderContext.java | 10 + .../impl/BinaryCheckParametersImpl.java | 46 ++ .../impl/LanguageParametersImpl.java | 42 ++ .../parameter/impl/NodeParametersImpl.java | 14 +- .../gentics/mesh/example/NodeExamples.java | 29 +- .../client/LanguageParametersImpl.java | 7 + .../impl/AbstractMeshOkHttpRequest.java | 193 ++++++++ .../client/impl/MeshOkHttpRequestImpl.java | 25 +- .../client/impl/MeshRestHttpClientImpl.java | 15 +- .../rest/client/impl/OkHttpWebsocket.java | 31 +- .../gentics/mesh/rest/client/impl/Util.java | 9 +- .../client/method/BranchClientMethods.java | 4 +- .../admin/localconfig/LocalConfigModel.java | 2 + .../mesh/parameter/LanguageParameters.java | 38 ++ .../mesh/parameter/NodeParameters.java | 24 +- .../openapi/OpenAPIMeshRawRequestImpl.java | 32 ++ .../test/openapi/OpenAPIMeshRestClient.java | 432 ++++++++++++------ .../mesh/test/openapi/UpgradedApiClient.java | 118 +++++ .../mesh/test/openapi/UpgradedCall.java | 103 +++++ .../mesh/test/openapi/UpgradedDefaultApi.java | 260 ++++++++++- 25 files changed, 1183 insertions(+), 288 deletions(-) create mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/BinaryCheckParametersImpl.java create mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/LanguageParametersImpl.java create mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/LanguageParametersImpl.java create mode 100644 rest-client/src/main/java/com/gentics/mesh/rest/client/impl/AbstractMeshOkHttpRequest.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/LanguageParameters.java create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRawRequestImpl.java create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedApiClient.java create mode 100644 tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedCall.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java index 1f96677865..db7578fb1f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/admin/AdminEndpoint.java @@ -331,6 +331,8 @@ protected void addRuntimeConfigHandler() { postRoute.method(POST); postRoute.setMutating(false); postRoute.produces(APPLICATION_JSON); + postRoute.consumes(APPLICATION_JSON); + postRoute.exampleRequest(localConfig.createExample()); postRoute.description("Sets the currently active local configuration of this instance."); postRoute.exampleResponse(OK, localConfig.createExample(), "The currently active local configuration"); postRoute.handler(rc -> localConfigHandler.handleSetActiveConfig(wrap(rc))); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java index 4ebd7c099e..bd0913bcb8 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java @@ -247,7 +247,7 @@ private void addUpdateHandler() { upsertBranch.setMutating(true); upsertBranch.consumes(APPLICATION_JSON); upsertBranch.produces(APPLICATION_JSON); - upsertBranch.exampleRequest(versioningExamples.createBranchUpdateRequest("Winter Collection Branch")); + upsertBranch.exampleRequest(versioningExamples.createBranchCreateRequest("Winter Collection Branch")); upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated or new branch"); upsertBranch.events(BRANCH_CREATED, BRANCH_UPDATED); upsertBranch.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/AbstractBinaryUploadHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/AbstractBinaryUploadHandler.java index 0d36e78e0a..3bae414dd2 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/AbstractBinaryUploadHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/AbstractBinaryUploadHandler.java @@ -7,6 +7,9 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static org.apache.commons.lang3.StringUtils.isEmpty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.gentics.mesh.context.InternalActionContext; import com.gentics.mesh.core.data.HibLanguage; import com.gentics.mesh.core.data.HibNodeFieldContainer; @@ -28,8 +31,6 @@ import com.gentics.mesh.etc.config.MeshOptions; import io.reactivex.Flowable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Base class for binary upload handler and S3 binary upload handler. @@ -63,19 +64,19 @@ public void handleBinaryCheckResult(InternalActionContext ac, String nodeUuid, S validateParameter(nodeUuid, "uuid"); validateParameter(fieldName, "fieldName"); - String languageTag = ac.getParameter("lang"); + String[] languageTag = ac.getLanguageParameters().getLanguages(); - if (isEmpty(languageTag)) { + if (languageTag == null || languageTag.length < 1) { throw error(BAD_REQUEST, "upload_error_no_language"); } - String nodeVersion = ac.getParameter("version"); + String nodeVersion = ac.getVersioningParameters().getVersion(); if (isEmpty(nodeVersion)) { throw error(BAD_REQUEST, "upload_error_no_version"); } - String checkSecret = ac.getParameter("secret"); + String checkSecret = ac.getBinaryCheckParameters().getSecret(); if (isEmpty(checkSecret)) { throw error(BAD_REQUEST, "error_binaryfield_invalid_check_secret", fieldName); @@ -85,7 +86,7 @@ public void handleBinaryCheckResult(InternalActionContext ac, String nodeUuid, S (batch, tx) -> { CommonTx ctx = tx.unwrap(); HibProject project = tx.getProject(ac); - HibLanguage language = tx.languageDao().findByLanguageTag(project, languageTag); + HibLanguage language = tx.languageDao().findByLanguageTag(project, languageTag[0]); if (language == null) { throw error(NOT_FOUND, "error_language_not_found", languageTag); @@ -94,7 +95,7 @@ public void handleBinaryCheckResult(InternalActionContext ac, String nodeUuid, S HibBranch branch = tx.getBranch(ac); NodeDao nodeDao = tx.nodeDao(); HibNode node = nodeDao.loadObjectByUuid(project, ac, nodeUuid, UPDATE_PERM); - HibNodeFieldContainer nodeFieldContainer = ctx.contentDao().findVersion(node, languageTag, branch.getUuid(), nodeVersion); + HibNodeFieldContainer nodeFieldContainer = ctx.contentDao().findVersion(node, languageTag[0], branch.getUuid(), nodeVersion); if (nodeFieldContainer == null) { throw error(BAD_REQUEST, "object_not_found_for_uuid_version", nodeUuid, nodeVersion); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index eef9fbe5be..d608b03184 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -43,6 +43,7 @@ import com.gentics.mesh.core.rest.navigation.NavigationResponse; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.json.JsonUtil; +import com.gentics.mesh.parameter.impl.BinaryCheckParametersImpl; import com.gentics.mesh.parameter.impl.BranchParametersImpl; import com.gentics.mesh.parameter.impl.DeleteParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; @@ -221,10 +222,17 @@ private void addBinaryHandlers() { binaryUploadHandler.handleUpdateField(ac, uuid, fieldName, attributes); }, isOrderedBlockingHandlers()); + QueryParameter lang = new QueryParameter(); + lang.setExample("en"); + lang.setRequired(true); + lang.setDescription("Node language"); InternalEndpointRoute checkCallback = createRoute(); checkCallback.path("/:nodeUuid/binary/:fieldName/checkCallback"); checkCallback.addUriParameter("nodeUuid", "Uuid of the node.", NODE_DELOREAN_UUID); checkCallback.addUriParameter("fieldName", "Name of the field for which the check status is to be updated.", "stringField"); + checkCallback.addQueryParameters(VersioningParametersImpl.class); + checkCallback.addQueryParameter("lang", lang); + checkCallback.addQueryParameters(BinaryCheckParametersImpl.class); checkCallback.method(POST); checkCallback.produces(APPLICATION_JSON); checkCallback.exampleRequest(nodeExamples.getExampleBinaryCheckCallbackParameters()); @@ -337,7 +345,7 @@ private void addS3BinaryHandlers() { fieldUpdate.addQueryParameters(VersioningParametersImpl.class); fieldUpdate.method(POST); fieldUpdate.produces(APPLICATION_JSON); - fieldUpdate.exampleRequest(nodeExamples.getExampleBinaryUploadFormParameters()); + fieldUpdate.exampleRequest(nodeExamples.getExampleS3BinaryUploadFormParameters()); fieldUpdate.exampleResponse(OK, nodeExamples.getNodeResponseWithAllFields(), "The response contains the updated node."); fieldUpdate.exampleResponse(NOT_FOUND, miscExamples.createMessageResponse(), "The node or the field could not be found."); fieldUpdate.description("Create the s3 binaryfield with the given name."); @@ -375,7 +383,7 @@ private void addS3BinaryHandlers() { fieldMetadataExtraction.addQueryParameters(VersioningParametersImpl.class); fieldMetadataExtraction.method(POST); fieldMetadataExtraction.produces(APPLICATION_JSON); - fieldMetadataExtraction.exampleRequest(nodeExamples.getExampleBinaryUploadFormParameters()); + fieldMetadataExtraction.exampleRequest(nodeExamples.getExampleS3BinaryUploadFormParameters()); fieldMetadataExtraction.exampleResponse(OK, nodeExamples.getNodeResponseWithAllFields(), "The response contains the updated node."); fieldMetadataExtraction.exampleResponse(NOT_FOUND, miscExamples.createMessageResponse(), "The node or the field could not be found."); fieldMetadataExtraction.description("Parse metadata of s3 binaryfield with the given name."); diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index ba400ea884..1813ba81df 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -1480,7 +1480,7 @@ public MeshRequest updateBranch(String projectName, String branc } @Override - public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid) { + public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { // TODO Auto-generated method stub return null; } @@ -1503,7 +1503,7 @@ public MeshRequest assignBranchSchemaVersions(String proje } @Override - public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid) { + public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { // TODO Auto-generated method stub return null; } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index 86d6559e2f..8d3eb22159 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -2,6 +2,7 @@ import com.gentics.mesh.handler.ActionContext; import com.gentics.mesh.parameter.impl.BackupParametersImpl; +import com.gentics.mesh.parameter.impl.BinaryCheckParametersImpl; import com.gentics.mesh.parameter.impl.BranchParametersImpl; import com.gentics.mesh.parameter.impl.ConsistencyCheckParametersImpl; import com.gentics.mesh.parameter.impl.DeleteParametersImpl; @@ -12,6 +13,7 @@ import com.gentics.mesh.parameter.impl.ImageManipulationRetrievalParametersImpl; import com.gentics.mesh.parameter.impl.IndexMaintenanceParametersImpl; import com.gentics.mesh.parameter.impl.JobParametersImpl; +import com.gentics.mesh.parameter.impl.LanguageParametersImpl; import com.gentics.mesh.parameter.impl.NodeParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.ProjectLoadParametersImpl; @@ -45,6 +47,10 @@ default VersioningParameters getVersioningParameters() { return new VersioningParametersImpl(this); } + default LanguageParameters getLanguageParameters() { + return new LanguageParametersImpl(this); + } + default PagingParameters getPagingParameters() { return new PagingParametersImpl(this); } @@ -116,4 +122,8 @@ default DisplayParameters getDisplayParameters() { default ConsistencyCheckParameters getConsistencyCheckParameters() { return new ConsistencyCheckParametersImpl(this); } + + default BinaryCheckParameters getBinaryCheckParameters() { + return new BinaryCheckParametersImpl(this); + } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/BinaryCheckParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/BinaryCheckParametersImpl.java new file mode 100644 index 0000000000..eaf18e9657 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/BinaryCheckParametersImpl.java @@ -0,0 +1,46 @@ +package com.gentics.mesh.parameter.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.raml.model.ParamType; +import org.raml.model.parameter.QueryParameter; + +import com.gentics.mesh.handler.ActionContext; +import com.gentics.mesh.parameter.AbstractParameters; +import com.gentics.mesh.parameter.BinaryCheckParameters; + +public class BinaryCheckParametersImpl extends AbstractParameters implements BinaryCheckParameters { + + public BinaryCheckParametersImpl(ActionContext ac) { + super(ac); + } + + public BinaryCheckParametersImpl() { + super(); + } + + @Override + public void validate() { + + } + + @Override + public Map getRAMLParameters() { + Map parameters = new HashMap<>(); + + QueryParameter langParameter = new QueryParameter(); + langParameter.setDescription("."); + langParameter.setExample("lhdgfsgfvaoyegfy"); + langParameter.setRequired(true); + langParameter.setType(ParamType.STRING); + parameters.put(SECRET_PARAMETER_KEY, langParameter); + + return parameters; + } + + @Override + public String getName() { + return "Binary check parameters"; + } +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/LanguageParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/LanguageParametersImpl.java new file mode 100644 index 0000000000..a76b703526 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/LanguageParametersImpl.java @@ -0,0 +1,42 @@ +package com.gentics.mesh.parameter.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.raml.model.ParamType; +import org.raml.model.parameter.QueryParameter; + +import com.gentics.mesh.handler.ActionContext; +import com.gentics.mesh.parameter.AbstractParameters; + +public class LanguageParametersImpl extends AbstractParameters implements com.gentics.mesh.parameter.LanguageParameters { + + public LanguageParametersImpl(ActionContext ac) { + super(ac); + } + + public LanguageParametersImpl() { + super(); + } + + @Override + public Map getRAMLParameters() { + Map parameters = new HashMap<>(); + + // lang + QueryParameter langParameter = new QueryParameter(); + langParameter.setDescription( + "ISO 639-1 language tag of the language which should be loaded. Fallback handling can be applied by specifying multiple languages in a comma-separated list. The first matching language will be returned. If omitted or the requested language is not available then the _defaultLanguage_ as configured in _mesh.yml_ will be returned."); + langParameter.setExample("en,de"); + langParameter.setRequired(false); + langParameter.setType(ParamType.STRING); + parameters.put(LANGUAGES_QUERY_PARAM_KEY, langParameter); + + return parameters; + } + + @Override + public String getName() { + return "Language parameters"; + } +} diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java index 60309d4ce5..d4609333d2 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/NodeParametersImpl.java @@ -9,14 +9,13 @@ import org.raml.model.parameter.QueryParameter; import com.gentics.mesh.handler.ActionContext; -import com.gentics.mesh.parameter.AbstractParameters; import com.gentics.mesh.parameter.LinkType; import com.gentics.mesh.parameter.NodeParameters; /** * @see NodeParameters */ -public class NodeParametersImpl extends AbstractParameters implements NodeParameters { +public class NodeParametersImpl extends LanguageParametersImpl implements NodeParameters { public NodeParametersImpl(ActionContext ac) { super(ac); @@ -38,16 +37,7 @@ public String getName() { @Override public Map getRAMLParameters() { - Map parameters = new HashMap<>(); - - // lang - QueryParameter langParameter = new QueryParameter(); - langParameter.setDescription( - "ISO 639-1 language tag of the language which should be loaded. Fallback handling can be applied by specifying multiple languages in a comma-separated list. The first matching language will be returned. If omitted or the requested language is not available then the _defaultLanguage_ as configured in _mesh.yml_ will be returned."); - langParameter.setExample("en,de"); - langParameter.setRequired(false); - langParameter.setType(ParamType.STRING); - parameters.put(LANGUAGES_QUERY_PARAM_KEY, langParameter); + Map parameters = new HashMap<>(super.getRAMLParameters()); // resolveLinks QueryParameter resolveLinksParameter = new QueryParameter(); diff --git a/mdm/common/src/main/java/com/gentics/mesh/example/NodeExamples.java b/mdm/common/src/main/java/com/gentics/mesh/example/NodeExamples.java index 241bb0bca7..c34b7fc27e 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/example/NodeExamples.java +++ b/mdm/common/src/main/java/com/gentics/mesh/example/NodeExamples.java @@ -40,6 +40,7 @@ import com.gentics.mesh.MeshVersion; import com.gentics.mesh.core.rest.navigation.NavigationElement; import com.gentics.mesh.core.rest.navigation.NavigationResponse; +import com.gentics.mesh.core.rest.node.BinaryCheckUpdateRequest; import com.gentics.mesh.core.rest.node.FieldMap; import com.gentics.mesh.core.rest.node.FieldMapImpl; import com.gentics.mesh.core.rest.node.NodeChildrenInfo; @@ -48,6 +49,7 @@ import com.gentics.mesh.core.rest.node.NodeResponse; import com.gentics.mesh.core.rest.node.NodeUpdateRequest; import com.gentics.mesh.core.rest.node.PublishStatusModel; +import com.gentics.mesh.core.rest.node.field.BinaryCheckStatus; import com.gentics.mesh.core.rest.node.field.BinaryField; import com.gentics.mesh.core.rest.node.field.BinaryFieldTransformRequest; import com.gentics.mesh.core.rest.node.field.Field; @@ -60,6 +62,7 @@ import com.gentics.mesh.core.rest.node.field.impl.HtmlFieldImpl; import com.gentics.mesh.core.rest.node.field.impl.NumberFieldImpl; import com.gentics.mesh.core.rest.node.field.impl.StringFieldImpl; +import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryUploadRequest; import com.gentics.mesh.core.rest.node.version.NodeVersionsResponse; import com.gentics.mesh.core.rest.node.version.VersionInfo; import com.gentics.mesh.core.rest.schema.impl.SchemaReferenceImpl; @@ -334,22 +337,18 @@ public Map> getExampleBinaryUploadFormParameters() { return parameters; } - public Map> getExampleBinaryCheckCallbackParameters() { - Map> parameters = new HashMap<>(); - FormParameter statusParameter = new FormParameter(); - statusParameter.setExample("DENIED"); - statusParameter.setType(ParamType.STRING); - statusParameter.setDescription("The result of the binary check. One of ACCEPTED or DENIED."); - statusParameter.setRequired(true); - parameters.put("status", Arrays.asList(statusParameter)); - - FormParameter reasonParameter = new FormParameter(); - reasonParameter.setExample("Malware detected"); - reasonParameter.setType(ParamType.STRING); - reasonParameter.setDescription("The reason why the binary was denied."); - reasonParameter.setRequired(false); - parameters.put("reason", Arrays.asList(reasonParameter)); + public S3BinaryUploadRequest getExampleS3BinaryUploadFormParameters() { + S3BinaryUploadRequest parameters = new S3BinaryUploadRequest(); + parameters.setVersion("1.0"); + parameters.setLanguage("en"); + parameters.setFilename("blumen/blume.jpg"); + return parameters; + } + public BinaryCheckUpdateRequest getExampleBinaryCheckCallbackParameters() { + BinaryCheckUpdateRequest parameters = new BinaryCheckUpdateRequest(); + parameters.setStatus(BinaryCheckStatus.DENIED); + parameters.setReason("Malware detected"); return parameters; } diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/LanguageParametersImpl.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/LanguageParametersImpl.java new file mode 100644 index 0000000000..787c0175f8 --- /dev/null +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/LanguageParametersImpl.java @@ -0,0 +1,7 @@ +package com.gentics.mesh.parameter.client; + +import com.gentics.mesh.parameter.LanguageParameters; + +public class LanguageParametersImpl extends AbstractParameters implements LanguageParameters { + +} diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/AbstractMeshOkHttpRequest.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/AbstractMeshOkHttpRequest.java new file mode 100644 index 0000000000..2647350b09 --- /dev/null +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/AbstractMeshOkHttpRequest.java @@ -0,0 +1,193 @@ +package com.gentics.mesh.rest.client.impl; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import org.apache.commons.lang.StringUtils; + +import com.gentics.mesh.core.rest.common.GenericMessageResponse; +import com.gentics.mesh.core.rest.error.GenericRestException; +import com.gentics.mesh.json.JsonUtil; +import com.gentics.mesh.rest.client.MeshBinaryResponse; +import com.gentics.mesh.rest.client.MeshRequest; +import com.gentics.mesh.rest.client.MeshResponse; +import com.gentics.mesh.rest.client.MeshRestClientMessageException; +import com.gentics.mesh.rest.client.MeshWebrootFieldResponse; +import com.gentics.mesh.rest.client.MeshWebrootResponse; + +import io.reactivex.Completable; +import io.reactivex.Single; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +/** + * Default OkHTTP based client implementation + * + * @param + */ +public abstract class AbstractMeshOkHttpRequest implements MeshRequest { + + protected final OkHttpClient client; + protected final Class resultClass; + + public AbstractMeshOkHttpRequest(OkHttpClient client, Class resultClass) { + this.client = client; + this.resultClass = resultClass; + } + + @Override + public Single> getResponse() { + return getOkResponse().map(response -> new MeshResponse() { + Supplier body = Util.lazily(() -> mapResponse(response)); + + @Override + public Map> getHeaders() { + return response.headers().toMultimap(); + } + + @Override + public List getHeaders(String name) { + return response.headers(name); + } + + @Override + public int getStatusCode() { + return response.code(); + } + + @Override + public String getBodyAsString() { + try { + return response.body().string(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public R getBody() { + return body.get(); + } + + @Override + public void close() { + Optional.ofNullable(response).map(Response::body).ifPresent(ResponseBody::close); + } + }); + } + + @Override + public Completable toCompletable() { + return getOkResponse() + .doOnSuccess(this::throwOnError) + .doOnSuccess(response -> Optional.ofNullable(response) + .ifPresent(Response::close)) + .ignoreElement(); + } + + @Override + public Single toSingle() { + return getOkResponse().map(this::mapResponse); + } + + public Single getOkResponse() { + return Single.create(sub -> { + Call call = createCall(); + call.enqueue(new Callback() { + @Override + public void onFailure(Call call, IOException e) { + // Don't call the onError twice, but notify about multiple exceptions (should not occur often). + if (sub.isDisposed()) { + e.printStackTrace(); + } else { + sub.onError(new IOException(String.format("I/O Error in %s %s : %s (%s)", + HttpMethod.valueOf(call.request().method().toUpperCase()), call.request().url(), e.getClass().getSimpleName(), e.getLocalizedMessage()), e)); + } + } + + @Override + public void onResponse(Call call, Response response) throws IOException { + if (!sub.isDisposed()) { + sub.onSuccess(response); + } else { + response.close(); + } + } + }); + }); + } + + protected abstract Request createRequest(); + + protected Call createCall() { + return client.newCall(createRequest()); + } + + @SuppressWarnings("unchecked") + private R mapResponse(Response response) throws IOException, MeshRestClientMessageException { + throwOnError(response); + + String contentType = response.header("Content-Type"); + if (!response.isSuccessful()) { + return null; + } else if (resultClass.isAssignableFrom(EmptyResponse.class)) { + return (R) EmptyResponse.getInstance(); + } else if (resultClass.isAssignableFrom(MeshBinaryResponse.class)) { + return (R) new OkHttpBinaryResponse(response); + } else if (resultClass.isAssignableFrom(MeshWebrootResponse.class)) { + return (R) new OkHttpWebrootResponse(response); + } else if (resultClass.isAssignableFrom(MeshWebrootFieldResponse.class)) { + return (R) new OkHttpWebrootFieldResponse(response, false); + } else if (contentType != null && contentType.startsWith("application/json") && !resultClass.isAssignableFrom(String.class)) { + return JsonUtil.readValue(response.body().string(), resultClass); + } else if (resultClass.isAssignableFrom(String.class)) { + return (R) response.body().string(); + } else { + throw new RuntimeException("Request can't be handled by this handler since the content type was {" + contentType + "}"); + } + } + + private void throwOnError(Response response) throws IOException, MeshRestClientMessageException { + if (!response.isSuccessful() && response.code() != 304) { + String body = response.body().string(); + MeshRestClientMessageException err; + try { + GenericMessageResponse msg = null; + if (!StringUtils.isEmpty(body)) { + msg = JsonUtil.readValue(body, GenericMessageResponse.class); + } + err = new MeshRestClientMessageException( + response.code(), + response.message(), + msg, + HttpMethod.valueOf(response.request().method().toUpperCase()), + stripOrigin(response.request().url().toString())); + } catch (GenericRestException e) { + err = new MeshRestClientMessageException( + response.code(), + response.message(), + body, + HttpMethod.valueOf(response.request().method().toUpperCase()), + stripOrigin(response.request().url().toString())); + } + response.close(); + throw err; + } + } + + private String stripOrigin(String url) { + URI uri = URI.create(url); + String query = uri.getQuery(); + return uri.getPath() + (query == null || query.length() == 0 + ? "" + : "?" + query); + } +} diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java index b828fb1409..a9ad5163bd 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshOkHttpRequestImpl.java @@ -27,7 +27,6 @@ import com.gentics.mesh.rest.client.MeshWebrootFieldResponse; import com.gentics.mesh.rest.client.MeshWebrootResponse; -import io.reactivex.Completable; import io.reactivex.Flowable; import io.reactivex.Observable; import io.reactivex.Single; @@ -53,12 +52,10 @@ * @see MeshRequest * @param */ -public class MeshOkHttpRequestImpl implements MeshRequest { +public class MeshOkHttpRequestImpl extends AbstractMeshOkHttpRequest { private final MeshRestClient meshClient; - private final OkHttpClient client; private final MeshRestClientConfig config; - private final Class resultClass; private final String method; private final String url; @@ -67,10 +64,9 @@ public class MeshOkHttpRequestImpl implements MeshRequest { private MeshOkHttpRequestImpl(MeshRestClient meshClient, OkHttpClient client, MeshRestClientConfig config, Class resultClass, String method, String url, Map headers, RequestBody requestBody) { + super(client, resultClass); this.meshClient = meshClient; - this.client = client; this.config = config; - this.resultClass = resultClass; this.method = method; this.url = url; this.headers = headers; @@ -200,7 +196,8 @@ public void setHeaders(Map headers) { this.headers.putAll(headers); } - private Request createRequest() { + @Override + protected Request createRequest() { Request.Builder builder = new Request.Builder() .url(url) .headers(Headers.of(headers)); @@ -265,20 +262,6 @@ public void onResponse(Call call, Response response) throws IOException { return response.retryWhen(retryOnNetworkErrors(retries, delay)); } - @Override - public Single toSingle() { - return getOkResponse().map(this::mapResponse); - } - - @Override - public Completable toCompletable() { - return getOkResponse() - .doOnSuccess(this::throwOnError) - .doOnSuccess(response -> Optional.ofNullable(response) - .ifPresent(Response::close)) - .ignoreElement(); - } - @SuppressWarnings("unchecked") private T mapResponse(Response response) throws IOException, MeshRestClientMessageException { throwOnError(response); diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 1ea7fa1703..5ac9c330b4 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -1197,18 +1197,13 @@ public MeshRequest downloadBinaryField(String projectName, S @Override public MeshRequest downloadBinaryField(String projectName, String nodeUuid, String languageTag, String fieldKey, long from, long to, ParameterProvider... parameters) { - Objects.requireNonNull(projectName, "projectName must not be null"); - Util.requireUuid(nodeUuid, "nodeUuid"); - Objects.requireNonNull(fieldKey, "fieldKey must not be null"); Util.requireNonNegative(from, "from"); Util.requireNonNegative(to, "to"); if (to < from) { throw new InvalidParameterException(String.format("Parameter to must be equal or greater then from. Given values: %d-%d", from, to)); } - String path = "/" + encodeSegment(projectName) + "/nodes/" + nodeUuid + "/binary/" + encodeSegment(fieldKey) + getQuery(getConfig(), parameters); - - MeshRequest request = prepareRequest(GET, path, MeshBinaryResponse.class); + MeshRequest request = downloadBinaryField(projectName, nodeUuid, languageTag, fieldKey, parameters); request.setHeader("Range", String.format("bytes=%d-%d", from, to)); return request; } @@ -1403,11 +1398,11 @@ public MeshRequest getOpenAPI() { } @Override - public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid) { + public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Util.requireUuid(branchUuid, "branchUuid"); - return prepareRequest(GET, "/" + encodeSegment(projectName) + "/branches/" + branchUuid + "/schemas", BranchInfoSchemaList.class); + return prepareRequest(GET, "/" + encodeSegment(projectName) + "/branches/" + branchUuid + "/schemas" + getQuery(getConfig(), parameters), BranchInfoSchemaList.class); } @Override @@ -1430,11 +1425,11 @@ public MeshRequest assignBranchSchemaVersions(String proje } @Override - public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid) { + public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Util.requireUuid(branchUuid, "branchUuid"); - return prepareRequest(GET, "/" + encodeSegment(projectName) + "/branches/" + branchUuid + "/microschemas", + return prepareRequest(GET, "/" + encodeSegment(projectName) + "/branches/" + branchUuid + "/microschemas" + getQuery(getConfig(), parameters), BranchInfoMicroschemaList.class); } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/OkHttpWebsocket.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/OkHttpWebsocket.java index 00378f696b..5d3226cc8e 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/OkHttpWebsocket.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/OkHttpWebsocket.java @@ -1,19 +1,33 @@ package com.gentics.mesh.rest.client.impl; +import static com.gentics.mesh.rest.client.impl.Util.eventbusMessage; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Stream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.rest.client.EventbusEvent; import com.gentics.mesh.rest.client.MeshRestClientConfig; import com.gentics.mesh.rest.client.MeshWebsocket; + import io.reactivex.Completable; import io.reactivex.Maybe; import io.reactivex.Observable; import io.reactivex.disposables.Disposable; import io.reactivex.subjects.PublishSubject; import io.reactivex.subjects.Subject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -21,19 +35,6 @@ import okhttp3.WebSocketListener; import okio.ByteString; -import java.io.IOException; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Stream; - -import static com.gentics.mesh.rest.client.impl.Util.eventbusMessage; - /** * Websocket client implementation for {@link OkHttpClient}. */ diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/Util.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/Util.java index 9bf123f7e6..4787ef5775 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/Util.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/Util.java @@ -3,6 +3,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.security.InvalidParameterException; +import java.util.concurrent.Callable; import java.util.function.Supplier; import java.util.stream.Stream; @@ -79,7 +80,7 @@ public static String eventbusMessage(EventbusMessageType type, String address, O * @param * @return */ - public static Supplier lazily(WrappedSupplier supplier) { + public static Supplier lazily(Callable supplier) { return new Supplier() { T value; boolean supplied = false; @@ -88,7 +89,7 @@ public static Supplier lazily(WrappedSupplier supplier) { public synchronized T get() { if (!supplied) { try { - value = supplier.get(); + value = supplier.call(); supplied = true; } catch (Exception e) { throw new RuntimeException(e); @@ -170,8 +171,4 @@ public static String normalizedPath(String path) throws URISyntaxException { return Strings.CI.removeStart(normalizedURI, "http://host"); } - - interface WrappedSupplier { - T get() throws Exception; - } } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java index 46906da38a..c1f555d13e 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java @@ -81,7 +81,7 @@ MeshRequest createBranch(String projectName, String uuid, Branch * @param branchUuid * @return */ - MeshRequest getBranchSchemaVersions(String projectName, String branchUuid); + MeshRequest getBranchSchemaVersions(String projectName, String branchUuid, ParameterProvider... parameters); /** * Assign the given schema versions to the branch. @@ -110,7 +110,7 @@ MeshRequest createBranch(String projectName, String uuid, Branch * @param branchUuid * @return */ - MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid); + MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid, ParameterProvider... parameters); /** * Assign the given microschema versions to the branch. diff --git a/rest-model/src/main/java/com/gentics/mesh/core/rest/admin/localconfig/LocalConfigModel.java b/rest-model/src/main/java/com/gentics/mesh/core/rest/admin/localconfig/LocalConfigModel.java index 051a86b3fc..75620e5d83 100644 --- a/rest-model/src/main/java/com/gentics/mesh/core/rest/admin/localconfig/LocalConfigModel.java +++ b/rest-model/src/main/java/com/gentics/mesh/core/rest/admin/localconfig/LocalConfigModel.java @@ -12,6 +12,8 @@ */ public class LocalConfigModel implements RestModel, Serializable { + private static final long serialVersionUID = 7018419343844051338L; + @JsonProperty @JsonPropertyDescription("If true, mutating requests to this instance are not allowed.") private Boolean readOnly = false; diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/LanguageParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/LanguageParameters.java new file mode 100644 index 0000000000..cfa34f543c --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/LanguageParameters.java @@ -0,0 +1,38 @@ +package com.gentics.mesh.parameter; + +/** + * Language parameters + */ +public interface LanguageParameters extends ParameterProvider { + + /** + * Query parameter key: {@value #LANGUAGES_QUERY_PARAM_KEY} + */ + public static final String LANGUAGES_QUERY_PARAM_KEY = "lang"; + + + /** + * Set the {@value #LANGUAGES_QUERY_PARAM_KEY} request parameter values. + * + * @param languageTags + * @return Fluent API + */ + default LanguageParameters setLanguages(String... languageTags) { + setParameter(LANGUAGES_QUERY_PARAM_KEY, convertToStr(languageTags)); + return this; + } + + /** + * Return the {@value #LANGUAGES_QUERY_PARAM_KEY} request parameter values. + * + * @return + */ + default String[] getLanguages() { + String value = getParameter(LANGUAGES_QUERY_PARAM_KEY); + String[] languages = null; + if (value != null) { + languages = value.split(","); + } + return languages; + } +} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/NodeParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/NodeParameters.java index 30ee7fba14..0b32dec895 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/NodeParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/NodeParameters.java @@ -8,12 +8,7 @@ /** * Interface for node query parameters. */ -public interface NodeParameters extends ParameterProvider { - - /** - * Query parameter key: {@value #LANGUAGES_QUERY_PARAM_KEY} - */ - public static final String LANGUAGES_QUERY_PARAM_KEY = "lang"; +public interface NodeParameters extends LanguageParameters { /** * Query parameter key: {@value #EXPANDFIELDS_QUERY_PARAM_KEY} @@ -36,25 +31,12 @@ public interface NodeParameters extends ParameterProvider { * @param languageTags * @return Fluent API */ + @Override default NodeParameters setLanguages(String... languageTags) { - setParameter(LANGUAGES_QUERY_PARAM_KEY, convertToStr(languageTags)); + LanguageParameters.super.setLanguages(languageTags); return this; } - /** - * Return the {@value #LANGUAGES_QUERY_PARAM_KEY} request parameter values. - * - * @return - */ - default String[] getLanguages() { - String value = getParameter(LANGUAGES_QUERY_PARAM_KEY); - String[] languages = null; - if (value != null) { - languages = value.split(","); - } - return languages; - } - /** * Set the {@value #RESOLVE_LINKS_QUERY_PARAM_KEY} request parameter. * diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRawRequestImpl.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRawRequestImpl.java new file mode 100644 index 0000000000..94ef335a84 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRawRequestImpl.java @@ -0,0 +1,32 @@ +package com.gentics.mesh.test.openapi; + +import com.gentics.mesh.rest.client.impl.AbstractMeshOkHttpRequest; + +import okhttp3.Call; +import okhttp3.OkHttpClient; +import okhttp3.Request; + +public class OpenAPIMeshRawRequestImpl extends AbstractMeshOkHttpRequest { + + private final UpgradedCall call; + + public OpenAPIMeshRawRequestImpl(OkHttpClient client, UpgradedCall call, Class targetType) { + super(client, targetType); + this.call = call; + } + + @Override + public void setHeader(String name, String value) { + call.getBuilder().header(name, value); + } + + @Override + protected Request createRequest() { + return call.build(); + } + + @Override + protected Call createCall() { + return call; + } +} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index ab37a01f41..d71c056010 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -1,10 +1,16 @@ package com.gentics.mesh.test.openapi; +import java.io.File; +import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; -import org.openapitools.client.ApiClient; +import org.apache.commons.io.FileUtils; +import org.openapitools.client.ApiException; import org.openapitools.client.JSON; import org.openapitools.client.model.LoginRequest; @@ -40,6 +46,7 @@ import com.gentics.mesh.core.rest.microschema.impl.MicroschemaResponse; import com.gentics.mesh.core.rest.microschema.impl.MicroschemaUpdateRequest; import com.gentics.mesh.core.rest.navigation.NavigationResponse; +import com.gentics.mesh.core.rest.node.BinaryCheckUpdateRequest; import com.gentics.mesh.core.rest.node.NodeCreateRequest; import com.gentics.mesh.core.rest.node.NodeListResponse; import com.gentics.mesh.core.rest.node.NodeResponse; @@ -48,6 +55,7 @@ import com.gentics.mesh.core.rest.node.PublishStatusModel; import com.gentics.mesh.core.rest.node.PublishStatusResponse; import com.gentics.mesh.core.rest.node.field.BinaryCheckStatus; +import com.gentics.mesh.core.rest.node.field.BinaryFieldTransformRequest; import com.gentics.mesh.core.rest.node.field.image.ImageManipulationRequest; import com.gentics.mesh.core.rest.node.field.image.ImageVariantsResponse; import com.gentics.mesh.core.rest.node.field.s3binary.S3BinaryMetadataRequest; @@ -102,8 +110,10 @@ import com.gentics.mesh.parameter.EtagParameters; import com.gentics.mesh.parameter.GenericParameters; import com.gentics.mesh.parameter.ImageManipulationParameters; +import com.gentics.mesh.parameter.ImageManipulationRetrievalParameters; import com.gentics.mesh.parameter.IndexMaintenanceParameters; import com.gentics.mesh.parameter.JobParameters; +import com.gentics.mesh.parameter.NavigationParameters; import com.gentics.mesh.parameter.NodeParameters; import com.gentics.mesh.parameter.PagingParameters; import com.gentics.mesh.parameter.ParameterProvider; @@ -114,7 +124,6 @@ import com.gentics.mesh.parameter.SearchParameters; import com.gentics.mesh.parameter.SortingParameters; import com.gentics.mesh.parameter.VersioningParameters; -import com.gentics.mesh.parameter.impl.ConsistencyCheckParametersImpl; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; import com.gentics.mesh.rest.client.MeshRequest; @@ -136,7 +145,7 @@ @SuppressWarnings({"unchecked","rawtypes"}) public class OpenAPIMeshRestClient implements MeshRestClient { - private final ApiClient apiClient; + private final UpgradedApiClient apiClient; private final UpgradedDefaultApi api; private final MeshRestClientConfig config; private JWTAuthentication authentication = new JWTAuthentication(); @@ -175,7 +184,8 @@ protected static O adaptRequest(R r) { } public OpenAPIMeshRestClient(MeshRestClientConfig config, OkHttpClient okHttp) { - this.apiClient = new ApiClient(okHttp).setBasePath("%s://%s:%d".formatted((config.isSsl() ? "https" : "http"), config.getHost(), config.getPort())); + this.apiClient = new UpgradedApiClient(okHttp); + this.apiClient.setBasePath("%s://%s:%d".formatted((config.isSsl() ? "https" : "http"), config.getHost(), config.getPort())); this.api = new UpgradedDefaultApi(this.apiClient); this.config = config; } @@ -580,16 +590,20 @@ public MeshRequest revokeTagFamilyRolePermissions(Stri @Override public MeshRequest webroot(String projectName, String path, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathGetWithHttpInfo(path, projectName, - findParameter(ImageManipulationParameters.FOCAL_POINT_Z_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.RECT_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), - findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters)), MeshWebrootResponse.class); + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), (UpgradedCall) api.apiV2ProjectWebrootPathGetCall(path, projectName, + findParameter(ImageManipulationParameters.FOCAL_POINT_Z_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RECT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshWebrootResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override @@ -1301,299 +1315,343 @@ public MeshRequest diffMicroschema(String uuid, Microsch @Override public MeshRequest getMicroschemaRolePermissions(String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidRolePermissionsGetWithHttpInfo(uuid), ObjectPermissionResponse.class); } @Override public MeshRequest grantMicroschemaRolePermissions(String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidRolePermissionsPostWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeMicroschemaRolePermissions(String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidRolePermissionsPutWithHttpInfo(uuid, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest updateNodeBinaryField(String projectName, String nodeUuid, String languageTag, String nodeVersion, String fieldKey, InputStream fileData, long fileSize, String fileName, String contentType, boolean publish, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + File tmpFile = Files.createTempFile(fileName, fieldKey).toFile(); + FileUtils.copyInputStreamToFile(fileData, tmpFile); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNamePostWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter("publish", parameters), tmpFile, languageTag, nodeVersion), NodeResponse.class); + } catch (IOException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest downloadBinaryField(String projectName, String nodeUuid, String languageTag, String fieldKey, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), (UpgradedCall) api.apiV2ProjectNodesNodeUuidBinaryFieldNameGetCall(fieldKey, nodeUuid, projectName, + findParameter(ImageManipulationParameters.FOCAL_POINT_Z_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RECT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshBinaryResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest downloadBinaryField(String projectName, String nodeUuid, String languageTag, String fieldKey, long from, long to, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + MeshRequest request = downloadBinaryField(projectName, nodeUuid, languageTag, fieldKey, parameters); + request.setHeader("Range", String.format("bytes=%d-%d", from, to)); + return request; } @Override public MeshRequest transformNodeBinaryField(String projectName, String nodeUuid, String languageTag, - String version, String fieldKey, ImageManipulationParameters imageManipulationParameter) { - // TODO Auto-generated method stub - return null; + String version, String fieldKey, ImageManipulationParameters parameters) { + BinaryFieldTransformRequest transformRequest = new BinaryFieldTransformRequest(); + transformRequest.setCropRect(parameters.getRect()); + transformRequest.setWidth(parameters.getWidth()); + transformRequest.setHeight(parameters.getHeight()); + transformRequest.setCropMode(parameters.getCropMode()); + transformRequest.setResizeMode(parameters.getResizeMode()); + transformRequest.setLanguage(languageTag).setVersion(version); + if (parameters.hasFocalPoint()) { + transformRequest.setFocalPoint(parameters.getFocalPoint()); + } + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryTransformFieldNamePostWithHttpInfo(fieldKey, nodeUuid, projectName, adaptRequest(transformRequest)), NodeResponse.class); } @Override public MeshRequest updateNodeBinaryFieldCheckStatus(String projectName, String nodeUuid, String languageTag, String nodeVersion, String fieldKey, String secret, String branchUuid, BinaryCheckStatus status, String reason) { - // TODO Auto-generated method stub - return null; + BinaryCheckUpdateRequest updateRequest = new BinaryCheckUpdateRequest() + .setStatus(status) + .setReason(reason); + + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameCheckCallbackPostWithHttpInfo(languageTag, secret, fieldKey, nodeUuid, projectName, nodeVersion, adaptRequest(updateRequest)), NodeResponse.class); } @Override public MeshRequest upsertNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ImageManipulationRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsPostWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters), + adaptRequest(request)), ImageVariantsResponse.class); } @Override public MeshRequest clearNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsDeleteWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters)), EmptyResponse.class); } @Override public MeshRequest getNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsGetWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters)), ImageVariantsResponse.class); } @Override public MeshRequest updateNodeS3BinaryField(String projectName, String nodeUuid, String fieldKey, S3BinaryUploadRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidS3binaryFieldNamePostWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), adaptRequest(request)), S3RestResponse.class); } @Override public MeshRequest extractMetadataNodeS3BinaryField(String projectName, String nodeUuid, String fieldKey, S3BinaryMetadataRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidS3binaryFieldNameParseMetadataPostWithHttpInfo(fieldKey, nodeUuid, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), adaptRequest(request)), NodeResponse.class); } @Override public MeshRequest resolveLinks(String body, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2UtilitiesLinkResolverPostCall(body, + findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), + findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), null), String.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest validateSchema(SchemaModel schema) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UtilitiesValidateSchemaPostWithHttpInfo(adaptRequest(schema)), SchemaValidationResponse.class); } @Override public MeshRequest validateMicroschema(MicroschemaModel microschemaModel) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UtilitiesValidateMicroschemaPostWithHttpInfo(adaptRequest(microschemaModel)), SchemaValidationResponse.class); } @Override public MeshRequest loadNavigation(String projectName, String uuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidNavigationGetWithHttpInfo(uuid, projectName, + findParameter(NavigationParameters.MAX_DEPTH_QUERY_PARAM_KEY, parameters), + findParameter(NavigationParameters.INCLUDE_ALL_QUERY_PARAM_KEY, parameters)), NavigationResponse.class); } @Override public MeshRequest navroot(String projectName, String path, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNavrootPathGetWithHttpInfo(path, projectName, + findParameter(NavigationParameters.MAX_DEPTH_QUERY_PARAM_KEY, parameters), + findParameter(NavigationParameters.INCLUDE_ALL_QUERY_PARAM_KEY, parameters)), NavigationResponse.class); } @Override public MeshWebsocket eventbus() { - // TODO Auto-generated method stub + // OpenAPI supports no websocket return null; } @Override public MeshRequest createBranch(String projectName, BranchCreateRequest branchCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesPostWithHttpInfo(projectName, adaptRequest(branchCreateRequest)), BranchResponse.class); } @Override public MeshRequest createBranch(String projectName, String uuid, BranchCreateRequest branchCreateRequest, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(uuid, projectName, adaptRequest(branchCreateRequest)), BranchResponse.class); } @Override public MeshRequest findBranchByUuid(String projectName, String branchUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidGetWithHttpInfo(branchUuid, projectName, + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), BranchResponse.class); } @Override public MeshRequest findBranches(String projectName, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesGetWithHttpInfo(projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), BranchListResponse.class); } @Override public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPutWithHttpInfo(branchUuid, projectName, adaptRequest(request)), BranchResponse.class); } @Override - public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid) { - // TODO Auto-generated method stub - return null; + public MeshRequest getBranchSchemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidSchemasGetWithHttpInfo(branchUuid, projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), BranchInfoSchemaList.class); } @Override public MeshRequest assignBranchSchemaVersions(String projectName, String branchUuid, BranchInfoSchemaList schemaVersionReferences) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidSchemasPostWithHttpInfo(branchUuid, projectName, adaptRequest(schemaVersionReferences)), BranchInfoSchemaList.class); } @Override public MeshRequest assignBranchSchemaVersions(String projectName, String branchUuid, SchemaReference... schemaVersionReferences) { - // TODO Auto-generated method stub - return null; + BranchInfoSchemaList list = new BranchInfoSchemaList(); + list.add(schemaVersionReferences); + return assignBranchSchemaVersions(projectName, branchUuid, list); } @Override - public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid) { - // TODO Auto-generated method stub - return null; + public MeshRequest getBranchMicroschemaVersions(String projectName, String branchUuid, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidMicroschemasGetWithHttpInfo(branchUuid, projectName, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), BranchInfoMicroschemaList.class); } @Override public MeshRequest assignBranchMicroschemaVersions(String projectName, String branchUuid, BranchInfoMicroschemaList microschemaVersionReferences) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidMicroschemasPostWithHttpInfo(branchUuid, projectName, adaptRequest(microschemaVersionReferences)), BranchInfoMicroschemaList.class); } @Override public MeshRequest assignBranchMicroschemaVersions(String projectName, String branchUuid, MicroschemaReference... microschemaVersionReferences) { - // TODO Auto-generated method stub - return null; + BranchInfoMicroschemaList list = new BranchInfoMicroschemaList(); + list.add(microschemaVersionReferences); + return assignBranchMicroschemaVersions(projectName, branchUuid, list); } @Override public MeshRequest migrateBranchSchemas(String projectName, String branchUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidMigrateSchemasPostWithHttpInfo(branchUuid, projectName), GenericMessageResponse.class); } @Override public MeshRequest migrateBranchMicroschemas(String projectName, String branchUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidMigrateMicroschemasPostWithHttpInfo(branchUuid, projectName), GenericMessageResponse.class); } @Override public MeshRequest setLatestBranch(String projectName, String branchUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidLatestPostWithHttpInfo(branchUuid, projectName), BranchResponse.class); } @Override public MeshRequest addTagToBranch(String projectName, String branchUuid, String tagUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidTagsTagUuidPostWithHttpInfo(tagUuid, branchUuid, projectName), BranchResponse.class); } @Override public MeshRequest removeTagFromBranch(String projectName, String branchUuid, String tagUuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectBranchesBranchUuidTagsTagUuidDeleteWithHttpInfo(tagUuid, branchUuid, projectName), BranchResponse.class); } @Override public MeshRequest findTagsForBranch(String projectName, String branchUuid, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidTagsGetWithHttpInfo(branchUuid, projectName), TagListResponse.class); } @Override public MeshRequest updateTagsForBranch(String projectName, String branchUuid, TagListUpdateRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidTagsPostWithHttpInfo(branchUuid, projectName, adaptRequest(request)), TagListResponse.class); } @Override public MeshRequest getBranchRolePermissions(String projectName, String uuid) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidRolePermissionsGetWithHttpInfo(uuid, projectName), ObjectPermissionResponse.class); } @Override public MeshRequest grantBranchRolePermissions(String projectName, String uuid, ObjectPermissionGrantRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidRolePermissionsPostWithHttpInfo(uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest revokeBranchRolePermissions(String projectName, String uuid, ObjectPermissionRevokeRequest request) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidRolePermissionsPutWithHttpInfo(uuid, projectName, adaptRequest(request)), ObjectPermissionResponse.class); } @Override public MeshRequest getApiInfo() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GetWithHttpInfo(), MeshServerInfoModel.class); } @Override public MeshRequest getRAML() { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RamlGetCall(null), String.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest getOpenAPI() { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2OpenapiYamlGetCall(null), String.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest graphql(String projectName, GraphQLRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectGraphqlPostWithHttpInfo(projectName, + findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), adaptRequest(request)), GraphQLResponse.class); } @Override public MeshRequest findJobs(ParameterProvider... parameters) { - // TODO Auto-generated method stub return new OpenAPIMeshRequestImpl(() -> api.apiV2AdminJobsGetWithHttpInfo( findParameter(JobParameters.FROM_VERSION_PARAMETER_KEY, parameters), findParameter(JobParameters.MICROSCHEMA_NAME_PARAMETER_KEY, parameters), @@ -1634,137 +1692,211 @@ public MeshRequest invokeJobProcessing() { @Override public MeshRequest get(String path) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "GET", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest get(String path, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "GET", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest post(String path) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "POST", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest post(String path, JsonObject body) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "POST", List.of(), List.of(), body, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest post(String path, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "POST", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest post(String path, T request, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "POST", List.of(), List.of(), request, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest put(String path) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "PUT", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest put(String path, JsonObject body) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "PUT", List.of(), List.of(), body, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest put(String path, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "PUT", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest put(String path, T request, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "PUT", List.of(), List.of(), request, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest delete(String path) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "DELETE", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + JsonObject.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest deleteEmpty(String path) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "DELETE", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + EmptyResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest delete(String path, Class responseClass) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), + new UpgradedCall(api.getApiClient().buildRequestBuilder(apiClient.getBasePath(), path, "DELETE", List.of(), List.of(), null, Map.of(), Map.of(), Map.of(), null, null), apiClient.getHttpClient()), + responseClass); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest ready() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2HealthReadyGetWithHttpInfo(), EmptyResponse.class); } @Override public MeshRequest live() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2HealthLiveGetWithHttpInfo(), EmptyResponse.class); } @Override public MeshRequest writable() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2HealthWritableGetWithHttpInfo(), EmptyResponse.class); } @Override public MeshRequest loadLocalConfig() { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminConfigGetWithHttpInfo(), LocalConfigModel.class); } @Override public MeshRequest updateLocalConfig(LocalConfigModel localConfigModel) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2AdminConfigPostWithHttpInfo(adaptRequest(localConfigModel)), LocalConfigModel.class); } @Override public MeshRequest webrootField(String projectName, String fieldName, String path, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2ProjectWebrootfieldFieldNamePathGetCall(path, fieldName, projectName, + findParameter(ImageManipulationParameters.FOCAL_POINT_Z_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RECT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshWebrootFieldResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest webrootField(String projectName, String fieldName, String[] pathSegments, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return webrootField(projectName, fieldName, Arrays.stream(pathSegments).collect(Collectors.joining("/")), parameters); } @Override public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, String path, ImageManipulationRequest request, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootfieldFieldNamePathPostWithHttpInfo(path, fieldName, projectName, + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), + findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters), + adaptRequest(request)), ImageVariantsResponse.class); } @Override public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, String[] pathSegments, ImageManipulationRequest request, ParameterProvider... parameters) { - return null; + return upsertWebrootFieldImageVariants(projectName, fieldName, Arrays.stream(pathSegments).collect(Collectors.joining("/")), request, parameters); } @Override @@ -1863,13 +1995,13 @@ public String getAPIKey() { @Override public MeshRestClient disableAnonymousAccess() { - // TODO Auto-generated method stub + apiClient.disableAnonymousAccess(true); return this; } @Override public MeshRestClient enableAnonymousAccess() { - // TODO Auto-generated method stub + apiClient.disableAnonymousAccess(false); return this; } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedApiClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedApiClient.java new file mode 100644 index 0000000000..654ffdfa8c --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedApiClient.java @@ -0,0 +1,118 @@ +package com.gentics.mesh.test.openapi; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.openapitools.client.ApiCallback; +import org.openapitools.client.ApiClient; +import org.openapitools.client.ApiException; +import org.openapitools.client.Pair; +import org.openapitools.client.ProgressRequestBody; + +import com.gentics.mesh.http.MeshHeaders; + +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.internal.http.HttpMethod; + +/** + * API client with an access to the request builder + */ +public class UpgradedApiClient extends ApiClient { + + private boolean disableAnonymousAccess; + + public UpgradedApiClient() { + super(); + } + + public UpgradedApiClient(OkHttpClient client) { + super(client); + } + + @SuppressWarnings("rawtypes") + public Request.Builder buildRequestBuilder(String baseUrl, String path, String method, List queryParams, + List collectionQueryParams, Object body, Map headerParams, + Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) + throws ApiException { + final String url = buildUrl(baseUrl, path, queryParams, collectionQueryParams); + + // prepare HTTP request body + RequestBody reqBody; + String contentType = headerParams.get("Content-Type"); + String contentTypePure = contentType; + if (contentTypePure != null && contentTypePure.contains(";")) { + contentTypePure = contentType.substring(0, contentType.indexOf(";")); + } + if (!HttpMethod.permitsRequestBody(method)) { + reqBody = null; + } else if ("application/x-www-form-urlencoded".equals(contentTypePure)) { + reqBody = buildRequestBodyFormEncoding(formParams); + } else if ("multipart/form-data".equals(contentTypePure)) { + 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("", contentType == null ? null : MediaType.parse(contentType)); + } + } else { + reqBody = serialize(body, contentType); + } + + List updatedQueryParams = new ArrayList<>(queryParams); + + // update parameters with authentication settings + updateParamsForAuth(authNames, updatedQueryParams, headerParams, cookieParams, requestBodyToString(reqBody), method, URI.create(url)); + + final Request.Builder reqBuilder = new Request.Builder().url(buildUrl(baseUrl, path, updatedQueryParams, collectionQueryParams)); + processHeaderParams(headerParams, reqBuilder); + processCookieParams(cookieParams, reqBuilder); + + // Associate callback with request (if not null) so interceptor can + // access it when creating ProgressResponseBody + reqBuilder.tag(callback); + + if (callback != null && reqBody != null) { + ProgressRequestBody progressRequestBody = new ProgressRequestBody(reqBody, callback); + reqBuilder.method(method, progressRequestBody); + } else { + reqBuilder.method(method, reqBody); + } + + if (disableAnonymousAccess) { + headerParams.put(MeshHeaders.ANONYMOUS_AUTHENTICATION, "disable"); + } + return reqBuilder; + } + + @SuppressWarnings("rawtypes") + @Override + public Request buildRequest(String baseUrl, String path, String method, List queryParams, + List collectionQueryParams, Object body, Map headerParams, + Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) + throws ApiException { + return buildRequestBuilder(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback).build(); + } + + @SuppressWarnings("rawtypes") + @Override + public UpgradedCall buildCall(String baseUrl, String path, String method, List queryParams, + List collectionQueryParams, Object body, Map headerParams, + Map cookieParams, Map formParams, String[] authNames, ApiCallback callback) + throws ApiException { + Request.Builder request = buildRequestBuilder(baseUrl, path, method, queryParams, collectionQueryParams, body, headerParams, cookieParams, formParams, authNames, callback); + + return new UpgradedCall(request, httpClient); + } + + public void disableAnonymousAccess(boolean b) { + this.disableAnonymousAccess = b; + } +} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedCall.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedCall.java new file mode 100644 index 0000000000..f6b23b67d3 --- /dev/null +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedCall.java @@ -0,0 +1,103 @@ +package com.gentics.mesh.test.openapi; + +import java.io.IOException; + +import kotlin.jvm.functions.Function0; +import kotlin.reflect.KClass; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Request.Builder; +import okhttp3.Response; +import okio.Timeout; + +/** + * Lazily build {@link Call} + */ +public class UpgradedCall implements Call { + + private final Request.Builder builder; + private final OkHttpClient client; + private Call call = null; + + public UpgradedCall(Builder builder, OkHttpClient client) { + this.builder = builder; + this.client = client; + } + + @Override + public void cancel() { + lazy().cancel(); + } + + @Override + public void enqueue(Callback arg0) { + lazy().enqueue(arg0); + } + + @Override + public Response execute() throws IOException { + return lazy().execute(); + } + + @Override + public boolean isCanceled() { + return lazy().isCanceled(); + } + + @Override + public boolean isExecuted() { + return lazy().isExecuted(); + } + + @Override + public Request request() { + return lazy().request(); + } + + @Override + public T tag(KClass arg0) { + return lazy().tag(arg0); + } + + @Override + public T tag(Class arg0) { + return lazy().tag(arg0); + } + + @Override + public T tag(KClass arg0, Function0 arg1) { + return lazy().tag(arg0, arg1); + } + + @Override + public T tag(Class arg0, Function0 arg1) { + return lazy().tag(arg0, arg1); + } + + @Override + public Timeout timeout() { + return lazy().timeout(); + } + + @Override + public UpgradedCall clone() { + return new UpgradedCall(builder, client); + } + + public Request.Builder getBuilder() { + return builder; + } + + public Request build() { + return lazy().request(); + } + + private Call lazy() { + if (call == null) { + call = client.newCall(builder.build()); + } + return call; + } +} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java index 61a040c790..5d3fd2a9c9 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java @@ -8,7 +8,6 @@ import java.util.Optional; import org.openapitools.client.ApiCallback; -import org.openapitools.client.ApiClient; import org.openapitools.client.ApiException; import org.openapitools.client.ApiResponse; import org.openapitools.client.Pair; @@ -27,32 +26,246 @@ @SuppressWarnings("rawtypes") public class UpgradedDefaultApi extends DefaultApi { - public UpgradedDefaultApi() { - super(); - } + private UpgradedApiClient localVarApiClient; - public UpgradedDefaultApi(ApiClient apiClient) { + public UpgradedDefaultApi(UpgradedApiClient apiClient) { super(apiClient); + this.localVarApiClient = apiClient; } - /** - * Build call for apiV2ProjectNodesNodeUuidPost - * @param nodeUuid Uuid of the node (required) - * @param project (required) - * @param body Json body - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - - - -
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { + @Override + public UpgradedApiClient getApiClient() { + return localVarApiClient; + } + + // Fix for no generated response + + public UpgradedCall apiV2RamlGetCall(final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/raml"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/x-yaml" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2ProjectWebrootfieldFieldNamePathGetCall(@jakarta.annotation.Nonnull String path, @jakarta.annotation.Nonnull String fieldName, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Double fpz, @jakarta.annotation.Nullable String rect, @jakarta.annotation.Nullable Double w, @jakarta.annotation.Nullable Double h, @jakarta.annotation.Nullable String resize, @jakarta.annotation.Nullable String version, @jakarta.annotation.Nullable Double fpy, @jakarta.annotation.Nullable String crop, @jakarta.annotation.Nullable Double fpx, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/{project}/webrootfield/{fieldName}/{path}" + .replace("{" + "path" + "}", getApiClient().escapeString(path.toString())) + .replace("{" + "fieldName" + "}", getApiClient().escapeString(fieldName.toString())) + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (fpz != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fpz", fpz)); + } + + if (rect != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("rect", rect)); + } + + if (w != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("w", w)); + } + + if (h != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("h", h)); + } + + if (resize != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("resize", resize)); + } + + if (version != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("version", version)); + } + + if (fpy != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fpy", fpy)); + } + + if (crop != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("crop", crop)); + } + + if (fpx != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fpx", fpx)); + } + + final String[] localVarAccepts = { + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2OpenapiYamlGetCall(final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/openapi.yaml"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + final String[] localVarAccepts = { + "application/x-yaml" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "GET", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + // Fix for no generated body parameter + + public UpgradedCall apiV2UtilitiesLinkResolverPostCall(String body, String lang, String resolveLinks, ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = body; + + // create path and map variables + String localVarPath = "/api/v2/utilities/linkResolver"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (lang != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("lang", lang)); + } + + if (resolveLinks != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("resolveLinks", resolveLinks)); + } + + final String[] localVarAccepts = { + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "text/plain" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public okhttp3.Call apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -182,6 +395,7 @@ public okhttp3.Call apiV2ProjectNodesNodeUuidPostAsync(@jakarta.annotation.Nonnu } // Missing JSON body fix + /** * Build call for apiV2SearchNodesPost * @param wait Specify whether search should wait for the search to be idle before responding. (optional) From 12a13c7f8e5608c65d9c63a64020f28d75b9f668 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 24 Mar 2026 16:44:00 +0100 Subject: [PATCH 64/75] Generated client fullfilled --- .../mesh/core/endpoint/node/NodeEndpoint.java | 4 +- .../mesh/rest/MeshLocalClientImpl.java | 40 +- .../client/impl/MeshRestHttpClientImpl.java | 40 +- .../client/method/SearchClientMethods.java | 20 +- .../test/openapi/OpenAPIMeshRestClient.java | 258 ++- .../mesh/test/openapi/UpgradedDefaultApi.java | 1555 ++++++++++++----- 6 files changed, 1308 insertions(+), 609 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index d608b03184..c78314d4fe 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -42,7 +42,6 @@ import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; import com.gentics.mesh.core.rest.navigation.NavigationResponse; import com.gentics.mesh.etc.config.MeshOptions; -import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.impl.BinaryCheckParametersImpl; import com.gentics.mesh.parameter.impl.BranchParametersImpl; import com.gentics.mesh.parameter.impl.DeleteParametersImpl; @@ -58,7 +57,6 @@ import com.gentics.mesh.rest.InternalEndpointRoute; import io.vertx.core.MultiMap; -import io.vertx.core.json.JsonObject; /** * The content verticle adds rest endpoints for manipulating nodes. @@ -598,7 +596,7 @@ private void addUpdateHandler() { postEndpoint.method(POST); postEndpoint.consumes(APPLICATION_JSON); postEndpoint.produces(APPLICATION_JSON); - postEndpoint.exampleRequest(new JsonObject(JsonUtil.toJson(nodeExamples.getNodeCreateRequest2()))); + postEndpoint.exampleRequest(nodeExamples.getNodeCreateRequest2()); postEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); postEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); postEndpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 1813ba81df..239e436c78 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -1001,8 +1001,8 @@ public MeshRequest searchUsers(String json, ParameterProvider. } @Override - public MeshRequest searchUsersRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchUsersRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1013,8 +1013,8 @@ public MeshRequest searchGroups(String json, ParameterProvide } @Override - public MeshRequest searchGroupsRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchGroupsRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1026,8 +1026,8 @@ public MeshRequest searchRoles(String json, ParameterProvider. } @Override - public MeshRequest searchRolesRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchRolesRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1039,8 +1039,8 @@ public MeshRequest searchProjects(String json, ParameterPro } @Override - public MeshRequest searchProjectsRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchProjectsRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1051,8 +1051,8 @@ public MeshRequest searchTags(String json, ParameterProvider... } @Override - public MeshRequest searchTagsRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchTagsRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1063,8 +1063,8 @@ public MeshRequest searchTagFamilies(String json, Paramet } @Override - public MeshRequest searchTagFamiliesRaw(String projectName, String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchTagFamiliesRaw(String projectName, String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1075,8 +1075,8 @@ public MeshRequest searchSchemas(String json, ParameterProvi } @Override - public MeshRequest searchSchemasRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchSchemasRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1087,8 +1087,8 @@ public MeshRequest searchMicroschemas(String json, Para } @Override - public MeshRequest searchMicroschemasRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchMicroschemasRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1589,8 +1589,8 @@ public MeshRequest searchTags(String projectName, String json, } @Override - public MeshRequest searchTagsRaw(String projectName, String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchTagsRaw(String projectName, String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1601,8 +1601,8 @@ public MeshRequest searchTagFamilies(String projectName, } @Override - public MeshRequest searchTagFamiliesRaw(String json) { - LocalActionContextImpl ac = createContext(ObjectNode.class); + public MeshRequest searchTagFamiliesRaw(String json, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ObjectNode.class, parameters); return new MeshLocalRequestImpl<>(ac.getFuture()); } diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index 5ac9c330b4..df761ac1e9 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -983,9 +983,9 @@ public MeshRequest searchUsers(String json, ParameterProvider. } @Override - public MeshRequest searchUsersRaw(String json) { + public MeshRequest searchUsersRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/users", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/users" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -995,9 +995,9 @@ public MeshRequest searchGroups(String json, ParameterProvide } @Override - public MeshRequest searchGroupsRaw(String json) { + public MeshRequest searchGroupsRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/groups", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/groups" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1007,9 +1007,9 @@ public MeshRequest searchRoles(String json, ParameterProvider. } @Override - public MeshRequest searchRolesRaw(String json) { + public MeshRequest searchRolesRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/roles", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/roles" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1019,9 +1019,9 @@ public MeshRequest searchMicroschemas(String json, Para } @Override - public MeshRequest searchMicroschemasRaw(String json) { + public MeshRequest searchMicroschemasRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/microschemas", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/microschemas" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1031,9 +1031,9 @@ public MeshRequest searchProjects(String json, ParameterPro } @Override - public MeshRequest searchProjectsRaw(String json) { + public MeshRequest searchProjectsRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/projects", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/projects" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1043,9 +1043,9 @@ public MeshRequest searchTags(String json, ParameterProvider... } @Override - public MeshRequest searchTagsRaw(String json) { + public MeshRequest searchTagsRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/tags", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/tags" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1056,10 +1056,10 @@ public MeshRequest searchTags(String projectName, String json, } @Override - public MeshRequest searchTagsRaw(String projectName, String json) { + public MeshRequest searchTagsRaw(String projectName, String json, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/" + encodeSegment(projectName) + "/rawSearch/tags", ObjectNode.class, json); + return handleRequest(POST, "/" + encodeSegment(projectName) + "/rawSearch/tags" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1069,9 +1069,9 @@ public MeshRequest searchSchemas(String json, ParameterProvi } @Override - public MeshRequest searchSchemasRaw(String json) { + public MeshRequest searchSchemasRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/schemas", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/schemas" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1081,9 +1081,9 @@ public MeshRequest searchTagFamilies(String json, Paramet } @Override - public MeshRequest searchTagFamiliesRaw(String json) { + public MeshRequest searchTagFamiliesRaw(String json, ParameterProvider... parameters) { Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/rawSearch/tagFamilies", ObjectNode.class, json); + return handleRequest(POST, "/rawSearch/tagFamilies" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override @@ -1095,10 +1095,10 @@ public MeshRequest searchTagFamilies(String projectName, } @Override - public MeshRequest searchTagFamiliesRaw(String projectName, String json) { + public MeshRequest searchTagFamiliesRaw(String projectName, String json, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(json, "json must not be null"); - return handleRequest(POST, "/" + encodeSegment(projectName) + "/rawSearch/tagFamilies", ObjectNode.class, json); + return handleRequest(POST, "/" + encodeSegment(projectName) + "/rawSearch/tagFamilies" + getQuery(getConfig(), parameters), ObjectNode.class, json); } @Override diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/SearchClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/SearchClientMethods.java index a227bc9ce3..3b27ce16a5 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/SearchClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/SearchClientMethods.java @@ -77,7 +77,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchUsersRaw(String json); + MeshRequest searchUsersRaw(String json, ParameterProvider... parameters); /** * Search groups. @@ -95,7 +95,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchGroupsRaw(String json); + MeshRequest searchGroupsRaw(String json, ParameterProvider... parameters); /** * Search roles. @@ -113,7 +113,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchRolesRaw(String json); + MeshRequest searchRolesRaw(String json, ParameterProvider... parameters); /** * Search projects. @@ -131,7 +131,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchProjectsRaw(String json); + MeshRequest searchProjectsRaw(String json, ParameterProvider... parameters); /** * Search tags. @@ -149,7 +149,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchTagsRaw(String json); + MeshRequest searchTagsRaw(String json, ParameterProvider... parameters); /** * Search tags in project @@ -170,7 +170,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchTagsRaw(String projectName, String json); + MeshRequest searchTagsRaw(String projectName, String json, ParameterProvider... parameters); /** * Search tag families. @@ -187,7 +187,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchTagFamiliesRaw(String json); + MeshRequest searchTagFamiliesRaw(String json, ParameterProvider... parameters); /** * Search tag families in project. @@ -206,7 +206,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchTagFamiliesRaw(String projectName, String json); + MeshRequest searchTagFamiliesRaw(String projectName, String json, ParameterProvider... parameters); /** * Search schemas. @@ -224,7 +224,7 @@ public interface SearchClientMethods { * @param json * @return */ - MeshRequest searchSchemasRaw(String json); + MeshRequest searchSchemasRaw(String json, ParameterProvider... parameters); /** * Search microschemas. @@ -243,7 +243,7 @@ public interface SearchClientMethods { * Elasticsearch search request * @return */ - MeshRequest searchMicroschemasRaw(String json); + MeshRequest searchMicroschemasRaw(String json, ParameterProvider... parameters); /** * Clear all search indices by removing and re-creating them. diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index d71c056010..075533db75 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -212,14 +212,14 @@ public MeshRequest createNode(String projectName, NodeCreateReques @Override public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeCreateRequest.toJson())), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeCreateRequest)), NodeResponse.class); } @Override public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, new JsonObject(nodeUpsertRequest.toJson())), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeUpsertRequest)), NodeResponse.class); } @@ -1037,160 +1037,272 @@ public MeshRequest me(ParameterProvider... parameters) { @Override public MeshRequest searchNodes(String json, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SearchNodesPostWithHttpInfo(json, - findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), - findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), - findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), - findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), - findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), - findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), - findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchNodesPostCall(json, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(BranchParameters.BRANCH_QUERY_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), NodeListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchNodesRaw(String json, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2RawSearchNodesPost(json, - findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters)), ObjectNode.class); + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchNodesPostCall(json, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchNodes(String projectName, String json, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectSearchNodesPostWithHttpInfo(json, projectName, - findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), - findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), - findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), - findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), - findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), - findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2ProjectSearchNodesPostCall(json, projectName, + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(GenericParameters.ETAG_PARAM_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), NodeListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchNodesRaw(String projectName, String json, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectRawSearchNodesPostWithHttpInfo(json, projectName, - findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters)), ObjectNode.class); + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2ProjectRawSearchNodesPostCall(json, projectName, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchUsers(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchUsersPostCall(json, + findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), UserListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchUsersRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchUsersRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchUsersPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchGroups(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchGroupsPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), GroupListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchGroupsRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchGroupsRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchGroupsPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchRoles(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchRolesPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), RoleListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchRolesRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchRolesRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchRolesPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchProjects(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchProjectsPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), ProjectListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchProjectsRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchProjectsRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchProjectsPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchTags(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchTagsPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), TagListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchTagsRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchTagsRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchTagsPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchTags(String projectName, String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2ProjectSearchTagsPostCall(json, projectName, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), TagListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchTagsRaw(String projectName, String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchTagsRaw(String projectName, String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2ProjectRawSearchTagsPostCall(json, projectName, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchTagFamilies(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2RawSearchTagFamiliesPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchTagFamiliesRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchTagFamiliesRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2RawSearchTagFamiliesPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchTagFamilies(String projectName, String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2ProjectSearchTagFamiliesPostCall(json, projectName, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), TagFamilyListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchTagFamiliesRaw(String projectName, String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchTagFamiliesRaw(String projectName, String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl(apiClient.getHttpClient(), api.apiV2ProjectRawSearchTagFamiliesPostCall(json, projectName, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchSchemas(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchSchemasPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), SchemaListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchSchemasRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchSchemasRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchSchemasPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override public MeshRequest searchMicroschemas(String json, ParameterProvider... parameters) { - // TODO Auto-generated method stub - return null; + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2SearchMicroschemasPostCall(json, findParameter(SortingParameters.SORT_BY_PARAMETER_KEY, parameters), + findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), + findParameter(PagingParameters.PER_PAGE_PARAMETER_KEY, parameters), + findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters), null), MicroschemaListResponse.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override - public MeshRequest searchMicroschemasRaw(String json) { - // TODO Auto-generated method stub - return null; + public MeshRequest searchMicroschemasRaw(String json, ParameterProvider... parameters) { + try { + return new OpenAPIMeshRawRequestImpl<>(apiClient.getHttpClient(), api.apiV2RawSearchMicroschemasPostCall(json, findParameter(SearchParameters.WAIT_PARAMETER_KEY, parameters), null), ObjectNode.class); + } catch (ApiException e) { + throw new IllegalStateException(e); + } } @Override diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java index 5d3fd2a9c9..d5a48818af 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/UpgradedDefaultApi.java @@ -1,6 +1,5 @@ package com.gentics.mesh.test.openapi; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -9,14 +8,10 @@ import org.openapitools.client.ApiCallback; import org.openapitools.client.ApiException; -import org.openapitools.client.ApiResponse; import org.openapitools.client.Pair; import org.openapitools.client.api.DefaultApi; -import org.openapitools.client.model.NodeListResponse; -import org.openapitools.client.model.NodeResponse; import com.google.gson.JsonParser; -import com.google.gson.reflect.TypeToken; import io.vertx.core.json.JsonObject; @@ -265,7 +260,7 @@ public UpgradedCall apiV2UtilitiesLinkResolverPostCall(String body, String lang, return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - public okhttp3.Call apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { + public UpgradedCall apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -313,111 +308,9 @@ public okhttp3.Call apiV2ProjectNodesNodeUuidPostCall(@jakarta.annotation.Nonnul return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - private okhttp3.Call apiV2ProjectNodesNodeUuidPostValidateBeforeCall(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { - // verify the required parameter 'nodeUuid' is set - if (nodeUuid == null) { - throw new ApiException("Missing the required parameter 'nodeUuid' when calling apiV2ProjectNodesNodeUuidPost(Async)"); - } - - // verify the required parameter 'project' is set - if (project == null) { - throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectNodesNodeUuidPost(Async)"); - } - - return apiV2ProjectNodesNodeUuidPostCall(nodeUuid, project, body, _callback); - } - - /** - * - * - * @param nodeUuid Uuid of the node (required) - * @param project (required) - * @return NodeResponse - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - - -
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
- */ - public NodeResponse apiV2ProjectNodesNodeUuidPost(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body) throws ApiException { - ApiResponse localVarResp = apiV2ProjectNodesNodeUuidPostWithHttpInfo(nodeUuid, project, body); - return localVarResp.getData(); - } - - /** - * - * - * @param nodeUuid Uuid of the node (required) - * @param project (required) - * @return ApiResponse<NodeResponse> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - - -
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
- */ - public ApiResponse apiV2ProjectNodesNodeUuidPostWithHttpInfo(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body) throws ApiException { - okhttp3.Call localVarCall = apiV2ProjectNodesNodeUuidPostValidateBeforeCall(nodeUuid, project, body, null); - Type localVarReturnType = new TypeToken(){}.getType(); - return getApiClient().execute(localVarCall, localVarReturnType); - } - - /** - * (asynchronously) - * - * @param nodeUuid Uuid of the node (required) - * @param project (required) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - - - -
Response Details
Status Code Description Response Headers
200 New or updated node. -
409 A conflict has been detected. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectNodesNodeUuidPostAsync(@jakarta.annotation.Nonnull String nodeUuid, @jakarta.annotation.Nonnull String project, JsonObject body, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = apiV2ProjectNodesNodeUuidPostValidateBeforeCall(nodeUuid, project, body, _callback); - Type localVarReturnType = new TypeToken(){}.getType(); - getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; - } - // Missing JSON body fix - /** - * Build call for apiV2SearchNodesPost - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
- */ - public okhttp3.Call apiV2SearchNodesPostCall(String jsonbody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + public UpgradedCall apiV2SearchNodesPostCall(String jsonbody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -494,110 +387,56 @@ public okhttp3.Call apiV2SearchNodesPostCall(String jsonbody, @jakarta.annotatio return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - @SuppressWarnings("rawtypes") - private okhttp3.Call apiV2SearchNodesPostValidateBeforeCall(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { - return apiV2SearchNodesPostCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, _callback); + public UpgradedCall apiV2RawSearchNodesPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; - } + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } - /** - * - * Invoke a search query for nodes and return a paged list response. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @return NodeListResponse - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
- */ - public NodeListResponse apiV2SearchNodesPost(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order) throws ApiException { - ApiResponse localVarResp = apiV2SearchNodesPostWithHttpInfo(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order); - return localVarResp.getData(); - } + Object localVarPostBody = JsonParser.parseString(json); - /** - * - * Invoke a search query for nodes and return a paged list response. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @return ApiResponse<NodeListResponse> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
- */ - public ApiResponse apiV2SearchNodesPostWithHttpInfo(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order) throws ApiException { - okhttp3.Call localVarCall = apiV2SearchNodesPostValidateBeforeCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, null); - Type localVarReturnType = new TypeToken(){}.getType(); - return getApiClient().execute(localVarCall, localVarReturnType); - } + // create path and map variables + String localVarPath = "/api/v2/rawSearch/nodes"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } - /** - * (asynchronously) - * Invoke a search query for nodes and return a paged list response. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param branch Specifies the branch to be used for loading data. The latest project branch will be used if this parameter is omitted. (optional) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result for nodes -
0 application/json -
- */ - public okhttp3.Call apiV2SearchNodesPostAsync(String jsonBody, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String branch, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = apiV2SearchNodesPostValidateBeforeCall(jsonBody, wait, perPage, sortBy, etag, page, fields, branch, order, _callback); - Type localVarReturnType = new TypeToken(){}.getType(); - getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - /** - * Build call for apiV2RawSearchNodesPost - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public okhttp3.Call apiV2RawSearchNodesPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + public UpgradedCall apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -614,7 +453,8 @@ public okhttp3.Call apiV2RawSearchNodesPostCall(String json, @jakarta.annotation Object localVarPostBody = JsonParser.parseString(json); // create path and map variables - String localVarPath = "/api/v2/rawSearch/nodes"; + String localVarPath = "/api/v2/{project}/rawSearch/nodes" + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); List localVarQueryParams = new ArrayList(); List localVarCollectionQueryParams = new ArrayList(); @@ -645,90 +485,56 @@ public okhttp3.Call apiV2RawSearchNodesPostCall(String json, @jakarta.annotation String[] localVarAuthNames = new String[] { "bearerAuth" }; return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } + public UpgradedCall apiV2RawSearchGroupsPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; - @SuppressWarnings("rawtypes") - private okhttp3.Call apiV2RawSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { - return apiV2RawSearchNodesPostCall(json, wait, _callback); - } + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } - /** - * - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @return Object - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public Object apiV2RawSearchNodesPost(String json, @jakarta.annotation.Nullable Boolean wait) throws ApiException { - ApiResponse localVarResp = apiV2RawSearchNodesPostWithHttpInfo(json, wait); - return localVarResp.getData(); - } + Object localVarPostBody = null; - /** - * - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @return ApiResponse<Object> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public ApiResponse apiV2RawSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nullable Boolean wait) throws ApiException { - okhttp3.Call localVarCall = apiV2RawSearchNodesPostValidateBeforeCall(json, wait, null); - Type localVarReturnType = new TypeToken(){}.getType(); - return getApiClient().execute(localVarCall, localVarReturnType); - } + // create path and map variables + String localVarPath = "/api/v2/rawSearch/groups"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } - /** - * (asynchronously) - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public okhttp3.Call apiV2RawSearchNodesPostAsync(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = apiV2RawSearchNodesPostValidateBeforeCall(json, wait, _callback); - Type localVarReturnType = new TypeToken(){}.getType(); - getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - /** - * Build call for apiV2ProjectRawSearchNodesPost - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + public UpgradedCall apiV2RawSearchRolesPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -742,11 +548,10 @@ public okhttp3.Call apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.ann basePath = null; } - Object localVarPostBody = JsonParser.parseString(json); + Object localVarPostBody = null; // create path and map variables - String localVarPath = "/api/v2/{project}/rawSearch/nodes" - .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + String localVarPath = "/api/v2/rawSearch/roles"; List localVarQueryParams = new ArrayList(); List localVarCollectionQueryParams = new ArrayList(); @@ -755,13 +560,13 @@ public okhttp3.Call apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.ann Map localVarFormParams = new HashMap(); if (wait != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); } final String[] localVarAccepts = { "application/json" }; - final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); if (localVarAccept != null) { localVarHeaderParams.put("Accept", localVarAccept); } @@ -769,113 +574,65 @@ public okhttp3.Call apiV2ProjectRawSearchNodesPostCall(String json, @jakarta.ann final String[] localVarContentTypes = { "application/json" }; - final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); if (localVarContentType != null) { localVarHeaderParams.put("Content-Type", localVarContentType); } String[] localVarAuthNames = new String[] { "bearerAuth" }; - return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - @SuppressWarnings("rawtypes") - private okhttp3.Call apiV2ProjectRawSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { - // verify the required parameter 'project' is set - if (project == null) { - throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectRawSearchNodesPost(Async)"); + public UpgradedCall apiV2RawSearchProjectsPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; } - return apiV2ProjectRawSearchNodesPostCall(json, project, wait, _callback); + Object localVarPostBody = JsonParser.parseString(json); - } + // create path and map variables + String localVarPath = "/api/v2/rawSearch/projects"; - /** - * - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @return Object - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public Object apiV2ProjectRawSearchNodesPost(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait) throws ApiException { - ApiResponse localVarResp = apiV2ProjectRawSearchNodesPostWithHttpInfo(json, project, wait); - return localVarResp.getData(); - } + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); - /** - * - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @return ApiResponse<Object> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public ApiResponse apiV2ProjectRawSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait) throws ApiException { - okhttp3.Call localVarCall = apiV2ProjectRawSearchNodesPostValidateBeforeCall(json, project, wait, null); - Type localVarReturnType = new TypeToken(){}.getType(); - return getApiClient().execute(localVarCall, localVarReturnType); - } + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } - /** - * (asynchronously) - * Invoke a search query for nodes and return the unmodified Elasticsearch response. Note that the query will be executed using the multi search API of Elasticsearch. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Raw search response. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectRawSearchNodesPostAsync(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = apiV2ProjectRawSearchNodesPostValidateBeforeCall(json, project, wait, _callback); - Type localVarReturnType = new TypeToken(){}.getType(); - getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - /** - * Build call for apiV2ProjectSearchNodesPost - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @param _callback Callback for upload/download progress - * @return Call to execute - * @throws ApiException If fail to serialize the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + public UpgradedCall apiV2RawSearchTagsPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { String basePath = null; // Operation Servers String[] localBasePaths = new String[] { }; @@ -892,8 +649,7 @@ public okhttp3.Call apiV2ProjectSearchNodesPostCall(String json, @jakarta.annota Object localVarPostBody = JsonParser.parseString(json); // create path and map variables - String localVarPath = "/api/v2/{project}/search/nodes" - .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + String localVarPath = "/api/v2/rawSearch/tags"; List localVarQueryParams = new ArrayList(); List localVarCollectionQueryParams = new ArrayList(); @@ -902,37 +658,78 @@ public okhttp3.Call apiV2ProjectSearchNodesPostCall(String json, @jakarta.annota Map localVarFormParams = new HashMap(); if (wait != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); } - if (perPage != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("perPage", perPage)); + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2SearchSchemasPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; } + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/search/schemas"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + if (sortBy != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("sortBy", sortBy)); + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); } - if (etag != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("etag", etag)); + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); } if (page != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("page", page)); + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); } - if (fields != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("fields", fields)); + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); } if (order != null) { - localVarQueryParams.addAll(getApiClient().parameterToPair("order", order)); + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); } final String[] localVarAccepts = { "application/json" }; - final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); if (localVarAccept != null) { localVarHeaderParams.put("Accept", localVarAccept); } @@ -940,106 +737,898 @@ public okhttp3.Call apiV2ProjectSearchNodesPostCall(String json, @jakarta.annota final String[] localVarContentTypes = { "application/json" }; - final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); if (localVarContentType != null) { localVarHeaderParams.put("Content-Type", localVarContentType); } String[] localVarAuthNames = new String[] { "bearerAuth" }; - return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } - @SuppressWarnings("rawtypes") - private okhttp3.Call apiV2ProjectSearchNodesPostValidateBeforeCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { - // verify the required parameter 'project' is set - if (project == null) { - throw new ApiException("Missing the required parameter 'project' when calling apiV2ProjectSearchNodesPost(Async)"); + public UpgradedCall apiV2SearchMicroschemasPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; } - return apiV2ProjectSearchNodesPostCall(json, project, wait, perPage, sortBy, etag, page, fields, order, _callback); + Object localVarPostBody = JsonParser.parseString(json); - } + // create path and map variables + String localVarPath = "/api/v2/search/microschemas"; - /** - * - * Invoke a search query for nodes and return a paged list response. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @return NodeListResponse - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
- */ - public NodeListResponse apiV2ProjectSearchNodesPost(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order) throws ApiException { - ApiResponse localVarResp = apiV2ProjectSearchNodesPostWithHttpInfo(json, project, wait, perPage, sortBy, etag, page, fields, order); - return localVarResp.getData(); - } + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } - /** - * - * Invoke a search query for nodes and return a paged list response. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @return ApiResponse<NodeListResponse> - * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
- */ - public ApiResponse apiV2ProjectSearchNodesPostWithHttpInfo(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order) throws ApiException { - okhttp3.Call localVarCall = apiV2ProjectSearchNodesPostValidateBeforeCall(json, project, wait, perPage, sortBy, etag, page, fields, order, null); - Type localVarReturnType = new TypeToken(){}.getType(); - return getApiClient().execute(localVarCall, localVarReturnType); + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } + public UpgradedCall apiV2ProjectSearchTagFamiliesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; - /** - * (asynchronously) - * Invoke a search query for nodes and return a paged list response. - * @param project (required) - * @param wait Specify whether search should wait for the search to be idle before responding. (optional) - * @param perPage Number of elements per page. (optional) - * @param sortBy Field name to sort the result by. (optional) - * @param etag Parameter which can be used to disable the etag parameter generation and thus increase performance when etags are not needed. (optional, default to true) - * @param page Number of page to be loaded. (optional) - * @param fields Limit the output to certain fields. This is useful in order to reduce the response JSON overhead. (optional, default to ) - * @param order Field order (ASC/DESC) to sort the result by. (optional) - * @param _callback The callback to be executed when the API call finishes - * @return The request call - * @throws ApiException If fail to process the API call, e.g. serializing the request body object - * @http.response.details - - - - - -
Response Details
Status Code Description Response Headers
200 Paged search result list. -
0 application/json -
- */ - public okhttp3.Call apiV2ProjectSearchNodesPostAsync(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { - - okhttp3.Call localVarCall = apiV2ProjectSearchNodesPostValidateBeforeCall(json, project, wait, perPage, sortBy, etag, page, fields, order, _callback); - Type localVarReturnType = new TypeToken(){}.getType(); - getApiClient().executeAsync(localVarCall, localVarReturnType, _callback); - return localVarCall; + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/{project}/search/tagFamilies" + .replace("{" + "project" + "}", localVarApiClient.escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + public UpgradedCall apiV2ProjectSearchTagsPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/search/tags" + .replace("{" + "project" + "}", localVarApiClient.escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + public UpgradedCall apiV2SearchTagsPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/search/tags"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + public UpgradedCall apiV2SearchProjectsPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/search/projects"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + public UpgradedCall apiV2SearchRolesPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/search/roles"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + public UpgradedCall apiV2SearchGroupsPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/search/groups"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2ProjectRawSearchTagsPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/rawSearch/tags" + .replace("{" + "project" + "}", localVarApiClient.escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2ProjectRawSearchTagFamiliesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/rawSearch/tagFamilies" + .replace("{" + "project" + "}", localVarApiClient.escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2RawSearchTagFamiliesPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = null; + + // create path and map variables + String localVarPath = "/api/v2/rawSearch/tagFamilies"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2RawSearchSchemasPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/rawSearch/schemas"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2RawSearchMicroschemasPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/rawSearch/microschemas"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2RawSearchUsersPostCall(String json, @jakarta.annotation.Nullable Boolean wait, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/rawSearch/users"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2SearchUsersPostCall(String json, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/search/users"; + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (sortBy != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("sortBy", sortBy)); + } + + if (wait != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("wait", wait)); + } + + if (page != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("page", page)); + } + + if (perPage != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("perPage", perPage)); + } + + if (order != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = localVarApiClient.selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = localVarApiClient.selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return localVarApiClient.buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); + } + + public UpgradedCall apiV2ProjectSearchNodesPostCall(String json, @jakarta.annotation.Nonnull String project, @jakarta.annotation.Nullable Boolean wait, @jakarta.annotation.Nullable Double perPage, @jakarta.annotation.Nullable String sortBy, @jakarta.annotation.Nullable Boolean etag, @jakarta.annotation.Nullable Double page, @jakarta.annotation.Nullable String fields, @jakarta.annotation.Nullable String order, final ApiCallback _callback) throws ApiException { + String basePath = null; + // Operation Servers + String[] localBasePaths = new String[] { }; + + // Determine Base Path to Use + if (getCustomBaseUrl() != null){ + basePath = getCustomBaseUrl(); + } else if ( localBasePaths.length > 0 ) { + basePath = localBasePaths[getHostIndex()]; + } else { + basePath = null; + } + + Object localVarPostBody = JsonParser.parseString(json); + + // create path and map variables + String localVarPath = "/api/v2/{project}/search/nodes" + .replace("{" + "project" + "}", getApiClient().escapeString(project.toString())); + + List localVarQueryParams = new ArrayList(); + List localVarCollectionQueryParams = new ArrayList(); + Map localVarHeaderParams = new HashMap(); + Map localVarCookieParams = new HashMap(); + Map localVarFormParams = new HashMap(); + + if (wait != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("wait", wait)); + } + + if (perPage != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("perPage", perPage)); + } + + if (sortBy != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("sortBy", sortBy)); + } + + if (etag != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("etag", etag)); + } + + if (page != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("page", page)); + } + + if (fields != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("fields", fields)); + } + + if (order != null) { + localVarQueryParams.addAll(getApiClient().parameterToPair("order", order)); + } + + final String[] localVarAccepts = { + "application/json" + }; + final String localVarAccept = getApiClient().selectHeaderAccept(localVarAccepts); + if (localVarAccept != null) { + localVarHeaderParams.put("Accept", localVarAccept); + } + + final String[] localVarContentTypes = { + "application/json" + }; + final String localVarContentType = getApiClient().selectHeaderContentType(localVarContentTypes); + if (localVarContentType != null) { + localVarHeaderParams.put("Content-Type", localVarContentType); + } + + String[] localVarAuthNames = new String[] { "bearerAuth" }; + return getApiClient().buildCall(basePath, localVarPath, "POST", localVarQueryParams, localVarCollectionQueryParams, localVarPostBody, localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAuthNames, _callback); } } From bba08dfbe3ca40626d86b97cc4a6ee611e283b1e Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 25 Mar 2026 10:54:06 +0100 Subject: [PATCH 65/75] Generated client fixes --- .../microschema/MicroschemaEndpoint.java | 3 +- .../core/endpoint/schema/SchemaEndpoint.java | 3 +- .../gentics/mesh/example/SchemaExamples.java | 1 + .../test/openapi/OpenAPIMeshRestClient.java | 76 +++++++++++++------ 4 files changed, 56 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java index 78714680d8..5aad742170 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java @@ -25,6 +25,7 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.RolePermissionHandlingEndpoint; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; +import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; @@ -188,7 +189,7 @@ private void addUpdateHandler() { updateEndpoint.consumes(APPLICATION_JSON); updateEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); - updateEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); + updateEndpoint.exampleResponse(OK, new GenericMessageResponse("The schema has been updated. Migration has been invoked."), "Migration message."); updateEndpoint.description("Update the microschema with the given uuid."); updateEndpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); updateEndpoint.blockingHandler(rc -> { diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java index 9dd1da86d3..f5558b1dab 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java @@ -25,6 +25,7 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.RolePermissionHandlingEndpoint; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; +import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; @@ -143,7 +144,7 @@ private void addUpdateHandler() { updateEndpoint.produces(APPLICATION_JSON); updateEndpoint.addQueryParameters(SchemaUpdateParametersImpl.class); updateEndpoint.exampleRequest(schemaExamples.getSchemaUpdateRequest()); - updateEndpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated schema."); + updateEndpoint.exampleResponse(OK, new GenericMessageResponse("The schema has been updated. Migration has been invoked."), "Updated schema."); updateEndpoint.events(SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); updateEndpoint.blockingHandler(rc -> { // Update operations should always be executed sequentially - never in parallel diff --git a/mdm/common/src/main/java/com/gentics/mesh/example/SchemaExamples.java b/mdm/common/src/main/java/com/gentics/mesh/example/SchemaExamples.java index 20b5b7e337..4a769e0fae 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/example/SchemaExamples.java +++ b/mdm/common/src/main/java/com/gentics/mesh/example/SchemaExamples.java @@ -7,6 +7,7 @@ import static com.gentics.mesh.core.rest.schema.change.impl.SchemaChangeOperation.UPDATEFIELD; import static com.gentics.mesh.example.ExampleUuids.UUID_1; +import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.core.rest.schema.HtmlFieldSchema; import com.gentics.mesh.core.rest.schema.ListFieldSchema; import com.gentics.mesh.core.rest.schema.MicronodeFieldSchema; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 075533db75..145da12e44 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -7,6 +7,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; @@ -103,7 +104,6 @@ import com.gentics.mesh.core.rest.user.UserResponse; import com.gentics.mesh.core.rest.user.UserUpdateRequest; import com.gentics.mesh.core.rest.validation.SchemaValidationResponse; -import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.BranchParameters; import com.gentics.mesh.parameter.ConsistencyCheckParameters; import com.gentics.mesh.parameter.DeleteParameters; @@ -159,7 +159,33 @@ public class OpenAPIMeshRestClient implements MeshRestClient { * @return */ protected static final T findParameter(String key, ParameterProvider... parameters) { - return (T) Arrays.stream(parameters).map(p -> p.getParameter(key)).findAny().orElse(null); + return findParameter(key, false, parameters); + } + + /** + * Find the parameter in the providers. + * + * @param + * @param key + * @param forceString do not cast to the desired type + * @param parameters + * @return + */ + protected static final T findParameter(String key, boolean forceString, ParameterProvider... parameters) { + return (T) Arrays.stream(parameters).map(p -> p.getParameter(key)).filter(Objects::nonNull).map(p -> { + if (forceString) { + return String.valueOf(p); + } + try { + return Boolean.valueOf(p); + } catch (Throwable e) { + } + try { + return Double.valueOf(p); + } catch (Throwable e) { + } + return p; + }).findAny().orElse(null); } /** @@ -198,7 +224,7 @@ public MeshRequest findNodeByUuid(String projectName, String uuid, findParameter(RolePermissionParameters.ROLE_PERMISSION_QUERY_PARAM_KEY, parameters), findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters)), NodeResponse.class); } @@ -206,7 +232,7 @@ public MeshRequest findNodeByUuid(String projectName, String uuid, public MeshRequest createNode(String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesPostWithHttpInfo(projectName, - org.openapitools.client.model.NodeCreateRequest.fromJson(JsonUtil.toJson(nodeCreateRequest))), NodeResponse.class); + adaptRequest(nodeCreateRequest)), NodeResponse.class); } @Override @@ -227,7 +253,7 @@ public MeshRequest upsertNode(String projectName, String uuid, Nod public MeshRequest updateNode(String projectName, String uuid, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPutWithHttpInfo(uuid, projectName, - org.openapitools.client.model.NodeUpdateRequest.fromJson(JsonUtil.toJson(nodeUpdateRequest))), NodeResponse.class); + adaptRequest(nodeUpdateRequest)), NodeResponse.class); } @Override @@ -257,7 +283,7 @@ public MeshRequest findNodes(String projectName, ParameterProv findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @@ -273,7 +299,7 @@ public MeshRequest findNodeChildren(String projectName, String findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(PagingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), NodeListResponse.class); } @@ -292,7 +318,7 @@ public MeshRequest addTagToNode(String projectName, String nodeUui ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsTagUuidPostWithHttpInfo( tagUuid, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), NodeResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters)), NodeResponse.class); } @Override @@ -306,7 +332,7 @@ public MeshRequest moveNode(String projectName, String nodeUuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidMoveToToUuidPostWithHttpInfo( targetFolderUuid, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), EmptyResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters)), EmptyResponse.class); } @Override @@ -314,7 +340,7 @@ public MeshRequest findTagsForNode(String projectName, String n ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsGetWithHttpInfo(nodeUuid, projectName, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), TagListResponse.class); } @@ -323,7 +349,7 @@ public MeshRequest updateTagsForNode(String projectName, String TagListUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidTagsPostWithHttpInfo( nodeUuid, projectName, - org.openapitools.client.model.TagListUpdateRequest.fromJson(JsonUtil.toJson(request))), TagListResponse.class); + adaptRequest(request)), TagListResponse.class); } @Override @@ -597,7 +623,7 @@ public MeshRequest webroot(String projectName, String path, findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshWebrootResponse.class); @@ -655,7 +681,7 @@ public MeshRequest createSchema(String uuid, SchemaCreateRequest public MeshRequest findSchemaByUuid(String uuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidGetWithHttpInfo(uuid, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), SchemaResponse.class); } @@ -767,7 +793,7 @@ public MeshRequest revokeSchemaRolePermissions(String public MeshRequest findGroupByUuid(String uuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidGetWithHttpInfo(uuid, findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(GenericParameters.ETAG_PARAM_KEY, parameters)), GroupResponse.class); } @@ -848,7 +874,7 @@ public MeshRequest findUserByUuid(String uuid, ParameterProvider.. findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), findParameter(NodeParameters.RESOLVE_LINKS_QUERY_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), UserResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters)), UserResponse.class); } @Override @@ -862,7 +888,7 @@ public MeshRequest findUsers(ParameterProvider... parameters) findParameter(PagingParameters.PAGE_PARAMETER_KEY, parameters), findParameter(NodeParameters.LANGUAGES_QUERY_PARAM_KEY, parameters), findParameter(GenericParameters.FIELDS_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(SortingParameters.SORT_ORDER_PARAMETER_KEY, parameters)), UserListResponse.class); } @@ -1401,7 +1427,7 @@ public MeshRequest createMicroschema(String uuid, Microsche @Override public MeshRequest findMicroschemaByUuid(String uuid, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidGetWithHttpInfo(uuid, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters)), MicroschemaResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters)), MicroschemaResponse.class); } @Override @@ -1466,7 +1492,7 @@ public MeshRequest downloadBinaryField(String projectName, S findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshBinaryResponse.class); @@ -1514,7 +1540,7 @@ public MeshRequest updateNodeBinaryFieldCheckStatus(String project public MeshRequest upsertNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ImageManipulationRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsPostWithHttpInfo(fieldKey, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters), adaptRequest(request)), ImageVariantsResponse.class); @@ -1524,7 +1550,7 @@ public MeshRequest upsertNodeBinaryFieldImageVariants(Str public MeshRequest clearNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsDeleteWithHttpInfo(fieldKey, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters)), EmptyResponse.class); } @@ -1533,7 +1559,7 @@ public MeshRequest clearNodeBinaryFieldImageVariants(String proje public MeshRequest getNodeBinaryFieldImageVariants(String projectName, String nodeUuid, String fieldKey, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidBinaryFieldNameVariantsGetWithHttpInfo(fieldKey, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters)), ImageVariantsResponse.class); } @@ -1542,14 +1568,14 @@ public MeshRequest getNodeBinaryFieldImageVariants(String public MeshRequest updateNodeS3BinaryField(String projectName, String nodeUuid, String fieldKey, S3BinaryUploadRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidS3binaryFieldNamePostWithHttpInfo(fieldKey, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), adaptRequest(request)), S3RestResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), adaptRequest(request)), S3RestResponse.class); } @Override public MeshRequest extractMetadataNodeS3BinaryField(String projectName, String nodeUuid, String fieldKey, S3BinaryMetadataRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectNodesNodeUuidS3binaryFieldNameParseMetadataPostWithHttpInfo(fieldKey, nodeUuid, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), adaptRequest(request)), NodeResponse.class); + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), adaptRequest(request)), NodeResponse.class); } @Override @@ -1980,7 +2006,7 @@ public MeshRequest webrootField(String projectName, St findParameter(ImageManipulationParameters.WIDTH_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.HEIGHT_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.RESIZE_MODE_QUERY_PARAM_KEY, parameters), - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_Y_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.CROP_MODE_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationParameters.FOCAL_POINT_X_QUERY_PARAM_KEY, parameters), null), MeshWebrootFieldResponse.class); @@ -1999,7 +2025,7 @@ public MeshRequest webrootField(String projectName, St public MeshRequest upsertWebrootFieldImageVariants(String projectName, String fieldName, String path, ImageManipulationRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootfieldFieldNamePathPostWithHttpInfo(path, fieldName, projectName, - findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, parameters), + findParameter(VersioningParameters.VERSION_QUERY_PARAM_KEY, true, parameters), findParameter(ImageManipulationRetrievalParameters.ORIGINAL_QUERY_PARAM_KEY, parameters), findParameter(ImageManipulationRetrievalParameters.FILESIZE_QUERY_PARAM_KEY, parameters), adaptRequest(request)), ImageVariantsResponse.class); From 4e66222d7cdeb7168268a5224e94a9f27bc73bf2 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Wed, 25 Mar 2026 14:15:04 +0100 Subject: [PATCH 66/75] Regression of the JsonSchema generation and the generated Java client --- .../com/gentics/mesh/util/MeshOpenAPIv3Generator.java | 11 +++++++++++ .../mesh/test/openapi/OpenAPIMeshRestClient.java | 8 +++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java index 855b621f9f..0755667fbc 100644 --- a/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java +++ b/common/src/main/java/com/gentics/mesh/util/MeshOpenAPIv3Generator.java @@ -83,4 +83,15 @@ public String generate(String name, Map routers, Format format, // fill the component model Optional.empty()); } + + @SuppressWarnings("rawtypes") + @Override + protected void postProcess(Context context) { + // The JSON schema mapper behaves awfully with the field map, making it unusable. + Schema fieldMapSchema = new Schema<>(); + fieldMapSchema.set$ref("#/components/schemas/AnyJson"); + context.usedComponents.add("AnyJson"); + context.openApi.getComponents().addSchemas("FieldMap", fieldMapSchema); + super.postProcess(context); + } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 145da12e44..5114373fb2 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -176,9 +176,11 @@ protected static final T findParameter(String key, boolean forceString, Para if (forceString) { return String.valueOf(p); } - try { - return Boolean.valueOf(p); - } catch (Throwable e) { + if ("true".equalsIgnoreCase(p)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(p)) { + return Boolean.FALSE; } try { return Double.valueOf(p); From 43222c9c32fb482ade075a23f93dd7336a376726 Mon Sep 17 00:00:00 2001 From: "Serhii Plyhun (commercial)" Date: Wed, 25 Mar 2026 15:04:55 +0100 Subject: [PATCH 67/75] Fix OpenAPI feature possibilities in references.asciidoc --- doc/src/main/hugo/content/docs/references.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/main/hugo/content/docs/references.asciidoc b/doc/src/main/hugo/content/docs/references.asciidoc index 9703d974a4..81160253e7 100644 --- a/doc/src/main/hugo/content/docs/references.asciidoc +++ b/doc/src/main/hugo/content/docs/references.asciidoc @@ -24,7 +24,7 @@ NOTE: All REST API responses are available in JSON, only, except for binary data The link:https://swagger.io/specification/[OpenAPI specification] is available for Gentics Mesh in two variants: * The generic link:https://spec.openapis.org/oas/v3.0.0.html[OpenAPI v3.0 specification], unrelated to any actual Gentics Mesh installation, can be checked right link:/docs/api/openapi.yaml[here] in the YAML format. -* The installation-aware specification, which is generated right in Mesh at runtime, and, in addition to the generic specification, contains also the endpoints of all the plugins currently running along, as well as some technical info of an installation (servers). It also allows choosing the right specification version, either 3.0 or 3.1. +* The installation-aware specification, which is generated right in Mesh at runtime, and, in addition to the generic specification, contains also the endpoints of all the plugins currently running along, as well as some technical info of an installation (servers). === RAML Specification From 2ed82208678daf313ddf6f8fa162beb5d5038527 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 27 Mar 2026 15:28:32 +0100 Subject: [PATCH 68/75] OOM fix --- .../com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java index 837e9f676f..243ccf9790 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java @@ -213,7 +213,7 @@ public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method, boolean } if (uuid != null) { - nodeCrudHandler.handleUpdate(ac, uuid); + nodeCrudHandler.handleUpdate(ac, uuid, createInexisting); } else { nodeCrudHandler.handleCreate(ac); } From 43ea0a31b55aa5994dfd5a6c61f0fcb787ec6a0b Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Mon, 30 Mar 2026 12:11:40 +0200 Subject: [PATCH 69/75] Upsert parameter --- .../core/endpoint/branch/BranchEndpoint.java | 4 +- .../core/endpoint/group/GroupEndpoint.java | 4 +- .../endpoint/handler/AbstractCrudHandler.java | 5 -- .../microschema/MicroschemaEndpoint.java | 4 +- .../mesh/core/endpoint/node/NodeEndpoint.java | 4 +- .../endpoint/project/ProjectEndpoint.java | 4 +- .../mesh/core/endpoint/role/RoleEndpoint.java | 4 +- .../core/endpoint/schema/SchemaEndpoint.java | 4 +- .../endpoint/tagfamily/TagFamilyEndpoint.java | 7 +- .../mesh/core/endpoint/user/UserEndpoint.java | 4 +- .../mesh/rest/MeshLocalClientImpl.java | 66 ++++++++++--------- .../parameter/ParameterProviderContext.java | 5 ++ .../parameter/impl/UpdateParametersImpl.java | 52 +++++++++++++++ .../core/endpoint/handler/CrudHandler.java | 9 --- .../parameter/client/UpdateParameters.java | 8 +++ .../client/impl/MeshRestHttpClientImpl.java | 32 ++++----- .../client/method/BranchClientMethods.java | 2 +- .../client/method/GroupClientMethods.java | 2 +- .../client/method/ProjectClientMethods.java | 2 +- .../rest/client/method/RoleClientMethods.java | 2 +- .../rest/client/method/TagClientMethods.java | 2 +- .../client/method/TagFamilyClientMethods.java | 2 +- .../mesh/parameter/BackupParameters.java | 2 +- .../parameter/ConsistencyCheckParameters.java | 2 +- .../mesh/parameter/DeleteParameters.java | 2 +- .../mesh/parameter/PublishParameters.java | 2 +- .../parameter/SchemaUpdateParameters.java | 2 +- .../mesh/parameter/UpdateParameters.java | 32 +++++++++ .../mesh/core/branch/BranchEndpointTest.java | 3 +- .../mesh/core/group/GroupEndpointTest.java | 3 +- .../mesh/core/node/NodeEndpointTest.java | 8 +-- .../core/project/ProjectEndpointTest.java | 3 +- .../mesh/core/role/RoleEndpointTest.java | 3 +- .../core/schema/MicroschemaEndpointTest.java | 3 +- .../mesh/core/schema/SchemaEndpointTest.java | 3 +- .../mesh/core/tag/TagEndpointTest.java | 3 +- .../core/tagfamily/TagFamilyEndpointTest.java | 3 +- .../mesh/core/user/UserEndpointTest.java | 3 +- .../test/openapi/OpenAPIMeshRestClient.java | 50 ++++++++------ 39 files changed, 240 insertions(+), 115 deletions(-) create mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java create mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java create mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java index bd0913bcb8..4b156045ab 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java @@ -35,6 +35,7 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -247,13 +248,14 @@ private void addUpdateHandler() { upsertBranch.setMutating(true); upsertBranch.consumes(APPLICATION_JSON); upsertBranch.produces(APPLICATION_JSON); + upsertBranch.addQueryParameters(UpdateParametersImpl.class); upsertBranch.exampleRequest(versioningExamples.createBranchCreateRequest("Winter Collection Branch")); upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated or new branch"); upsertBranch.events(BRANCH_CREATED, BRANCH_UPDATED); upsertBranch.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = rc.request().params().get("branchUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); InternalEndpointRoute updateBranch = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java index 59a27666c8..d2652006f3 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java @@ -32,6 +32,7 @@ import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -207,13 +208,14 @@ private void addUpdateHandler() { upsertEndpoint.method(POST); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.exampleRequest(groupExamples.getGroupCreateRequest("New group name")); upsertEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); upsertEndpoint.events(GROUP_CREATED, GROUP_UPDATED); upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("groupUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java index 3da8079137..b1300c00f3 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java @@ -83,11 +83,6 @@ public void handleRead(InternalActionContext ac, String uuid) { utils.readElement(ac, uuid, crudActions(), READ_PERM); } - @Override - public void handleUpdate(InternalActionContext ac, String uuid) { - handleUpdate(ac, uuid, true); - } - @Override public void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting) { validateParameter(uuid, "uuid"); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java index 5aad742170..c08a726ff9 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java @@ -28,6 +28,7 @@ import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.PagingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -170,6 +171,7 @@ private void addUpdateHandler() { upserEndpoint.method(POST); upserEndpoint.produces(APPLICATION_JSON); upserEndpoint.consumes(APPLICATION_JSON); + upserEndpoint.addQueryParameters(UpdateParametersImpl.class); upserEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaCreateRequest()); // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); upserEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); @@ -178,7 +180,7 @@ private void addUpdateHandler() { upserEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("microschemaUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); InternalEndpointRoute updateEndpoint = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index c78314d4fe..9c9f93d7c6 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -53,6 +53,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.PublishParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -596,6 +597,7 @@ private void addUpdateHandler() { postEndpoint.method(POST); postEndpoint.consumes(APPLICATION_JSON); postEndpoint.produces(APPLICATION_JSON); + postEndpoint.addQueryParameters(UpdateParametersImpl.class); postEndpoint.exampleRequest(nodeExamples.getNodeCreateRequest2()); postEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); postEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); @@ -604,7 +606,7 @@ private void addUpdateHandler() { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("nodeUuid"); ac.getVersioningParameters().setVersion("draft"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); InternalEndpointRoute endpoint = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java index a33f48d0b9..4c9a08db43 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java @@ -28,6 +28,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.ProjectPurgeParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -93,13 +94,14 @@ private void addUpdateHandler() { upsertEndpoint.method(POST); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.exampleRequest(projectExamples.getProjectCreateRequest("New project name")); upsertEndpoint.exampleResponse(OK, projectExamples.getProjectResponse("New project name"), "Updated project."); upsertEndpoint.events(PROJECT_CREATED, PROJECT_UPDATED); upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("projectUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java index 865c06ad89..2ec294fd05 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java @@ -26,6 +26,7 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -124,13 +125,14 @@ private void addUpdateHandler() { upsertEndpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); upsertEndpoint.method(POST); upsertEndpoint.consumes(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.exampleRequest(roleExamples.getRoleCreateRequest("New role name")); upsertEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated or new role."); upsertEndpoint.events(ROLE_UPDATED, ROLE_CREATED); upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("roleUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); InternalEndpointRoute updateEndpoint = createRoute(); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java index f5558b1dab..31b102352f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaEndpoint.java @@ -30,6 +30,7 @@ import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.SchemaUpdateParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -162,6 +163,7 @@ private void addUpdateHandler() { upsertEndpoint.description("Update or create the schema."); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.addQueryParameters(SchemaUpdateParametersImpl.class); upsertEndpoint.exampleRequest(schemaExamples.getSchemaCreateRequest()); upsertEndpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated or new schema."); @@ -171,7 +173,7 @@ private void addUpdateHandler() { synchronized (schemaLock.mutex()) { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("schemaUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); } }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index 5722fdf7dd..7a35e9f3af 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -37,6 +37,7 @@ import com.gentics.mesh.parameter.impl.EtagParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -100,6 +101,7 @@ private void addTagUpdateHandler() { upsertEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); upsertEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); upsertEndpoint.method(POST); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); upsertEndpoint.description("Update the specified tag. The tag is created if no tag with the specified uuid could be found."); @@ -110,7 +112,7 @@ private void addTagUpdateHandler() { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); String uuid = PathParameters.getTagUuid(rc); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, true); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); InternalEndpointRoute updateEndpoint = createRoute(); @@ -375,13 +377,14 @@ private void addTagFamilyUpdateHandler() { upsertEndpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyCreateRequest("Nicer colors")); upsertEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or created tag family."); upsertEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java index 943bd20dc6..ae9b74e914 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java @@ -25,6 +25,7 @@ import com.gentics.mesh.parameter.impl.NodeParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -237,6 +238,7 @@ private void addUpdateHandler() { upsertEndpoint.setMutating(true); upsertEndpoint.consumes(APPLICATION_JSON); upsertEndpoint.produces(APPLICATION_JSON); + upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); upsertEndpoint.addQueryParameters(UserParametersImpl.class); upsertEndpoint.exampleRequest(userExamples.getUserCreateRequest("jdoe42")); upsertEndpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated or created user response."); @@ -244,7 +246,7 @@ private void addUpdateHandler() { upsertEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("userUuid"); - crudHandler.handleUpdate(ac, uuid); + crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index 239e436c78..cad7846a2b 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -261,7 +261,7 @@ public MeshRequest upsertNode(String projectName, String uuid, Nod ac.setProject(projectName); ac.setPayloadObject(nodeUpsetRequest); ac.getVersioningParameters().setVersion("draft"); - nodeCrudHandler.handleUpdate(ac, uuid); + nodeCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -282,7 +282,7 @@ public MeshRequest createNode(String uuid, String projectName, Nod ac.setProject(projectName); ac.setPayloadObject(nodeCreateRequest); ac.getVersioningParameters().setVersion("draft"); - nodeCrudHandler.handleUpdate(ac, uuid); + nodeCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -292,7 +292,7 @@ public MeshRequest updateNode(String projectName, String uuid, Nod LocalActionContextImpl ac = createContext(NodeResponse.class, parameters); ac.setPayloadObject(nodeUpdateRequest); ac.setProject(projectName); - nodeCrudHandler.handleUpdate(ac, uuid); + nodeCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -404,13 +404,13 @@ public MeshRequest findTagByUuid(String projectName, String tagFami } @Override - public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request) { - LocalActionContextImpl ac = createContext(TagResponse.class); + public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(TagResponse.class, parameters); ac.setProject(projectName); ac.setPayloadObject(request); ac.setParameter("tagUuid", uuid); ac.setParameter("tagFamilyUuid", tagFamilyUuid); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, false); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -488,14 +488,14 @@ public MeshRequest createProject(ProjectCreateRequest request) public MeshRequest createProject(String uuid, ProjectCreateRequest request) { LocalActionContextImpl ac = createContext(ProjectResponse.class); ac.setPayloadObject(request); - projectCrudHandler.handleUpdate(ac, uuid); + projectCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @Override - public MeshRequest updateProject(String uuid, ProjectUpdateRequest request) { - LocalActionContextImpl ac = createContext(ProjectResponse.class); - projectCrudHandler.handleUpdate(ac, uuid); + public MeshRequest updateProject(String uuid, ProjectUpdateRequest request, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(ProjectResponse.class, parameters); + projectCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -590,11 +590,11 @@ public MeshRequest deleteTagFamily(String projectName, String uui } @Override - public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request) { - LocalActionContextImpl ac = createContext(TagFamilyResponse.class); + public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(TagFamilyResponse.class, parameters); ac.setPayloadObject(request); ac.setProject(projectName); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -603,7 +603,7 @@ public MeshRequest createTagFamily(String projectName, String LocalActionContextImpl ac = createContext(TagFamilyResponse.class); ac.setPayloadObject(request); ac.setProject(projectName); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -709,7 +709,7 @@ public MeshRequest createSchema(SchemaCreateRequest request, Par public MeshRequest createSchema(String uuid, SchemaCreateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(SchemaResponse.class, parameters); ac.setPayloadObject(request); - schemaCrudHandler.handleUpdate(ac, uuid); + schemaCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -724,7 +724,7 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(GenericMessageResponse.class, parameters); ac.setPayloadObject(request); - schemaCrudHandler.handleUpdate(ac, uuid); + schemaCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -791,15 +791,15 @@ public MeshRequest createGroup(GroupCreateRequest createRequest) public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { LocalActionContextImpl ac = createContext(GroupResponse.class); ac.setPayloadObject(createRequest); - groupCrudHandler.handleUpdate(ac, uuid); + groupCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @Override - public MeshRequest updateGroup(String uuid, GroupUpdateRequest request) { - LocalActionContextImpl ac = createContext(GroupResponse.class); + public MeshRequest updateGroup(String uuid, GroupUpdateRequest request, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(GroupResponse.class, parameters); ac.setPayloadObject(request); - groupCrudHandler.handleUpdate(ac, uuid); + groupCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -864,7 +864,7 @@ public MeshRequest createUser(UserCreateRequest request, Parameter public MeshRequest createUser(String uuid, UserCreateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(UserResponse.class, parameters); ac.setPayloadObject(request); - userCrudHandler.handleUpdate(ac, uuid); + userCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -872,7 +872,7 @@ public MeshRequest createUser(String uuid, UserCreateRequest reque public MeshRequest updateUser(String uuid, UserUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(UserResponse.class, parameters); ac.setPayloadObject(request); - userCrudHandler.handleUpdate(ac, uuid); + userCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -923,7 +923,7 @@ public MeshRequest createRole(RoleCreateRequest request) { public MeshRequest createRole(String uuid, RoleCreateRequest request) { LocalActionContextImpl ac = createContext(RoleResponse.class); ac.setPayloadObject(request); - roleCrudHandler.handleUpdate(ac, uuid); + roleCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -956,10 +956,10 @@ public MeshRequest readRolePermissions(String roleUuid, } @Override - public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { - LocalActionContextImpl ac = createContext(RoleResponse.class); + public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(RoleResponse.class, parameters); ac.setPayloadObject(restRole); - roleCrudHandler.handleUpdate(ac, uuid); + roleCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1139,7 +1139,7 @@ public MeshRequest createMicroschema(MicroschemaCreateReque public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { LocalActionContextImpl ac = createContext(MicroschemaResponse.class); ac.setPayloadObject(request); - microschemaCrudHandler.handleUpdate(ac, uuid); + microschemaCrudHandler.handleUpdate(ac, uuid, true); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1154,7 +1154,7 @@ public MeshRequest findMicroschemaByUuid(String uuid, Param public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(GenericMessageResponse.class, parameters); ac.setPayloadObject(request); - microschemaCrudHandler.handleUpdate(ac, uuid); + microschemaCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1474,9 +1474,13 @@ public MeshRequest findBranches(String projectName, Paramete } @Override - public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request) { - // TODO Auto-generated method stub - return null; + public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request, ParameterProvider... parameters) { + LocalActionContextImpl ac = createContext(BranchResponse.class, parameters); + ac.setProject(projectName); + ac.setPayloadObject(request); + ac.setParameter("uuid", branchUuid); + branchCrudHandler.handleUpdate(ac, branchUuid, ac.getUpdateParameters().isUpsert()); + return new MeshLocalRequestImpl<>(ac.getFuture()); } @Override diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index 8d3eb22159..5c645a79f0 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -23,6 +23,7 @@ import com.gentics.mesh.parameter.impl.SchemaUpdateParametersImpl; import com.gentics.mesh.parameter.impl.SearchParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; @@ -126,4 +127,8 @@ default ConsistencyCheckParameters getConsistencyCheckParameters() { default BinaryCheckParameters getBinaryCheckParameters() { return new BinaryCheckParametersImpl(this); } + + default UpdateParameters getUpdateParameters() { + return new UpdateParametersImpl(this); + } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java new file mode 100644 index 0000000000..d33c5a5584 --- /dev/null +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java @@ -0,0 +1,52 @@ +package com.gentics.mesh.parameter.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.raml.model.ParamType; +import org.raml.model.parameter.QueryParameter; + +import com.gentics.mesh.handler.ActionContext; +import com.gentics.mesh.parameter.AbstractParameters; +import com.gentics.mesh.parameter.UpdateParameters; + +import io.vertx.core.MultiMap; + +/** + * A {@link UpdateParametersImpl} can be used to add additional update parameters to the rest requests. + */ +public class UpdateParametersImpl extends AbstractParameters implements UpdateParameters { + + public UpdateParametersImpl() { + super(); + } + + public UpdateParametersImpl(ActionContext ac) { + super(ac); + } + + public UpdateParametersImpl(MultiMap parameters) { + super(parameters); + } + + @Override + public Map getRAMLParameters() { + Map parameters = new HashMap<>(); + + // upsert + QueryParameter perPageParameter = new QueryParameter(); + perPageParameter.setDescription("Whether to create the new entity with the given UUID and parameters, if an existing one was not found."); + perPageParameter.setExample("true"); + perPageParameter.setRequired(false); + perPageParameter.setType(ParamType.BOOLEAN); + parameters.put(UPSERT_PARAMETER_KEY, perPageParameter); + + return parameters; + } + + @Override + public String getName() { + return "Update parameters"; + } + +} diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java index 6c209acc04..9b148b51c2 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java @@ -32,15 +32,6 @@ public interface CrudHandler { */ void handleRead(InternalActionContext ac, String uuid); - /** - * Handle update requests. - * - * @param ac - * @param uuid - * Uuid of the element which should be updated - */ - void handleUpdate(InternalActionContext ac, String uuid); - /** * Handle update requests. * diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java new file mode 100644 index 0000000000..7f5c46ec78 --- /dev/null +++ b/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java @@ -0,0 +1,8 @@ +package com.gentics.mesh.parameter.client; + +/** + * @see UpdateParameters + */ +public class UpdateParameters extends AbstractParameters implements com.gentics.mesh.parameter.UpdateParameters { + +} diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java index df761ac1e9..479f7ef8bb 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/impl/MeshRestHttpClientImpl.java @@ -178,7 +178,7 @@ public MeshRequest updateNode(String projectName, String uuid, Nod Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(nodeUpdateRequest, "nodeUpdateRequest must not be null"); Util.requireUuid(uuid, "uuid"); - return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/nodes/" + uuid + getQuery(getConfig(), parameters), NodeResponse.class, + return prepareRequest(POST, "/" + encodeSegment(projectName) + "/nodes/" + uuid + getQuery(getConfig(), parameters), NodeResponse.class, nodeUpdateRequest); } @@ -280,12 +280,12 @@ public MeshRequest findTagByUuid(String projectName, String tagFami } @Override - public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest tagUpdateRequest) { + public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest tagUpdateRequest, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Objects.requireNonNull(tagUpdateRequest, "tagUpdateRequest must not be null"); Util.requireUuid(tagFamilyUuid, "tagFamilyUuid"); Util.requireUuid(uuid, "uuid"); - return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid + "/tags/" + uuid, TagResponse.class, + return prepareRequest(POST, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid + "/tags/" + uuid + getQuery(getConfig(), parameters), TagResponse.class, tagUpdateRequest); } @@ -442,10 +442,10 @@ public MeshRequest createProject(String uuid, ProjectCreateRequ } @Override - public MeshRequest updateProject(String uuid, ProjectUpdateRequest projectUpdateRequest) { + public MeshRequest updateProject(String uuid, ProjectUpdateRequest projectUpdateRequest, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); Objects.requireNonNull(projectUpdateRequest, "projectUpdateRequest must not be null"); - return prepareRequest(PUT, "/projects/" + uuid, ProjectResponse.class, projectUpdateRequest); + return prepareRequest(POST, "/projects/" + uuid + getQuery(getConfig(), parameters), ProjectResponse.class, projectUpdateRequest); } @Override @@ -528,11 +528,11 @@ public MeshRequest deleteTagFamily(String projectName, String uui } @Override - public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest tagFamilyUpdateRequest) { + public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest tagFamilyUpdateRequest, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Util.requireUuid(tagFamilyUuid, "tagFamilyUuid"); Objects.requireNonNull(tagFamilyUpdateRequest, "tagFamilyUpdateRequest must not be null"); - return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid, TagFamilyResponse.class, + return prepareRequest(POST, "/" + encodeSegment(projectName) + "/tagFamilies/" + tagFamilyUuid + getQuery(getConfig(), parameters), TagFamilyResponse.class, tagFamilyUpdateRequest); } @@ -570,10 +570,10 @@ public MeshRequest createGroup(String uuid, GroupCreateRequest gr } @Override - public MeshRequest updateGroup(String uuid, GroupUpdateRequest groupUpdateRequest) { + public MeshRequest updateGroup(String uuid, GroupUpdateRequest groupUpdateRequest, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); Objects.requireNonNull(groupUpdateRequest, "groupUpdateRequest must not be null"); - return prepareRequest(PUT, "/groups/" + uuid, GroupResponse.class, groupUpdateRequest); + return prepareRequest(POST, "/groups/" + uuid + getQuery(getConfig(), parameters), GroupResponse.class, groupUpdateRequest); } @Override @@ -610,7 +610,7 @@ public MeshRequest createUser(String uuid, UserCreateRequest userC public MeshRequest updateUser(String uuid, UserUpdateRequest userUpdateRequest, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); Objects.requireNonNull(userUpdateRequest, "userUpdateRequest must not be null"); - return prepareRequest(PUT, "/users/" + uuid + getQuery(getConfig(), parameters), UserResponse.class, userUpdateRequest); + return prepareRequest(POST, "/users/" + uuid + getQuery(getConfig(), parameters), UserResponse.class, userUpdateRequest); } @Override @@ -806,10 +806,10 @@ public MeshRequest createSchema(String uuid, SchemaCreateRequest } @Override - public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { + public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); Objects.requireNonNull(restRole, "restRole must not be null"); - return prepareRequest(PUT, "/roles/" + uuid, RoleResponse.class, restRole); + return prepareRequest(POST, "/roles/" + uuid + getQuery(getConfig(), parameters), RoleResponse.class, restRole); } @Override @@ -821,7 +821,7 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid @Override public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); - return prepareRequest(PUT, "/schemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); + return prepareRequest(POST, "/schemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); } @Override @@ -1305,7 +1305,7 @@ public MeshRequest findMicroschemas(ParameterProvider.. public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { Util.requireUuid(uuid, "uuid"); Objects.requireNonNull(request, "request must not be null"); - return prepareRequest(PUT, "/microschemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); + return prepareRequest(POST, "/microschemas/" + uuid + getQuery(getConfig(), parameters), GenericMessageResponse.class, request); } @Override @@ -1371,11 +1371,11 @@ public MeshRequest findBranches(String projectName, Paramete } @Override - public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request) { + public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request, ParameterProvider... parameters) { Objects.requireNonNull(projectName, "projectName must not be null"); Util.requireUuid(branchUuid, "branchUuid"); - return prepareRequest(PUT, "/" + encodeSegment(projectName) + "/branches/" + branchUuid, BranchResponse.class, request); + return prepareRequest(POST, "/" + encodeSegment(projectName) + "/branches/" + branchUuid + getQuery(getConfig(), parameters), BranchResponse.class, request); } @Override diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java index c1f555d13e..7fc34484ec 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/BranchClientMethods.java @@ -72,7 +72,7 @@ MeshRequest createBranch(String projectName, String uuid, Branch * @param request * @return */ - MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request); + MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request, ParameterProvider... parameters); /** * Get schema versions assigned to a branch. diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/GroupClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/GroupClientMethods.java index fdae5c62e7..090f0b6452 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/GroupClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/GroupClientMethods.java @@ -57,7 +57,7 @@ public interface GroupClientMethods { * @param request * @return */ - MeshRequest updateGroup(String uuid, GroupUpdateRequest request); + MeshRequest updateGroup(String uuid, GroupUpdateRequest request, ParameterProvider... parameters); /** * Delete the group. diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java index fcb36c7c64..96c307e2ec 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/ProjectClientMethods.java @@ -88,7 +88,7 @@ public interface ProjectClientMethods { * @param request * @return */ - MeshRequest updateProject(String uuid, ProjectUpdateRequest request); + MeshRequest updateProject(String uuid, ProjectUpdateRequest request, ParameterProvider... parameters); /** * Delete the project. diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java index 3eb73c30f0..938cf0d3ad 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/RoleClientMethods.java @@ -101,7 +101,7 @@ public interface RoleClientMethods { * @param restRole * @return */ - MeshRequest updateRole(String uuid, RoleUpdateRequest restRole); + MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters); /** * Get the role permissions on the role diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagClientMethods.java index f57da8ba48..5174b57336 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagClientMethods.java @@ -53,7 +53,7 @@ public interface TagClientMethods { * Update request * @return */ - MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request); + MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request, ParameterProvider... parameters); /** * Create the tag with the given uuid diff --git a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagFamilyClientMethods.java b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagFamilyClientMethods.java index bf6e9d3bfa..3ca322fd52 100644 --- a/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagFamilyClientMethods.java +++ b/rest-client/src/main/java/com/gentics/mesh/rest/client/method/TagFamilyClientMethods.java @@ -70,7 +70,7 @@ public interface TagFamilyClientMethods { * Update request * @return */ - MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request); + MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request, ParameterProvider... parameters); /** * Create the tag family with the given uuid. diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/BackupParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/BackupParameters.java index d94d292628..f9b144f295 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/BackupParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/BackupParameters.java @@ -26,6 +26,6 @@ default BackupParameters setConsistencyCheck(boolean flag) { * @return */ default boolean isConsistencyCheck() { - return BooleanUtils.toBooleanDefaultIfNull(Boolean.valueOf(getParameter(CONSISTENCY_CHECK_PARAMETER_KEY)), false); + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(CONSISTENCY_CHECK_PARAMETER_KEY)), false); } } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/ConsistencyCheckParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/ConsistencyCheckParameters.java index 09864656b6..a9e0d2b902 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/ConsistencyCheckParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/ConsistencyCheckParameters.java @@ -28,6 +28,6 @@ default ConsistencyCheckParameters setAsync(boolean flag) { * @return flag value */ default boolean isAsync() { - return BooleanUtils.toBooleanDefaultIfNull(Boolean.valueOf(getParameter(ASYNC_PARAMETER_KEY)), false); + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(ASYNC_PARAMETER_KEY)), false); } } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/DeleteParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/DeleteParameters.java index 6efbde63c7..b5e4a23609 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/DeleteParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/DeleteParameters.java @@ -26,6 +26,6 @@ default DeleteParameters setRecursive(boolean flag) { * @return */ default boolean isRecursive() { - return BooleanUtils.toBooleanDefaultIfNull(Boolean.valueOf(getParameter(RECURSIVE_PARAMETER_KEY)), false); + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(RECURSIVE_PARAMETER_KEY)), false); } } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/PublishParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/PublishParameters.java index 7587d61098..c3aefc34b6 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/PublishParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/PublishParameters.java @@ -27,7 +27,7 @@ default PublishParameters setRecursive(boolean flag) { */ default boolean isRecursive() { - return BooleanUtils.toBooleanDefaultIfNull(Boolean.valueOf(getParameter(RECURSIVE_PARAMETER_KEY)), false); + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(RECURSIVE_PARAMETER_KEY)), false); } } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/SchemaUpdateParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/SchemaUpdateParameters.java index 40b0db2c16..ff14ddf385 100644 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/SchemaUpdateParameters.java +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/SchemaUpdateParameters.java @@ -91,7 +91,7 @@ default SchemaUpdateParameters setStrictValidation(boolean flag) { * @return */ default boolean isStrictValidation() { - return BooleanUtils.toBooleanDefaultIfNull(Boolean.valueOf(getParameter(STRICT_VALIDATION_KEY)), false); + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(STRICT_VALIDATION_KEY)), false); } } diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java new file mode 100644 index 0000000000..3a7e25fe5c --- /dev/null +++ b/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java @@ -0,0 +1,32 @@ +package com.gentics.mesh.parameter; + +import org.apache.commons.lang.BooleanUtils; + +/** + * Parameters, applied on an entity update. + */ +public interface UpdateParameters extends ParameterProvider { + + public static final String UPSERT_PARAMETER_KEY = "upsert"; + public static final boolean DEFAULT_UPSERT_PARAMETER_VALUE = true; + + /** + * Set the flag to upsert (create if not found) the value on update. + * + * @param flag + * @return + */ + default UpdateParameters setUpsert(boolean flag) { + setParameter(UPSERT_PARAMETER_KEY, String.valueOf(flag)); + return this; + } + + /** + * Check if an upsert (create if not found) is requested. + * + * @return + */ + default boolean isUpsert() { + return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(UPSERT_PARAMETER_KEY)), DEFAULT_UPSERT_PARAMETER_VALUE); + } +} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java index 016aa2037e..f998414622 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java @@ -93,6 +93,7 @@ import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SchemaUpdateParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -736,7 +737,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { BranchUpdateRequest request = new BranchUpdateRequest(); // request.setActive(false); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateBranch(PROJECT_NAME, uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateBranch(PROJECT_NAME, uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java index eb48608f70..5b47ff2abc 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java @@ -54,6 +54,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -505,7 +506,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { GroupUpdateRequest request = new GroupUpdateRequest(); request.setName(name); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateGroup(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateGroup(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java index b4ad375b00..78e6ed0661 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java @@ -102,6 +102,7 @@ import com.gentics.mesh.parameter.impl.PublishParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.client.MeshWebrootResponse; import com.gentics.mesh.test.MeshTestSetting; @@ -1887,11 +1888,10 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { NodeUpdateRequest request = new NodeUpdateRequest(); request.setLanguage("en"); - NodeParametersImpl parameters = new NodeParametersImpl(); - parameters.setLanguages("en", "de"); - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateNode(PROJECT_NAME, uuid, request, parameters), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateNode(PROJECT_NAME, uuid, request, + new NodeParametersImpl().setLanguages("en", "de"), new UpdateParametersImpl().setUpsert(false)), + NOT_FOUND, "object_not_found_for_uuid", uuid); } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java index 83e296be68..76214563c9 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java @@ -81,6 +81,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.TestSize; @@ -657,7 +658,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { ProjectUpdateRequest request = new ProjectUpdateRequest(); request.setName("new Name"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateProject(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateProject(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java index b32fd8d387..183d680634 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java @@ -57,6 +57,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -464,7 +465,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { request.setName("renamed role"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateRole(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateRole(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java index f2da141c61..91d8f4b4e0 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java @@ -54,6 +54,7 @@ import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -341,7 +342,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { request.setName("new-name"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateMicroschema(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateMicroschema(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); HibMicroschema reloaded = tx.microschemaDao().findByUuid(microschema.getUuid()); assertEquals("The name should not have been changed.", oldName, reloaded.getName()); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java index 14f95195e8..fef46b5728 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java @@ -86,6 +86,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -613,7 +614,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { request.setName("new-name"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateSchema(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateSchema(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); HibSchema reloaded = schemaDao.findByUuid(schema.getUuid()); assertEquals("The name should not have been changed.", oldName, reloaded.getName()); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java index f59b5235e4..980a68b84a 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java @@ -62,6 +62,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.client.MeshRestClientMessageException; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -675,7 +676,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { String uuid = UUIDUtil.randomUUID(); try (Tx tx = tx()) { HibTagFamily parentTagFamily = tagFamily("colors"); - call(() -> client().updateTag(PROJECT_NAME, parentTagFamily.getUuid(), uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateTag(PROJECT_NAME, parentTagFamily.getUuid(), uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java index 12c6026577..e7015634d7 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java @@ -67,6 +67,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -531,7 +532,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { request.setName("new Name"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateTagFamily(PROJECT_NAME, uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateTagFamily(PROJECT_NAME, uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java index 3cac86b568..b52cf4fb2a 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java @@ -78,6 +78,7 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; +import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.client.MeshResponse; @@ -697,7 +698,7 @@ public void testUpdateWithBogusUuid() throws GenericRestException, Exception { UserUpdateRequest request = new UserUpdateRequest(); request.setUsername("New Name"); String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateUser(uuid, request), NOT_FOUND, "object_not_found_for_uuid", uuid); + call(() -> client().updateUser(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); } @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 5114373fb2..31d45b7b6f 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -123,6 +123,7 @@ import com.gentics.mesh.parameter.SchemaUpdateParameters; import com.gentics.mesh.parameter.SearchParameters; import com.gentics.mesh.parameter.SortingParameters; +import com.gentics.mesh.parameter.UpdateParameters; import com.gentics.mesh.parameter.VersioningParameters; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; @@ -240,14 +241,14 @@ public MeshRequest createNode(String projectName, NodeCreateReques @Override public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeCreateRequest)), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(nodeCreateRequest)), NodeResponse.class); } @Override public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeUpsertRequest)), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(nodeUpsertRequest)), NodeResponse.class); } @@ -436,8 +437,9 @@ public MeshRequest findTagByUuid(String projectName, String tagFami @Override public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, - TagUpdateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, adaptRequest(request)), TagResponse.class); + TagUpdateRequest request, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), TagResponse.class); } @Override @@ -522,8 +524,9 @@ public MeshRequest createProject(String uuid, ProjectCreateRequ } @Override - public MeshRequest updateProject(String uuid, ProjectUpdateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, adaptRequest(request)), ProjectResponse.class); + public MeshRequest updateProject(String uuid, ProjectUpdateRequest request, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), ProjectResponse.class); } @Override @@ -580,14 +583,15 @@ public MeshRequest deleteTagFamily(String projectName, String uui @Override public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, - TagFamilyUpdateRequest request) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPut(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); + TagFamilyUpdateRequest request, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), TagFamilyResponse.class); } @Override public MeshRequest createTagFamily(String projectName, String tagFamilyUuid, TagFamilyCreateRequest request) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, adaptRequest(request)), TagFamilyResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, true, adaptRequest(request)), TagFamilyResponse.class); } @Override @@ -673,6 +677,7 @@ public MeshRequest createSchema(SchemaCreateRequest request, Par public MeshRequest createSchema(String uuid, SchemaCreateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), @@ -818,12 +823,13 @@ public MeshRequest createGroup(GroupCreateRequest createRequest) @Override public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, adaptRequest(createRequest)), GroupResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, true, adaptRequest(createRequest)), GroupResponse.class); } @Override - public MeshRequest updateGroup(String uuid, GroupUpdateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPutWithHttpInfo(uuid, adaptRequest(request)), GroupResponse.class); + public MeshRequest updateGroup(String uuid, GroupUpdateRequest request, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), GroupResponse.class); } @Override @@ -902,7 +908,9 @@ public MeshRequest createUser(UserCreateRequest request, Parameter @Override public MeshRequest createUser(String uuid, UserCreateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, getAPIKey(), adaptRequest(request)), UserResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), + getAPIKey(), adaptRequest(request)), UserResponse.class); } @Override @@ -987,7 +995,7 @@ public MeshRequest createRole(RoleCreateRequest request) { @Override public MeshRequest createRole(String uuid, RoleCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, adaptRequest(request)), RoleResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, true, adaptRequest(request)), RoleResponse.class); } @Override @@ -1013,8 +1021,9 @@ public MeshRequest readRolePermissions(String roleUuid, } @Override - public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, adaptRequest(restRole)), RoleResponse.class); + public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(restRole)), RoleResponse.class); } @Override @@ -1423,7 +1432,7 @@ public MeshRequest createMicroschema(MicroschemaCreateReque @Override public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, adaptRequest(request)), MicroschemaResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, true, adaptRequest(request)), MicroschemaResponse.class); } @Override @@ -1631,7 +1640,7 @@ public MeshRequest createBranch(String projectName, BranchCreate @Override public MeshRequest createBranch(String projectName, String uuid, BranchCreateRequest branchCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(uuid, projectName, adaptRequest(branchCreateRequest)), BranchResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(branchCreateRequest)), BranchResponse.class); } @Override @@ -1655,8 +1664,9 @@ public MeshRequest findBranches(String projectName, Paramete @Override public MeshRequest updateBranch(String projectName, String branchUuid, - BranchUpdateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPutWithHttpInfo(branchUuid, projectName, adaptRequest(request)), BranchResponse.class); + BranchUpdateRequest request, ParameterProvider... parameters) { + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(branchUuid, projectName, + findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), BranchResponse.class); } @Override From 884c5dfd9a26a7bd6524d4288bd9a290297bb516 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 31 Mar 2026 11:23:02 +0200 Subject: [PATCH 70/75] Revert optional entity upsert --- .../core/endpoint/branch/BranchEndpoint.java | 31 ++------ .../core/endpoint/group/GroupEndpoint.java | 31 ++------ .../endpoint/handler/AbstractCrudHandler.java | 4 +- .../microschema/MicroschemaCrudHandler.java | 6 +- .../microschema/MicroschemaEndpoint.java | 45 ++++-------- .../mesh/core/endpoint/node/NodeEndpoint.java | 28 +------- .../endpoint/project/ProjectEndpoint.java | 26 +------ .../mesh/core/endpoint/role/RoleEndpoint.java | 40 +++-------- .../endpoint/schema/SchemaCrudHandler.java | 6 +- .../core/endpoint/schema/SchemaEndpoint.java | 51 ++++--------- .../core/endpoint/tag/TagCrudHandler.java | 5 +- .../endpoint/tagfamily/TagFamilyEndpoint.java | 71 +++++-------------- .../mesh/core/endpoint/user/UserEndpoint.java | 55 ++++---------- .../endpoint/webroot/WebRootEndpoint.java | 19 +---- .../core/endpoint/webroot/WebRootHandler.java | 8 +-- .../mesh/rest/MeshLocalClientImpl.java | 40 +++++------ .../parameter/ParameterProviderContext.java | 5 -- .../parameter/impl/UpdateParametersImpl.java | 52 -------------- .../core/endpoint/handler/CrudHandler.java | 3 +- .../verticle/handler/HandlerUtilities.java | 26 ++----- .../parameter/client/UpdateParameters.java | 8 --- .../mesh/parameter/UpdateParameters.java | 32 --------- .../mesh/core/branch/BranchEndpointTest.java | 11 --- .../mesh/core/group/GroupEndpointTest.java | 11 --- .../core/language/LanguageEndpointTest.java | 7 -- .../mesh/core/node/NodeEndpointTest.java | 16 ----- .../core/project/ProjectEndpointTest.java | 12 ---- .../mesh/core/role/RoleEndpointTest.java | 14 ---- .../core/schema/MicroschemaEndpointTest.java | 19 ----- .../mesh/core/schema/SchemaEndpointTest.java | 19 ----- .../mesh/core/tag/TagEndpointTest.java | 16 ----- .../core/tagfamily/TagFamilyEndpointTest.java | 13 ---- .../mesh/core/user/UserEndpointTest.java | 11 --- .../definition/CrudEndpointTestCases.java | 3 - .../test/openapi/OpenAPIMeshRestClient.java | 69 ++++++++++-------- 35 files changed, 163 insertions(+), 650 deletions(-) delete mode 100644 mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java delete mode 100644 rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java delete mode 100644 rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java index 4b156045ab..2532d7d6f9 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/branch/BranchEndpoint.java @@ -20,7 +20,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -35,7 +34,6 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -239,41 +237,22 @@ private void addUpdateHandler() { crudHandler.handleSetLatest(ac, uuid); }, isOrderedBlockingHandlers()); - InternalEndpointRoute upsertBranch = createRoute(); - upsertBranch.path("/:branchUuid"); - upsertBranch.addUriParameter("branchUuid", "Uuid of the branch", BRANCH_UUID); - upsertBranch - .description("Update the branch with the given uuid. The branch is created if no branch with the specified uuid could be found."); - upsertBranch.method(POST); - upsertBranch.setMutating(true); - upsertBranch.consumes(APPLICATION_JSON); - upsertBranch.produces(APPLICATION_JSON); - upsertBranch.addQueryParameters(UpdateParametersImpl.class); - upsertBranch.exampleRequest(versioningExamples.createBranchCreateRequest("Winter Collection Branch")); - upsertBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated or new branch"); - upsertBranch.events(BRANCH_CREATED, BRANCH_UPDATED); - upsertBranch.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = rc.request().params().get("branchUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); - }, isOrderedBlockingHandlers()); - InternalEndpointRoute updateBranch = createRoute(); updateBranch.path("/:branchUuid"); updateBranch.addUriParameter("branchUuid", "Uuid of the branch", BRANCH_UUID); updateBranch - .description("Update the branch with the given uuid."); - updateBranch.method(PUT); + .description("Update the branch with the given uuid. The branch is created if no branch with the specified uuid could be found."); + updateBranch.method(POST); updateBranch.setMutating(true); updateBranch.consumes(APPLICATION_JSON); updateBranch.produces(APPLICATION_JSON); updateBranch.exampleRequest(versioningExamples.createBranchUpdateRequest("Winter Collection Branch")); - updateBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated branch"); - updateBranch.events(BRANCH_UPDATED); + updateBranch.exampleResponse(OK, versioningExamples.createBranchResponse("Winter Collection Branch", false), "Updated or new branch"); + updateBranch.events(BRANCH_CREATED, BRANCH_UPDATED); updateBranch.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = rc.request().params().get("branchUuid"); - crudHandler.handleUpdate(ac, uuid, false); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java index d2652006f3..555b073cf0 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java @@ -19,7 +19,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -32,7 +31,6 @@ import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -188,34 +186,17 @@ private void addUpdateHandler() { InternalEndpointRoute updateEndpoint = createRoute(); updateEndpoint.path("/:groupUuid"); updateEndpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); - updateEndpoint.description("Update the group with the given uuid."); - updateEndpoint.method(PUT); + updateEndpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); + updateEndpoint.method(POST); updateEndpoint.consumes(APPLICATION_JSON); updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.exampleRequest(groupExamples.getGroupUpdateRequest("Group name")); - updateEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("Group name"), "Updated group."); - updateEndpoint.events(GROUP_UPDATED); + updateEndpoint.exampleRequest(groupExamples.getGroupUpdateRequest("New group name")); + updateEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); + updateEndpoint.events(GROUP_CREATED, GROUP_UPDATED); updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("groupUuid"); - crudHandler.handleUpdate(ac, uuid, false); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:groupUuid"); - upsertEndpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); - upsertEndpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); - upsertEndpoint.method(POST); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.exampleRequest(groupExamples.getGroupCreateRequest("New group name")); - upsertEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); - upsertEndpoint.events(GROUP_CREATED, GROUP_UPDATED); - upsertEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("groupUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java index b1300c00f3..e3f14aff5f 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/handler/AbstractCrudHandler.java @@ -84,9 +84,9 @@ public void handleRead(InternalActionContext ac, String uuid) { } @Override - public void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting) { + public void handleUpdate(InternalActionContext ac, String uuid) { validateParameter(uuid, "uuid"); - utils.updateElement(ac, uuid, crudActions(), createInexisting); + utils.updateElement(ac, uuid, crudActions()); } @Override diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java index a97e464ce6..44c1ad2701 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaCrudHandler.java @@ -62,7 +62,7 @@ public MicroschemaCrudHandler(Database db, MicroschemaComparatorImpl comparator, } @Override - public void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting) { + public void handleUpdate(InternalActionContext ac, String uuid) { validateParameter(uuid, "uuid"); try (WriteLock lock = writeLock.lock(ac)) { @@ -78,13 +78,13 @@ public void handleUpdate(InternalActionContext ac, String uuid, boolean createIn } MicroschemaDao microschemaDao = tx.microschemaDao(); HibMicroschema microschema = microschemaDao.findByUuid(uuid); - return microschema == null && createInexisting; + return microschema == null; }); // Delegate to handle update which will create the microschema if (delegateToCreate) { ac.skipWriteLock(); - super.handleUpdate(ac, uuid, createInexisting); + super.handleUpdate(ac, uuid); return; } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java index c08a726ff9..6851bc67e5 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/microschema/MicroschemaEndpoint.java @@ -14,7 +14,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -25,10 +24,8 @@ import com.gentics.mesh.core.db.Database; import com.gentics.mesh.core.endpoint.RolePermissionHandlingEndpoint; import com.gentics.mesh.core.endpoint.admin.LocalConfigApi; -import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.PagingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -165,39 +162,21 @@ private void addDeleteHandler() { } private void addUpdateHandler() { - InternalEndpointRoute upserEndpoint = createRoute(); - upserEndpoint.path("/:microschemaUuid"); - upserEndpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); - upserEndpoint.method(POST); - upserEndpoint.produces(APPLICATION_JSON); - upserEndpoint.consumes(APPLICATION_JSON); - upserEndpoint.addQueryParameters(UpdateParametersImpl.class); - upserEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaCreateRequest()); - // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); - upserEndpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); - upserEndpoint.description("Update or create the microschema with the given uuid."); - upserEndpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_CREATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); - upserEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("microschemaUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:microschemaUuid"); - updateEndpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); - updateEndpoint.method(PUT); - updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:microschemaUuid"); + endpoint.addUriParameter("microschemaUuid", "Uuid of the microschema.", MICROSCHEMA_UUID); + endpoint.method(POST); + endpoint.produces(APPLICATION_JSON); + endpoint.consumes(APPLICATION_JSON); + endpoint.exampleRequest(microschemaExamples.getGeolocationMicroschemaUpdateRequest()); // endpoint.exampleResponse(OK, microschemaExamples.getGeolocationMicroschemaResponse(), "Updated microschema."); - updateEndpoint.exampleResponse(OK, new GenericMessageResponse("The schema has been updated. Migration has been invoked."), "Migration message."); - updateEndpoint.description("Update the microschema with the given uuid."); - updateEndpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); - updateEndpoint.blockingHandler(rc -> { + endpoint.exampleResponse(OK, miscExamples.createMessageResponse(), "Migration message."); + endpoint.description("Update or create the microschema with the given uuid."); + endpoint.events(MICROSCHEMA_UPDATED, MICROSCHEMA_CREATED, MICROSCHEMA_MIGRATION_START, MICROSCHEMA_MIGRATION_FINISHED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("microschemaUuid"); - crudHandler.handleUpdate(ac, uuid, false); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index 9c9f93d7c6..ded498fd41 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -25,7 +25,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -53,7 +52,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.PublishParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -597,8 +595,7 @@ private void addUpdateHandler() { postEndpoint.method(POST); postEndpoint.consumes(APPLICATION_JSON); postEndpoint.produces(APPLICATION_JSON); - postEndpoint.addQueryParameters(UpdateParametersImpl.class); - postEndpoint.exampleRequest(nodeExamples.getNodeCreateRequest2()); + postEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest2()); postEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); postEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); postEndpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); @@ -606,28 +603,7 @@ private void addUpdateHandler() { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("nodeUuid"); ac.getVersioningParameters().setVersion("draft"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute endpoint = createRoute(); - endpoint.description("Update the node with the given uuid. " - + "Mesh will automatically check for version conflicts if a version was specified in the request and return a 409 error if a conflict has been detected. " - + "Additional conflict checks for WebRoot path conflicts will also be performed."); - endpoint.path("/:nodeUuid"); - endpoint.addUriParameter("nodeUuid", "Uuid of the node", NODE_DELOREAN_UUID); - endpoint.method(PUT); - endpoint.consumes(APPLICATION_JSON); - endpoint.produces(APPLICATION_JSON); - endpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); - endpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "Updated node."); - endpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); - endpoint.exampleResponse(NOT_FOUND, miscExamples.createMessageResponse(), "The node could not be found."); - endpoint.events(NODE_UPDATED); - endpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("nodeUuid"); - ac.getVersioningParameters().setVersion("draft"); - crudHandler.handleUpdate(ac, uuid, false); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java index 4c9a08db43..a4cc6f5deb 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/project/ProjectEndpoint.java @@ -13,7 +13,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -28,7 +27,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.ProjectPurgeParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -72,9 +70,9 @@ private void addUpdateHandler() { InternalEndpointRoute updateEndpoint = createRoute(); updateEndpoint.path("/:projectUuid"); updateEndpoint - .description("Update the project with the given uuid."); + .description("Update the project with the given uuid. The project is created if no project with the specified uuid could be found."); updateEndpoint.addUriParameter("projectUuid", "Uuid of the project.", PROJECT_DEMO_UUID); - updateEndpoint.method(PUT); + updateEndpoint.method(POST); updateEndpoint.consumes(APPLICATION_JSON); updateEndpoint.produces(APPLICATION_JSON); updateEndpoint.exampleRequest(projectExamples.getProjectUpdateRequest("New project name")); @@ -83,25 +81,7 @@ private void addUpdateHandler() { updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("projectUuid"); - crudHandler.handleUpdate(ac, uuid, false); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:projectUuid"); - upsertEndpoint - .description("Update the project with the given uuid. The project is created if no project with the specified uuid could be found."); - upsertEndpoint.addUriParameter("projectUuid", "Uuid of the project.", PROJECT_DEMO_UUID); - upsertEndpoint.method(POST); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.exampleRequest(projectExamples.getProjectCreateRequest("New project name")); - upsertEndpoint.exampleResponse(OK, projectExamples.getProjectResponse("New project name"), "Updated project."); - upsertEndpoint.events(PROJECT_CREATED, PROJECT_UPDATED); - upsertEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("projectUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java index 2ec294fd05..f5fac3e1cc 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/role/RoleEndpoint.java @@ -14,7 +14,6 @@ import static io.vertx.core.http.HttpMethod.DELETE; import static io.vertx.core.http.HttpMethod.GET; import static io.vertx.core.http.HttpMethod.POST; -import static io.vertx.core.http.HttpMethod.PUT; import javax.inject.Inject; @@ -26,7 +25,6 @@ import com.gentics.mesh.etc.config.MeshOptions; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -119,35 +117,19 @@ private void addDeleteHandler() { } private void addUpdateHandler() { - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:roleUuid"); - upsertEndpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); - upsertEndpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); - upsertEndpoint.method(POST); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.exampleRequest(roleExamples.getRoleCreateRequest("New role name")); - upsertEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated or new role."); - upsertEndpoint.events(ROLE_UPDATED, ROLE_CREATED); - upsertEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("roleUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:roleUuid"); - updateEndpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); - updateEndpoint.description("Update the role with the given uuid."); - updateEndpoint.method(PUT); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); - updateEndpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated role."); - updateEndpoint.events(ROLE_UPDATED); - updateEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:roleUuid"); + endpoint.addUriParameter("roleUuid", "Uuid of the role.", ROLE_CLIENT_UUID); + endpoint.description("Update the role with the given uuid. The role is created if no role with the specified uuid could be found."); + endpoint.method(POST); + endpoint.consumes(APPLICATION_JSON); + endpoint.exampleRequest(roleExamples.getRoleUpdateRequest("New role name")); + endpoint.exampleResponse(OK, roleExamples.getRoleResponse1("New role name"), "Updated or new role."); + endpoint.events(ROLE_UPDATED, ROLE_CREATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("roleUuid"); - crudHandler.handleUpdate(ac, uuid, false); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java index 22ad9a4cd8..a19cf16472 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/schema/SchemaCrudHandler.java @@ -77,7 +77,7 @@ public SchemaCrudHandler(Database db, SchemaComparatorImpl comparator, Lazy { - // Update operations should always be executed sequentially - never in parallel - synchronized (schemaLock.mutex()) { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("schemaUuid"); - crudHandler.handleUpdate(ac, uuid, false); - } - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:schemaUuid"); - upsertEndpoint.addUriParameter("schemaUuid", "Uuid of the schema.", SCHEMA_VEHICLE_UUID); - upsertEndpoint.method(POST); - upsertEndpoint.description("Update or create the schema."); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.addQueryParameters(SchemaUpdateParametersImpl.class); - upsertEndpoint.exampleRequest(schemaExamples.getSchemaCreateRequest()); - upsertEndpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated or new schema."); - upsertEndpoint.events(SCHEMA_CREATED, SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); - upsertEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:schemaUuid"); + endpoint.addUriParameter("schemaUuid", "Uuid of the schema.", SCHEMA_VEHICLE_UUID); + endpoint.method(POST); + endpoint.description("Update or create the schema."); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + + endpoint.addQueryParameters(SchemaUpdateParametersImpl.class); + endpoint.exampleRequest(schemaExamples.getSchemaUpdateRequest()); + endpoint.exampleResponse(OK, schemaExamples.getSchemaResponse(), "Updated or new schema."); + endpoint.events(SCHEMA_CREATED, SCHEMA_UPDATED, SCHEMA_MIGRATION_START, SCHEMA_MIGRATION_FINISHED); + endpoint.blockingHandler(rc -> { // Update operations should always be executed sequentially - never in parallel synchronized (schemaLock.mutex()) { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("schemaUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + crudHandler.handleUpdate(ac, uuid); } }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java index b8152cce72..875d9510ef 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tag/TagCrudHandler.java @@ -157,16 +157,15 @@ public void handleCreate(InternalActionContext ac, String tagFamilyUuid) { * The tags tagfamily uuid * @param tagUuid * Uuid of the tag which should be deleted - * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. */ - public void handleUpdate(InternalActionContext ac, String tagFamilyUuid, String tagUuid, boolean createInexisting) { + public void handleUpdate(InternalActionContext ac, String tagFamilyUuid, String tagUuid) { validateParameter(tagFamilyUuid, "tagFamilyUuid"); validateParameter(tagUuid, "tagUuid"); Function tagFamilyLoader = tx -> { return tx.tagFamilyActions().loadByUuid(context(tx, ac), tagFamilyUuid, READ_PERM, true); }; - utils.createOrUpdateElement(ac, tagFamilyLoader, tagUuid, tagActions, createInexisting); + utils.createOrUpdateElement(ac, tagFamilyLoader, tagUuid, tagActions); } /** diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index 7a35e9f3af..8a4676007c 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -37,7 +37,6 @@ import com.gentics.mesh.parameter.impl.EtagParametersImpl; import com.gentics.mesh.parameter.impl.GenericParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; /** @@ -96,41 +95,22 @@ public void registerEndPoints() { } private void addTagUpdateHandler() { - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:tagFamilyUuid/tags/:tagUuid"); - upsertEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - upsertEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); - upsertEndpoint.method(POST); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.description("Update the specified tag. The tag is created if no tag with the specified uuid could be found."); - upsertEndpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); - upsertEndpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated tag."); - upsertEndpoint.events(TAG_UPDATED, TAG_CREATED); - upsertEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); - String uuid = PathParameters.getTagUuid(rc); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, ac.getUpdateParameters().isUpsert()); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:tagFamilyUuid/tags/:tagUuid"); - updateEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - updateEndpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); - updateEndpoint.method(PUT); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.description("Update the specified tag."); - updateEndpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); - updateEndpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated tag."); - updateEndpoint.events(TAG_UPDATED); - updateEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:tagFamilyUuid/tags/:tagUuid"); + endpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + endpoint.addUriParameter("tagUuid", "Uuid of the tag.", TAG_BLUE_UUID); + endpoint.method(POST); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + endpoint.description("Update the specified tag. The tag is created if no tag with the specified uuid could be found."); + endpoint.exampleRequest(tagExamples.createTagUpdateRequest("Red")); + endpoint.exampleResponse(OK, tagExamples.createTagResponse1("Red"), "Updated or new tag."); + endpoint.events(TAG_UPDATED, TAG_CREATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); String uuid = PathParameters.getTagUuid(rc); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, false); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); }, isOrderedBlockingHandlers()); } @@ -357,34 +337,17 @@ private void addTagFamilyUpdateHandler() { InternalEndpointRoute updateEndpoint = createRoute(); updateEndpoint.path("/:tagFamilyUuid"); updateEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - updateEndpoint.method(PUT); - updateEndpoint.description("Update the tag family with the given uuid."); + updateEndpoint.method(POST); + updateEndpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); updateEndpoint.consumes(APPLICATION_JSON); updateEndpoint.produces(APPLICATION_JSON); updateEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyUpdateRequest("Nicer colors")); - updateEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated tag family."); + updateEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or new tag family."); updateEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); updateEndpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, false); - }, isOrderedBlockingHandlers()); - - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:tagFamilyUuid"); - upsertEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - upsertEndpoint.method(POST); - upsertEndpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyCreateRequest("Nicer colors")); - upsertEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or created tag family."); - upsertEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); - upsertEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, ac.getUpdateParameters().isUpsert()); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); }, isOrderedBlockingHandlers()); } } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java index ae9b74e914..da89625972 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/user/UserEndpoint.java @@ -25,7 +25,6 @@ import com.gentics.mesh.parameter.impl.NodeParametersImpl; import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.InternalEndpointRoute; @@ -198,31 +197,6 @@ private void addDeleteHandler() { private void addUpdateHandler() { - // Add the user token handler first in order to allow for recovery token handling - getRouter().route("/:userUuid").method(PUT).handler(userTokenHandler); - // Chain the regular auth handler afterwards in order to handle non-token code requests - if (chain != null) { - chain.secure(getRouter().route("/:userUuid").method(PUT)); - } - - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:userUuid"); - updateEndpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); - updateEndpoint.description("Update the user with the given uuid."); - updateEndpoint.method(PUT); - updateEndpoint.setMutating(true); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.addQueryParameters(UserParametersImpl.class); - updateEndpoint.exampleRequest(userExamples.getUserUpdateRequest("jdoe42")); - updateEndpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated user response."); - updateEndpoint.events(USER_UPDATED); - updateEndpoint.blockingHandler(rc -> { - InternalActionContext ac = wrap(rc); - String uuid = ac.getParameter("userUuid"); - crudHandler.handleUpdate(ac, uuid, false); - }, isOrderedBlockingHandlers()); - // Add the user token handler first in order to allow for recovery token handling getRouter().route("/:userUuid").method(POST).handler(userTokenHandler); // Chain the regular auth handler afterwards in order to handle non-token code requests @@ -230,23 +204,22 @@ private void addUpdateHandler() { chain.secure(getRouter().route("/:userUuid").method(POST)); } - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.path("/:userUuid"); - upsertEndpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); - upsertEndpoint.description("Update the user with the given uuid. The user is created if no user with the specified uuid could be found."); - upsertEndpoint.method(POST); - upsertEndpoint.setMutating(true); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); - upsertEndpoint.addQueryParameters(UpdateParametersImpl.class); - upsertEndpoint.addQueryParameters(UserParametersImpl.class); - upsertEndpoint.exampleRequest(userExamples.getUserCreateRequest("jdoe42")); - upsertEndpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated or created user response."); - upsertEndpoint.events(USER_CREATED, USER_UPDATED); - upsertEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:userUuid"); + endpoint.addUriParameter("userUuid", "Uuid of the user.", USER_EDITOR_UUID); + endpoint.description("Update the user with the given uuid. The user is created if no user with the specified uuid could be found."); + endpoint.method(POST); + endpoint.setMutating(true); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + endpoint.addQueryParameters(UserParametersImpl.class); + endpoint.exampleRequest(userExamples.getUserUpdateRequest("jdoe42")); + endpoint.exampleResponse(OK, userExamples.getUserResponse1("jdoe42"), "Updated or new user response."); + endpoint.events(USER_CREATED, USER_UPDATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("userUuid"); - crudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + crudHandler.handleUpdate(ac, uuid); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java index 449800b186..85715d9af3 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java @@ -69,23 +69,6 @@ private void addPathReadHandler() { } private void addPathUpdateCreateHandler() { - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.pathRegex("\\/(.*)"); - updateEndpoint.setRAMLPath("/{path}"); - updateEndpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); - updateEndpoint.method(PUT); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.produces(APPLICATION_JSON); - - updateEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); - updateEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "Updated node."); - updateEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); - - updateEndpoint.description("Update a node for the given path."); - updateEndpoint.blockingHandler(rc -> { - handler.handleUpdateCreatePath(rc, POST, false); - }, isOrderedBlockingHandlers()); - InternalEndpointRoute upsertEndpoint = createRoute(); upsertEndpoint.pathRegex("\\/(.*)"); upsertEndpoint.setRAMLPath("/{path}"); @@ -100,7 +83,7 @@ private void addPathUpdateCreateHandler() { upsertEndpoint.description("Update or create a node for the given path."); upsertEndpoint.blockingHandler(rc -> { - handler.handleUpdateCreatePath(rc, POST, true); + handler.handleUpdateCreatePath(rc, POST); }, isOrderedBlockingHandlers()); } diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java index 243ccf9790..3f0a175441 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootHandler.java @@ -141,7 +141,7 @@ public void handleGetPath(RoutingContext rc) { } - public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method, boolean createInexisting) { + public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method) { InternalActionContext ac = new InternalRoutingActionContextImpl(rc); String path = rc.request().path().substring( rc.mountPoint().length()); @@ -179,7 +179,7 @@ public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method, boolean } ac.setBody(request); return contentDao.getNode(container).getUuid(); - } else if (createInexisting) { + } else { int diff = nodePath.getInitialStack().size() - nodePath.getSegments().size(); if (diff > 1) { String resolvedPath = nodePath.getResolvedPath(); @@ -206,14 +206,12 @@ public void handleUpdateCreatePath(RoutingContext rc, HttpMethod method, boolean ac.put(WEBROOT_LAST_SEGMENT, nodePath.getInitialStack().firstElement()); ac.setBody(request); return null; - } else { - throw error(NOT_FOUND, "node_not_found_for_path", decodeSegment(nodePath.getTargetPath())); } }); } if (uuid != null) { - nodeCrudHandler.handleUpdate(ac, uuid, createInexisting); + nodeCrudHandler.handleUpdate(ac, uuid); } else { nodeCrudHandler.handleCreate(ac); } diff --git a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java index cad7846a2b..0f36c23e54 100644 --- a/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java +++ b/core/src/main/java/com/gentics/mesh/rest/MeshLocalClientImpl.java @@ -261,7 +261,7 @@ public MeshRequest upsertNode(String projectName, String uuid, Nod ac.setProject(projectName); ac.setPayloadObject(nodeUpsetRequest); ac.getVersioningParameters().setVersion("draft"); - nodeCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + nodeCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -282,7 +282,7 @@ public MeshRequest createNode(String uuid, String projectName, Nod ac.setProject(projectName); ac.setPayloadObject(nodeCreateRequest); ac.getVersioningParameters().setVersion("draft"); - nodeCrudHandler.handleUpdate(ac, uuid, true); + nodeCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -292,7 +292,7 @@ public MeshRequest updateNode(String projectName, String uuid, Nod LocalActionContextImpl ac = createContext(NodeResponse.class, parameters); ac.setPayloadObject(nodeUpdateRequest); ac.setProject(projectName); - nodeCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + nodeCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -410,7 +410,7 @@ public MeshRequest updateTag(String projectName, String tagFamilyUu ac.setPayloadObject(request); ac.setParameter("tagUuid", uuid); ac.setParameter("tagFamilyUuid", tagFamilyUuid); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, ac.getUpdateParameters().isUpsert()); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -421,7 +421,7 @@ public MeshRequest createTag(String projectName, String tagFamilyUu ac.setPayloadObject(request); ac.setParameter("tagUuid", uuid); ac.setParameter("tagFamilyUuid", tagFamilyUuid); - tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid, true); + tagCrudHandler.handleUpdate(ac, tagFamilyUuid, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -488,14 +488,14 @@ public MeshRequest createProject(ProjectCreateRequest request) public MeshRequest createProject(String uuid, ProjectCreateRequest request) { LocalActionContextImpl ac = createContext(ProjectResponse.class); ac.setPayloadObject(request); - projectCrudHandler.handleUpdate(ac, uuid, true); + projectCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @Override public MeshRequest updateProject(String uuid, ProjectUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(ProjectResponse.class, parameters); - projectCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + projectCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -594,7 +594,7 @@ public MeshRequest updateTagFamily(String projectName, String LocalActionContextImpl ac = createContext(TagFamilyResponse.class, parameters); ac.setPayloadObject(request); ac.setProject(projectName); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, ac.getUpdateParameters().isUpsert()); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -603,7 +603,7 @@ public MeshRequest createTagFamily(String projectName, String LocalActionContextImpl ac = createContext(TagFamilyResponse.class); ac.setPayloadObject(request); ac.setProject(projectName); - tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid, true); + tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -709,7 +709,7 @@ public MeshRequest createSchema(SchemaCreateRequest request, Par public MeshRequest createSchema(String uuid, SchemaCreateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(SchemaResponse.class, parameters); ac.setPayloadObject(request); - schemaCrudHandler.handleUpdate(ac, uuid, true); + schemaCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -724,7 +724,7 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(GenericMessageResponse.class, parameters); ac.setPayloadObject(request); - schemaCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + schemaCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -791,7 +791,7 @@ public MeshRequest createGroup(GroupCreateRequest createRequest) public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { LocalActionContextImpl ac = createContext(GroupResponse.class); ac.setPayloadObject(createRequest); - groupCrudHandler.handleUpdate(ac, uuid, true); + groupCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -799,7 +799,7 @@ public MeshRequest createGroup(String uuid, GroupCreateRequest cr public MeshRequest updateGroup(String uuid, GroupUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(GroupResponse.class, parameters); ac.setPayloadObject(request); - groupCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + groupCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -864,7 +864,7 @@ public MeshRequest createUser(UserCreateRequest request, Parameter public MeshRequest createUser(String uuid, UserCreateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(UserResponse.class, parameters); ac.setPayloadObject(request); - userCrudHandler.handleUpdate(ac, uuid, true); + userCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -872,7 +872,7 @@ public MeshRequest createUser(String uuid, UserCreateRequest reque public MeshRequest updateUser(String uuid, UserUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(UserResponse.class, parameters); ac.setPayloadObject(request); - userCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + userCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -923,7 +923,7 @@ public MeshRequest createRole(RoleCreateRequest request) { public MeshRequest createRole(String uuid, RoleCreateRequest request) { LocalActionContextImpl ac = createContext(RoleResponse.class); ac.setPayloadObject(request); - roleCrudHandler.handleUpdate(ac, uuid, true); + roleCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -959,7 +959,7 @@ public MeshRequest readRolePermissions(String roleUuid, public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(RoleResponse.class, parameters); ac.setPayloadObject(restRole); - roleCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + roleCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1139,7 +1139,7 @@ public MeshRequest createMicroschema(MicroschemaCreateReque public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { LocalActionContextImpl ac = createContext(MicroschemaResponse.class); ac.setPayloadObject(request); - microschemaCrudHandler.handleUpdate(ac, uuid, true); + microschemaCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1154,7 +1154,7 @@ public MeshRequest findMicroschemaByUuid(String uuid, Param public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { LocalActionContextImpl ac = createContext(GenericMessageResponse.class, parameters); ac.setPayloadObject(request); - microschemaCrudHandler.handleUpdate(ac, uuid, ac.getUpdateParameters().isUpsert()); + microschemaCrudHandler.handleUpdate(ac, uuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } @@ -1479,7 +1479,7 @@ public MeshRequest updateBranch(String projectName, String branc ac.setProject(projectName); ac.setPayloadObject(request); ac.setParameter("uuid", branchUuid); - branchCrudHandler.handleUpdate(ac, branchUuid, ac.getUpdateParameters().isUpsert()); + branchCrudHandler.handleUpdate(ac, branchUuid); return new MeshLocalRequestImpl<>(ac.getFuture()); } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java index 5c645a79f0..8d3eb22159 100644 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java +++ b/mdm/api/src/main/java/com/gentics/mesh/parameter/ParameterProviderContext.java @@ -23,7 +23,6 @@ import com.gentics.mesh.parameter.impl.SchemaUpdateParametersImpl; import com.gentics.mesh.parameter.impl.SearchParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; @@ -127,8 +126,4 @@ default ConsistencyCheckParameters getConsistencyCheckParameters() { default BinaryCheckParameters getBinaryCheckParameters() { return new BinaryCheckParametersImpl(this); } - - default UpdateParameters getUpdateParameters() { - return new UpdateParametersImpl(this); - } } diff --git a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java b/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java deleted file mode 100644 index d33c5a5584..0000000000 --- a/mdm/api/src/main/java/com/gentics/mesh/parameter/impl/UpdateParametersImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.gentics.mesh.parameter.impl; - -import java.util.HashMap; -import java.util.Map; - -import org.raml.model.ParamType; -import org.raml.model.parameter.QueryParameter; - -import com.gentics.mesh.handler.ActionContext; -import com.gentics.mesh.parameter.AbstractParameters; -import com.gentics.mesh.parameter.UpdateParameters; - -import io.vertx.core.MultiMap; - -/** - * A {@link UpdateParametersImpl} can be used to add additional update parameters to the rest requests. - */ -public class UpdateParametersImpl extends AbstractParameters implements UpdateParameters { - - public UpdateParametersImpl() { - super(); - } - - public UpdateParametersImpl(ActionContext ac) { - super(ac); - } - - public UpdateParametersImpl(MultiMap parameters) { - super(parameters); - } - - @Override - public Map getRAMLParameters() { - Map parameters = new HashMap<>(); - - // upsert - QueryParameter perPageParameter = new QueryParameter(); - perPageParameter.setDescription("Whether to create the new entity with the given UUID and parameters, if an existing one was not found."); - perPageParameter.setExample("true"); - perPageParameter.setRequired(false); - perPageParameter.setType(ParamType.BOOLEAN); - parameters.put(UPSERT_PARAMETER_KEY, perPageParameter); - - return parameters; - } - - @Override - public String getName() { - return "Update parameters"; - } - -} diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java index 9b148b51c2..6b5d628d40 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/endpoint/handler/CrudHandler.java @@ -38,9 +38,8 @@ public interface CrudHandler { * @param ac * @param uuid * Uuid of the element which should be updated - * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. */ - void handleUpdate(InternalActionContext ac, String uuid, boolean createInexisting); + void handleUpdate(InternalActionContext ac, String uuid); /** * Handle read list requests. diff --git a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java index ff5d21beb5..c4ba95c066 100644 --- a/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java +++ b/mdm/common/src/main/java/com/gentics/mesh/core/verticle/handler/HandlerUtilities.java @@ -7,7 +7,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.CREATED; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.NO_CONTENT; import static io.netty.handler.codec.http.HttpResponseStatus.OK; @@ -162,20 +161,6 @@ public , RM extends RestModel> void updateElement(I createOrUpdateElement(ac, uuid, actions); } - /** - * Locate and update or create the element using the action context data. - * - * @param ac - * @param uuid - * Uuid of the element which should be updated - * @param actions - * Handler which provides the root vertex which should be used when loading the element - */ - public , RM extends RestModel> void updateElement(InternalActionContext ac, String uuid, - DAOActions actions, boolean createInexisting) { - createOrUpdateElement(ac, null, uuid, actions, createInexisting); - } - /** * Handle a create/update of the given element by uuid * @@ -192,7 +177,7 @@ public , RM extends RestModel> void updateElement(I */ public , RM extends RestModel> void createOrUpdateElement(InternalActionContext ac, String uuid, DAOActions actions) { - createOrUpdateElement(ac, null, uuid, actions, true); + createOrUpdateElement(ac, null, uuid, actions); } /** @@ -204,10 +189,9 @@ public , RM extends RestModel> void createOrUpdateE * @param uuid * Uuid of the element to create or update. If null, an element will be created with random Uuid * @param actions - * @param createInexisting if true, and the existing user with given UUID is not found, a new one will be created. */ public , RM extends RestModel> void createOrUpdateElement(InternalActionContext ac, Function parentLoader, - String uuid, DAOActions actions, boolean createInexisting) { + String uuid, DAOActions actions) { ac.setHttpServerConfig(meshOptions.getHttpServerOptions()); try (WriteLock lock = writeLock.lock(ac)) { AtomicBoolean created = new AtomicBoolean(false); @@ -231,15 +215,13 @@ public , RM extends RestModel> void createOrUpdateE actions.update(tx, updateElement, ac, bac.batch()); RM model = actions.transformToRestSync(tx, updateElement, ac, 0); return model; - } else if (createInexisting) { + } else { created.set(true); T createdElement = actions.create(tx, ac, bac.batch(), uuid); RM model = actions.transformToRestSync(tx, createdElement, ac, 0); String path = actions.getAPIPath(tx, ac, createdElement); ac.setLocation(path); return model; - } else { - throw error(NOT_FOUND, "object_not_found_for_uuid", uuid); } }, model -> ac.send(model, created.get() ? CREATED : OK)); } @@ -284,7 +266,7 @@ public , RM extends RestModel> void readElement(Int T element = actions.loadByUuid(context(tx, ac, parent), uuid, perm, true); // Handle etag - if (ac.getEtagParameters().getETag()) { + if (ac.getGenericParameters().getETag()) { String etag = actions.getETag(tx, ac, element); ac.setEtag(etag, true); if (ac.matches(etag, true)) { diff --git a/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java b/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java deleted file mode 100644 index 7f5c46ec78..0000000000 --- a/rest-client/src/main/java/com/gentics/mesh/parameter/client/UpdateParameters.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.gentics.mesh.parameter.client; - -/** - * @see UpdateParameters - */ -public class UpdateParameters extends AbstractParameters implements com.gentics.mesh.parameter.UpdateParameters { - -} diff --git a/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java b/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java deleted file mode 100644 index 3a7e25fe5c..0000000000 --- a/rest-model/src/main/java/com/gentics/mesh/parameter/UpdateParameters.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.gentics.mesh.parameter; - -import org.apache.commons.lang.BooleanUtils; - -/** - * Parameters, applied on an entity update. - */ -public interface UpdateParameters extends ParameterProvider { - - public static final String UPSERT_PARAMETER_KEY = "upsert"; - public static final boolean DEFAULT_UPSERT_PARAMETER_VALUE = true; - - /** - * Set the flag to upsert (create if not found) the value on update. - * - * @param flag - * @return - */ - default UpdateParameters setUpsert(boolean flag) { - setParameter(UPSERT_PARAMETER_KEY, String.valueOf(flag)); - return this; - } - - /** - * Check if an upsert (create if not found) is requested. - * - * @return - */ - default boolean isUpsert() { - return BooleanUtils.toBooleanDefaultIfNull(BooleanUtils.toBooleanObject(getParameter(UPSERT_PARAMETER_KEY)), DEFAULT_UPSERT_PARAMETER_VALUE); - } -} diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java index f998414622..de40132225 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/branch/BranchEndpointTest.java @@ -63,7 +63,6 @@ import com.gentics.mesh.core.rest.branch.info.BranchMicroschemaInfo; import com.gentics.mesh.core.rest.branch.info.BranchSchemaInfo; import com.gentics.mesh.core.rest.common.ListResponse; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.branch.BranchMicroschemaAssignModel; import com.gentics.mesh.core.rest.event.branch.BranchSchemaAssignEventModel; import com.gentics.mesh.core.rest.event.impl.MeshElementEventModelImpl; @@ -93,7 +92,6 @@ import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SchemaUpdateParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -731,15 +729,6 @@ public void testUpdateByUUIDWithoutPerm() throws Exception { UPDATE_PERM.getRestPerm().getName()); } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - BranchUpdateRequest request = new BranchUpdateRequest(); - // request.setActive(false); - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateBranch(PROJECT_NAME, uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - } - @Test public void testUpdateHostname() { String hostname = "new.hostname"; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java index 5b47ff2abc..30ff5c4076 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/group/GroupEndpointTest.java @@ -54,7 +54,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -499,16 +498,6 @@ public void testReadWithRolePermsSync() throws Exception { } } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - final String name = "New Name"; - GroupUpdateRequest request = new GroupUpdateRequest(); - request.setName(name); - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateGroup(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - } - @Test @Override public void testDeleteByUUID() throws Exception { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java index 3b975286d7..b709a08769 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/language/LanguageEndpointTest.java @@ -17,7 +17,6 @@ import com.gentics.mesh.FieldUtil; import com.gentics.mesh.core.data.perm.InternalPermission; import com.gentics.mesh.core.rest.SortOrder; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.lang.LanguageListResponse; import com.gentics.mesh.core.rest.lang.LanguageResponse; import com.gentics.mesh.core.rest.node.NodeCreateRequest; @@ -125,12 +124,6 @@ public void testUpdate() throws Exception { public void testUpdateByUUIDWithoutPerm() throws Exception { } - @Override - @Ignore("No Language creation allowed") - @Test - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - } - @Override @Ignore("No Language removal allowed") @Test diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java index 78e6ed0661..6acce2aee3 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeEndpointTest.java @@ -102,7 +102,6 @@ import com.gentics.mesh.parameter.impl.PublishParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.rest.client.MeshWebrootResponse; import com.gentics.mesh.test.MeshTestSetting; @@ -1880,21 +1879,6 @@ public void testUpdateByUUIDWithoutPerm() throws Exception { } } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - - try (Tx tx = tx()) { - NodeUpdateRequest request = new NodeUpdateRequest(); - request.setLanguage("en"); - - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateNode(PROJECT_NAME, uuid, request, - new NodeParametersImpl().setLanguages("en", "de"), new UpdateParametersImpl().setUpsert(false)), - NOT_FOUND, "object_not_found_for_uuid", uuid); - } - } - @Test public void testCreateNodeWithExtraField() throws UnknownHostException, InterruptedException { try (Tx tx = tx()) { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java index 76214563c9..71a6506b63 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/project/ProjectEndpointTest.java @@ -64,7 +64,6 @@ import com.gentics.mesh.core.rest.branch.BranchUpdateRequest; import com.gentics.mesh.core.rest.common.Permission; import com.gentics.mesh.core.rest.common.PermissionInfo; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.impl.MeshElementEventModelImpl; import com.gentics.mesh.core.rest.event.node.NodeMeshEventModel; import com.gentics.mesh.core.rest.node.NodeResponse; @@ -81,7 +80,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.TestSize; @@ -652,16 +650,6 @@ public void testUpdate() throws Exception { } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - ProjectUpdateRequest request = new ProjectUpdateRequest(); - request.setName("new Name"); - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateProject(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - - } - @Test @Override public void testUpdateByUUIDWithoutPerm() throws JsonProcessingException, Exception { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java index 183d680634..d435704097 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/role/RoleEndpointTest.java @@ -18,7 +18,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -46,7 +45,6 @@ import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.GenericMessageResponse; import com.gentics.mesh.core.rest.common.Permission; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.impl.MeshElementEventModelImpl; import com.gentics.mesh.core.rest.group.GroupCreateRequest; import com.gentics.mesh.core.rest.group.GroupResponse; @@ -57,7 +55,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -458,17 +455,6 @@ public void testUpdateConflictCheck() { call(() -> client().updateRole(roleUuid(), request), CONFLICT, "role_conflicting_name"); } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - RoleUpdateRequest request = new RoleUpdateRequest(); - request.setName("renamed role"); - - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateRole(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - - } - @Test public void testUpdateOwnRole() throws JsonGenerationException, JsonMappingException, IOException, Exception { try (Tx tx = tx()) { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java index 91d8f4b4e0..bd11cd8983 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/MicroschemaEndpointTest.java @@ -35,7 +35,6 @@ import com.gentics.mesh.core.db.Tx; import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.Permission; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.impl.MeshElementEventModelImpl; import com.gentics.mesh.core.rest.micronode.MicronodeResponse; import com.gentics.mesh.core.rest.microschema.impl.MicroschemaCreateRequest; @@ -54,7 +53,6 @@ import com.gentics.mesh.json.JsonUtil; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -332,23 +330,6 @@ public void testUpdateByUUIDWithoutPerm() throws Exception { } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - try (Tx tx = tx()) { - HibMicroschema microschema = microschemaContainers().get("vcard"); - String oldName = microschema.getName(); - MicroschemaUpdateRequest request = new MicroschemaUpdateRequest(); - request.setName("new-name"); - - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateMicroschema(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - - HibMicroschema reloaded = tx.microschemaDao().findByUuid(microschema.getUuid()); - assertEquals("The name should not have been changed.", oldName, reloaded.getName()); - } - } - @Test @Override public void testDeleteByUUID() throws Exception { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java index fef46b5728..c0c5958dec 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/schema/SchemaEndpointTest.java @@ -86,7 +86,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.VersioningParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -603,24 +602,6 @@ public void testUpdateWithConflictingName() { call(() -> client().updateSchema(response.getUuid(), updateRequest), CONFLICT, "schema_conflicting_name", "folder"); } - @Test - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - try (Tx tx = tx()) { - SchemaDao schemaDao = tx.schemaDao(); - - HibSchema schema = schemaContainer("content"); - String oldName = schema.getName(); - SchemaUpdateRequest request = new SchemaUpdateRequest(); - request.setName("new-name"); - - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateSchema(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - - HibSchema reloaded = schemaDao.findByUuid(schema.getUuid()); - assertEquals("The name should not have been changed.", oldName, reloaded.getName()); - } - } - @Test public void testDeleteWithChanges() { SchemaCreateRequest schemaCreateRequest = new SchemaCreateRequest(); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java index 980a68b84a..33bdfea1f7 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tag/TagEndpointTest.java @@ -24,7 +24,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -51,7 +50,6 @@ import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.ContainerType; import com.gentics.mesh.core.rest.common.ListResponse; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.node.NodeTaggedEventModel; import com.gentics.mesh.core.rest.event.tag.TagMeshEventModel; import com.gentics.mesh.core.rest.tag.TagCreateRequest; @@ -62,7 +60,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.rest.client.MeshRestClientMessageException; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; @@ -668,19 +665,6 @@ public void testReadByUUIDWithMissingPermission() throws Exception { } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - TagUpdateRequest request = new TagUpdateRequest(); - request.setName("newName"); - String uuid = UUIDUtil.randomUUID(); - try (Tx tx = tx()) { - HibTagFamily parentTagFamily = tagFamily("colors"); - call(() -> client().updateTag(PROJECT_NAME, parentTagFamily.getUuid(), uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - } - - } - @Test @Override public void testPermissionResponse() { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java index e7015634d7..e8804101e4 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/tagfamily/TagFamilyEndpointTest.java @@ -22,7 +22,6 @@ import static io.netty.handler.codec.http.HttpResponseStatus.CONFLICT; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR; -import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -57,7 +56,6 @@ import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.ContainerType; import com.gentics.mesh.core.rest.common.Permission; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.tag.TagMeshEventModel; import com.gentics.mesh.core.rest.event.tagfamily.TagFamilyMeshEventModel; import com.gentics.mesh.core.rest.tag.TagFamilyCreateRequest; @@ -67,7 +65,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.context.AbstractMeshTest; import com.gentics.mesh.test.definition.BasicRestTestcases; @@ -525,16 +522,6 @@ public void testUpdateNodeIndex() { } } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - TagFamilyUpdateRequest request = new TagFamilyUpdateRequest(); - request.setName("new Name"); - - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateTagFamily(PROJECT_NAME, uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - } - @Test @Override public void testUpdateByUUIDWithoutPerm() { diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java index b52cf4fb2a..2e991e2854 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/user/UserEndpointTest.java @@ -62,7 +62,6 @@ import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.ListResponse; import com.gentics.mesh.core.rest.common.Permission; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.core.rest.event.impl.MeshElementEventModelImpl; import com.gentics.mesh.core.rest.node.NodeResponse; import com.gentics.mesh.core.rest.user.NodeReference; @@ -78,7 +77,6 @@ import com.gentics.mesh.parameter.impl.PagingParametersImpl; import com.gentics.mesh.parameter.impl.RolePermissionParametersImpl; import com.gentics.mesh.parameter.impl.SortingParametersImpl; -import com.gentics.mesh.parameter.impl.UpdateParametersImpl; import com.gentics.mesh.parameter.impl.UserParametersImpl; import com.gentics.mesh.rest.client.MeshRequest; import com.gentics.mesh.rest.client.MeshResponse; @@ -692,15 +690,6 @@ public void testUpdateWithSpecialCharacters() throws Exception { } - @Test - @Override - public void testUpdateWithBogusUuid() throws GenericRestException, Exception { - UserUpdateRequest request = new UserUpdateRequest(); - request.setUsername("New Name"); - String uuid = UUIDUtil.randomUUID(); - call(() -> client().updateUser(uuid, request, new UpdateParametersImpl().setUpsert(false)), NOT_FOUND, "object_not_found_for_uuid", uuid); - } - @Test public void testUpdateUserAndSetNodeReference() throws Exception { String nodeUuid = tx(() -> folder("news").getUuid()); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/definition/CrudEndpointTestCases.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/definition/CrudEndpointTestCases.java index d0b0824ad5..24d26d4d99 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/definition/CrudEndpointTestCases.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/definition/CrudEndpointTestCases.java @@ -9,7 +9,6 @@ import com.gentics.mesh.core.rest.SortOrder; import com.gentics.mesh.core.rest.common.ListResponse; -import com.gentics.mesh.core.rest.error.GenericRestException; import com.gentics.mesh.parameter.SortingParameters; import com.gentics.mesh.parameter.client.SortingParametersImpl; @@ -49,8 +48,6 @@ public interface CrudEndpointTestCases { void testUpdateByUUIDWithoutPerm() throws Exception; - void testUpdateWithBogusUuid() throws GenericRestException, Exception; - // Delete void testDeleteByUUID() throws Exception; diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java index 31d45b7b6f..799332f6da 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/openapi/OpenAPIMeshRestClient.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import org.apache.commons.io.FileUtils; @@ -123,7 +124,6 @@ import com.gentics.mesh.parameter.SchemaUpdateParameters; import com.gentics.mesh.parameter.SearchParameters; import com.gentics.mesh.parameter.SortingParameters; -import com.gentics.mesh.parameter.UpdateParameters; import com.gentics.mesh.parameter.VersioningParameters; import com.gentics.mesh.rest.JWTAuthentication; import com.gentics.mesh.rest.client.MeshBinaryResponse; @@ -191,6 +191,7 @@ protected static final T findParameter(String key, boolean forceString, Para }).findAny().orElse(null); } + /** * Adapt the request of Mesh REST model to an OpenAPI model. * @@ -200,11 +201,24 @@ protected static final T findParameter(String key, boolean forceString, Para * @return */ protected static O adaptRequest(R r) { + return adaptRequest(r, Optional.empty()); + } + + /** + * Adapt the request of a selected Mesh REST model to an OpenAPI model. + * + * @param + * @param + * @param r + * @param maybeEndClass optional custom end model + * @return + */ + protected static O adaptRequest(R r, Optional> maybeEndClass) { if (r == null) { return null; } try { - String modelName = r.getClass().getSimpleName(); + String modelName = maybeEndClass.map(Class::getSimpleName).orElse(r.getClass().getSimpleName()); Class openApiModelClass = (Class) Class.forName("org.openapitools.client.model." + modelName); return JSON.getGson().fromJson(r.toJson(), openApiModelClass); } catch (JsonSyntaxException | ClassNotFoundException e) { @@ -241,21 +255,21 @@ public MeshRequest createNode(String projectName, NodeCreateReques @Override public MeshRequest createNode(String uuid, String projectName, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(nodeCreateRequest)), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeCreateRequest)), NodeResponse.class); } @Override public MeshRequest upsertNode(String projectName, String uuid, NodeUpsertRequest nodeUpsertRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(nodeUpsertRequest)), + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeUpsertRequest, Optional.of(org.openapitools.client.model.NodeUpdateRequest.class))), NodeResponse.class); } @Override public MeshRequest updateNode(String projectName, String uuid, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPutWithHttpInfo(uuid, projectName, + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectNodesNodeUuidPostWithHttpInfo(uuid, projectName, adaptRequest(nodeUpdateRequest)), NodeResponse.class); } @@ -439,7 +453,7 @@ public MeshRequest findTagByUuid(String projectName, String tagFami public MeshRequest updateTag(String projectName, String tagFamilyUuid, String uuid, TagUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidTagsTagUuidPostWithHttpInfo(tagFamilyUuid, uuid, projectName, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), TagResponse.class); + adaptRequest(request)), TagResponse.class); } @Override @@ -520,13 +534,12 @@ public MeshRequest createProject(ProjectCreateRequest request) @Override public MeshRequest createProject(String uuid, ProjectCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPutWithHttpInfo(uuid, adaptRequest(request)), ProjectResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, adaptRequest(request, Optional.of(org.openapitools.client.model.ProjectUpdateRequest.class))), ProjectResponse.class); } @Override public MeshRequest updateProject(String uuid, ProjectUpdateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), ProjectResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectsProjectUuidPostWithHttpInfo(uuid, adaptRequest(request)), ProjectResponse.class); } @Override @@ -585,13 +598,13 @@ public MeshRequest deleteTagFamily(String projectName, String uui public MeshRequest updateTagFamily(String projectName, String tagFamilyUuid, TagFamilyUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), TagFamilyResponse.class); + adaptRequest(request)), TagFamilyResponse.class); } @Override public MeshRequest createTagFamily(String projectName, String tagFamilyUuid, TagFamilyCreateRequest request) { - return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, true, adaptRequest(request)), TagFamilyResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectTagFamiliesTagFamilyUuidPost(tagFamilyUuid, projectName, adaptRequest(request, Optional.of(org.openapitools.client.model.TagFamilyUpdateRequest.class))), TagFamilyResponse.class); } @Override @@ -647,7 +660,7 @@ public MeshRequest webroot(String projectName, String[] pat @Override public MeshRequest webrootUpdate(String projectName, String path, NodeUpdateRequest nodeUpdateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathPutWithHttpInfo(path, projectName, adaptRequest(nodeUpdateRequest)), NodeResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectWebrootPathPostWithHttpInfo(path, projectName, adaptRequest(nodeUpdateRequest)), NodeResponse.class); } @Override @@ -659,7 +672,7 @@ public MeshRequest webrootUpdate(String projectName, String[] path @Override public MeshRequest webrootCreate(String projectName, String path, NodeCreateRequest nodeCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectWebrootPathPostWithHttpInfo(path, projectName, adaptRequest(nodeCreateRequest)), NodeResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectWebrootPathPostWithHttpInfo(path, projectName, adaptRequest(nodeCreateRequest, Optional.of(org.openapitools.client.model.NodeUpdateRequest.class))), NodeResponse.class); } @Override @@ -676,12 +689,11 @@ public MeshRequest createSchema(SchemaCreateRequest request, Par @Override public MeshRequest createSchema(String uuid, SchemaCreateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), + return new OpenAPIMeshRequestImpl(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), - adaptRequest(request)), SchemaResponse.class); + adaptRequest(request, Optional.of(org.openapitools.client.model.SchemaUpdateRequest.class))), SchemaResponse.class); } @Override @@ -695,7 +707,7 @@ public MeshRequest findSchemaByUuid(String uuid, ParameterProvid @Override public MeshRequest updateSchema(String uuid, SchemaUpdateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPutWithHttpInfo(uuid, + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2SchemasSchemaUuidPostWithHttpInfo(uuid, findParameter(SchemaUpdateParameters.UPDATE_ASSIGNED_BRANCHES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.UPDATE_BRANCH_NAMES_QUERY_PARAM_KEY, parameters), findParameter(SchemaUpdateParameters.STRICT_VALIDATION_KEY, parameters), @@ -823,13 +835,13 @@ public MeshRequest createGroup(GroupCreateRequest createRequest) @Override public MeshRequest createGroup(String uuid, GroupCreateRequest createRequest) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, true, adaptRequest(createRequest)), GroupResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, adaptRequest(createRequest, Optional.of(org.openapitools.client.model.GroupUpdateRequest.class))), GroupResponse.class); } @Override public MeshRequest updateGroup(String uuid, GroupUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2GroupsGroupUuidPostWithHttpInfo(uuid, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), GroupResponse.class); + adaptRequest(request)), GroupResponse.class); } @Override @@ -908,15 +920,14 @@ public MeshRequest createUser(UserCreateRequest request, Parameter @Override public MeshRequest createUser(String uuid, UserCreateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), - getAPIKey(), adaptRequest(request)), UserResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, + getAPIKey(), adaptRequest(request, Optional.of(org.openapitools.client.model.UserUpdateRequest.class))), UserResponse.class); } @Override public MeshRequest updateUser(String uuid, UserUpdateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPutWithHttpInfo(uuid, getAPIKey(), adaptRequest(request)), UserResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2UsersUserUuidPostWithHttpInfo(uuid, getAPIKey(), adaptRequest(request)), UserResponse.class); } @Override @@ -995,7 +1006,7 @@ public MeshRequest createRole(RoleCreateRequest request) { @Override public MeshRequest createRole(String uuid, RoleCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, true, adaptRequest(request)), RoleResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, adaptRequest(request, Optional.of(org.openapitools.client.model.RoleUpdateRequest.class))), RoleResponse.class); } @Override @@ -1023,7 +1034,7 @@ public MeshRequest readRolePermissions(String roleUuid, @Override public MeshRequest updateRole(String uuid, RoleUpdateRequest restRole, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2RolesRoleUuidPostWithHttpInfo(uuid, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(restRole)), RoleResponse.class); + adaptRequest(restRole)), RoleResponse.class); } @Override @@ -1432,7 +1443,7 @@ public MeshRequest createMicroschema(MicroschemaCreateReque @Override public MeshRequest createMicroschema(String uuid, MicroschemaCreateRequest request) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, true, adaptRequest(request)), MicroschemaResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, adaptRequest(request)), MicroschemaResponse.class); } @Override @@ -1444,7 +1455,7 @@ public MeshRequest findMicroschemaByUuid(String uuid, Param @Override public MeshRequest updateMicroschema(String uuid, MicroschemaUpdateRequest request, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPutWithHttpInfo(uuid, adaptRequest(request)), GenericMessageResponse.class); + return new OpenAPIMeshRequestImpl<>(() -> api.apiV2MicroschemasMicroschemaUuidPostWithHttpInfo(uuid, adaptRequest(request)), GenericMessageResponse.class); } @Override @@ -1640,7 +1651,7 @@ public MeshRequest createBranch(String projectName, BranchCreate @Override public MeshRequest createBranch(String projectName, String uuid, BranchCreateRequest branchCreateRequest, ParameterProvider... parameters) { - return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(uuid, projectName, true, adaptRequest(branchCreateRequest)), BranchResponse.class); + return new OpenAPIMeshRequestImpl(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(uuid, projectName, adaptRequest(branchCreateRequest, Optional.of(org.openapitools.client.model.BranchUpdateRequest.class))), BranchResponse.class); } @Override @@ -1666,7 +1677,7 @@ public MeshRequest findBranches(String projectName, Paramete public MeshRequest updateBranch(String projectName, String branchUuid, BranchUpdateRequest request, ParameterProvider... parameters) { return new OpenAPIMeshRequestImpl<>(() -> api.apiV2ProjectBranchesBranchUuidPostWithHttpInfo(branchUuid, projectName, - findParameter(UpdateParameters.UPSERT_PARAMETER_KEY, parameters), adaptRequest(request)), BranchResponse.class); + adaptRequest(request)), BranchResponse.class); } @Override From f986eadd298e2a6e84b43c2291e296cef0fad045 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 31 Mar 2026 12:26:34 +0200 Subject: [PATCH 71/75] Other fixes --- .../core/endpoint/group/GroupEndpoint.java | 22 ++++++++--------- .../mesh/core/endpoint/node/NodeEndpoint.java | 24 +++++++++---------- .../endpoint/tagfamily/TagFamilyEndpoint.java | 22 ++++++++--------- .../endpoint/webroot/WebRootEndpoint.java | 24 +++++++++---------- .../core/node/NodeConflictEndpointTest.java | 4 ++-- 5 files changed, 48 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java index 555b073cf0..596c115872 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/group/GroupEndpoint.java @@ -183,17 +183,17 @@ private void addDeleteHandler() { // TODO Determine what we should do about conflicting group names. Should we let neo4j handle those cases? // TODO update timestamps private void addUpdateHandler() { - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:groupUuid"); - updateEndpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); - updateEndpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); - updateEndpoint.method(POST); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.exampleRequest(groupExamples.getGroupUpdateRequest("New group name")); - updateEndpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); - updateEndpoint.events(GROUP_CREATED, GROUP_UPDATED); - updateEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:groupUuid"); + endpoint.addUriParameter("groupUuid", "Uuid of the group which should be updated.", GROUP_CLIENT_UUID); + endpoint.description("Update the group with the given uuid. The group is created if no group with the specified uuid could be found."); + endpoint.method(POST); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + endpoint.exampleRequest(groupExamples.getGroupUpdateRequest("New group name")); + endpoint.exampleResponse(OK, groupExamples.getGroupResponse1("New group name"), "Updated or new group."); + endpoint.events(GROUP_CREATED, GROUP_UPDATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("groupUuid"); crudHandler.handleUpdate(ac, uuid); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java index ded498fd41..5557bda3bb 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/node/NodeEndpoint.java @@ -586,20 +586,20 @@ private void addDeleteHandler() { // TODO use schema and only handle those i18n properties that were specified // within the schema. private void addUpdateHandler() { - InternalEndpointRoute postEndpoint = createRoute(); - postEndpoint.description("Update or create the node with the given uuid. " + InternalEndpointRoute endpoint = createRoute(); + endpoint.description("Update or create the node with the given uuid. " + "Mesh will automatically check for version conflicts if a version was specified in the request and return a 409 error if a conflict has been detected. " + "Additional conflict checks for WebRoot path conflicts will also be performed. The node is created if no node with the specified uuid could be found."); - postEndpoint.path("/:nodeUuid"); - postEndpoint.addUriParameter("nodeUuid", "Uuid of the node", NODE_DELOREAN_UUID); - postEndpoint.method(POST); - postEndpoint.consumes(APPLICATION_JSON); - postEndpoint.produces(APPLICATION_JSON); - postEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest2()); - postEndpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); - postEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); - postEndpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); - postEndpoint.blockingHandler(rc -> { + endpoint.path("/:nodeUuid"); + endpoint.addUriParameter("nodeUuid", "Uuid of the node", NODE_DELOREAN_UUID); + endpoint.method(POST); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + endpoint.exampleRequest(nodeExamples.getNodeUpdateRequest2()); + endpoint.exampleResponse(OK, nodeExamples.getNodeResponse2(), "New or updated node."); + endpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); + endpoint.events(NODE_UPDATED, NODE_CREATED, NODE_CONTENT_CREATED, NODE_UPDATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String uuid = ac.getParameter("nodeUuid"); ac.getVersioningParameters().setVersion("draft"); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java index 8a4676007c..2e9b1679f7 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/tagfamily/TagFamilyEndpoint.java @@ -334,17 +334,17 @@ private void addTagFamilyCreateHandler() { } private void addTagFamilyUpdateHandler() { - InternalEndpointRoute updateEndpoint = createRoute(); - updateEndpoint.path("/:tagFamilyUuid"); - updateEndpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); - updateEndpoint.method(POST); - updateEndpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); - updateEndpoint.consumes(APPLICATION_JSON); - updateEndpoint.produces(APPLICATION_JSON); - updateEndpoint.exampleRequest(tagFamilyExamples.getTagFamilyUpdateRequest("Nicer colors")); - updateEndpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or new tag family."); - updateEndpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); - updateEndpoint.blockingHandler(rc -> { + InternalEndpointRoute endpoint = createRoute(); + endpoint.path("/:tagFamilyUuid"); + endpoint.addUriParameter("tagFamilyUuid", "Uuid of the tag family.", TAGFAMILY_COLORS_UUID); + endpoint.method(POST); + endpoint.description("Update the tag family with the given uuid. The tag family will be created if it can't be found for the given uuid."); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); + endpoint.exampleRequest(tagFamilyExamples.getTagFamilyUpdateRequest("Nicer colors")); + endpoint.exampleResponse(OK, tagFamilyExamples.getTagFamilyResponse("Nicer colors"), "Updated or new tag family."); + endpoint.events(TAG_FAMILY_UPDATED, TAG_FAMILY_CREATED); + endpoint.blockingHandler(rc -> { InternalActionContext ac = wrap(rc); String tagFamilyUuid = PathParameters.getTagFamilyUuid(rc); tagFamilyCrudHandler.handleUpdate(ac, tagFamilyUuid); diff --git a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java index 85715d9af3..78aecd2197 100644 --- a/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java +++ b/core/src/main/java/com/gentics/mesh/core/endpoint/webroot/WebRootEndpoint.java @@ -69,20 +69,20 @@ private void addPathReadHandler() { } private void addPathUpdateCreateHandler() { - InternalEndpointRoute upsertEndpoint = createRoute(); - upsertEndpoint.pathRegex("\\/(.*)"); - upsertEndpoint.setRAMLPath("/{path}"); - upsertEndpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); - upsertEndpoint.method(POST); - upsertEndpoint.consumes(APPLICATION_JSON); - upsertEndpoint.produces(APPLICATION_JSON); + InternalEndpointRoute endpoint = createRoute(); + endpoint.pathRegex("\\/(.*)"); + endpoint.setRAMLPath("/{path}"); + endpoint.addUriParameter("path", "Path to the node", "/News/2015/Images/flower.jpg"); + endpoint.method(POST); + endpoint.consumes(APPLICATION_JSON); + endpoint.produces(APPLICATION_JSON); - upsertEndpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); - upsertEndpoint.exampleResponse(CREATED, nodeExamples.getNodeResponse2(), "Created node."); - upsertEndpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); + endpoint.exampleRequest(nodeExamples.getNodeUpdateRequest()); + endpoint.exampleResponse(CREATED, nodeExamples.getNodeResponse2(), "Created node."); + endpoint.exampleResponse(CONFLICT, miscExamples.createMessageResponse(), "A conflict has been detected."); - upsertEndpoint.description("Update or create a node for the given path."); - upsertEndpoint.blockingHandler(rc -> { + endpoint.description("Update or create a node for the given path."); + endpoint.blockingHandler(rc -> { handler.handleUpdateCreatePath(rc, POST); }, isOrderedBlockingHandlers()); } diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java index 35f9b2b908..9f7da55672 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/core/node/NodeConflictEndpointTest.java @@ -119,7 +119,7 @@ public void testConflictDetection() { assertThat((List) conflictException.getResponseMessage().getProperty("conflicts")).hasSize(1).containsExactly("teaser"); assertThat(conflictException.getStatusCode()).isEqualTo(CONFLICT.code()); - assertThat(conflictException.getMessage()).isEqualTo("Error:409 in PUT " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + assertThat(conflictException.getMessage()).isEqualTo("Error:409 in POST " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + "?lang=en,de : Conflict Info: " + I18NUtil.get(Locale.ENGLISH, "node_error_conflict_detected")); assertThat(conflictException.getResponseMessage().getProperty("oldVersion")).isEqualTo("1.0"); assertThat(conflictException.getResponseMessage().getProperty("newVersion")).isEqualTo("1.2"); @@ -357,7 +357,7 @@ public void testConflictInMicronode() { assertThat(((List) conflictException.getResponseMessage().getProperty("conflicts"))).hasSize(2).containsExactly("micronode.firstName", "micronode.lastName"); assertThat(conflictException.getStatusCode()).isEqualTo(CONFLICT.code()); - assertThat(conflictException.getMessage()).isEqualTo("Error:409 in PUT " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + assertThat(conflictException.getMessage()).isEqualTo("Error:409 in POST " + CURRENT_API_BASE_PATH + "/dummy/nodes/" + node.getUuid() + "?lang=en,de : Conflict Info: " + I18NUtil.get(Locale.ENGLISH, "node_error_conflict_detected")); assertThat(conflictException.getResponseMessage().getProperty("oldVersion")).isEqualTo("1.1"); assertThat(conflictException.getResponseMessage().getProperty("newVersion")).isEqualTo("1.2"); From edee7e98e82302a19349aa7058312d7b9583a872 Mon Sep 17 00:00:00 2001 From: "Serhii Plyhun (commercial)" Date: Fri, 3 Apr 2026 15:13:56 +0200 Subject: [PATCH 72/75] Update Maven options and environment variables in Jenkinsfile Increase memory limits for Maven and adjust environment variables. --- Jenkinsfile.split | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.split b/Jenkinsfile.split index bae2205498..818d207e74 100644 --- a/Jenkinsfile.split +++ b/Jenkinsfile.split @@ -72,7 +72,7 @@ final def testPart(partName, current, branches) { sh "mvn -pl :mesh-database-connector-mariadb docker:start -Dskip.mariadb.tests=${noMariadb} " } // run the tests - withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1g -XX:MaxMetaspaceSize=128m ", "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { + withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1536m -XX:MaxMetaspaceSize=256m ", "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { sh ".jenkins/run-splits.sh includes-${postfix} ${jacoco} ${partName} ${partId}" } } finally { From db6d019258a7397ff0ac8c5a2b6a064988336905 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Tue, 7 Apr 2026 12:07:43 +0200 Subject: [PATCH 73/75] Revert "Update Maven options and environment variables in Jenkinsfile" This reverts commit edee7e98e82302a19349aa7058312d7b9583a872. --- Jenkinsfile.split | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile.split b/Jenkinsfile.split index 818d207e74..bae2205498 100644 --- a/Jenkinsfile.split +++ b/Jenkinsfile.split @@ -72,7 +72,7 @@ final def testPart(partName, current, branches) { sh "mvn -pl :mesh-database-connector-mariadb docker:start -Dskip.mariadb.tests=${noMariadb} " } // run the tests - withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1536m -XX:MaxMetaspaceSize=256m ", "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { + withEnv(["TESTCONTAINERS_RYUK_DISABLED=true", "MESH_CONSISTENCY_CHECKS=" + (Boolean.valueOf(params.consistencyChecks) ? "true" : "false"), "MAVEN_OPTS=-Xmx1g -XX:MaxMetaspaceSize=128m ", "MESH_REST_CLIENT_CLASS=" + (Boolean.valueOf(params.useOpenApiTestClient) ? "com.gentics.mesh.test.openapi.OpenAPIMeshRestClient" : "")]) { sh ".jenkins/run-splits.sh includes-${postfix} ${jacoco} ${partName} ${partId}" } } finally { From 068101f443b537c049f75a2bdadc88512d0109c7 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Thu, 9 Apr 2026 16:52:22 +0200 Subject: [PATCH 74/75] Fix the tests --- .../main/java/com/gentics/mesh/cli/MeshIntegerationTest.java | 2 ++ .../java/com/gentics/mesh/test/AbstractIntegrationTest.java | 1 + 2 files changed, 3 insertions(+) diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/cli/MeshIntegerationTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/cli/MeshIntegerationTest.java index bbc9acfcd2..0ca9b86613 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/cli/MeshIntegerationTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/cli/MeshIntegerationTest.java @@ -7,6 +7,7 @@ import org.apache.commons.io.FileUtils; import org.junit.Before; import org.junit.FixMethodOrder; +import org.junit.Ignore; import org.junit.Test; import org.junit.runners.MethodSorters; @@ -28,6 +29,7 @@ public void cleanup() throws IOException { } @Test + @Ignore("Makes nothing but heavy runtime load") public void testStartup() throws Exception { MeshOptions options = getOptions(); Mesh mesh = Mesh.create(options); diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/test/AbstractIntegrationTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/test/AbstractIntegrationTest.java index b1edaa4ebe..289f2eb15c 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/test/AbstractIntegrationTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/test/AbstractIntegrationTest.java @@ -5,4 +5,5 @@ public class AbstractIntegrationTest { * Timeout of {@value #DEFAULT_TIMEOUT_SECONDS} seconds. */ protected static final long DEFAULT_TIMEOUT_SECONDS = 45; + } From 8b9d361300505f3523db32cebcb345d2fb2699e5 Mon Sep 17 00:00:00 2001 From: Serhii Plyhun Date: Fri, 10 Apr 2026 10:21:09 +0200 Subject: [PATCH 75/75] Auth token test fix --- .../mesh/client/MeshRestClientTokenTest.java | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/tests/tests-core/src/main/java/com/gentics/mesh/client/MeshRestClientTokenTest.java b/tests/tests-core/src/main/java/com/gentics/mesh/client/MeshRestClientTokenTest.java index e21902f8d4..5697d9c571 100644 --- a/tests/tests-core/src/main/java/com/gentics/mesh/client/MeshRestClientTokenTest.java +++ b/tests/tests-core/src/main/java/com/gentics/mesh/client/MeshRestClientTokenTest.java @@ -4,14 +4,19 @@ import static com.gentics.mesh.test.ClientHelper.call; import static com.gentics.mesh.test.ElasticsearchTestMode.NONE; import static com.gentics.mesh.test.TestSize.PROJECT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; import org.junit.Before; import org.junit.Test; import com.gentics.mesh.demo.UserInfo; import com.gentics.mesh.etc.config.MeshOptions; +import com.gentics.mesh.rest.client.MeshRestClientMessageException; import com.gentics.mesh.test.MeshOptionChanger; import com.gentics.mesh.test.MeshTestSetting; import com.gentics.mesh.test.TestDataProvider; @@ -67,13 +72,34 @@ public void setUp() throws Exception { public void testLogin() throws Exception { client().setLogin(username, password).login().blockingGet(); - // now get "me" for 10 seconds (once every second). This will fail, when the token expires - Flowable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS).flatMapSingle(v -> { - return client().me().toSingle(); - }).doOnNext(response -> { - // assert that we still are the correct user - assertThat(response).hasName(username); - }).blockingSubscribe(); + AtomicLong startAt = new AtomicLong(System.currentTimeMillis()); + try { + // now get "me" for 10 seconds (once every second). This will fail in 5 (TOKEN_EXPIRATION_TIME_SECONDS), when the token expires + Flowable.intervalRange(0, 10, 0, 1, TimeUnit.SECONDS).flatMapSingle(v -> { + return client().me().toSingle(); + }).doOnError(t -> { + // assert the expired token + assertThat((System.currentTimeMillis() - startAt.get())).as("Token expiration time").isGreaterThan(TOKEN_EXPIRATION_TIME_SECONDS * 1000); + Consumer assertException = tt -> { + assertThat(tt.getStatusCode()).as("REST client error status").isEqualTo(401); + }; + if (t instanceof MeshRestClientMessageException tt) { + assertException.accept(tt); + } else if (t.getCause() instanceof MeshRestClientMessageException tt) { + assertException.accept(tt); + } else { + fail(t); + } + }).doOnNext(response -> { + // assert that we still are the correct user + assertThat(response).hasName(username); + // assert the inexpired token + assertThat((System.currentTimeMillis() - startAt.get())).as("Token expiration time").isLessThanOrEqualTo(TOKEN_EXPIRATION_TIME_SECONDS * 1000); + }).blockingSubscribe(); + } catch (Exception e) { + e.printStackTrace(); + // already asserted + } } /**