Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feature/marp 763 doc for swagger UI on mp ap is #72

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class RequestParamConstants {
public static final String LANGUAGE = "language";
public static final String USER_ID = "userId";
public static final String AUTHORIZATION = "Authorization";
public static final String X_AUTHORIZATION = "X-Authorization";
public static final String RESET_SYNC = "resetSync";
public static final String SHOW_DEV_VERSION = "isShowDevVersion";
public static final String DESIGNER_VERSION = "designerVersion";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
package com.axonivy.market.controller;

import static com.axonivy.market.constants.RequestMappingConstants.BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.FEEDBACK;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_RATING_BY_ID;
import static com.axonivy.market.constants.RequestParamConstants.AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.ID;
import static com.axonivy.market.constants.RequestParamConstants.USER_ID;

import java.net.URI;
import java.util.List;

import com.axonivy.market.assembler.FeedbackModelAssembler;
import com.axonivy.market.constants.CommonConstants;
import com.axonivy.market.entity.Feedback;
import com.axonivy.market.model.FeedbackModel;
import com.axonivy.market.model.FeedbackModelRequest;
import com.axonivy.market.model.ProductRating;
import com.axonivy.market.service.FeedbackService;
import com.axonivy.market.service.JwtService;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
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 jakarta.validation.Valid;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PagedResourcesAssembler;
import org.springframework.hateoas.PagedModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -28,20 +37,20 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import com.axonivy.market.assembler.FeedbackModelAssembler;
import com.axonivy.market.constants.CommonConstants;
import com.axonivy.market.entity.Feedback;
import com.axonivy.market.model.FeedbackModel;
import com.axonivy.market.model.ProductRating;
import com.axonivy.market.service.FeedbackService;
import com.axonivy.market.service.JwtService;
import java.net.URI;
import java.util.List;

import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import static com.axonivy.market.constants.RequestMappingConstants.BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.FEEDBACK;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_RATING_BY_ID;
import static com.axonivy.market.constants.RequestParamConstants.X_AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.ID;
import static com.axonivy.market.constants.RequestParamConstants.USER_ID;

@RestController
@RequestMapping(FEEDBACK)
@Tag(name = "User Feedback Controllers", description = "API collection to handle user's feedback.")
public class FeedbackController {

private final FeedbackService feedbackService;
Expand All @@ -50,18 +59,21 @@ public class FeedbackController {

private final PagedResourcesAssembler<Feedback> pagedResourcesAssembler;

public FeedbackController(FeedbackService feedbackService, JwtService jwtService,
FeedbackModelAssembler feedbackModelAssembler, PagedResourcesAssembler<Feedback> pagedResourcesAssembler) {
public FeedbackController(FeedbackService feedbackService, JwtService jwtService, FeedbackModelAssembler feedbackModelAssembler, PagedResourcesAssembler<Feedback> pagedResourcesAssembler) {
this.feedbackService = feedbackService;
this.jwtService = jwtService;
this.feedbackModelAssembler = feedbackModelAssembler;
this.pagedResourcesAssembler = pagedResourcesAssembler;
}

@Operation(summary = "Find all feedbacks by product id")
@GetMapping(PRODUCT_BY_ID)
public ResponseEntity<PagedModel<FeedbackModel>> findFeedbacks(@PathVariable(ID) String productId,
Pageable pageable) {
@Operation(summary = "Find feedbacks by product id with lazy loading", description = "Get all user feedback by product id (from meta.json) with lazy loading", parameters = {
@Parameter(name = "page", description = "Page number to retrieve", in = ParameterIn.QUERY, example = "0", required = true),
@Parameter(name = "size", description = "Number of items per page", in = ParameterIn.QUERY, example = "20", required = true),
@Parameter(name = "sort", description = "Sorting criteria in the format: Sorting criteria(popularity|alphabetically|recent), Sorting order(asc|desc)", in = ParameterIn.QUERY, example = "[\"popularity\",\"asc\"]", required = true)})
public ResponseEntity<PagedModel<FeedbackModel>> findFeedbacks(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "portal", in = ParameterIn.PATH) String productId,
@ParameterObject Pageable pageable) {
Page<Feedback> results = feedbackService.findFeedbacks(productId, pageable);
if (results.isEmpty()) {
return generateEmptyPagedModel();
Expand All @@ -72,25 +84,34 @@ public ResponseEntity<PagedModel<FeedbackModel>> findFeedbacks(@PathVariable(ID)
}

@GetMapping(BY_ID)
public ResponseEntity<FeedbackModel> findFeedback(@PathVariable(ID) String id) {
@Operation(summary = "Find all feedbacks by product id", description = "Get all feedbacks by product id(from meta.json) which is used in mobile viewport.")
public ResponseEntity<FeedbackModel> findFeedback(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "portal", in = ParameterIn.PATH) String id) {
Feedback feedback = feedbackService.findFeedback(id);
return ResponseEntity.ok(feedbackModelAssembler.toModel(feedback));
}

@Operation(summary = "Find all feedbacks by user id and product id")
@GetMapping()
public ResponseEntity<FeedbackModel> findFeedbackByUserIdAndProductId(@RequestParam(USER_ID) String userId,
@RequestParam("productId") String productId) {
@Operation(summary = "Find all feedbacks by user id and product id", description = "Get current user feedback on target product.")
public ResponseEntity<FeedbackModel> findFeedbackByUserIdAndProductId(
@RequestParam(USER_ID) @Parameter(description = "Id of current user from DB", example = "1234", in = ParameterIn.QUERY) String userId,
@RequestParam("productId") @Parameter(description = "Product id (from meta.json)", example = "portal", in = ParameterIn.QUERY) String productId) {
Feedback feedback = feedbackService.findFeedbackByUserIdAndProductId(userId, productId);
return ResponseEntity.ok(feedbackModelAssembler.toModel(feedback));
}

@PostMapping
public ResponseEntity<Void> createFeedback(@RequestBody @Valid FeedbackModel feedback,
@RequestHeader(value = AUTHORIZATION) String authorizationHeader) {
@Operation(summary = "Create user feedback", description = "Save user feedback of product with their token from Github account.")
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Example request body for feedback", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = FeedbackModelRequest.class)))
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Successfully created user feedback"),
@ApiResponse(responseCode = "401", description = "Unauthorized request")})
public ResponseEntity<Void> createFeedback(
@RequestBody @Valid FeedbackModelRequest feedbackRequest,
@RequestHeader(value = X_AUTHORIZATION) @Parameter(description = "JWT Bearer token", example = "Bearer 123456", in = ParameterIn.HEADER) String bearerToken) {
String token = null;
if (authorizationHeader != null && authorizationHeader.startsWith(CommonConstants.BEARER)) {
token = authorizationHeader.substring(CommonConstants.BEARER.length()).trim(); // Remove "Bearer " prefix
if (bearerToken != null && bearerToken.startsWith(CommonConstants.BEARER)) {
token = bearerToken.substring(CommonConstants.BEARER.length()).trim(); // Remove "Bearer " prefix
}

// Validate the token
Expand All @@ -99,18 +120,18 @@ public ResponseEntity<Void> createFeedback(@RequestBody @Valid FeedbackModel fee
}

Claims claims = jwtService.getClaimsFromToken(token);
feedback.setUserId(claims.getSubject());
Feedback newFeedback = feedbackService.upsertFeedback(feedback);
Feedback newFeedback = feedbackService.upsertFeedback(feedbackRequest, claims.getSubject());

URI location = ServletUriComponentsBuilder.fromCurrentRequest().path(BY_ID).buildAndExpand(newFeedback.getId())
.toUri();

return ResponseEntity.created(location).build();
}

@Operation(summary = "Find rating information of product by id")
@Operation(summary = "Find rating information of product by its id.", description = "Get overall rating of product by its id.")
@GetMapping(PRODUCT_RATING_BY_ID)
public ResponseEntity<List<ProductRating>> getProductRating(@PathVariable(ID) String productId) {
public ResponseEntity<List<ProductRating>> getProductRating(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "portal", in = ParameterIn.PATH) String productId) {
return ResponseEntity.ok(feedbackService.getProductRatingById(productId));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
import java.util.Collections;
import java.util.Map;

import io.swagger.v3.oas.annotations.Operation;
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 org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -39,11 +45,15 @@ public OAuth2Controller(GitHubService gitHubService, JwtService jwtService, GitH
}

@PostMapping(GIT_HUB_LOGIN)
@Operation(description = "Get rating authentication token")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully login to GitHub provider", content = @Content(mediaType = "application/json", schema = @Schema(implementation = Map.class))),
@ApiResponse(responseCode = "400", description = "Bad Request")})
@io.swagger.v3.oas.annotations.parameters.RequestBody(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(implementation = Oauth2AuthorizationCode.class)))
public ResponseEntity<Map<String, String>> gitHubLogin(@RequestBody Oauth2AuthorizationCode oauth2AuthorizationCode) {
String accessToken = EMPTY;
String accessToken;
try {
GitHubAccessTokenResponse tokenResponse = gitHubService.getAccessToken(oauth2AuthorizationCode.getCode(),
gitHubProperty);
GitHubAccessTokenResponse tokenResponse = gitHubService.getAccessToken(oauth2AuthorizationCode.getCode(), gitHubProperty);
accessToken = tokenResponse.getAccessToken();
} catch (Exception e) {
return new ResponseEntity<>(Map.of(e.getClass().getName(), e.getMessage()), HttpStatus.BAD_REQUEST);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
package com.axonivy.market.controller;

import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT;
import static com.axonivy.market.constants.RequestMappingConstants.SYNC;
import static com.axonivy.market.constants.RequestParamConstants.AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.KEYWORD;
import static com.axonivy.market.constants.RequestParamConstants.LANGUAGE;
import static com.axonivy.market.constants.RequestParamConstants.RESET_SYNC;
import static com.axonivy.market.constants.RequestParamConstants.TYPE;

import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import com.axonivy.market.assembler.ProductModelAssembler;
import com.axonivy.market.constants.CommonConstants;
import com.axonivy.market.constants.GitHubConstants;
Expand All @@ -13,6 +25,7 @@
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import org.apache.commons.lang3.time.StopWatch;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
Expand All @@ -30,16 +43,10 @@
import org.springframework.web.bind.annotation.RestController;

import static com.axonivy.market.constants.RequestMappingConstants.CUSTOM_SORT;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT;
import static com.axonivy.market.constants.RequestMappingConstants.SYNC;
import static com.axonivy.market.constants.RequestParamConstants.AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.KEYWORD;
import static com.axonivy.market.constants.RequestParamConstants.LANGUAGE;
import static com.axonivy.market.constants.RequestParamConstants.RESET_SYNC;
import static com.axonivy.market.constants.RequestParamConstants.TYPE;

@RestController
@RequestMapping(PRODUCT)
@Tag(name = "Product Controller", description = "API collection to get and search products")
public class ProductController {

private final ProductService productService;
Expand All @@ -48,18 +55,24 @@ public class ProductController {
private final PagedResourcesAssembler<Product> pagedResourcesAssembler;

public ProductController(ProductService productService, GitHubService gitHubService, ProductModelAssembler assembler,
PagedResourcesAssembler<Product> pagedResourcesAssembler) {
PagedResourcesAssembler<Product> pagedResourcesAssembler) {
this.productService = productService;
this.gitHubService = gitHubService;
this.assembler = assembler;
this.pagedResourcesAssembler = pagedResourcesAssembler;
}

@Operation(summary = "Find all products", description = "Be default system will finds product by type as 'all'")
@GetMapping()
public ResponseEntity<PagedModel<ProductModel>> findProducts(@RequestParam(name = TYPE) String type,
@RequestParam(required = false, name = KEYWORD) String keyword, @RequestParam(name = LANGUAGE) String language,
Pageable pageable) {
@Operation(summary = "Retrieve a paginated list of all products, optionally filtered by type, keyword, and language", description = "By default, the system finds products with type 'all'", parameters = {
@Parameter(name = "page", description = "Page number to retrieve", in = ParameterIn.QUERY, example = "0", required = true),
@Parameter(name = "size", description = "Number of items per page", in = ParameterIn.QUERY, example = "20", required = true),
@Parameter(name = "sort", description = "Sorting criteria in the format: Sorting criteria(popularity|alphabetically|recent), Sorting order(asc|desc)",
in = ParameterIn.QUERY, example = "[\"popularity\",\"asc\"]", required = true)})
public ResponseEntity<PagedModel<ProductModel>> findProducts(
@RequestParam(name = TYPE) @Parameter(description = "Type of product.", in = ParameterIn.QUERY, schema = @Schema(type = "string", allowableValues = {"all", "connectors", "utilities", "solutions", "demos"})) String type,
@RequestParam(required = false, name = KEYWORD) @Parameter(description = "Keyword that exist in product's name or short description", example = "connector", in = ParameterIn.QUERY) String keyword,
@RequestParam(name = LANGUAGE) @Parameter(description = "Language of product short description", in = ParameterIn.QUERY, schema = @Schema(allowableValues = {"en", "de"})) String language,
@ParameterObject Pageable pageable) {
Page<Product> results = productService.findProducts(type, keyword, language, pageable);
if (results.isEmpty()) {
return generateEmptyPagedModel();
Expand All @@ -70,7 +83,9 @@ public ResponseEntity<PagedModel<ProductModel>> findProducts(@RequestParam(name
}

@PutMapping(SYNC)
public ResponseEntity<Message> syncProducts(@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@Operation(hidden = true)
public ResponseEntity<Message> syncProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestParam(value = RESET_SYNC, required = false) Boolean resetSync) {
String token = getBearerToken(authorizationHeader);
gitHubService.validateUserOrganization(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME);
Expand All @@ -94,6 +109,7 @@ public ResponseEntity<Message> syncProducts(@RequestHeader(value = AUTHORIZATION
}

@PostMapping(CUSTOM_SORT)
@Operation(hidden = true)
public ResponseEntity<Message> createCustomSortProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestBody @Valid ProductCustomSortRequest productCustomSortRequest) {
Expand All @@ -108,7 +124,7 @@ public ResponseEntity<Message> createCustomSortProducts(
@SuppressWarnings("unchecked")
private ResponseEntity<PagedModel<ProductModel>> generateEmptyPagedModel() {
var emptyPagedModel = (PagedModel<ProductModel>) pagedResourcesAssembler.toEmptyModel(Page.empty(),
ProductModel.class);
ProductModel.class);
return new ResponseEntity<>(emptyPagedModel, HttpStatus.OK);
}

Expand Down
Loading
Loading