Commit 8d0f6477 authored by João Lino's avatar João Lino

Defining a REST API for basic Profile operations.

parent f5048ed7
Pipeline #6 failed with stages
...@@ -13,7 +13,6 @@ import java.util.UUID; ...@@ -13,7 +13,6 @@ import java.util.UUID;
@Data @Data
@SuperBuilder @SuperBuilder
@NoArgsConstructor @NoArgsConstructor
// @AllArgsConstructor
public class AbstractIdentification extends AbstractAudit { public class AbstractIdentification extends AbstractAudit {
/* A unique identifier of this model entity. */ /* A unique identifier of this model entity. */
......
...@@ -11,10 +11,12 @@ import org.springframework.data.mongodb.core.mapping.Document; ...@@ -11,10 +11,12 @@ import org.springframework.data.mongodb.core.mapping.Document;
@Data @Data
@SuperBuilder @SuperBuilder
@AllArgsConstructor @AllArgsConstructor
// @NoArgsConstructor
@Document @Document
public class Profile extends AbstractIdentification { public class Profile extends AbstractIdentification {
/** The full name of the person. */ /** The full name of the person. */
private final String fullName = null; private final String fullName = null;
/** Activation status of this profile. */
private final boolean isActive = true;
} }
package com.joaolino.demo.spring.microservice.profile.helper.validation;
import com.joaolino.demo.spring.microservice.profile.helper.validation.annotation.ValidUUIDv4;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** A UUID v4 constraint validator. */
public class UUIDv4 implements ConstraintValidator<ValidUUIDv4, UUID> {
/**
* Validates if a given UUID is of the version 4 and has the correct format of a UUID
*
* @param obj Represents the UUID of a given
* @param context {@link ConstraintValidator}
* @return Returns a boolean value that represents if the UUID is version is valid
*/
@Override
public boolean isValid(final UUID obj, final ConstraintValidatorContext context) {
final Pattern pattern =
Pattern.compile("^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$");
final Matcher matcher = pattern.matcher(obj.toString().toUpperCase());
return matcher.matches();
}
}
package com.joaolino.demo.spring.microservice.profile.helper.validation.annotation;
import com.joaolino.demo.spring.microservice.profile.helper.validation.UUIDv4;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/** Interface that will represent the validation of a version 4 UUID. */
@Constraint(validatedBy = UUIDv4.class)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidUUIDv4 {
/**
* Should default to an error message key, made of the fully-qualified class name of the
* constraint, followed by .message. For example "{com.acme.constraints.NotSafe.message}".
*/
String message() default "Invalid UUIDv4";
/** for user to customize the targeted groups */
Class<?>[] groups() default {};
/**
* For extensibility purposes. Payload type that can be attached to a given constraint
* declaration. Can be used to carry on metadata information consumed by a validation client.
*/
Class<? extends Payload>[] payload() default {};
}
package com.joaolino.demo.spring.microservice.profile.web.api;
import com.joaolino.demo.spring.microservice.profile.web.model.ProfileRead;
import com.joaolino.demo.spring.microservice.profile.web.model.ProfileWrite;
import com.joaolino.demo.spring.microservice.profile.web.model.error.ErrorDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.UUID;
/** Profile Microservice's REST API. */
@Tag(name = "profile", description = "Endpoints exposed by the Profile microservice REST API.")
public interface ProfileAPI {
/**
* Add a new profile to the store.
*
* @param profileWrite String with profile payload.
* @return A stream containing the profile added to the store.
*/
@RequestMapping(
method = RequestMethod.POST,
path = "/profiles",
consumes = {"application/json", "application/xml"},
produces = {"application/json", "application/xml"})
@Operation(summary = "Add a new profile to the store.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "The profile was added.",
content = @Content(schema = @Schema(implementation = ProfileRead.class))),
@ApiResponse(
responseCode = "400",
description = "The profile was not added.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "403",
description = "You are not allowed to preform this action, the profile was not added.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "422",
description = "Received invalid data, the profile was not added.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "500",
description = "Processing failure, the profile was not added.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class)))
})
ResponseEntity<Mono<ProfileRead>> addProfile(
@Parameter(
description =
"The object that contains the necessary information to add a new profile.",
name = "profileWrite",
required = true)
@RequestBody
ProfileWrite profileWrite);
/**
* Find profiles identified by the received profile ID.
*
* @param profileId The ID of the profile to be retrieved.
* @return The profile with the ID provided.
*/
@RequestMapping(
method = RequestMethod.GET,
path = "/profiles/{profileId}",
produces = {"application/json", "application/xml"})
@Operation(summary = "Find a profile identified by received provided ID.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successful operation.",
content = @Content(schema = @Schema(implementation = ProfileRead.class))),
@ApiResponse(
responseCode = "400",
description = "Invalid ID supplied.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "404",
description = "Profile not found.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "500",
description = "Processing failure.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class)))
})
ResponseEntity<Mono<ProfileRead>> getProfile(
@Parameter(
description = "The profile's unique identifier.",
name = "profileId",
example = "d4f1c83f-3fa7-413b-8d37-8fd5404b382f",
required = true)
@PathVariable(value = "profileId", required = false)
UUID profileId);
/**
* Find profiles based on the received request parameters.
*
* @param fullName the Full Name in the profile.
* @param isActive the activation status of the profile.
* @return A stream of matching profiles.
*/
@RequestMapping(
method = RequestMethod.GET,
path = "/profiles",
produces = {"application/json", "application/xml"})
@Operation(
summary =
"Find profiles matching the provided Profile properties, e.g. Full Name and activation status.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successful operation.",
content = @Content(schema = @Schema(implementation = ProfileRead.class))),
@ApiResponse(
responseCode = "400",
description = "Invalid ID supplied.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "404",
description = "Profile not found.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "500",
description = "Processing failure.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class)))
})
ResponseEntity<Flux<ProfileRead>> getProfiles(
@Parameter(description = "A full name of the person's profile.", name = "fullName")
@RequestParam(value = "fullName", required = false)
String fullName,
@Parameter(
description = "The profile activation status.",
name = "isActive",
example = "false")
@RequestParam(value = "isActive", required = false)
Boolean isActive);
/**
* Deactivate a profile.
*
* @param profileId ID of the profile to be deactivated.
* @return The deactivated profile.
*/
@RequestMapping(method = RequestMethod.PUT, path = "/profiles/{profileId}/deactivate")
@Operation(summary = "Deactivate a profile.")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "Successful operation.",
content = @Content(schema = @Schema(implementation = ProfileRead.class))),
@ApiResponse(
responseCode = "400",
description = "Invalid ID supplied.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "404",
description = "Profile not found.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class))),
@ApiResponse(
responseCode = "500",
description = "Processing failure.",
content = @Content(schema = @Schema(implementation = ErrorDetails.class)))
})
ResponseEntity<Mono<ProfileRead>> deactivateProfile(
@Parameter(
description = "The profile's unique identifier.",
name = "profileId",
example = "d4f1c83f-3fa7-413b-8d37-8fd5404b382f",
required = true)
@PathVariable("profileId")
UUID profileId);
}
package com.joaolino.demo.spring.microservice.profile.web.model;
import com.joaolino.demo.spring.microservice.profile.helper.validation.annotation.ValidUUIDv4;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.util.UUID;
/**
* Represents a Profile Read object. This is only returned by the REST interface, in the context of
* communication with other services/platforms, and is not intended to be used in any internal
* operations. It contains the profile ID, a full name and the profile's activation state.
*/
@Data
@AllArgsConstructor
@Schema(description = "The Profile Read object, used when returning a Profiles in REST endpoints.")
@Builder
public class ProfileRead {
/** The identifier used to uniquely identify a profile. */
@Schema(required = true, description = "The identifier used to uniquely identify a profile.")
@NotNull
@ValidUUIDv4
private UUID id;
/** The full name of the person this profile belongs to. */
@Schema(required = true, description = "The full name of the person this profile belongs to.")
@NotNull
private String fullName;
/** Flag indicating the activation status of the profile. */
@Schema(required = true, description = "Flag indicating the activation status of the profile.")
@NotNull
private Boolean isActive;
}
package com.joaolino.demo.spring.microservice.profile.web.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* Represents a Profile Write object. This is only accepted by the REST interface, in the context of
* communication with other services/platforms, and is not intended to be used in any internal
* operations. It contains the full name of the person the profile belongs to.
*/
@Data
@AllArgsConstructor
@Schema(description = "The Profile Write object, used when receiving a Profiles in REST endpoints.")
@Builder
public class ProfileWrite {
/** The full name of the person this profile belongs to. */
@Schema(required = true, description = "The full name of the person this profile belongs to.")
@NotNull
private String fullName;
}
package com.joaolino.demo.spring.microservice.profile.web.model.error;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import org.springframework.http.HttpStatus;
import java.net.URI;
import java.util.List;
import java.util.UUID;
/**
* The common error object, used to standardize error communication between systems. Adheres to the
* RFC7807 specification.
*/
@Data
@Builder
@Schema(
description =
"The common error object, used to standardize error communication between systems. Adheres to the RFC7807 specification.")
public class ErrorDetails {
// Standard Members
/**
* The primary identifier for the problem. A URI reference that illustrates the general type of
* problem. It's encouraged that it provides human readable documentation for the problem. If
* absent, it's assumed to be represent a blank value.
*/
@Schema(
description =
"The primary identifier for the problem. A URI reference that illustrates the general type of problem. It's encouraged that it provides human readable documentation for the problem. If absent, it's assumed to be represent a blank value.")
private URI type;
/** A brief description of the error. */
@Schema(description = "A brief description of the error.")
private String title;
/**
* This member represents the HTTP status code returned by the application. Must always reflect
* what is being sent in the HTTP headers. It exists only in an advisory capacity and should not
* impact the error interpretation, it should only inform the user if presented.
*/
@Schema(
description =
"This member represents the HTTP status code returned by the application. Must always reflect what is being sent in the HTTP headers. It exists only in an advisory capacity and should not impact the error interpretation, it should only inform the user if presented.")
private HttpStatus status;
/**
* A detailed explanation of the error. Provided to help the error consumer correct the problem.
*/
@Schema(
description =
"A detailed explanation of the error. Provided to help the error consumer correct the problem.")
private String detail;
/**
* A URI that references the specific occurrence of the problem. Tends to represent the URI of the
* API call.
*/
@Schema(
description =
"A URI that references the specific occurrence of the problem. Tends to represent the URI of the API call.")
private URI instance;
// Extensions
/**
* For node tracing purposes. Should identify the information processing flow that was executing
* at the time this error occurred.
*/
@Schema(
description =
"For node tracing purposes. Should identify the information processing flow that was executing at the time this error occurred.",
required = true)
private String processingFlowId;
/**
* For user tracing purposes. Should identify the consumer session triggering the action that
* eventually resulted in this error.
*/
@Schema(
description =
"For user tracing purposes. Should identify the consumer session triggering the action that eventually resulted in this error.",
required = true)
private String consumerSessionId;
/**
* Used when handling validation errors. Can contain multiple invalid param reports for a single
* call.
*/
@Schema(
description =
"Used when handling validation errors. Can contain multiple invalid param reports for a single call.")
private List<InvalidParameterDetail> invalidParams;
/** The ID of the entity that was accessed / requested. */
@Schema(description = "The ID of the entity that was accessed / requested.")
private UUID entityId;
/** The name of the entity that was accessed / requested. */
@Schema(description = "The name of the entity that was accessed / requested.")
private String entityName;
}
package com.joaolino.demo.spring.microservice.profile.web.model.error;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/** Used when handling validation errors. Details what parameter is invalid and the reason why. */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class InvalidParameterDetail {
/** The name of the parameter that is invalid. */
private String name;
/** The reason why the parameter was asserted as invalid. */
private String reason;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment