From aeff426065335d85fdd9958cab4d0afb86572dcb Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Fri, 20 Sep 2024 13:24:11 -0300 Subject: [PATCH] feat: add notes api issue #46 --- .../java_api/controller/NoteController.java | 185 ++++++++++++++++++ .../java_api/entity/NoteEntity.java | 36 ++++ .../java_api/entity/NoteUrlEntity.java | 31 +++ .../exception/NoteNotFoundException.java | 14 ++ .../java_api/repository/NoteRepository.java | 14 ++ .../repository/NoteUrlRepository.java | 10 + .../java_api/request/NotePatchRequest.java | 10 + .../java_api/request/NoteRequest.java | 11 ++ .../java_api/request/NoteUrlPatchRequest.java | 3 + .../java_api/response/NoteResponse.java | 38 ++++ .../java_api/response/NoteUrlResponse.java | 9 + .../java_api/service/NoteService.java | 20 ++ .../service/impl/NoteServiceImpl.java | 168 ++++++++++++++++ .../migration/V202409201235__note_table.sql | 15 ++ 14 files changed, 564 insertions(+) create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/controller/NoteController.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteEntity.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteUrlEntity.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/exception/NoteNotFoundException.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteRepository.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteUrlRepository.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/request/NotePatchRequest.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteRequest.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteUrlPatchRequest.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteResponse.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteUrlResponse.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/service/NoteService.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/NoteServiceImpl.java create mode 100644 java-api/src/main/resources/db/migration/V202409201235__note_table.sql diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/NoteController.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/NoteController.java new file mode 100644 index 0000000..6388f6e --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/NoteController.java @@ -0,0 +1,185 @@ +package br.com.tasknoteapp.java_api.controller; + +import br.com.tasknoteapp.java_api.entity.NoteEntity; +import br.com.tasknoteapp.java_api.request.NotePatchRequest; +import br.com.tasknoteapp.java_api.request.NoteRequest; +import br.com.tasknoteapp.java_api.response.NoteResponse; +import br.com.tasknoteapp.java_api.service.NoteService; +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.tags.Tag; +import jakarta.validation.Valid; +import java.util.List; +import lombok.AllArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +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.RestController; + +@RestController +@RequestMapping("/rest/notes") +@AllArgsConstructor +@Tag(name = "Notes", description = "Notes resources to handle stored notes.") +public class NoteController { + + private final NoteService noteService; + + /** + * Get all notes. + * + * @return List of NoteResponse with all found notes and its urls, if any. + */ + @GetMapping + @Operation( + summary = "Get all notes", + description = "Get all notes for the current user and its urls, if any", + responses = { + @ApiResponse( + responseCode = "200", + description = "Notes successfully retrieved", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = NoteResponse.class, type = "array"))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))) + }) + public List getAllNotes() { + return noteService.getAllNotes(); + } + + /** + * Patch a note. + * + * @param id The note id to be patched. + * @param noteRequest Note data to be patched, including optionally its urls. + * @return NoteResponse containing data that was updated. + * @throws NoteNotFoundException when note not found. + */ + @PatchMapping("/{id}") + @Operation( + summary = "Patch a note", + description = "Patch a note and all its urls. Option to patch only the urls.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Note successfully patched", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = NoteResponse.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "404", + description = "Note not found", + content = @Content(schema = @Schema(implementation = Void.class))) + }) + public ResponseEntity putNote( + @Parameter( + name = "id", + in = ParameterIn.PATH, + description = "Note id to be patched.", + required = true, + schema = @Schema(type = "integer", format = "int64")) + @PathVariable + Long id, + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "Note data to be patched, including optionally its urls.", + required = true) + @RequestBody + @Valid + NotePatchRequest noteRequest) { + return ResponseEntity.ok(noteService.patchNote(id, noteRequest)); + } + + /** + * Create a note. + * + * @param noteRequest Note data to be created, including optionally its urls. Following RESTful + * API pattern from https://restfulapi.net/rest-put-vs-post/. + * @return NoteResponse containing data that was created. + */ + @PostMapping + @Operation( + summary = "Create a note", + description = "Create a note and all its urls.", + responses = { + @ApiResponse( + responseCode = "201", + description = "Note successfully crated.", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = NoteResponse.class))), + @ApiResponse( + responseCode = "400", + description = "Wrong or missing information", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + }) + public ResponseEntity postNotes( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "Note data to be created, including optionally its urls.", + required = true) + @RequestBody + @Valid + NoteRequest noteRequest) { + NoteEntity createdNote = noteService.createNote(noteRequest); + return ResponseEntity.status(HttpStatus.CREATED).body(NoteResponse.fromEntity(createdNote)); + } + + /** + * Delete a note given its ID. + * + * @param id Note identification. + * @throws NoteNotFoundException when note not found. + */ + @DeleteMapping("/{id}") + @Operation( + summary = "Delete a note", + description = "Delete a note given its ID.", + responses = { + @ApiResponse( + responseCode = "204", + description = "Note successfully deleted", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "404", + description = "Note not found", + content = @Content(schema = @Schema(implementation = Void.class))) + }) + public ResponseEntity deleteNotes( + @Parameter( + name = "id", + in = ParameterIn.PATH, + description = "Note id to be patched.", + required = true, + schema = @Schema(type = "integer", format = "int64")) + @PathVariable + Long id) { + noteService.deleteNote(id); + return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); + } +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteEntity.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteEntity.java new file mode 100644 index 0000000..93af319 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteEntity.java @@ -0,0 +1,36 @@ +package br.com.tasknoteapp.java_api.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import java.util.List; + +@Data +@Entity +@Table(name = "notes") +@ToString +@EqualsAndHashCode +public class NoteEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String description; + + @JoinColumn(name = "user_id", referencedColumnName = "id", nullable = false, updatable = false) + @ManyToOne(fetch = FetchType.LAZY) + private UserEntity user; + + @OneToMany(mappedBy = "note", fetch = FetchType.LAZY) + private List urls; +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteUrlEntity.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteUrlEntity.java new file mode 100644 index 0000000..fc5385b --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/entity/NoteUrlEntity.java @@ -0,0 +1,31 @@ +package br.com.tasknoteapp.java_api.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@Entity +@ToString +@Table(name = "note_urls") +@EqualsAndHashCode +public class NoteUrlEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String url; + + @JoinColumn(name = "note_id", referencedColumnName = "id", nullable = false, updatable = false) + @ManyToOne(fetch = FetchType.LAZY) + private NoteEntity note; +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/NoteNotFoundException.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/NoteNotFoundException.java new file mode 100644 index 0000000..10e14ef --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/exception/NoteNotFoundException.java @@ -0,0 +1,14 @@ +package br.com.tasknoteapp.java_api.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.server.ResponseStatusException; + +/** This class represents a Note Not Found request. */ +@ResponseStatus(code = HttpStatus.NOT_FOUND) +public class NoteNotFoundException extends ResponseStatusException { + + public NoteNotFoundException() { + super(HttpStatus.NOT_FOUND, "Task not found"); + } +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteRepository.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteRepository.java new file mode 100644 index 0000000..9dcbfa9 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteRepository.java @@ -0,0 +1,14 @@ +package br.com.tasknoteapp.java_api.repository; + +import br.com.tasknoteapp.java_api.entity.NoteEntity; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface NoteRepository extends JpaRepository { + + List findAllByUser_id(Long userId); + + @Query("select n from NoteEntity n where upper(n.description) like %?1% and n.user.id = ?2") + List findAllBySearchTerm(String searchTerm, Long userId); +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteUrlRepository.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteUrlRepository.java new file mode 100644 index 0000000..60a7810 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/repository/NoteUrlRepository.java @@ -0,0 +1,10 @@ +package br.com.tasknoteapp.java_api.repository; + +import br.com.tasknoteapp.java_api.entity.NoteUrlEntity; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface NoteUrlRepository extends JpaRepository { + + void deleteAllByIdIn(List ids); +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NotePatchRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NotePatchRequest.java new file mode 100644 index 0000000..bc4972a --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NotePatchRequest.java @@ -0,0 +1,10 @@ +package br.com.tasknoteapp.java_api.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +/** This record represents a note patch payload. */ +@Schema(description = "Note patch payload.") +public record NotePatchRequest( + @Schema(description = "Note description. Optional.") String description, + @Schema(description = "Note urls. Optional.") List urls) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteRequest.java new file mode 100644 index 0000000..96f76ef --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteRequest.java @@ -0,0 +1,11 @@ +package br.com.tasknoteapp.java_api.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import java.util.List; + +/** This record represents a note request to be created. */ +@Schema(description = "Note request to be created.") +public record NoteRequest( + @Schema(description = "Note description.") @NotNull String description, + @Schema(description = "Note urls. Optional.") List urls) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteUrlPatchRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteUrlPatchRequest.java new file mode 100644 index 0000000..28d2fda --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/NoteUrlPatchRequest.java @@ -0,0 +1,3 @@ +package br.com.tasknoteapp.java_api.request; + +public record NoteUrlPatchRequest(Long id, String url) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteResponse.java new file mode 100644 index 0000000..801b806 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteResponse.java @@ -0,0 +1,38 @@ +package br.com.tasknoteapp.java_api.response; + +import br.com.tasknoteapp.java_api.entity.NoteEntity; +import br.com.tasknoteapp.java_api.entity.NoteUrlEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** This record represents a task and its urls object to be returned. */ +@Schema(description = "This record represents a task and its urls object to be returned.") +public record NoteResponse( + @Schema(description = "The id of the note", example = "1") Long id, + @Schema(description = "The description of the note", example = "Note 1") String description, + @Schema(description = "The urls of the task, zero, one or more.", example = "[]") + List urls) { + + /** + * Creates a NoteResponse given a NoteEntity and its Urls. + * + * @param entity The NoteEntity source data. + * @return NoteResponse instance with all note data and urls, if any. + */ + public static NoteResponse fromEntity(NoteEntity entity) { + List urls = entity.getUrls(); + List urlsResponse = new ArrayList<>(); + if (Objects.isNull(urls)) { + urls = List.of(); + } else { + for (NoteUrlEntity url : urls) { + NoteUrlResponse urlResponse = new NoteUrlResponse(url.getId(), url.getUrl()); + urlsResponse.add(urlResponse); + } + } + + return new NoteResponse(entity.getId(), entity.getDescription(), urlsResponse); + } +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteUrlResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteUrlResponse.java new file mode 100644 index 0000000..285bb34 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/NoteUrlResponse.java @@ -0,0 +1,9 @@ +package br.com.tasknoteapp.java_api.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +/** This record represents a note url object. */ +@Schema(description = "This record represents a note url object.") +public record NoteUrlResponse( + @Schema(description = "Note url id", example = "1") Long id, + @Schema(description = "Note url link", example = "http://duckduckgo.com") String url) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/NoteService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/NoteService.java new file mode 100644 index 0000000..0a81c7b --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/NoteService.java @@ -0,0 +1,20 @@ +package br.com.tasknoteapp.java_api.service; + +import br.com.tasknoteapp.java_api.entity.NoteEntity; +import br.com.tasknoteapp.java_api.request.NotePatchRequest; +import br.com.tasknoteapp.java_api.request.NoteRequest; +import br.com.tasknoteapp.java_api.response.NoteResponse; +import java.util.List; + +public interface NoteService { + + public List getAllNotes(); + + public NoteEntity createNote(NoteRequest noteRequest); + + public NoteResponse patchNote(Long taskId, NotePatchRequest taskRequest); + + public void deleteNote(Long noteId); + + public List searchNotes(String searchTerm); +} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/NoteServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/NoteServiceImpl.java new file mode 100644 index 0000000..2f83004 --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/NoteServiceImpl.java @@ -0,0 +1,168 @@ +package br.com.tasknoteapp.java_api.service.impl; + +import br.com.tasknoteapp.java_api.entity.NoteEntity; +import br.com.tasknoteapp.java_api.entity.NoteUrlEntity; +import br.com.tasknoteapp.java_api.entity.UserEntity; +import br.com.tasknoteapp.java_api.exception.NoteNotFoundException; +import br.com.tasknoteapp.java_api.repository.NoteRepository; +import br.com.tasknoteapp.java_api.repository.NoteUrlRepository; +import br.com.tasknoteapp.java_api.request.NotePatchRequest; +import br.com.tasknoteapp.java_api.request.NoteRequest; +import br.com.tasknoteapp.java_api.request.NoteUrlPatchRequest; +import br.com.tasknoteapp.java_api.response.NoteResponse; +import br.com.tasknoteapp.java_api.service.AuthService; +import br.com.tasknoteapp.java_api.service.NoteService; +import br.com.tasknoteapp.java_api.util.AuthUtil; +import jakarta.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +@AllArgsConstructor +public class NoteServiceImpl implements NoteService { + + private final NoteRepository noteRepository; + + private final AuthService authService; + + private final AuthUtil authUtil; + + private final NoteUrlRepository noteUrlRepository; + + @Override + public List getAllNotes() { + UserEntity user = getCurrentUser(); + + log.info("Get all notes to user {}", user.getId()); + + List notes = noteRepository.findAllByUser_id(user.getId()); + log.info("{} notes found!", notes.size()); + + return notes.stream().map(NoteResponse::fromEntity).toList(); + } + + @Override + public NoteEntity createNote(NoteRequest noteRequest) { + UserEntity user = getCurrentUser(); + + log.info("Creating note to user {}", user.getId()); + + NoteEntity note = new NoteEntity(); + note.setDescription(noteRequest.description()); + note.setUser(user); + NoteEntity created = noteRepository.save(note); + + if (!Objects.isNull(noteRequest.urls()) && !noteRequest.urls().isEmpty()) { + List urls = saveUrls(note, noteRequest.urls()); + note.setUrls(urls); + } + + log.info("Note created! Id {}", created.getId()); + return created; + } + + @Override + @Transactional + public NoteResponse patchNote(Long noteId, NotePatchRequest patch) { + UserEntity user = getCurrentUser(); + + log.info("Patching task {} to user {}", noteId, user.getId()); + + Optional note = noteRepository.findById(noteId); + if (note.isEmpty()) { + throw new NoteNotFoundException(); + } + + NoteEntity noteEntity = note.get(); + if (!Objects.isNull(patch.description()) && !patch.description().isBlank()) { + noteEntity.setDescription(patch.description()); + } + + if (!Objects.isNull(patch.urls())) { + List urlIds = + patch.urls().stream().filter(p -> p.id() != null).map(NoteUrlPatchRequest::id).toList(); + if (!urlIds.isEmpty()) { + noteUrlRepository.deleteAllByIdIn(urlIds); + log.info("Deleted {} urls from task {}", urlIds.size(), noteId); + } else { + log.info("No urls to patch for task {}", noteId); + } + + List urlsList = + patch.urls().stream().filter(p -> p.id() == null).map(NoteUrlPatchRequest::url).toList(); + List urls = saveUrls(noteEntity, urlsList); + + noteEntity.setUrls(urls); + } + + NoteEntity patchedNote = noteRepository.save(noteEntity); + + log.info("Note patched! Id {}", patchedNote.getId()); + + return NoteResponse.fromEntity(patchedNote); + } + + @Override + @Transactional + public void deleteNote(Long noteId) { + UserEntity user = getCurrentUser(); + + log.info("Deleting note {} to user {}", noteId, user.getId()); + + Optional note = noteRepository.findById(noteId); + if (note.isEmpty()) { + throw new NoteNotFoundException(); + } + + List urls = note.get().getUrls(); + if (!urls.isEmpty()) { + noteUrlRepository.deleteAll(urls); + log.info("Deleted {} urls from task {}", urls.size(), noteId); + } else { + log.info("No urls to delete for task {}", noteId); + } + + noteRepository.delete(note.get()); + + log.info("Note deleted! Id {}", noteId); + } + + @Override + public List searchNotes(String searchTerm) { + UserEntity user = getCurrentUser(); + + log.info("Searching notes to user {}", user.getId()); + + List notes = noteRepository.findAllBySearchTerm(searchTerm, user.getId()); + log.info("{} tasks found!", notes.size()); + + return notes.stream().map(NoteResponse::fromEntity).toList(); + } + + private UserEntity getCurrentUser() { + Optional currentUserEmail = authUtil.getCurrentUserEmail(); + String email = currentUserEmail.orElseThrow(); + return authService.findByEmail(email).orElseThrow(); + } + + private List saveUrls(NoteEntity noteEntity, List urls) { + List tasksUrl = new ArrayList<>(); + for (String url : urls) { + NoteUrlEntity taskUrl = new NoteUrlEntity(); + taskUrl.setUrl(url); + taskUrl.setNote(noteEntity); + tasksUrl.add(taskUrl); + } + + List savedUrls = noteUrlRepository.saveAll(tasksUrl); + log.info("Saved {} urls to note {}", savedUrls.size(), noteEntity.getId()); + + return savedUrls; + } +} diff --git a/java-api/src/main/resources/db/migration/V202409201235__note_table.sql b/java-api/src/main/resources/db/migration/V202409201235__note_table.sql new file mode 100644 index 0000000..aa7581e --- /dev/null +++ b/java-api/src/main/resources/db/migration/V202409201235__note_table.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS tasknote.notes ( + id SERIAL, + user_id INTEGER NOT NULL, + description VARCHAR(300) NOT NULL, + CONSTRAINT notes_pk PRIMARY KEY (id), + CONSTRAINT notes_user_id_fk FOREIGN KEY (user_id) REFERENCES tasknote.users (id) +); + +CREATE TABLE IF NOT EXISTS tasknote.note_urls ( + id SERIAL, + note_id INTEGER NOT NULL, + url VARCHAR(200) NOT NULL, + CONSTRAINT note_url_pk PRIMARY KEY (id), + CONSTRAINT note_url_note_id_fk FOREIGN KEY (note_id) REFERENCES tasknote.notes (id) +);