Skip to content

Commit

Permalink
Merge branch 'develop' into feature/recover-password
Browse files Browse the repository at this point in the history
  • Loading branch information
jamcunha committed Aug 30, 2023
2 parents 9a9fa6c + 4d457a4 commit acac4b3
Show file tree
Hide file tree
Showing 56 changed files with 3,377 additions and 694 deletions.
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# Website NIAEFEUP - BackEnd
[![codecov](https://codecov.io/gh/NIAEFEUP/website-niaefeup-backend/branch/develop/graph/badge.svg?token=4OPGXYESGP)](https://codecov.io/gh/NIAEFEUP/website-niaefeup-backend)

The online platform for NIAEFEUP.

Below, you can find a quickstart guide with development setup and project structure. For additional information about any implementation or usage details, please refer to our [Wiki Page](https://github.com/NIAEFEUP/website-niaefeup-backend/wiki).

## Development setup

### Prerequisites
Expand Down Expand Up @@ -73,7 +76,7 @@ Run the following command in your shell:
```


### API Documentation
## API Documentation
API documentation is generated through the use of the [Spring REST Docs API specification Integration (aka restdocs-api-spec)](https://github.com/ePages-de/restdocs-api-spec), a [Spring Rest Docs](https://spring.io/projects/spring-restdocs) extension that builds an [OpenAPI specification](https://www.openapis.org/) or a [Postman collection](https://learning.postman.com/docs/sending-requests/intro-to-collections/) from its description, included in the controller tests. To see examples of how to document the API, hop to one of the controller tests and read the [API documentation wiki page](https://github.com/NIAEFEUP/website-niaefeup-backend/wiki/API-documentation).

Find the current version of the API documentation [here](https://develop--niaefeup-backend-docs.netlify.app/).
Expand All @@ -94,16 +97,18 @@ Run the following command in your shell:
Find the OpenAPI specification and Postman collection under `docs/` after running the task.


## Project Details

### Project Structure
## Project Structure

- `src/main`
- `controller/` - Methods that register endpoints for the app
- `model/` - Database entity models (Spring Data JPA entities)
- `backend/` - Contains all the source code (excluding tests and resources)
- `config/` - Configuration classes used at boot
- `controller/` - Methods that register endpoints for the app
- `model/` - Database entity models (Spring Data JPA entities)
- `dto/` - Data Transfer Objects for creating and modifying entities
- `repository/` - Data access layer methods (Spring Data repositories)
- `service/` - Business logic for the controllers
- `annotations/` - Custom annotations used in the project
- `validation/` - Custom validations used across the different models
- `repository/` - Data access layer methods (Spring Data repositories)
- `service/` - Business logic for the controllers
- `utils/` - Auxiliary packages used in the project
- `extensions/` - [Extension functions](https://kotlinlang.org/docs/extensions.html) used throughout the project
- `validation/` - Custom validations used across the different models
- `resources/` - All assets and static files needed, including static configurations
- `src/test/` - Self explanatory: unit tests, functional (end-to-end) tests, etc.
10 changes: 5 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.1.1"
id("io.spring.dependency-management") version "1.1.0"
kotlin("jvm") version "1.8.10"
kotlin("plugin.spring") version "1.8.10"
kotlin("plugin.jpa") version "1.8.10"
kotlin("jvm") version "1.9.0"
kotlin("plugin.spring") version "1.9.0"
kotlin("plugin.jpa") version "1.9.0"
id("org.jlleitschuh.gradle.ktlint") version "11.4.2"
id("com.epages.restdocs-api-spec") version "0.17.1"

Expand All @@ -34,7 +34,7 @@ dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
implementation("ch.qos.logback:logback-core:1.4.8")
implementation("org.slf4j:slf4j-api:2.0.6")
implementation("org.slf4j:slf4j-api:2.0.7")
implementation("com.cloudinary:cloudinary:1.0.14")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
implementation("org.springframework.boot:spring-boot-starter-validation:3.1.1")
Expand All @@ -43,7 +43,7 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("ch.qos.logback:logback-classic:1.4.8")
testImplementation("org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0")
testImplementation("com.epages:restdocs-api-spec-mockmvc:0.17.1")
testImplementation("com.epages:restdocs-api-spec-mockmvc:0.18.2")
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.mockito2", module = "mockito-core")
}
Expand Down
7 changes: 7 additions & 0 deletions codecov.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
coverage:
token: ${{ secrets.CODECOV_TOKEN }}
status:
project:
default:
target: 80%
threshold: 5%
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package pt.up.fe.ni.website.backend.controller

import jakarta.validation.Valid
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
Expand Down Expand Up @@ -30,26 +29,27 @@ class AccountController(private val service: AccountService) {
@GetMapping("/{id}")
fun getAccountById(@PathVariable id: Long) = service.getAccountById(id)

@PostMapping("/changePassword/{id}")
fun changePassword(
@Valid @RequestBody
dto: ChangePasswordDto,
@PathVariable id: Long
): Map<String, String> {
service.changePassword(id, dto)
return emptyMap()
@PostMapping("/new", consumes = ["multipart/form-data"])
fun createAccount(
@RequestPart account: CreateAccountDto,
@RequestParam
@ValidImage
photo: MultipartFile?
): Account {
account.photoFile = photo
return service.createAccount(account)
}

@PutMapping("/{id}")
@PutMapping("/{id}", consumes = ["multipart/form-data"])
fun updateAccountById(
@PathVariable id: Long,
@RequestPart dto: UpdateAccountDto,
@RequestPart account: UpdateAccountDto,
@RequestParam
@ValidImage
photo: MultipartFile?
): Account {
dto.photoFile = photo
return service.updateAccountById(id, dto)
account.photoFile = photo
return service.updateAccountById(id, account)
}

@DeleteMapping("/{id}")
Expand All @@ -58,14 +58,9 @@ class AccountController(private val service: AccountService) {
return emptyMap()
}

@PostMapping("/new", consumes = ["multipart/form-data"])
fun createAccount(
@RequestPart dto: CreateAccountDto,
@RequestParam
@ValidImage
photo: MultipartFile?
): Account {
dto.photoFile = photo
return service.createAccount(dto)
@PostMapping("/changePassword/{id}")
fun changePassword(@PathVariable id: Long, @RequestBody dto: ChangePasswordDto): Map<String, String> {
service.changePassword(id, dto)
return emptyMap()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RestControllerAdvice
import org.springframework.web.multipart.MaxUploadSizeExceededException
import org.springframework.web.multipart.support.MissingServletRequestPartException
import pt.up.fe.ni.website.backend.config.Logging
import pt.up.fe.ni.website.backend.service.ErrorMessages

Expand Down Expand Up @@ -70,6 +71,12 @@ class ErrorController(private val objectMapper: ObjectMapper) : ErrorController,
return CustomError(errors)
}

@ExceptionHandler(MissingServletRequestPartException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun missingPart(e: MissingServletRequestPartException): CustomError {
return wrapSimpleError("required", param = e.requestPartName)
}

@ExceptionHandler(HttpMessageNotReadableException::class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
fun invalidRequestBody(e: HttpMessageNotReadableException): CustomError {
Expand Down Expand Up @@ -122,7 +129,7 @@ class ErrorController(private val objectMapper: ObjectMapper) : ErrorController,
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
fun unexpectedError(e: Exception): CustomError {
logger.error(e.message)
return wrapSimpleError("unexpected error: " + e.message)
return wrapSimpleError("unexpected error")
}

@ExceptionHandler(AccessDeniedException::class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package pt.up.fe.ni.website.backend.controller

import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
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.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import pt.up.fe.ni.website.backend.dto.entity.EventDto
import pt.up.fe.ni.website.backend.model.Event
import pt.up.fe.ni.website.backend.service.activity.EventService
import pt.up.fe.ni.website.backend.utils.validation.ValidImage

@RestController
@RequestMapping("/events")
@Validated
class EventController(private val service: EventService) {
@GetMapping
fun getAllEvents() = service.getAllEvents()
Expand All @@ -26,20 +32,34 @@ class EventController(private val service: EventService) {
@GetMapping("/{eventSlug}**")
fun getEvent(@PathVariable eventSlug: String) = service.getEventBySlug(eventSlug)

@PostMapping("/new")
fun createEvent(@RequestBody dto: EventDto) = service.createEvent(dto)
@PostMapping("/new", consumes = ["multipart/form-data"])
fun createEvent(
@RequestPart event: EventDto,
@RequestParam
@ValidImage
image: MultipartFile
): Event {
event.imageFile = image
return service.createEvent(event)
}

@DeleteMapping("/{id}")
fun deleteEventById(@PathVariable id: Long): Map<String, String> {
service.deleteEventById(id)
return emptyMap()
}

@PutMapping("/{id}")
@PutMapping("/{id}", consumes = ["multipart/form-data"])
fun updateEventById(
@PathVariable id: Long,
@RequestBody dto: EventDto
) = service.updateEventById(id, dto)
@RequestPart event: EventDto,
@RequestParam
@ValidImage
image: MultipartFile?
): Event {
event.imageFile = image
return service.updateEventById(id, event)
}

@PutMapping("/{idEvent}/addTeamMember/{idAccount}")
fun addTeamMemberById(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package pt.up.fe.ni.website.backend.controller

import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
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.RequestPart
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile
import pt.up.fe.ni.website.backend.dto.entity.ProjectDto
import pt.up.fe.ni.website.backend.model.Project
import pt.up.fe.ni.website.backend.service.activity.ProjectService
import pt.up.fe.ni.website.backend.utils.validation.ValidImage

@RestController
@RequestMapping("/projects")
@Validated
class ProjectController(private val service: ProjectService) {

@GetMapping
Expand All @@ -24,20 +30,34 @@ class ProjectController(private val service: ProjectService) {
@GetMapping("/{projectSlug}**")
fun getProjectBySlug(@PathVariable projectSlug: String) = service.getProjectBySlug(projectSlug)

@PostMapping("/new")
fun createNewProject(@RequestBody dto: ProjectDto) = service.createProject(dto)
@PostMapping("/new", consumes = ["multipart/form-data"])
fun createProject(
@RequestPart project: ProjectDto,
@RequestParam
@ValidImage
image: MultipartFile
): Project {
project.imageFile = image
return service.createProject(project)
}

@DeleteMapping("/{id}")
fun deleteProjectById(@PathVariable id: Long): Map<String, String> {
service.deleteProjectById(id)
return emptyMap()
}

@PutMapping("/{id}")
@PutMapping("/{id}", consumes = ["multipart/form-data"])
fun updateProjectById(
@PathVariable id: Long,
@RequestBody dto: ProjectDto
) = service.updateProjectById(id, dto)
@RequestPart project: ProjectDto,
@RequestParam
@ValidImage
image: MultipartFile?
): Project {
project.imageFile = image
return service.updateProjectById(id, project)
}

@PutMapping("/{id}/archive")
fun archiveProjectById(@PathVariable id: Long) = service.archiveProjectById(id)
Expand All @@ -56,4 +76,16 @@ class ProjectController(private val service: ProjectService) {
@PathVariable idProject: Long,
@PathVariable idAccount: Long
) = service.removeTeamMemberById(idProject, idAccount)

@PutMapping("/{idProject}/addHallOfFameMember/{idAccount}")
fun addHallOfFameMemberById(
@PathVariable idProject: Long,
@PathVariable idAccount: Long
) = service.addHallOfFameMemberById(idProject, idAccount)

@PutMapping("/{idProject}/removeHallOfFameMember/{idAccount}")
fun removeHallOfFameMemberById(
@PathVariable idProject: Long,
@PathVariable idAccount: Long
) = service.removeHallOfFameMemberById(idProject, idAccount)
}
Loading

0 comments on commit acac4b3

Please sign in to comment.