Skip to content

Tickets #43

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

Merged
merged 29 commits into from
Feb 15, 2024
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
132 changes: 132 additions & 0 deletions src/main/java/git/tracehub/pmo/controller/AdviceController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2023-2024 Tracehub.git
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to read
* the Software only. Permissions is hereby NOT GRANTED to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package git.tracehub.pmo.controller;

import git.tracehub.pmo.exception.ResourceNotFoundException;
import io.github.eocqrs.eokson.Jocument;
import io.github.eocqrs.eokson.MutableJson;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
* Exception handler.
*
* @checkstyle NonStaticMethodCheck (90 lines)
* @since 0.0.0
*/
@Slf4j
@RestControllerAdvice
public class AdviceController {

/**
* Handle MethodArgumentNotValidException.
*
* @param exception Exception
* @return ResponseEntity
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ResponseEntity<byte[]> handle(
final MethodArgumentNotValidException exception
) {
log.warn(exception.getMessage(), exception);
return new ResponseEntity<>(
new Jocument(
new MutableJson()
.with(
"message",
exception.getBindingResult().getFieldErrors()
.stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining(", "))
)
).byteArray(),
HttpStatus.BAD_REQUEST
);
}

/**
* Handle ResourceNotFoundException.
*
* @param exception Exception
* @return ResponseEntity
*/
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseEntity<byte[]> handle(
final ResourceNotFoundException exception
) {
log.warn(exception.getMessage(), exception);
return new ResponseEntity<>(
new Jocument(
new MutableJson()
.with("message", exception.getMessage())
).byteArray(),
HttpStatus.NOT_FOUND
);
}

/**
* Handle AccessDeniedException.
*
* @param exception Exception
* @return ResponseEntity
*/
@ExceptionHandler(AccessDeniedException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public ResponseEntity<byte[]> handle(
final AccessDeniedException exception
) {
log.warn(exception.getMessage(), exception);
return new ResponseEntity<>(
new Jocument(
new MutableJson()
.with("message", exception.getMessage())
).byteArray(),
HttpStatus.FORBIDDEN
);
}

/**
* Handle Exception.
*
* @param exception Exception
* @return ResponseEntity
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseEntity<byte[]> handle(final Exception exception) {
log.warn(exception.getMessage(), exception);
return new ResponseEntity<>(
new Jocument(
new MutableJson()
.with("message", exception.getMessage())
).byteArray(),
HttpStatus.INTERNAL_SERVER_ERROR
);
}

}
74 changes: 24 additions & 50 deletions src/main/java/git/tracehub/pmo/controller/ProjectController.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,20 @@

package git.tracehub.pmo.controller;

import com.jcabi.github.Coordinates;
import com.jcabi.github.Repo;
import com.jcabi.github.RtGithub;
import git.tracehub.pmo.platforms.Label;
import git.tracehub.pmo.controller.request.ProjectFromReq;
import git.tracehub.pmo.controller.request.RqProject;
import git.tracehub.pmo.platforms.Platform;
import git.tracehub.pmo.platforms.RepoPath;
import git.tracehub.pmo.platforms.github.CreateLabels;
import git.tracehub.pmo.platforms.github.InviteCollaborator;
import git.tracehub.pmo.platforms.github.webhook.CreateWebhook;
import git.tracehub.pmo.project.Project;
import git.tracehub.pmo.project.Projects;
import git.tracehub.pmo.security.ClaimOf;
import git.tracehub.pmo.security.ExistsRole;
import git.tracehub.pmo.security.IdProvider;
import git.tracehub.pmo.security.IdpToken;
import java.awt.Color;
import jakarta.validation.Valid;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.cactoos.list.ListOf;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
Expand All @@ -44,6 +40,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

Expand All @@ -54,6 +51,7 @@
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/projects")
public class ProjectController {

/**
Expand All @@ -62,16 +60,15 @@ public class ProjectController {
private final Projects projects;

/**
* Issuer url.
* Platforms.
*/
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String url;
private final Map<String, Platform> platforms;

/**
* Github host.
* Issuer url.
*/
@Value("${platforms.github}")
private String host;
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String url;

/**
* Projects by user.
Expand Down Expand Up @@ -106,44 +103,21 @@ public Project byId(@PathVariable final UUID id) {
* @return Project
* @checkstyle MethodBodyCommentsCheck (20 lines)
*/
@PostMapping()
@PreAuthorize("hasAuthority('user_github')")
@PostMapping
@PreAuthorize("hasAuthority('user_github') || hasAuthority('user_gitlab')")
@ResponseStatus(HttpStatus.CREATED)
public Project employ(
@RequestBody final Project project,
@RequestBody @Valid final RqProject project,
@AuthenticationPrincipal final Jwt jwt
) {
final Project created = this.projects.employ(project);
/*
* @todo #1:45min/DEV define appropriate agent according to location
* of the project. We need to define appropriate agent and call
* corresponding implementation to invite collaborators here.
*/
if (new ExistsRole(jwt, "user_github").value()) {
final String location = new RepoPath(created.getLocation()).value();
final String token = new IdpToken(jwt, "github", this.url).value();
final Repo repo = new RtGithub(token).repos()
.get(
new Coordinates.Simple(location)
);
new InviteCollaborator(
repo,
"tracehubgit"
).exec();
new CreateLabels(
repo,
new ListOf<>(
new Label("new", Color.PINK)
)
).exec();
new CreateWebhook(
this.host,
token,
location,
"url",
new ListOf<>("push", "issues")
).exec();
}
final Project created = this.projects.employ(
new ProjectFromReq(project)
);
final String provider = new IdProvider(jwt).value();
this.platforms.get(provider).prepare(
new IdpToken(jwt, provider, this.url),
new RepoPath(created.getLocation())
);
return created;
}

Expand Down
94 changes: 94 additions & 0 deletions src/main/java/git/tracehub/pmo/controller/TicketController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2023-2024 Tracehub.git
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to read
* the Software only. Permissions is hereby NOT GRANTED to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package git.tracehub.pmo.controller;

import git.tracehub.pmo.controller.request.RqTicket;
import git.tracehub.pmo.controller.request.TicketFromReq;
import git.tracehub.pmo.ticket.Ticket;
import git.tracehub.pmo.ticket.Tickets;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

/**
* Ticket Controller.
*
* @since 0.0.0
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/tickets")
public class TicketController {

/**
* Tickets.
*/
private final Tickets tickets;

/**
* Ticket by path to job and repository.
*
* @param job Path to job
* @param repo Repository name
* @return Ticket
*/
@GetMapping("/job")
public Ticket byJob(
@RequestParam final String job,
@RequestParam final String repo
) {
return this.tickets.byJob(job, repo);
}

/**
* Ticket by issue number and repository.
*
* @param number Issue number
* @param repo Repository name
* @return Ticket
*/
@GetMapping("/number")
public Ticket byNumber(
@RequestParam final Integer number,
@RequestParam final String repo
) {
return this.tickets.byNumber(number, repo);
}

/**
* Create ticket.
*
* @param ticket Ticket
* @return Ticket
*/
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Ticket create(@RequestBody @Valid final RqTicket ticket) {
return this.tickets.create(
new TicketFromReq(ticket)
);
}

}
Loading