Add twirp RPC backend API for lure-web
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
parent
19ced9795c
commit
6cd0802f64
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
/lure
|
/lure
|
||||||
|
/lure-api
|
||||||
/dist/
|
/dist/
|
||||||
/internal/config/version.txt
|
/internal/config/version.txt
|
157
cmd/lure-api/api.go
Normal file
157
cmd/lure-api/api.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/genjidb/genji"
|
||||||
|
"github.com/genjidb/genji/document"
|
||||||
|
"github.com/genjidb/genji/types"
|
||||||
|
"go.arsenm.dev/lure/cmd/lure-api/internal/api"
|
||||||
|
"go.arsenm.dev/lure/internal/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type lureWebAPI struct {
|
||||||
|
db *genji.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lureWebAPI) CreateComment(ctx context.Context, req *api.CreateCommentRequest) (*api.CreateCommentResponse, error) {
|
||||||
|
count, err := db.CountComments(l.db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.InsertComment(l.db, db.Comment{
|
||||||
|
CommentID: count,
|
||||||
|
PackageName: req.PkgName,
|
||||||
|
PackageRepo: req.Repository,
|
||||||
|
TimeCreated: time.Now().Unix(),
|
||||||
|
Contents: req.Contents,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &api.CreateCommentResponse{CommentId: count}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lureWebAPI) EditComment(ctx context.Context, req *api.EditCommentRequest) (*api.EmptyResponse, error) {
|
||||||
|
doc, err := db.GetComment(l.db, "comment_id = ?", req.CommentId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var comment db.Comment
|
||||||
|
err = document.ScanDocument(doc, &comment)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
comment.Contents = req.NewContents
|
||||||
|
|
||||||
|
err = db.InsertComment(l.db, comment)
|
||||||
|
return &api.EmptyResponse{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lureWebAPI) GetComments(ctx context.Context, req *api.GetCommentsRequest) (*api.GetCommentsResponse, error) {
|
||||||
|
doc, err := db.GetComments(
|
||||||
|
l.db,
|
||||||
|
"package_repo = ? AND package_name = ? AND time_created >= ? LIMIT ?",
|
||||||
|
req.PkgName,
|
||||||
|
req.Repository,
|
||||||
|
req.CreatedSince,
|
||||||
|
req.Limit,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
out := &api.GetCommentsResponse{}
|
||||||
|
err = doc.Iterate(func(d types.Document) error {
|
||||||
|
comment := &api.Comment{}
|
||||||
|
err = document.ScanDocument(d, comment)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out.Comments = append(out.Comments, comment)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l lureWebAPI) Search(ctx context.Context, req *api.SearchRequest) (*api.SearchResponse, error) {
|
||||||
|
query := "(name LIKE ? OR description LIKE ? OR ? IN provides)"
|
||||||
|
args := []any{"%" + req.Query + "%", "%" + req.Query + "%", req.Query}
|
||||||
|
|
||||||
|
if req.FilterValue != nil && req.FilterType != api.FILTER_TYPE_NO_FILTER {
|
||||||
|
switch req.FilterType {
|
||||||
|
case api.FILTER_TYPE_IN_REPOSITORY:
|
||||||
|
query += " AND repository = ?"
|
||||||
|
case api.FILTER_TYPE_SUPPORTS_ARCH:
|
||||||
|
query += " AND ? IN architectures"
|
||||||
|
}
|
||||||
|
args = append(args, *req.FilterValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.SortBy != api.SORT_BY_UNSORTED {
|
||||||
|
switch req.SortBy {
|
||||||
|
case api.SORT_BY_NAME:
|
||||||
|
query += " ORDER BY name"
|
||||||
|
case api.SORT_BY_REPOSITORY:
|
||||||
|
query += " ORDER BY repository"
|
||||||
|
case api.SORT_BY_VERSION:
|
||||||
|
query += " ORDER BY version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Limit != 0 {
|
||||||
|
query += " LIMIT " + strconv.FormatInt(req.Limit, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
doc, err := db.GetPkgs(l.db, query, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(query, args)
|
||||||
|
out := &api.SearchResponse{}
|
||||||
|
err = doc.Iterate(func(d types.Document) error {
|
||||||
|
pkg := &db.Package{}
|
||||||
|
err = document.ScanDocument(d, pkg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out.Packages = append(out.Packages, &api.Package{
|
||||||
|
Name: pkg.Name,
|
||||||
|
Repository: pkg.Repository,
|
||||||
|
Version: pkg.Version,
|
||||||
|
Release: int64(pkg.Release),
|
||||||
|
Epoch: ptr(int64(pkg.Epoch)),
|
||||||
|
Description: &pkg.Description,
|
||||||
|
Homepage: &pkg.Homepage,
|
||||||
|
Maintainer: &pkg.Maintainer,
|
||||||
|
Architectures: pkg.Architectures,
|
||||||
|
Licenses: pkg.Licenses,
|
||||||
|
Provides: pkg.Provides,
|
||||||
|
Conflicts: pkg.Conflicts,
|
||||||
|
Replaces: pkg.Replaces,
|
||||||
|
Depends: dbMapToAPI(pkg.Depends),
|
||||||
|
BuildDepends: dbMapToAPI(pkg.BuildDepends),
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptr[T any](v T) *T {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbMapToAPI(m map[string][]string) map[string]*api.StringList {
|
||||||
|
out := make(map[string]*api.StringList, len(m))
|
||||||
|
for override, list := range m {
|
||||||
|
out[override] = &api.StringList{Entries: list}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
23
cmd/lure-api/db.go
Normal file
23
cmd/lure-api/db.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/genjidb/genji"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
"go.arsenm.dev/lure/internal/config"
|
||||||
|
"go.arsenm.dev/lure/internal/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
var gdb *genji.DB
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
gdb, err = genji.Open(config.DBPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error opening database").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
err = db.Init(gdb)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error initializing database").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
1202
cmd/lure-api/internal/api/lure.pb.go
Normal file
1202
cmd/lure-api/internal/api/lure.pb.go
Normal file
File diff suppressed because it is too large
Load Diff
1993
cmd/lure-api/internal/api/lure.twirp.go
Normal file
1993
cmd/lure-api/internal/api/lure.twirp.go
Normal file
File diff suppressed because it is too large
Load Diff
109
cmd/lure-api/lure.proto
Normal file
109
cmd/lure-api/lure.proto
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
package lure;
|
||||||
|
option go_package = "/internal/api";
|
||||||
|
|
||||||
|
// EmptyResponse is an empty API response
|
||||||
|
message EmptyResponse {}
|
||||||
|
|
||||||
|
message Comment {
|
||||||
|
int64 comment_id = 1;
|
||||||
|
int64 time_created = 2;
|
||||||
|
string contents = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCommentRequest is a request to create a comment
|
||||||
|
message CreateCommentRequest {
|
||||||
|
string repository = 1;
|
||||||
|
string pkg_name = 2;
|
||||||
|
string contents = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateCommentResponse is a response to CreateCommentRequest
|
||||||
|
message CreateCommentResponse {
|
||||||
|
int64 comment_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditCommentRequest is a request to edit a comment
|
||||||
|
message EditCommentRequest {
|
||||||
|
int64 comment_id = 1;
|
||||||
|
string new_contents = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditCommentRequest is a request to get comments on a package
|
||||||
|
message GetCommentsRequest {
|
||||||
|
string repository = 1;
|
||||||
|
string pkg_name = 2;
|
||||||
|
int64 created_since = 3;
|
||||||
|
int64 limit = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditCommentRequest is a response to GetCommentsRequest
|
||||||
|
message GetCommentsResponse {
|
||||||
|
repeated Comment comments = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SORT_BY represents possible things to sort packages by
|
||||||
|
enum SORT_BY {
|
||||||
|
UNSORTED = 0;
|
||||||
|
NAME = 1;
|
||||||
|
REPOSITORY = 2;
|
||||||
|
VERSION = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILTER_TYPE represents possible filters for packages
|
||||||
|
enum FILTER_TYPE {
|
||||||
|
NO_FILTER = 0;
|
||||||
|
IN_REPOSITORY = 1;
|
||||||
|
SUPPORTS_ARCH = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchRequest is a request to search for packages
|
||||||
|
message SearchRequest {
|
||||||
|
string query = 1;
|
||||||
|
int64 limit = 2;
|
||||||
|
SORT_BY sort_by = 3;
|
||||||
|
FILTER_TYPE filter_type = 4;
|
||||||
|
optional string filter_value = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringList contains a list of strings
|
||||||
|
message StringList {
|
||||||
|
repeated string entries = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Package represents a LURE package
|
||||||
|
message Package {
|
||||||
|
string name = 1;
|
||||||
|
string repository = 2;
|
||||||
|
string version = 3;
|
||||||
|
int64 release = 4;
|
||||||
|
optional int64 epoch = 5;
|
||||||
|
optional string description = 6;
|
||||||
|
optional string homepage = 7;
|
||||||
|
optional string maintainer = 8;
|
||||||
|
repeated string architectures = 9;
|
||||||
|
repeated string licenses = 10;
|
||||||
|
repeated string provides = 11;
|
||||||
|
repeated string conflicts = 12;
|
||||||
|
repeated string replaces = 13;
|
||||||
|
map<string, StringList> depends = 14;
|
||||||
|
map<string, StringList> build_depends = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SearchResponse contains returned packages
|
||||||
|
message SearchResponse {
|
||||||
|
repeated Package packages = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Web is the LURE Web service
|
||||||
|
service API {
|
||||||
|
// CreateComment creates a new comment on the given package
|
||||||
|
rpc CreateComment(CreateCommentRequest) returns (CreateCommentResponse);
|
||||||
|
// EditComment edits an existing comment
|
||||||
|
rpc EditComment(EditCommentRequest) returns (EmptyResponse);
|
||||||
|
// GetComments returns the comments on a particular package
|
||||||
|
rpc GetComments(GetCommentsRequest) returns (GetCommentsResponse);
|
||||||
|
|
||||||
|
// Search searches through LURE packages in the database
|
||||||
|
rpc Search(SearchRequest) returns (SearchResponse);
|
||||||
|
}
|
53
cmd/lure-api/main.go
Normal file
53
cmd/lure-api/main.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/twitchtv/twirp"
|
||||||
|
"go.arsenm.dev/logger"
|
||||||
|
"go.arsenm.dev/logger/log"
|
||||||
|
"go.arsenm.dev/lure/cmd/lure-api/internal/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate protoc --twirp_out=. lure.proto
|
||||||
|
//go:generate protoc --go_out=. lure.proto
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log.Logger = logger.NewPretty(os.Stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
addr := flag.String("a", ":8080", "Listen address for API server")
|
||||||
|
logFile := flag.String("l", "", "Output file for JSON log")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *logFile != "" {
|
||||||
|
fl, err := os.Create(*logFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error creating log file").Err(err).Send()
|
||||||
|
}
|
||||||
|
defer fl.Close()
|
||||||
|
|
||||||
|
log.Logger = logger.NewMulti(log.Logger, logger.NewJSON(fl))
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := api.NewAPIServer(
|
||||||
|
lureWebAPI{db: gdb},
|
||||||
|
twirp.WithServerPathPrefix(""),
|
||||||
|
)
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp", *addr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error starting listener").Err(err).Send()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Starting HTTP API server").Str("addr", ln.Addr().String()).Send()
|
||||||
|
|
||||||
|
err = http.Serve(ln, srv)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error while running server").Err(err).Send()
|
||||||
|
}
|
||||||
|
}
|
3
go.mod
3
go.mod
@ -18,10 +18,12 @@ require (
|
|||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/muesli/reflow v0.3.0
|
github.com/muesli/reflow v0.3.0
|
||||||
github.com/pelletier/go-toml/v2 v2.0.5
|
github.com/pelletier/go-toml/v2 v2.0.5
|
||||||
|
github.com/twitchtv/twirp v8.1.3+incompatible
|
||||||
github.com/urfave/cli/v2 v2.16.3
|
github.com/urfave/cli/v2 v2.16.3
|
||||||
go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334
|
go.arsenm.dev/logger v0.0.0-20221007032343-cbffce4f4334
|
||||||
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab
|
||||||
|
google.golang.org/protobuf v1.27.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
mvdan.cc/sh/v3 v3.5.1
|
mvdan.cc/sh/v3 v3.5.1
|
||||||
)
|
)
|
||||||
@ -108,6 +110,5 @@ require (
|
|||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
|
||||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||||
)
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -550,6 +550,8 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||||
|
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
||||||
|
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
|
||||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
@ -2,6 +2,7 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/genjidb/genji"
|
"github.com/genjidb/genji"
|
||||||
|
"github.com/genjidb/genji/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Package is a LURE package's database representation
|
// Package is a LURE package's database representation
|
||||||
@ -23,6 +24,15 @@ type Package struct {
|
|||||||
Repository string
|
Repository string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Package is a LURE Web comment's database representation
|
||||||
|
type Comment struct {
|
||||||
|
CommentID int64
|
||||||
|
PackageName string
|
||||||
|
PackageRepo string
|
||||||
|
TimeCreated int64
|
||||||
|
Contents string
|
||||||
|
}
|
||||||
|
|
||||||
// Init initializes the database
|
// Init initializes the database
|
||||||
func Init(db *genji.DB) error {
|
func Init(db *genji.DB) error {
|
||||||
return db.Exec(`
|
return db.Exec(`
|
||||||
@ -44,6 +54,16 @@ func Init(db *genji.DB) error {
|
|||||||
builddepends (...),
|
builddepends (...),
|
||||||
UNIQUE(name, repository)
|
UNIQUE(name, repository)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS comments (
|
||||||
|
comment_id INT PRIMARY KEY,
|
||||||
|
package_name TEXT NOT NULL,
|
||||||
|
package_repo TEXT NOT NULL,
|
||||||
|
time_created INT NOT NULL,
|
||||||
|
contents TEXT NOT NULL,
|
||||||
|
UNIQUE(comment_id),
|
||||||
|
UNIQUE(package_name, package_repo)
|
||||||
|
);
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,3 +85,42 @@ func GetPkgs(db *genji.DB, where string, args ...any) (*genji.Result, error) {
|
|||||||
func DeletePkgs(db *genji.DB, where string, args ...any) error {
|
func DeletePkgs(db *genji.DB, where string, args ...any) error {
|
||||||
return db.Exec("DELETE FROM pkgs WHERE "+where, args...)
|
return db.Exec("DELETE FROM pkgs WHERE "+where, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func InsertComment(db *genji.DB, c Comment) error {
|
||||||
|
return db.Exec("INSERT INTO comments VALUES ? ON CONFLICT DO REPLACE;", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountComments(db *genji.DB) (int64, error) {
|
||||||
|
doc, err := db.QueryDocument("SELECT count(*) FROM comments;")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
val, err := doc.GetByField("COUNT(*)")
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return val.V().(int64), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetComments returns a result containing comments that match the where conditions
|
||||||
|
func GetComments(db *genji.DB, where string, args ...any) (*genji.Result, error) {
|
||||||
|
stream, err := db.Query("SELECT * FROM comments WHERE "+where, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetComment returns a comment that matches the where conditions
|
||||||
|
func GetComment(db *genji.DB, where string, args ...any) (types.Document, error) {
|
||||||
|
doc, err := db.QueryDocument("SELECT * FROM comments WHERE "+where+" LIMIT 1", args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return doc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteComments deletes all comments matching the where conditions
|
||||||
|
func DeleteComments(db *genji.DB, where string, args ...any) error {
|
||||||
|
return db.Exec("DELETE FROM comments WHERE "+where, args...)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user