Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
4e02bf9c2e | |||
d332e83f29 | |||
bc79e0ac2c | |||
3e07e2f06e | |||
f8ab5c8358 | |||
f4f3a4b304 | |||
005b47d0ee | |||
03156ff06d | |||
7adc97c1c9 | |||
22181fb56b | |||
6b8ae2f236 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
_*
|
_*
|
||||||
*.sqlite
|
*.sqlite
|
||||||
|
api-cds-search
|
||||||
|
@ -5,8 +5,5 @@ FROM golang:latest
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
ADD . /app
|
ADD . /app
|
||||||
|
|
||||||
RUN go mod download
|
|
||||||
RUN go build -o ./api-cds-search
|
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
CMD ["./api-cds-search"]
|
CMD ["./api-cds-search"]
|
||||||
|
3
build.sh
Executable file
3
build.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
go build -o ./api-cds-search
|
||||||
|
|
||||||
|
sudo docker build -t code.achtarmig.org/sap/api-cds-search:pas-ui-latest .
|
@ -33,9 +33,9 @@ type CDSViewFieldJSON struct {
|
|||||||
|
|
||||||
//TODO: "https://api.sap.com/api/1.0/container/CDSViews/artifacts?containerType=contentType&$filter=Type%20eq%20'CDSVIEW'&$top=" + fmt.Sprint(top) + "&$skip=" + fmt.Sprint(skip)
|
//TODO: "https://api.sap.com/api/1.0/container/CDSViews/artifacts?containerType=contentType&$filter=Type%20eq%20'CDSVIEW'&$top=" + fmt.Sprint(top) + "&$skip=" + fmt.Sprint(skip)
|
||||||
|
|
||||||
func GetCDSViewField(CDSViewTechnicalName string) error {
|
func GetCDSViewField(CDSViewTechnicalName table.TechnicalName) error {
|
||||||
var json CDSViewFieldJSON
|
var json CDSViewFieldJSON
|
||||||
path := "https://api.sap.com/odata/1.0/catalog.svc/CdsViewsContent.CdsViews('" + CDSViewTechnicalName + "')/$value"
|
path := string("https://api.sap.com/odata/1.0/catalog.svc/CdsViewsContent.CdsViews('" + CDSViewTechnicalName + "')/$value")
|
||||||
fmt.Println("Getting: ", CDSViewTechnicalName)
|
fmt.Println("Getting: ", CDSViewTechnicalName)
|
||||||
err := RequestJSON(path, &json)
|
err := RequestJSON(path, &json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -60,7 +60,7 @@ func GetCDSViewFields() {
|
|||||||
|
|
||||||
for _, cdsView := range *cdsViews {
|
for _, cdsView := range *cdsViews {
|
||||||
var json CDSViewFieldJSON
|
var json CDSViewFieldJSON
|
||||||
path := "https://api.sap.com/odata/1.0/catalog.svc/CdsViewsContent.CdsViews('" + cdsView + "')/$value"
|
path := string("https://api.sap.com/odata/1.0/catalog.svc/CdsViewsContent.CdsViews('" + cdsView + "')/$value")
|
||||||
fmt.Println("Getting: ", cdsView)
|
fmt.Println("Getting: ", cdsView)
|
||||||
err := RequestJSON(path, &json)
|
err := RequestJSON(path, &json)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -23,19 +23,19 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CDSView struct {
|
type CDSView struct {
|
||||||
TechnicalName string `json:"Name"`
|
TechnicalName `json:"Name"`
|
||||||
DisplayName string `json:"DisplayName"`
|
DisplayName `json:"DisplayName"`
|
||||||
Description string `json:"Description"`
|
Description `json:"Description"`
|
||||||
Version string `json:"Version"`
|
Version `json:"Version"`
|
||||||
State string `json:"State"`
|
State `json:"State"`
|
||||||
CreatedAt int `json:"CreatedAt"`
|
CreatedAt `json:"CreatedAt"`
|
||||||
ModifiedAt int `json:"ModifiedAt"`
|
ModifiedAt `json:"ModifiedAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed sql/query_cds_view.sql
|
//go:embed sql/query_cds_view.sql
|
||||||
var query_cds_view string
|
var query_cds_view string
|
||||||
|
|
||||||
func GetCDSView(TechnicalName string) (*CDSView, error) {
|
func GetCDSView(TechnicalName TechnicalName) (*CDSView, error) {
|
||||||
row := database.DB.QueryRow(query_cds_view, TechnicalName)
|
row := database.DB.QueryRow(query_cds_view, TechnicalName)
|
||||||
|
|
||||||
var CDSView CDSView
|
var CDSView CDSView
|
||||||
@ -50,15 +50,15 @@ func GetCDSView(TechnicalName string) (*CDSView, error) {
|
|||||||
//go:embed sql/query_all_cds_view_technical_names.sql
|
//go:embed sql/query_all_cds_view_technical_names.sql
|
||||||
var query_all_cds_view_technical_names string
|
var query_all_cds_view_technical_names string
|
||||||
|
|
||||||
func QueryAllCDSViewTechnicalNames() (*[]string, error) {
|
func QueryAllCDSViewTechnicalNames() (*[]TechnicalName, error) {
|
||||||
rows, err := database.DB.Query(query_all_cds_view_technical_names)
|
rows, err := database.DB.Query(query_all_cds_view_technical_names)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var technicalNames []string
|
var technicalNames []TechnicalName
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var technicalName string
|
var technicalName TechnicalName
|
||||||
err := rows.Scan(&technicalName)
|
err := rows.Scan(&technicalName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -23,11 +23,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type CDSViewField struct {
|
type CDSViewField struct {
|
||||||
CDSViewTechnicalName string
|
CDSViewTechnicalName TechnicalName
|
||||||
FieldName string `json:"fieldname"`
|
FieldName `json:"fieldname"`
|
||||||
Description string `json:"description"`
|
Description `json:"description"`
|
||||||
DataType string `json:"datatype"`
|
DataType `json:"datatype"`
|
||||||
FieldLength string `json:"fieldlength"`
|
FieldLength `json:"fieldlength"`
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:embed sql/query_cds_view_fields.sql
|
//go:embed sql/query_cds_view_fields.sql
|
||||||
@ -53,7 +53,7 @@ func GetCDSViewFields(CDSViewTechnicalName string) (*[]CDSViewField, error) {
|
|||||||
//go:embed sql/query_cds_view_number_of_fields.sql
|
//go:embed sql/query_cds_view_number_of_fields.sql
|
||||||
var query_cds_view_number_of_fields string
|
var query_cds_view_number_of_fields string
|
||||||
|
|
||||||
func GetCDSViewNumberOfFields(CDSViewTechnicalName string) (int, error) {
|
func GetCDSViewNumberOfFields(CDSViewTechnicalName TechnicalName) (int, error) {
|
||||||
rows, err := database.DB.Query(query_cds_view_number_of_fields, CDSViewTechnicalName)
|
rows, err := database.DB.Query(query_cds_view_number_of_fields, CDSViewTechnicalName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
135
cmd/database/table/field.go
Normal file
135
cmd/database/table/field.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package table
|
||||||
|
|
||||||
|
import "code.achtarmig.org/pas/ui/field"
|
||||||
|
|
||||||
|
// CDS View
|
||||||
|
type (
|
||||||
|
TechnicalName string
|
||||||
|
DisplayName string
|
||||||
|
Description string
|
||||||
|
Version string
|
||||||
|
State string
|
||||||
|
CreatedAt int
|
||||||
|
ModifiedAt int
|
||||||
|
)
|
||||||
|
|
||||||
|
// CDS View Field
|
||||||
|
type (
|
||||||
|
FieldName string
|
||||||
|
DataType string
|
||||||
|
FieldLength string
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TechnicalName) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Technical Name",
|
||||||
|
Details: "Technical Name of the CDS View",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t TechnicalName) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t DisplayName) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Name",
|
||||||
|
Details: "Name of the CDS View",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t DisplayName) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Description) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Description",
|
||||||
|
Details: "Discription",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Description) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Version) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Version",
|
||||||
|
Details: "Version of the CDS View",
|
||||||
|
Justify: field.JustifyRight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Version) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t State) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Release State",
|
||||||
|
Details: "Release State of the CDS View. Non-Released views may not be used",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t State) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t CreatedAt) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Created At",
|
||||||
|
Details: "Timestamp of creation of the dataset",
|
||||||
|
Justify: field.JustifyRight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t CreatedAt) Int() int {
|
||||||
|
return int(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ModifiedAt) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Modified At",
|
||||||
|
Details: "Timestamp of last modification of the dataset",
|
||||||
|
Justify: field.JustifyRight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t ModifiedAt) Int() int {
|
||||||
|
return int(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t FieldName) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Field",
|
||||||
|
Details: "Name of the CDS View field",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t FieldName) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t DataType) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Type",
|
||||||
|
Details: "Data type of the CDS View field",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t DataType) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t FieldLength) Info(language string) field.Info {
|
||||||
|
return field.Info{
|
||||||
|
Title: "Length",
|
||||||
|
Details: "Length of the CDS View field",
|
||||||
|
Justify: field.JustifyRight,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t FieldLength) String() string {
|
||||||
|
return string(t)
|
||||||
|
}
|
@ -23,7 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Keyword struct {
|
type Keyword struct {
|
||||||
CDSViewTechnicalName string
|
CDSViewTechnicalName TechnicalName
|
||||||
Keyword string
|
Keyword string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,15 +29,16 @@ import (
|
|||||||
|
|
||||||
type CDSViewFieldModel struct {
|
type CDSViewFieldModel struct {
|
||||||
table.CDSViewField
|
table.CDSViewField
|
||||||
DataTypeTitle string
|
FieldNameOut table.FieldName
|
||||||
FieldLengthOut string
|
DataTypeTitle table.DataType
|
||||||
DescriptionOut string
|
FieldLengthOut table.FieldLength
|
||||||
|
DescriptionOut table.Description
|
||||||
}
|
}
|
||||||
|
|
||||||
type CDSViewModel struct {
|
type CDSViewModel struct {
|
||||||
*table.CDSView
|
*table.CDSView
|
||||||
StateTitle string
|
StateTitle table.State
|
||||||
TechnicalNameEncoded string
|
TechnicalNameEncoded table.TechnicalName
|
||||||
NumberOfFields int
|
NumberOfFields int
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,8 +54,9 @@ func GetCDSViewModelFields(TechnicalName string) (*[]CDSViewFieldModel, error) {
|
|||||||
var fieldsModel []CDSViewFieldModel
|
var fieldsModel []CDSViewFieldModel
|
||||||
for _, field := range *fields {
|
for _, field := range *fields {
|
||||||
fieldModel.CDSViewField = field
|
fieldModel.CDSViewField = field
|
||||||
fieldModel.DataTypeTitle = englishCases.String(field.DataType)
|
fieldModel.FieldNameOut = field.FieldName
|
||||||
fieldModel.FieldLengthOut = strings.TrimLeft(field.FieldLength, "0")
|
fieldModel.DataTypeTitle = table.DataType(englishCases.String(field.DataType.String()))
|
||||||
|
fieldModel.FieldLengthOut = table.FieldLength(strings.TrimLeft(field.FieldLength.String(), "0"))
|
||||||
fieldModel.DescriptionOut = field.Description
|
fieldModel.DescriptionOut = field.Description
|
||||||
if fieldModel.DescriptionOut == "" {
|
if fieldModel.DescriptionOut == "" {
|
||||||
fieldModel.DescriptionOut = "-"
|
fieldModel.DescriptionOut = "-"
|
||||||
@ -65,7 +67,7 @@ func GetCDSViewModelFields(TechnicalName string) (*[]CDSViewFieldModel, error) {
|
|||||||
return &fieldsModel, nil
|
return &fieldsModel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCDSViewModel(TechnicalName string) (*CDSViewModel, error) {
|
func GetCDSViewModel(TechnicalName table.TechnicalName) (*CDSViewModel, error) {
|
||||||
var model CDSViewModel
|
var model CDSViewModel
|
||||||
|
|
||||||
cdsView, err := table.GetCDSView(TechnicalName)
|
cdsView, err := table.GetCDSView(TechnicalName)
|
||||||
@ -75,8 +77,8 @@ func GetCDSViewModel(TechnicalName string) (*CDSViewModel, error) {
|
|||||||
|
|
||||||
model.CDSView = cdsView
|
model.CDSView = cdsView
|
||||||
|
|
||||||
model.StateTitle = englishCases.String(model.State)
|
model.StateTitle = table.State(englishCases.String(model.State.String()))
|
||||||
model.TechnicalNameEncoded = strings.Replace(base64.StdEncoding.EncodeToString([]byte(model.TechnicalName)), "=", "", -1)
|
model.TechnicalNameEncoded = table.TechnicalName(strings.Replace(base64.StdEncoding.EncodeToString([]byte(model.TechnicalName)), "=", "", -1))
|
||||||
|
|
||||||
model.NumberOfFields, err = table.GetCDSViewNumberOfFields(TechnicalName)
|
model.NumberOfFields, err = table.GetCDSViewNumberOfFields(TechnicalName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -18,37 +18,23 @@
|
|||||||
package router
|
package router
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"api-cds-search/cmd/handler"
|
|
||||||
"api-cds-search/cmd/search"
|
|
||||||
"api-cds-search/cmd/ui"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.achtarmig.org/pas/ui/context"
|
||||||
|
"code.achtarmig.org/pas/ui/route"
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Load() *chi.Mux {
|
func Load() *chi.Mux {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Group(func(r chi.Router) {
|
r.Group(func(r chi.Router) {
|
||||||
ui.Load()
|
r.Use(context.Middleware)
|
||||||
r.Get("/", handleRoot)
|
route.Routes(r)
|
||||||
r.Route("/search", func(r chi.Router) {
|
})
|
||||||
r.Get("/", search.HandleSearch)
|
|
||||||
})
|
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
r.Route("/cds", func(r chi.Router) {
|
http.Redirect(w, r, "/search", http.StatusFound)
|
||||||
r.Get("/field/", handler.GetCDSField)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
r.Handle("/ui/css/*", http.StripPrefix("/ui/css", http.FileServer(http.Dir("./cmd/ui/css"))))
|
|
||||||
r.Handle("/ui/svg/*", http.StripPrefix("/ui/svg", http.FileServer(http.Dir("./cmd/ui/svg"))))
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRoot(w http.ResponseWriter, r *http.Request) {
|
|
||||||
err := ui.Template.ExecuteTemplate(w, "main", nil)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
package search
|
package search
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"api-cds-search/cmd/database/table"
|
||||||
"api-cds-search/cmd/model"
|
"api-cds-search/cmd/model"
|
||||||
"api-cds-search/cmd/ui"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -33,7 +33,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type fuzzyResult struct {
|
type fuzzyResult struct {
|
||||||
CDSViewTechnicalName string
|
CDSViewTechnicalName table.TechnicalName
|
||||||
hits int
|
hits int
|
||||||
perfectHits int
|
perfectHits int
|
||||||
averageDistance float32
|
averageDistance float32
|
||||||
@ -43,7 +43,6 @@ type fuzzyResult struct {
|
|||||||
type resultBuffer struct {
|
type resultBuffer struct {
|
||||||
createdAt time.Time
|
createdAt time.Time
|
||||||
hits int
|
hits int
|
||||||
mu sync.Mutex
|
|
||||||
results []fuzzyResult
|
results []fuzzyResult
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,8 +53,9 @@ var (
|
|||||||
ErrNoResults = errors.New("no results")
|
ErrNoResults = errors.New("no results")
|
||||||
)
|
)
|
||||||
|
|
||||||
var searchTargets map[string][]string = make(map[string][]string)
|
var searchTargets map[table.TechnicalName][]string = make(map[table.TechnicalName][]string)
|
||||||
var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer)
|
var resultsBuffer map[string]*resultBuffer = make(map[string]*resultBuffer)
|
||||||
|
var resultsBufferMu sync.Mutex
|
||||||
|
|
||||||
func Load() error {
|
func Load() error {
|
||||||
keywords, err := model.GetKeywordsModel()
|
keywords, err := model.GetKeywordsModel()
|
||||||
@ -82,8 +82,8 @@ func Load() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetSearchTerm(r *http.Request) string {
|
func GetSearchTerm(q string) string {
|
||||||
return strings.ToLower(r.URL.Query().Get("q"))
|
return strings.ToLower(q)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPage(r *http.Request) int {
|
func GetPage(r *http.Request) int {
|
||||||
@ -95,6 +95,9 @@ func GetPage(r *http.Request) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetBuffer(searchTerm string) (*resultBuffer, error) {
|
func GetBuffer(searchTerm string) (*resultBuffer, error) {
|
||||||
|
resultsBufferMu.Lock()
|
||||||
|
defer resultsBufferMu.Unlock()
|
||||||
|
|
||||||
buffer, exists := resultsBuffer[searchTerm]
|
buffer, exists := resultsBuffer[searchTerm]
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -186,35 +189,18 @@ func BuildBuffer(searchTerm string) error {
|
|||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
resultsBufferMu.Lock()
|
||||||
resultsBuffer[searchTerm] = &resultBuffer{
|
resultsBuffer[searchTerm] = &resultBuffer{
|
||||||
createdAt: time.Now(),
|
createdAt: time.Now(),
|
||||||
hits: hits,
|
hits: hits,
|
||||||
results: fuzzyResults,
|
results: fuzzyResults,
|
||||||
}
|
}
|
||||||
|
resultsBufferMu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
func GetPages(buffer *resultBuffer, p int) (int, int) {
|
||||||
searchTerm := GetSearchTerm(r)
|
|
||||||
|
|
||||||
buffer, err := GetBuffer(searchTerm)
|
|
||||||
if err != nil {
|
|
||||||
err := BuildBuffer(searchTerm)
|
|
||||||
if err != nil {
|
|
||||||
err := ui.Template.ExecuteTemplate(w, "search-placeholder-no-result", nil)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buffer, _ = GetBuffer(searchTerm)
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.mu.Lock()
|
|
||||||
defer buffer.mu.Unlock()
|
|
||||||
|
|
||||||
page := GetPage(r)
|
|
||||||
var maxPage int
|
var maxPage int
|
||||||
if buffer.hits <= pageStep {
|
if buffer.hits <= pageStep {
|
||||||
maxPage = 0
|
maxPage = 0
|
||||||
@ -222,10 +208,15 @@ func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
maxPage = (buffer.hits / pageStep)
|
maxPage = (buffer.hits / pageStep)
|
||||||
}
|
}
|
||||||
|
|
||||||
if page+1 > maxPage {
|
if p+1 > maxPage {
|
||||||
page = maxPage
|
return maxPage, maxPage
|
||||||
|
} else if p < 0 {
|
||||||
|
return 0, maxPage
|
||||||
}
|
}
|
||||||
|
return p, maxPage
|
||||||
|
}
|
||||||
|
|
||||||
|
func Search(buffer *resultBuffer, page int, maxPage int) *model.ResultsModel {
|
||||||
resultsModel := model.NewResultsModel()
|
resultsModel := model.NewResultsModel()
|
||||||
for i := page * pageStep; i < page*pageStep+pageStep; i++ {
|
for i := page * pageStep; i < page*pageStep+pageStep; i++ {
|
||||||
if i > len(buffer.results)-1 {
|
if i > len(buffer.results)-1 {
|
||||||
@ -241,23 +232,77 @@ func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
|||||||
resultsModel.AppendResultsModelViews(cdsView, result.score)
|
resultsModel.AppendResultsModelViews(cdsView, result.score)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultsModel.SearchTerm = searchTerm
|
|
||||||
resultsModel.CurrentPage = page
|
resultsModel.CurrentPage = page
|
||||||
resultsModel.MaxPage = maxPage
|
resultsModel.MaxPage = maxPage
|
||||||
|
|
||||||
err = ui.Template.ExecuteTemplate(w, "results", resultsModel)
|
return resultsModel
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func HandleSearch(w http.ResponseWriter, r *http.Request) {
|
||||||
|
searchTerm := GetSearchTerm(r)
|
||||||
|
|
||||||
|
buffer, err := GetBuffer(searchTerm)
|
||||||
|
if err != nil {
|
||||||
|
err := BuildBuffer(searchTerm)
|
||||||
|
if err != nil {
|
||||||
|
err := ui.Template.ExecuteTemplate(w, "search-placeholder-no-result", nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buffer, _ = GetBuffer(searchTerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.mu.Lock()
|
||||||
|
defer buffer.mu.Unlock()
|
||||||
|
|
||||||
|
page := GetPage(r)
|
||||||
|
var maxPage int
|
||||||
|
if buffer.hits <= pageStep {
|
||||||
|
maxPage = 0
|
||||||
|
} else {
|
||||||
|
maxPage = (buffer.hits / pageStep)
|
||||||
|
}
|
||||||
|
|
||||||
|
if page+1 > maxPage {
|
||||||
|
page = maxPage
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsModel := model.NewResultsModel()
|
||||||
|
for i := page * pageStep; i < page*pageStep+pageStep; i++ {
|
||||||
|
if i > len(buffer.results)-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result := buffer.results[i]
|
||||||
|
cdsView, err := model.GetCDSViewModel(result.CDSViewTechnicalName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsModel.AppendResultsModelViews(cdsView, result.score)
|
||||||
|
}
|
||||||
|
|
||||||
|
resultsModel.SearchTerm = searchTerm
|
||||||
|
resultsModel.CurrentPage = page
|
||||||
|
resultsModel.MaxPage = maxPage
|
||||||
|
|
||||||
|
err = ui.Template.ExecuteTemplate(w, "results", resultsModel)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
func ClearOldBuffers() {
|
func ClearOldBuffers() {
|
||||||
|
resultsBufferMu.Lock()
|
||||||
|
defer resultsBufferMu.Unlock()
|
||||||
|
|
||||||
for key, buffer := range resultsBuffer {
|
for key, buffer := range resultsBuffer {
|
||||||
if time.Since(buffer.createdAt) >= time.Minute*10 {
|
if time.Since(buffer.createdAt) >= time.Minute*10 {
|
||||||
buffer.mu.Lock()
|
|
||||||
delete(resultsBuffer, key)
|
delete(resultsBuffer, key)
|
||||||
buffer.mu.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,12 @@
|
|||||||
--yellow-5: #e5a50a;
|
--yellow-5: #e5a50a;
|
||||||
|
|
||||||
--default-margin: 8px var(--base_margin) 8px var(--base_margin);
|
--default-margin: 8px var(--base_margin) 8px var(--base_margin);
|
||||||
|
|
||||||
|
--default-outline: transparent solid 2px;
|
||||||
|
--default-outline-offset: 1px;
|
||||||
|
|
||||||
|
--transiton-outline:
|
||||||
|
outline-offset 0.1s ease-in-out, outline-color 0.1s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
@ -313,13 +319,9 @@ button:active {
|
|||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
outline-offset: 1px;
|
outline-offset: var(--default-outline-offset);
|
||||||
outline-style: solid;
|
outline: var(--default-outline);
|
||||||
outline-width: 2px;
|
transition: var(--transiton-outline);
|
||||||
outline-color: transparent;
|
|
||||||
transition:
|
|
||||||
outline-offset 0.1s ease-in-out,
|
|
||||||
outline-color 0.1s ease-in-out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*:focus-visible {
|
*:focus-visible {
|
||||||
@ -412,17 +414,33 @@ button:active {
|
|||||||
width: 0%;
|
width: 0%;
|
||||||
height: 0%;
|
height: 0%;
|
||||||
margin: 0%;
|
margin: 0%;
|
||||||
visibility: hidden;
|
border: none;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.boxed-list > .expander {
|
.boxed-list > .expander {
|
||||||
border-bottom-color: transparent !important;
|
border-bottom-color: transparent !important;
|
||||||
transition: border-bottom 0.15s steps(1, end);
|
transition:
|
||||||
|
var(--transiton-outline),
|
||||||
|
border-bottom 0.15s steps(1, end),
|
||||||
|
border-bottom-right-radius 0.05s 0.1s,
|
||||||
|
border-bottom-left-radius 0.05s 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boxed-list > .active.expander:focus-within {
|
||||||
|
outline-offset: -1px;
|
||||||
|
outline-color: color-mix(in srgb, var(--accent) 50%, transparent);
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expander:has(> .expander-state:checked) {
|
.expander:has(> .expander-state:checked) {
|
||||||
border-bottom: 1px solid var(--border) !important;
|
border-bottom: 1px solid var(--border) !important;
|
||||||
transition: border-bottom 0.15s steps(1, start);
|
transition:
|
||||||
|
var(--transiton-outline),
|
||||||
|
border-bottom 0.15s steps(1, start),
|
||||||
|
border-bottom-right-radius 0.05s,
|
||||||
|
border-bottom-left-radius 0.05s;
|
||||||
|
transition-delay: 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.expander-content {
|
.expander-content {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
{{end}} {{block "result" .}}
|
{{end}} {{block "result" .}}
|
||||||
<label class="active expander" for="{{.TechnicalNameEncoded}}">
|
<label class="active expander" for="{{.TechnicalNameEncoded}}">
|
||||||
<input
|
<input
|
||||||
|
tabindex="0"
|
||||||
class="expander-state"
|
class="expander-state"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="{{.TechnicalNameEncoded}}"
|
id="{{.TechnicalNameEncoded}}"
|
||||||
@ -25,7 +26,7 @@
|
|||||||
{{.StateTitle}}
|
{{.StateTitle}}
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<div class="expander-content">
|
<div class="expander-content" tabindex="none">
|
||||||
<div class="label heading">Field Properties</div>
|
<div class="label heading">Field Properties</div>
|
||||||
<div class="container-center">
|
<div class="container-center">
|
||||||
{{template "fields-placeholder" dict "Result" .}}
|
{{template "fields-placeholder" dict "Result" .}}
|
||||||
|
89
cmd/view/cdsDetails.go
Normal file
89
cmd/view/cdsDetails.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"api-cds-search/cmd/model"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
db_table "api-cds-search/cmd/database/table"
|
||||||
|
|
||||||
|
"code.achtarmig.org/pas/ui/element"
|
||||||
|
"code.achtarmig.org/pas/ui/element/container"
|
||||||
|
"code.achtarmig.org/pas/ui/element/form"
|
||||||
|
"code.achtarmig.org/pas/ui/element/input"
|
||||||
|
"code.achtarmig.org/pas/ui/element/option"
|
||||||
|
"code.achtarmig.org/pas/ui/element/table"
|
||||||
|
"code.achtarmig.org/pas/ui/element/view"
|
||||||
|
"code.achtarmig.org/pas/ui/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
func cdsDetails() {
|
||||||
|
view.New("CDSDetails", func(p *view.Element) {
|
||||||
|
route.Register("/cds", p)
|
||||||
|
p.Title = "CDSDetails"
|
||||||
|
p.OnRequest = onRequestDetails
|
||||||
|
container.New(p, "Details", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
form.New(p, "qForm", func(p *form.Element) {
|
||||||
|
p.Method = http.MethodGet
|
||||||
|
p.Target = "#DetailsTable"
|
||||||
|
p.URL = "/cds"
|
||||||
|
p.PushURL = true
|
||||||
|
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
||||||
|
p.ValidateInput = true
|
||||||
|
p.RequestDelay = 200
|
||||||
|
p.Parameter = true
|
||||||
|
p.Placeholder = "CDSViewTechnicalName"
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
|
p.OnValidate = func(e *input.Element, v any) error {
|
||||||
|
_, err := db_table.GetCDSView(db_table.TechnicalName(v.(string)))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
container.New(p, "", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
table.New(p, "DetailsTable", func(p *table.Element) {
|
||||||
|
p.MinWidth = 20
|
||||||
|
p.MaxWidth = 32
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func onRequestDetails(e *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
||||||
|
cds, _ := e.GetElement("q").(*input.Element).GetDataString()
|
||||||
|
|
||||||
|
e.Title = fmt.Sprintf("CDSDetails - %s", cds)
|
||||||
|
|
||||||
|
fields, err := model.GetCDSViewModelFields(cds)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return view.ProcessElements{}
|
||||||
|
}
|
||||||
|
|
||||||
|
table := e.GetElement("DetailsTable").(*table.Element)
|
||||||
|
|
||||||
|
table.SetData(*fields)
|
||||||
|
|
||||||
|
for _, header := range table.Data.Header {
|
||||||
|
switch header.Name {
|
||||||
|
case "FieldNameOut":
|
||||||
|
header.Option.Font = option.FontMonospaced
|
||||||
|
case "DataTypeTitle":
|
||||||
|
case "FieldLengthOut":
|
||||||
|
header.Option.Font = option.FontMonospaced
|
||||||
|
case "DescriptionOut":
|
||||||
|
default:
|
||||||
|
header.Option.Hidden = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table.Data.SetFieldOrder("FieldNameOut", "DescriptionOut", "DataTypeTitle", "FieldLengthOut")
|
||||||
|
|
||||||
|
return view.ProcessElements{
|
||||||
|
Partial: []element.ElementInterface{e.GetElement("DetailsTable")},
|
||||||
|
}
|
||||||
|
}
|
6
cmd/view/load.go
Normal file
6
cmd/view/load.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
func Load() {
|
||||||
|
searchView()
|
||||||
|
cdsDetails()
|
||||||
|
}
|
67
cmd/view/results.go
Normal file
67
cmd/view/results.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"api-cds-search/cmd/model"
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"code.achtarmig.org/pas/ui/element"
|
||||||
|
"code.achtarmig.org/pas/ui/element/boxedlist"
|
||||||
|
"code.achtarmig.org/pas/ui/element/boxedlist/expander"
|
||||||
|
"code.achtarmig.org/pas/ui/element/input"
|
||||||
|
"code.achtarmig.org/pas/ui/element/option"
|
||||||
|
"code.achtarmig.org/pas/ui/element/placeholder"
|
||||||
|
"code.achtarmig.org/pas/ui/element/view"
|
||||||
|
)
|
||||||
|
|
||||||
|
func results(v *view.Element, m *model.ResultsModel) element.ElementInterface {
|
||||||
|
cont := v.GetElement("SearchResultsContainer")
|
||||||
|
cont.DeleteAllChildren()
|
||||||
|
|
||||||
|
bl, _ := boxedlist.New(cont, "", func(p *boxedlist.Element) {
|
||||||
|
p.MaxWidth = 42
|
||||||
|
p.MinWidth = 20
|
||||||
|
for _, result := range m.Views {
|
||||||
|
expander.New(p, func(p *expander.Element) {
|
||||||
|
p.Title = result.DisplayName.String()
|
||||||
|
p.Subtitle = result.TechnicalName.String()
|
||||||
|
p.PlaceholderHeight = 21 * result.NumberOfFields
|
||||||
|
p.CallbackLazyLoad = func(e *expander.Element, w http.ResponseWriter, r *http.Request) error {
|
||||||
|
http.Redirect(w, r, "/cds?q="+url.QueryEscape(result.TechnicalName.String()), http.StatusSeeOther)
|
||||||
|
return errors.New("redirect")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.HasSuffix = true
|
||||||
|
input.New(p, "", input.TypeText, func(p *input.Element) {
|
||||||
|
p.Shape = option.ShapePill
|
||||||
|
p.MaxWidth = 6
|
||||||
|
p.Disabled = true
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
|
||||||
|
p.SetData(result.StateTitle.String())
|
||||||
|
|
||||||
|
if result.State == "RELEASED" {
|
||||||
|
p.DisplayStyle = option.DisplayStyleSuccess
|
||||||
|
} else {
|
||||||
|
p.DisplayStyle = option.DisplayStyleError
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return bl
|
||||||
|
}
|
||||||
|
|
||||||
|
func noResults(v *view.Element) element.ElementInterface {
|
||||||
|
cont := v.GetElement("SearchResultsContainer")
|
||||||
|
cont.DeleteAllChildren()
|
||||||
|
|
||||||
|
pl := placeholder.New(cont, "", func(p *placeholder.Element) {
|
||||||
|
p.Icon = option.IconLoupe
|
||||||
|
p.Title = "No Results"
|
||||||
|
p.Subtitle = "Try refining your search term"
|
||||||
|
})
|
||||||
|
|
||||||
|
return pl
|
||||||
|
}
|
155
cmd/view/search.go
Normal file
155
cmd/view/search.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package view
|
||||||
|
|
||||||
|
import (
|
||||||
|
"api-cds-search/cmd/search"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"code.achtarmig.org/pas/ui/element"
|
||||||
|
"code.achtarmig.org/pas/ui/element/button"
|
||||||
|
"code.achtarmig.org/pas/ui/element/container"
|
||||||
|
"code.achtarmig.org/pas/ui/element/form"
|
||||||
|
"code.achtarmig.org/pas/ui/element/input"
|
||||||
|
"code.achtarmig.org/pas/ui/element/option"
|
||||||
|
"code.achtarmig.org/pas/ui/element/placeholder"
|
||||||
|
"code.achtarmig.org/pas/ui/element/view"
|
||||||
|
"code.achtarmig.org/pas/ui/route"
|
||||||
|
)
|
||||||
|
|
||||||
|
func noSearch(v *view.Element) element.ElementInterface {
|
||||||
|
cont := v.GetElement("SearchResultsContainer")
|
||||||
|
cont.DeleteAllChildren()
|
||||||
|
|
||||||
|
pl := placeholder.New(cont, "", func(p *placeholder.Element) {
|
||||||
|
p.Icon = option.IconLoupe
|
||||||
|
p.Title = "Search CDS-Views"
|
||||||
|
p.Subtitle = "Find CDS-Views from the Business Accelerator Hub"
|
||||||
|
})
|
||||||
|
return pl
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchView() {
|
||||||
|
view.New("Search", func(p *view.Element) {
|
||||||
|
route.Register("/search", p)
|
||||||
|
p.Title = "CDS-Search"
|
||||||
|
p.OnRequest = func(v *view.Element, w http.ResponseWriter, r *http.Request) view.ProcessElements {
|
||||||
|
inputQuery := v.GetElement("q").(*input.Element)
|
||||||
|
inputQueryValue, _ := v.GetElement("q").(*input.Element).GetDataString()
|
||||||
|
|
||||||
|
searchTerm := search.GetSearchTerm(inputQueryValue)
|
||||||
|
if inputQueryValue == "" {
|
||||||
|
noSearch(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputPage := v.GetElement("p").(*input.Element)
|
||||||
|
inputMaxPage := v.GetElement("MaxPage").(*input.Element)
|
||||||
|
|
||||||
|
currentPage, _ := inputPage.GetDataInt()
|
||||||
|
|
||||||
|
if inputQuery.GetDataChanged() && inputQueryValue != "" {
|
||||||
|
buf, err := search.GetBuffer(searchTerm)
|
||||||
|
if err != nil {
|
||||||
|
search.BuildBuffer(searchTerm)
|
||||||
|
buf, err = search.GetBuffer(searchTerm)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
noResults(v)
|
||||||
|
} else {
|
||||||
|
currentPage, maxPage := search.GetPages(buf, currentPage)
|
||||||
|
|
||||||
|
m := search.Search(buf, currentPage, maxPage)
|
||||||
|
|
||||||
|
inputPage.Max = m.MaxPage
|
||||||
|
inputMaxPage.Min = m.MaxPage
|
||||||
|
inputMaxPage.Max = m.MaxPage
|
||||||
|
inputMaxPage.SetData(m.MaxPage)
|
||||||
|
|
||||||
|
results(v, m)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return view.ProcessElements{
|
||||||
|
Partial: []element.ElementInterface{v.GetElement("SearchResultsContainer")},
|
||||||
|
OOBUpdate: []element.ElementInterface{v.GetElement("pagerForm")},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
container.New(p, "MainContainer", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
form.New(p, "SearchForm", func(p *form.Element) {
|
||||||
|
p.Method = http.MethodGet
|
||||||
|
p.Target = "#SearchResultsContainer>*"
|
||||||
|
p.Swap = "outerHTML"
|
||||||
|
p.URL = "/search"
|
||||||
|
p.PushURL = true
|
||||||
|
p.Params = "q, p"
|
||||||
|
|
||||||
|
container.New(p, "SearchFormLinked", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
p.LinkedHorizontal = true
|
||||||
|
input.New(p, "q", input.TypeText, func(p *input.Element) {
|
||||||
|
p.Parameter = true
|
||||||
|
p.Placeholder = "Search here"
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
|
p.MinWidth = 10
|
||||||
|
p.MaxWidth = 36
|
||||||
|
})
|
||||||
|
|
||||||
|
button.New(p, "SearchButton", func(p *button.Element) {
|
||||||
|
p.Label = "Search"
|
||||||
|
p.DisplayStyle = option.DisplayStyleSuggestedAction
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
|
p.MaxWidth = 6
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
container.New(p, "CenterPager", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
container.New(p, "RightCenterPager", func(p *container.Element) {
|
||||||
|
p.MaxWidth = 42
|
||||||
|
p.MinWidth = 10
|
||||||
|
p.Justify = option.JustifyRight
|
||||||
|
form.New(p, "pagerForm", func(p *form.Element) {
|
||||||
|
p.Method = http.MethodGet
|
||||||
|
p.Include = "[name='q'], [name='p']"
|
||||||
|
p.Target = "#SearchResultsContainer>*"
|
||||||
|
p.URL = "/search"
|
||||||
|
p.Swap = "outerHTML"
|
||||||
|
p.Trigger = "change from:#p"
|
||||||
|
p.PushURL = true
|
||||||
|
container.New(p, "LinkPager", func(p *container.Element) {
|
||||||
|
p.LinkedHorizontal = true
|
||||||
|
p.Justify = option.JustifyRight
|
||||||
|
input.New(p, "p", input.TypeNumber, func(p *input.Element) {
|
||||||
|
p.Parameter = true
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
|
||||||
|
p.SetMin = true
|
||||||
|
p.Min = 0
|
||||||
|
|
||||||
|
p.SetMax = true
|
||||||
|
p.Max = 0
|
||||||
|
|
||||||
|
p.MinWidth = 4
|
||||||
|
})
|
||||||
|
input.New(p, "MaxPage", input.TypeNumber, func(p *input.Element) {
|
||||||
|
p.Disabled = true
|
||||||
|
p.Shape = option.ShapeRound
|
||||||
|
p.MinWidth = 4
|
||||||
|
p.SetMin = true
|
||||||
|
p.SetMax = true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
container.New(p, "SearchResultsContainer", func(p *container.Element) {
|
||||||
|
p.Justify = option.JustifyCenter
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
image: "code.achtarmig.org/sap/api-cds-search"
|
image: "code.achtarmig.org/sap/api-cds-search:pas-ui-latest"
|
||||||
volumes:
|
volumes:
|
||||||
- ./db.sqlite:/app/db.sqlite
|
- ./db.sqlite:/app/db.sqlite
|
||||||
ports:
|
ports:
|
||||||
|
3
go.mod
3
go.mod
@ -3,6 +3,7 @@ module api-cds-search
|
|||||||
go 1.23.7
|
go 1.23.7
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523191647-bc6f223e5725
|
||||||
github.com/glebarez/go-sqlite v1.22.0
|
github.com/glebarez/go-sqlite v1.22.0
|
||||||
github.com/go-chi/chi v1.5.5
|
github.com/go-chi/chi v1.5.5
|
||||||
github.com/lithammer/fuzzysearch v1.1.8
|
github.com/lithammer/fuzzysearch v1.1.8
|
||||||
@ -11,7 +12,7 @@ require (
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/google/uuid v1.5.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
golang.org/x/sys v0.15.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
|
16
go.sum
16
go.sum
@ -1,3 +1,15 @@
|
|||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa h1:rMY1ksN8wtFO8ago/jMEgz52F/pFQIZLfN1YFSyTeEg=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250520194351-de0cc4fbbcfa/go.mod h1:4SnZejrC0bLr2Kq1yt4TBis8lo2hHA8K0m/0B2l6Uo0=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776 h1:VM1tir3yk+4iuJbDRVPmsLjrk1rJ+gTRPJh6LFTMMh0=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523170703-92fa955c1776/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d h1:/vt1BgqVi8fW7cdZWGMOfMarIRkrQuBMBdKhwEziaGI=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523172517-f2c3e6308b7d/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3 h1:Lsq0qH31XZG5b9j51y9vmXx+ZBmLD5H64aPlNBVjDfc=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523174346-c02bf81e9cf3/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523190341-a0f627648964 h1:/VlNxxqZZRBuvMFbvQN6qlyrR4RooFxUkansW3T0XG8=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523190341-a0f627648964/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523191647-bc6f223e5725 h1:VWlyudT8UDzl5Ns+NYO7xrUsKQcKvmkRkwsnHmRO+a4=
|
||||||
|
code.achtarmig.org/pas/ui v0.0.0-20250523191647-bc6f223e5725/go.mod h1:2stpDl/L6Zgd4a/r+fDMsMMuF59aKQbAIeByX1UqiQo=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
|
||||||
@ -6,8 +18,8 @@ github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
|||||||
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||||
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
15
main.go
15
main.go
@ -5,20 +5,33 @@ import (
|
|||||||
"api-cds-search/cmd/router"
|
"api-cds-search/cmd/router"
|
||||||
"api-cds-search/cmd/search"
|
"api-cds-search/cmd/search"
|
||||||
"api-cds-search/cmd/ui"
|
"api-cds-search/cmd/ui"
|
||||||
|
"api-cds-search/cmd/view"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"code.achtarmig.org/pas/ui/initialize"
|
||||||
|
"code.achtarmig.org/pas/ui/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
initialize.Init()
|
||||||
|
|
||||||
database.Load()
|
database.Load()
|
||||||
|
|
||||||
err := search.Load()
|
err := search.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.Load()
|
ui.Load()
|
||||||
|
view.Load()
|
||||||
|
log.SetPrintError(true)
|
||||||
|
|
||||||
r := router.Load()
|
r := router.Load()
|
||||||
|
|
||||||
fmt.Println("Listening on :8080")
|
fmt.Println("Listening on :8080")
|
||||||
http.ListenAndServe(":8080", r)
|
err = http.ListenAndServe(":8080", r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user