diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java index c37857f98..bab63e067 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/utils/SpringDocDataRestUtils.java @@ -99,6 +99,11 @@ public class SpringDocDataRestUtils { */ private final HashMap entityInoMap = new HashMap(); + /** + * The associations fields by entity. + */ + private final HashMap> allAssociationsFieldsMap = new HashMap<>(); + /** * The Repository rest configuration. */ @@ -136,6 +141,7 @@ public void customise(OpenAPI openAPI, ResourceMappings mappings, PersistentEnti entityInfo.setIgnoredFields(ignoredFields); List associationsFields = getAssociationsFields(resourceMetadata, entity); entityInfo.setAssociationsFields(associationsFields); + allAssociationsFieldsMap.put(domainType.getSimpleName(), getAllAssociationsFields(resourceMetadata, entity)); entityInoMap.put(domainType.getSimpleName(), entityInfo); } @@ -277,11 +283,43 @@ private Schema updateResponseSchema(String className, Schema existingSchema, Com else if (EMBEDDED.equals(propId)) { updateResponseSchemaEmbedded(components, entityInfo, entry, openapi31); } + else if (allAssociationsFieldsMap.get(className).contains(propId)) { + updateResponseSchemaProperty(entry.getValue(), components, openapi31); + } } } return existingSchema; } + /** + * Update a response schema property that points to an entity which is not an + * exported repository. Spring Data REST serializes these associations in + * the containing representation, so they need the same association filtering + * as an exported entity response. + * + * @param property the property + * @param components the components + * @param openapi31 the openapi 31 + */ + private void updateResponseSchemaProperty(Schema property, Components components, boolean openapi31) { + if (property == null) + return; + if (property.get$ref() != null && !property.get$ref().endsWith(RESPONSE)) { + String key = property.get$ref().substring(Components.COMPONENTS_SCHEMAS_REF.length()); + if (entityInoMap.containsKey(key)) { + String newKey = property.get$ref() + RESPONSE; + if (!components.getSchemas().containsKey(key + RESPONSE)) { + createNewResponseSchema(key, components, openapi31); + updateResponseSchema(key, components.getSchemas().get(key + RESPONSE), components, openapi31); + } + property.set$ref(newKey); + } + } + else if (property.getItems() != null) { + updateResponseSchemaProperty(property.getItems(), components, openapi31); + } + } + /** * Update response schema embedded. * @@ -447,6 +485,25 @@ private List getAssociationsFields(ResourceMetadata return associationsFields; } + /** + * Gets all associations fields. + * + * @param resourceMetadata the resource metadata + * @param entity the entity + * @return all associations fields + */ + private List getAllAssociationsFields(ResourceMetadata + resourceMetadata, PersistentEntity entity) { + List associationsFields = new ArrayList<>(); + entity.doWithAssociations((SimpleAssociationHandler) association -> { + PersistentProperty property = association.getInverse(); + ResourceMapping mapping = resourceMetadata.getMappingFor(property); + String fieldName = mapping.getRel().value(); + associationsFields.add(fieldName); + }); + return associationsFields; + } + /** * Gets ignored fields. * diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Goal.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Goal.java new file mode 100644 index 000000000..5d657bbb8 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Goal.java @@ -0,0 +1,59 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import java.util.HashSet; +import java.util.Set; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; + +/** + * Aggregate root with a collection of composite-key join entities. + * + * @author hej090224 + */ +@Entity +public class Goal { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + @OneToMany(mappedBy = "goal", cascade = CascadeType.ALL) + private Set impactsByInitiatives = new HashSet<>(); + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Set getImpactsByInitiatives() { + return impactsByInitiatives; + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalInitiativeImpactId.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalInitiativeImpactId.java new file mode 100644 index 000000000..bb50b5b1d --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalInitiativeImpactId.java @@ -0,0 +1,52 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import java.io.Serializable; + +import jakarta.persistence.Embeddable; + +/** + * Composite identifier for the join entity used by the issue reproducer. + * + * @author hej090224 + */ +@Embeddable +public class GoalInitiativeImpactId implements Serializable { + + private Long goalId; + + private Long initiativeId; + + public Long getGoalId() { + return goalId; + } + + public void setGoalId(Long goalId) { + this.goalId = goalId; + } + + public Long getInitiativeId() { + return initiativeId; + } + + public void setInitiativeId(Long initiativeId) { + this.initiativeId = initiativeId; + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalRepository.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalRepository.java new file mode 100644 index 000000000..99b3d5375 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/GoalRepository.java @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +/** + * Spring Data REST repository for the issue reproducer aggregate. + * + * @author hej090224 + */ +@RepositoryRestResource +public interface GoalRepository extends CrudRepository { +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Initiative.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Initiative.java new file mode 100644 index 000000000..685c84663 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/Initiative.java @@ -0,0 +1,47 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +/** + * Entity referenced by the composite-key join entity. + * + * @author hej090224 + */ +@Entity +public class Initiative { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeImpactOnGoal.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeImpactOnGoal.java new file mode 100644 index 000000000..0d1c2ee11 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeImpactOnGoal.java @@ -0,0 +1,73 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapsId; + +/** + * Join entity combining an embedded identifier with two mapped associations. + * + * @author hej090224 + */ +@Entity +public class InitiativeImpactOnGoal { + + @EmbeddedId + private GoalInitiativeImpactId id; + + @ManyToOne + @MapsId("goalId") + @JoinColumn(name = "goal_id") + private Goal goal; + + @ManyToOne + @MapsId("initiativeId") + @JoinColumn(name = "initiative_id") + private Initiative initiative; + + @Enumerated(EnumType.STRING) + private ImpactLevel impactLevel; + + public GoalInitiativeImpactId getId() { + return id; + } + + public Goal getGoal() { + return goal; + } + + public Initiative getInitiative() { + return initiative; + } + + public ImpactLevel getImpactLevel() { + return impactLevel; + } + + enum ImpactLevel { + TRIVIAL, + SIGNIFICANT + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeRepository.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeRepository.java new file mode 100644 index 000000000..9768eb39a --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/InitiativeRepository.java @@ -0,0 +1,31 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import org.springframework.data.repository.CrudRepository; +import org.springframework.data.rest.core.annotation.RepositoryRestResource; + +/** + * Spring Data REST repository for initiatives. + * + * @author hej090224 + */ +@RepositoryRestResource +public interface InitiativeRepository extends CrudRepository { +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/SpringDocApp40Test.java b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/SpringDocApp40Test.java new file mode 100644 index 000000000..b8d89f0fd --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/v31/app40/SpringDocApp40Test.java @@ -0,0 +1,35 @@ +/* + * + * * Copyright 2019-2026 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.v31.app40; + +import test.org.springdoc.api.v31.AbstractSpringDocTest; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Verifies the response schemas for an embedded ID and mapped associations. + * + * @author hej090224 + */ +public class SpringDocApp40Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp { + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/resources/results/3.1.0/app40.json b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/resources/results/3.1.0/app40.json new file mode 100644 index 000000000..bd04fd3dd --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/resources/results/3.1.0/app40.json @@ -0,0 +1,809 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/goals": { + "get": { + "tags": [ + "goal-entity-controller" + ], + "description": "get-goal", + "operationId": "getCollectionResource-goal-get", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelGoal" + } + }, + "application/x-spring-data-compact+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelGoal" + } + }, + "text/uri-list": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "post": { + "tags": [ + "goal-entity-controller" + ], + "description": "create-goal", + "operationId": "postCollectionResource-goal-post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalRequestBody" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + } + } + } + }, + "/goals/{id}": { + "get": { + "tags": [ + "goal-entity-controller" + ], + "description": "get-goal", + "operationId": "getItemResource-goal-get", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "goal-entity-controller" + ], + "description": "update-goal", + "operationId": "putItemResource-goal-put", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "goal-entity-controller" + ], + "description": "delete-goal", + "operationId": "deleteItemResource-goal-delete", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found" + } + } + }, + "patch": { + "tags": [ + "goal-entity-controller" + ], + "description": "patch-goal", + "operationId": "patchItemResource-goal-patch", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GoalRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/initiatives": { + "get": { + "tags": [ + "initiative-entity-controller" + ], + "description": "get-initiative", + "operationId": "getCollectionResource-initiative-get", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelInitiative" + } + }, + "application/x-spring-data-compact+json": { + "schema": { + "$ref": "#/components/schemas/CollectionModelEntityModelInitiative" + } + }, + "text/uri-list": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "post": { + "tags": [ + "initiative-entity-controller" + ], + "description": "create-initiative", + "operationId": "postCollectionResource-initiative-post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitiativeRequestBody" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Created", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + } + } + } + }, + "/initiatives/{id}": { + "get": { + "tags": [ + "initiative-entity-controller" + ], + "description": "get-initiative", + "operationId": "getItemResource-initiative-get", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + }, + "404": { + "description": "Not Found" + } + } + }, + "put": { + "tags": [ + "initiative-entity-controller" + ], + "description": "update-initiative", + "operationId": "putItemResource-initiative-put", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitiativeRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + }, + "201": { + "description": "Created", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + }, + "204": { + "description": "No Content" + } + } + }, + "delete": { + "tags": [ + "initiative-entity-controller" + ], + "description": "delete-initiative", + "operationId": "deleteItemResource-initiative-delete", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "404": { + "description": "Not Found" + } + } + }, + "patch": { + "tags": [ + "initiative-entity-controller" + ], + "description": "patch-initiative", + "operationId": "patchItemResource-initiative-patch", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InitiativeRequestBody" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + }, + "204": { + "description": "No Content" + } + } + } + }, + "/profile": { + "get": { + "tags": [ + "profile-controller" + ], + "operationId": "listAllFormsOfMetadata", + "responses": { + "200": { + "description": "OK", + "content": { + "application/vnd.hal+json": { + "schema": { + "$ref": "#/components/schemas/RepresentationModelObject" + } + } + } + } + } + } + }, + "/profile/goals": { + "get": { + "tags": [ + "profile-controller" + ], + "operationId": "descriptor", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + }, + "application/alps+json": { + "schema": { + "type": "string" + } + }, + "application/schema+json": { + "schema": { + "$ref": "#/components/schemas/JsonSchema" + } + } + } + } + } + } + }, + "/profile/initiatives": { + "get": { + "tags": [ + "profile-controller" + ], + "operationId": "descriptor_1", + "responses": { + "200": { + "description": "OK", + "content": { + "*/*": { + "schema": { + "type": "string" + } + }, + "application/alps+json": { + "schema": { + "type": "string" + } + }, + "application/schema+json": { + "schema": { + "$ref": "#/components/schemas/JsonSchema" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "AbstractJsonSchemaPropertyObject": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "readOnly": { + "type": "boolean" + } + } + }, + "Item": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/AbstractJsonSchemaPropertyObject" + } + }, + "requiredProperties": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "JsonSchema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/AbstractJsonSchemaPropertyObject" + } + }, + "requiredProperties": { + "type": "array", + "items": { + "type": "string" + } + }, + "definitions": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Item" + } + }, + "type": { + "type": "string" + }, + "$schema": { + "type": "string" + } + } + }, + "RepresentationModelObject": { + "type": "object", + "properties": { + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "EntityModelInitiative": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "Initiative": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "CollectionModelEntityModelInitiative": { + "type": "object", + "properties": { + "_embedded": { + "type": "object", + "properties": { + "initiatives": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityModelInitiative" + } + } + } + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "EntityModelGoal": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "impactsByInitiatives": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InitiativeImpactOnGoalResponse" + }, + "uniqueItems": true + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "Goal": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "impactsByInitiatives": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InitiativeImpactOnGoalResponse" + }, + "uniqueItems": true + } + } + }, + "GoalInitiativeImpactId": { + "type": "object", + "properties": { + "goalId": { + "type": "integer", + "format": "int64" + }, + "initiativeId": { + "type": "integer", + "format": "int64" + } + } + }, + "InitiativeImpactOnGoal": { + "type": "object", + "properties": { + "id": { + "$ref": "#/components/schemas/GoalInitiativeImpactId" + }, + "goal": { + "$ref": "#/components/schemas/Goal" + }, + "initiative": { + "$ref": "#/components/schemas/Initiative" + }, + "impactLevel": { + "type": "string", + "enum": [ + "TRIVIAL", + "SIGNIFICANT" + ] + } + } + }, + "CollectionModelEntityModelGoal": { + "type": "object", + "properties": { + "_embedded": { + "type": "object", + "properties": { + "goals": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EntityModelGoal" + } + } + } + }, + "_links": { + "$ref": "#/components/schemas/Links" + } + } + }, + "GoalRequestBody": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "impactsByInitiatives": { + "type": "array", + "items": { + "$ref": "#/components/schemas/InitiativeImpactOnGoal" + }, + "uniqueItems": true + } + } + }, + "InitiativeRequestBody": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + } + }, + "InitiativeImpactOnGoalResponse": { + "type": "object", + "properties": { + "impactLevel": { + "type": "string", + "enum": [ + "TRIVIAL", + "SIGNIFICANT" + ] + } + } + }, + "Link": { + "type": "object", + "properties": { + "href": { + "type": "string" + }, + "hreflang": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string" + }, + "deprecation": { + "type": "string" + }, + "profile": { + "type": "string" + }, + "name": { + "type": "string" + }, + "templated": { + "type": "boolean" + } + } + }, + "Links": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/Link" + } + } + } + } +}