Skip to content

Commit

Permalink
🐛 (#198): Add example, remove operationID
Browse files Browse the repository at this point in the history
If the operationID is set and it collides, then the OpenAPI gets broken. That resulted in #198.
  • Loading branch information
d135-1r43 committed Dec 23, 2024
1 parent dd70173 commit a3b5e8f
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
import io.quarkus.panache.common.Page;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.*;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.BeanParam;
import jakarta.ws.rs.DefaultValue;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.PATCH;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
Expand All @@ -33,7 +42,7 @@ public TransactionRecordResource(TransactionRecordRepository repository, @RestCl

@GET
@Path("/all")
@Operation(summary = "Get all transaction records", operationId = "getAllTransactionRecords", description = "Fetches all transaction records with optional filters for bommel association")
@Operation(summary = "Get all transaction records", description = "Fetches all transaction records with optional filters for bommel association")
@APIResponse(responseCode = "200", description = "List of transaction records, empty list if none are available", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TransactionRecord[].class)))
public List<TransactionRecord> getAll(@BeanParam AllParameters parameters) {
parameters.verifyOnlyOneIsActive();
Expand All @@ -54,7 +63,7 @@ public List<TransactionRecord> getAll(@BeanParam AllParameters parameters) {
@PATCH
@Transactional
@Path("{id}/bommel")
@Operation(summary = "Add a transaction record to a bommel", operationId = "updateBommel", description = "Attaches an transaction record to a bommel item")
@Operation(summary = "Add a transaction record to a bommel", description = "Attaches an transaction record to a bommel item")
@APIResponse(responseCode = "200", description = "Specified transaction record was attached to bommel")
@APIResponse(responseCode = "404", description = "Specified transaction record id was not found", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "400", description = "Bommel was not found", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand Down
11 changes: 11 additions & 0 deletions backend/app.hopps.org/src/main/java/app/hopps/org/jpa/Address.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package app.hopps.org.jpa;

import jakarta.persistence.Embeddable;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

@Embeddable
@Schema(name = "Organization", description = "An example of a valid address")
public class Address {

@Schema(examples = "Raketenstraße")
private String street;

@Schema(examples = "42a")
private String number;

@Schema(examples = "Raketenstadt")
private String city;

@Schema(examples = "4242")
private String plz;

@Schema(examples = "Hinterhaus")
private String additionalLine;

public Address() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,31 @@
import jakarta.persistence.ManyToMany;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

import java.util.ArrayList;
import java.util.Collection;

@Entity
@Schema(name = "Member", description = "An example of a Hopps Member")
public class Member extends PanacheEntity {

// TODO: Add OneToMany to Bommel here and test it

@NotBlank
@Schema(examples = "Kim")
private String firstName;

@NotBlank
@Schema(examples = "Rakete")
private String lastName;

@NotBlank
@Email
@Schema(examples = "[email protected]")
private String email;

@ManyToMany
@JoinTable(name = "member_verein", joinColumns = @JoinColumn(name = "member_id"))
@Schema(examples = "[]")
private Collection<Organization> organizations = new ArrayList<>();

@JsonIgnore
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,37 +6,48 @@
import jakarta.persistence.Entity;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.SequenceGenerator;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

import java.net.URL;
import java.util.HashSet;
import java.util.Set;

@Entity
@Schema(name = "Organization", description = "An example of a Hopps Organization, i.e. Verein")
public class Organization extends PanacheEntity {

@NotBlank
@Schema(examples = "Raketenfreunde e.V.")
private String name;

@NotBlank
@Pattern(regexp = "^[a-z0-9][a-z0-9-]*[a-z0-9]$")
@Schema(examples = "raketen-freunde")
private String slug;

@NotNull
@Schema(examples = "EINGETRAGENER_VEREIN")
private TYPE type;

@Embedded
private Address address;

@OneToOne(cascade = { CascadeType.DETACH, CascadeType.REFRESH, CascadeType.PERSIST, CascadeType.MERGE })
@Schema(examples = "null")
private Bommel rootBommel;

@ManyToMany(mappedBy = "organizations", cascade = CascadeType.PERSIST)
@Schema(examples = "[]")
private Set<Member> members = new HashSet<>();

@Schema(examples = "https://raketenfreunde.tld")
private URL website;

@Schema(examples = "https://example.com/avatar.png")
private URL profilePicture;

public Organization() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public BommelResource(

@GET
@Path("/{id}/children")
@Operation(summary = "Fetch the children of bommel", operationId = "getChildrenOfBommel")
@Operation(summary = "Fetch the children of bommel")
@APIResponse(responseCode = "200", description = "Children of bommel", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel[].class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -70,7 +70,7 @@ public Set<Bommel> getBommelChildren(@PathParam("id") long id) {

@GET
@Path("/{id}/children/recursive")
@Operation(summary = "Fetch the children of bommel recursively", operationId = "getChildrenOfBommelRecursive")
@Operation(summary = "Fetch the children of bommel recursively")
@APIResponse(responseCode = "200", description = "Recursive children of bommel", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TreeSearchBommel[].class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -83,7 +83,7 @@ public List<TreeSearchBommel> getBommelChildrenRecursive(@PathParam("id") long i

@GET
@Path("/{id}")
@Operation(summary = "Fetch a bommel by its id", operationId = "getBommel")
@Operation(summary = "Fetch a bommel by its id")
@APIResponse(responseCode = "200", description = "Bommel found", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -97,7 +97,7 @@ public Bommel getBommel(@PathParam("id") long id) {

@GET
@Path("/root/{orgId}")
@Operation(summary = "Fetch root-bommel for organization", operationId = "getRootBommel")
@Operation(summary = "Fetch root-bommel for organization")
@APIResponse(responseCode = "200", description = "Root-Bommel found", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -115,7 +115,7 @@ public Bommel getRootBommel(@PathParam("orgId") long orgId) {
@POST
@Path("/root")
@Transactional
@Operation(summary = "Create root-bommel for organization", operationId = "createRootBommel")
@Operation(summary = "Create root-bommel for organization")
@APIResponse(responseCode = "201", description = "Bommel created and added to the organization as root", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "400", description = """
<li> Organization not found
Expand Down Expand Up @@ -151,7 +151,7 @@ public Response createRoot(BommelInput input) {
@POST
@Path("/")
@Transactional
@Operation(summary = "Create bommel", operationId = "createBommel", description = "Create bommel underneath a root-bommel, without the parent-bommel it will fail.")
@Operation(summary = "Create bommel", description = "Create bommel underneath a root-bommel, without the parent-bommel it will fail.")
@APIResponse(responseCode = "201", description = "Bommel successfully created", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "400", description = "Bommel has no parent, cannot create root-bommel", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -173,7 +173,7 @@ public Response createBommel(Bommel bommel) {
@PUT
@Path("/{id}")
@Transactional
@Operation(summary = "Update bommel", operationId = "updateBommel")
@Operation(summary = "Update bommel")
@APIResponse(responseCode = "200", description = "Bommel successfully updated", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand All @@ -195,7 +195,7 @@ public Bommel updateBommel(Bommel bommel, @PathParam("id") long id) {
@PUT
@Path("/move/{id}/to/{newParentId}")
@Transactional
@Operation(summary = "Move a bommel underneath a different parent", operationId = "moveBommel")
@Operation(summary = "Move a bommel underneath a different parent")
@APIResponse(responseCode = "200", description = "Bommel successfully moved", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Bommel.class)))
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized to move bommel", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand Down Expand Up @@ -223,7 +223,7 @@ public Bommel moveBommel(@PathParam("id") long id, @PathParam("newParentId") lon
@DELETE
@Path("/{id}")
@Transactional
@Operation(summary = "Delete bommel", operationId = "deleteBommel")
@Operation(summary = "Delete bommel")
@APIResponse(responseCode = "204", description = "Bommel successfully deleted")
@APIResponse(responseCode = "401", description = "User not logged in", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "403", description = "User not authorized", content = @Content(mediaType = MediaType.TEXT_PLAIN))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;
import jakarta.validation.Validator;
import jakarta.ws.rs.*;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
Expand All @@ -29,10 +33,10 @@ public MemberResource(Validator validator) {
}

@POST
@Path(("/validate"))
@Path(("validate"))
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Validates the member input", operationId = "validate")
@Operation(summary = "Validates the member input")
@APIResponse(responseCode = "200", description = "Validation successful", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ValidationResult.class)))
@APIResponse(responseCode = "400", description = "Validation failed", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ValidationResult.class)))
public ValidationResult validate(Member member) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.Validator;
import jakarta.ws.rs.*;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.eclipse.microprofile.openapi.annotations.Operation;
Expand Down Expand Up @@ -43,9 +49,9 @@ public OrganizationResource(Validator validator, @Named("NewOrganization") Proce
}

@GET
@Path("/{slug}")
@Path("{slug}")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Get organization", operationId = "getOrganizationBySlug", description = "Retrieves the details of an organization using the unique slug identifier.")
@Operation(summary = "Get organization", description = "Retrieves the details of an organization using the unique slug identifier.")
@APIResponse(responseCode = "200", description = "Organization retrieved successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Organization.class)))
@APIResponse(responseCode = "404", description = "Organization not found for provided slug")
public Response getOrganizationBySlug(@PathParam("slug") String slug) {
Expand All @@ -58,9 +64,9 @@ public Response getOrganizationBySlug(@PathParam("slug") String slug) {
}

@GET
@Path("/{slug}/members")
@Path("{slug}/members")
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Get organization members", operationId = "getOrganizationMembers", description = "Retrieves the members of an organization using the unique slug identifier.")
@Operation(summary = "Get organization members", description = "Retrieves the members of an organization using the unique slug identifier.")
@APIResponse(responseCode = "200", description = "Members retrieved successfully", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Member[].class)))
@APIResponse(responseCode = "404", description = "Organization not found for provided slug")
public Response getOrganizationMembersBySlug(@PathParam("slug") String slug) {
Expand All @@ -73,7 +79,7 @@ public Response getOrganizationMembersBySlug(@PathParam("slug") String slug) {

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Operation(summary = "Create a new organization", operationId = "createNewOrganization")
@Operation(summary = "Create a new organization")
@APIResponse(responseCode = "201", description = "Creation started successfully", content = @Content(mediaType = MediaType.TEXT_PLAIN))
@APIResponse(responseCode = "400", description = "Validation of fields failed", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ValidationResult.class)))
public Response create(NewOrganizationInput input) {
Expand All @@ -89,10 +95,10 @@ public Response create(NewOrganizationInput input) {
}

@POST
@Path(("/validate"))
@Path(("validate"))
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "Validates the organization input", operationId = "validate")
@Operation(summary = "Validates the organization input")
@APIResponse(responseCode = "200", description = "Validation successful", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ValidationResult.class)))
@APIResponse(responseCode = "400", description = "Validation failed", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = ValidationResult.class)))
public ValidationResult validate(Organization organization) {
Expand Down

0 comments on commit a3b5e8f

Please sign in to comment.