From 54fe6c49ca232baca844774f9c80fb5303834490 Mon Sep 17 00:00:00 2001 From: querwurzel <> Date: Wed, 8 Nov 2023 21:38:42 +0100 Subject: [PATCH] set search contract --- .../binpastes/paste/api/PasteController.java | 16 +++--- .../binpastes/paste/api/model/ListView.java | 4 +- .../binpastes/paste/api/model/SearchView.java | 51 +++++++++++++++++++ .../binpastes/paste/api/model/SingleView.java | 2 +- .../domain/SimpleFullTextSupportImpl.java | 4 +- frontend/src/api/client.ts | 3 +- frontend/src/api/model/PasteListView.ts | 4 -- frontend/src/api/model/PasteSearchView.ts | 9 ++++ frontend/src/pages/Search.tsx | 28 +++++++++- 9 files changed, 103 insertions(+), 18 deletions(-) create mode 100644 backend/src/main/java/com/github/binpastes/paste/api/model/SearchView.java create mode 100644 frontend/src/api/model/PasteSearchView.ts diff --git a/backend/src/main/java/com/github/binpastes/paste/api/PasteController.java b/backend/src/main/java/com/github/binpastes/paste/api/PasteController.java index 7a6f5ed..59e2ae9 100644 --- a/backend/src/main/java/com/github/binpastes/paste/api/PasteController.java +++ b/backend/src/main/java/com/github/binpastes/paste/api/PasteController.java @@ -2,6 +2,8 @@ import com.github.binpastes.paste.api.model.CreateCmd; import com.github.binpastes.paste.api.model.ListView; +import com.github.binpastes.paste.api.model.SearchView; +import com.github.binpastes.paste.api.model.SearchView.SearchItemView; import com.github.binpastes.paste.api.model.SingleView; import com.github.binpastes.paste.domain.Paste; import com.github.binpastes.paste.domain.PasteService; @@ -61,7 +63,7 @@ public Mono findPaste(@PathVariable("pasteId") String pasteId, Serve response.getHeaders().add(HttpHeaders.CACHE_CONTROL, "max-age=300"); } }) - .map(reference -> SingleView.from(reference, remoteAddress(request))) + .map(reference -> SingleView.of(reference, remoteAddress(request))) .switchIfEmpty(Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND))); } @@ -69,18 +71,18 @@ public Mono findPaste(@PathVariable("pasteId") String pasteId, Serve public Mono findPastes() { return pasteService .findAll() - .map(ListItemView::from) + .map(ListItemView::of) .collectList() - .map(ListView::from); + .map(ListView::of); } @GetMapping("/search") - public Mono searchPastes(@RequestParam("term") @NotBlank @Size(min = 3) @Pattern(regexp = "[\\pL\\pN\\s]+") String term) { + public Mono searchPastes(@RequestParam("term") @NotBlank @Size(min = 3) @Pattern(regexp = "[\\pL\\pN\\s]+") String term) { return pasteService .findByFullText(term) - .map(ListItemView::from) + .map(paste -> SearchItemView.of(paste, term)) .collectList() - .map(ListView::from); + .map(SearchView::of); } @PostMapping @@ -95,7 +97,7 @@ public Mono createPaste(@Valid @RequestBody Mono createCm cmd.pasteExposure(), remoteAddress(request) )) - .map((Paste reference) -> SingleView.from(reference, remoteAddress(request))); + .map((Paste reference) -> SingleView.of(reference, remoteAddress(request))); } @DeleteMapping("/{pasteId:[a-zA-Z0-9]{40}}") diff --git a/backend/src/main/java/com/github/binpastes/paste/api/model/ListView.java b/backend/src/main/java/com/github/binpastes/paste/api/model/ListView.java index c87e219..5ae7839 100644 --- a/backend/src/main/java/com/github/binpastes/paste/api/model/ListView.java +++ b/backend/src/main/java/com/github/binpastes/paste/api/model/ListView.java @@ -8,7 +8,7 @@ public record ListView( List pastes ) { - public static ListView from(List pastes) { + public static ListView of(final List pastes) { return new ListView(pastes); } @@ -20,7 +20,7 @@ public record ListItemView ( LocalDateTime dateCreated, LocalDateTime dateOfExpiry ) { - public static ListItemView from(Paste reference) { + public static ListItemView of(final Paste reference) { return new ListItemView( reference.getId(), reference.getTitle(), diff --git a/backend/src/main/java/com/github/binpastes/paste/api/model/SearchView.java b/backend/src/main/java/com/github/binpastes/paste/api/model/SearchView.java new file mode 100644 index 0000000..921fbf3 --- /dev/null +++ b/backend/src/main/java/com/github/binpastes/paste/api/model/SearchView.java @@ -0,0 +1,51 @@ +package com.github.binpastes.paste.api.model; + +import com.github.binpastes.paste.domain.Paste; + +import java.time.LocalDateTime; +import java.util.List; + +public record SearchView( + List pastes +) { + + public static SearchView of(List pastes) { + return new SearchView(pastes); + } + + public record SearchItemView( + String id, + String title, + String highlight, + int sizeInBytes, + LocalDateTime dateCreated, + LocalDateTime dateOfExpiry + ) { + + private static final short HIGHLIGHT_RANGE = 25; + + public static SearchItemView of(final Paste reference, final String term) { + return new SearchItemView( + reference.getId(), + reference.getTitle(), + highlight(reference.getContent(), term), + reference.getContent().getBytes().length, + reference.getDateCreated(), + reference.getDateOfExpiry() + ); + } + + public static String highlight(final String content, final String term) { + final int idx = content.indexOf(term); + + if (idx == -1) { + return content; + } + + return content.substring( + Math.max(0, idx - HIGHLIGHT_RANGE), + Math.min(content.length(), idx + term.length() + HIGHLIGHT_RANGE) + ); + } + } +} diff --git a/backend/src/main/java/com/github/binpastes/paste/api/model/SingleView.java b/backend/src/main/java/com/github/binpastes/paste/api/model/SingleView.java index f315327..bab583f 100644 --- a/backend/src/main/java/com/github/binpastes/paste/api/model/SingleView.java +++ b/backend/src/main/java/com/github/binpastes/paste/api/model/SingleView.java @@ -18,7 +18,7 @@ public record SingleView( LocalDateTime lastViewed, long views ) { - public static SingleView from(Paste reference, String remoteAddress) { + public static SingleView of(final Paste reference, final String remoteAddress) { return new SingleView( reference.getId(), reference.getTitle(), diff --git a/backend/src/main/java/com/github/binpastes/paste/domain/SimpleFullTextSupportImpl.java b/backend/src/main/java/com/github/binpastes/paste/domain/SimpleFullTextSupportImpl.java index 2ee8c0a..0e93b05 100644 --- a/backend/src/main/java/com/github/binpastes/paste/domain/SimpleFullTextSupportImpl.java +++ b/backend/src/main/java/com/github/binpastes/paste/domain/SimpleFullTextSupportImpl.java @@ -32,9 +32,9 @@ public Flux searchByFullText(final String text) { .or(PasteSchema.DATE_OF_EXPIRY).greaterThan(LocalDateTime.now()) ) .and(Criteria - .where(PasteSchema.TITLE).like(text + '%') + .where(PasteSchema.TITLE).like('%' + text + '%') .or(Criteria - .where(PasteSchema.CONTENT).like(text + '%') + .where(PasteSchema.CONTENT).like('%' + text + '%') .and(PasteSchema.IS_ENCRYPTED).isFalse() ) ); diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index 2782383..94ba078 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -1,6 +1,7 @@ import {PasteCreateCmd} from './model/PasteCreateCmd'; import {PasteListView} from './model/PasteListView'; import {PasteView} from './model/PasteView'; +import {PasteSearchView} from './model/PasteSearchView'; const HOST_DEVELOPMENT = 'localhost:3000'; @@ -53,7 +54,7 @@ const findAll = (): Promise> => { .then(value => value.pastes); } -const searchAll = (term: string): Promise> => { +const searchAll = (term: string): Promise> => { const params = new URLSearchParams([['term', term]]); const url = new URL('/api/v1/paste/search?' + params.toString(), apiBaseUrl()); diff --git a/frontend/src/api/model/PasteListView.ts b/frontend/src/api/model/PasteListView.ts index 4ef1f6c..9764a9a 100644 --- a/frontend/src/api/model/PasteListView.ts +++ b/frontend/src/api/model/PasteListView.ts @@ -7,7 +7,3 @@ export type PasteListView = { dateCreated: string dateOfExpiry?: string } - -export type = PasteList { - pastes: Array -} diff --git a/frontend/src/api/model/PasteSearchView.ts b/frontend/src/api/model/PasteSearchView.ts new file mode 100644 index 0000000..3c3fc9b --- /dev/null +++ b/frontend/src/api/model/PasteSearchView.ts @@ -0,0 +1,9 @@ + +export type PasteSearchView = { + id: string + title?: string + highlight: string + sizeInBytes: number + dateCreated: string + dateOfExpiry?: string +} diff --git a/frontend/src/pages/Search.tsx b/frontend/src/pages/Search.tsx index 860a2b1..3e6fd88 100644 --- a/frontend/src/pages/Search.tsx +++ b/frontend/src/pages/Search.tsx @@ -1,9 +1,35 @@ -import {JSX} from 'solid-js'; +import {JSX, createResource} from 'solid-js'; +import {A, useNavigate, useLocation} from '@solidjs/router'; +import ApiClient from '../api/client'; +import {toDateTimeString} from '../datetime/DateTimeUtil'; const Search: () => JSX.Element = () => { + + const navigate = useNavigate(); + + const location = useLocation(); + + const [pastes, { mutate, refetch }] = createResource(() => location.query.q, (term) => ApiClient.searchAll(term)); + return ( <>

Search

+ Nothing found

}> +
    + {item => +
  1. +

    {item.title || 'Untitled' }

    +

    + Created: | + Expires: | + Size: {item.sizeInBytes} bytes +

    +
    {item.content}
    +
  2. + } +
    +
+
) }