Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kb): knowldge base crud endpoints #19

Merged
merged 5 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/influxdata/influxdb-client-go/v2 v2.12.3
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240527081147-f7cc66e329f9
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240530063823-abcb1c583452
github.com/instill-ai/usage-client v0.3.0-alpha.0.20240319060111-4a3a39f2fd61
github.com/instill-ai/x v0.3.0-alpha.0.20231219052200-6230a89e386c
github.com/knadh/koanf v1.5.0
Expand All @@ -38,6 +38,8 @@ require (
gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11
)

require github.com/google/uuid v1.6.0 // indirect

require (
github.com/catalinc/hashcash v0.0.0-20220723060415-5e3ec3e24f67 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
Expand Down Expand Up @@ -282,8 +284,8 @@ github.com/influxdata/influxdb-client-go/v2 v2.12.3 h1:28nRlNMRIV4QbtIUvxhWqaxn0
github.com/influxdata/influxdb-client-go/v2 v2.12.3/go.mod h1:IrrLUbCjjfkmRuaCiGQg4m2GbkaeJDcuWoxiWdQEbA0=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240527081147-f7cc66e329f9 h1:bjvRlomNglZpKsxd5Fv+YhoRyFNAD56hVVReesaXqdI=
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240527081147-f7cc66e329f9/go.mod h1:2blmpUwiTwxIDnrjIqT6FhR5ewshZZF554wzjXFvKpQ=
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240530063823-abcb1c583452 h1:uAkZouMby0pjaH0spCa5nUHMIKGjAxaBXRSVhe35M44=
github.com/instill-ai/protogen-go v0.3.3-alpha.0.20240530063823-abcb1c583452/go.mod h1:2blmpUwiTwxIDnrjIqT6FhR5ewshZZF554wzjXFvKpQ=
github.com/instill-ai/usage-client v0.3.0-alpha.0.20240319060111-4a3a39f2fd61 h1:smPTvmXDhn/QC7y/TPXyMTqbbRd0gvzmFgWBChwTfhE=
github.com/instill-ai/usage-client v0.3.0-alpha.0.20240319060111-4a3a39f2fd61/go.mod h1:/TAHs4ybuylk5icuy+MQtHRc4XUnIyXzeNKxX9qDFhw=
github.com/instill-ai/x v0.3.0-alpha.0.20231219052200-6230a89e386c h1:a2RVkpIV2QcrGnSHAou+t/L+vBsaIfFvk5inVg5Uh4s=
Expand Down
2 changes: 2 additions & 0 deletions pkg/constant/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ const MaxPayloadSize = 1024 * 1024 * 32

// Constants for resource owner
const DefaultUserID string = "admin"

// Header keys
const HeaderUserUIDKey = "Instill-User-Uid"
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ CREATE TABLE knowledge_base (
owner VARCHAR(255) NOT NULL,
create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
delete_time TIMESTAMP
delete_time TIMESTAMP,
CONSTRAINT unique_owner_name UNIQUE (owner, name)
);

COMMENT ON TABLE knowledge_base IS 'Table to store knowledge base information';
Expand Down
179 changes: 168 additions & 11 deletions pkg/handler/knowledgebase.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,186 @@ package handler
import (
"context"
"fmt"
"regexp"
"strings"

"google.golang.org/grpc/metadata"

"github.com/instill-ai/artifact-backend/pkg/constant"
"github.com/instill-ai/artifact-backend/pkg/repository"
artifactpb "github.com/instill-ai/protogen-go/artifact/artifact/v1alpha"
)

// todo: add error codes in the error message
const ErrorCreateKnowledgeBaseCode = 1000
const ErrorGetKnowledgeBasesCode = 1001
const ErrorUpdateKnowledgeBaseCode = 1002
const ErrorDeleteKnowledgeBaseCode = 1003

type ErrorMsg map[int]string
const ErrorCreateKnowledgeBaseMsg = "failed to create knowledge base: %v"
const ErrorListKnowledgeBasesMsg = "failed to get knowledge bases: %v "
const ErrorUpdateKnowledgeBaseMsg = "failed to update knowledge base: %v"
const ErrorDeleteKnowledgeBaseMsg = "failed to delete knowledge base: %v"

func (ph *PublicHandler) CreateKnowledgeBase(ctx context.Context, req *artifactpb.CreateKnowledgeBaseRequest) (*artifactpb.CreateKnowledgeBaseResponse, error) {
fmt.Println("CreateKnowledgeBase")
return nil, nil

uid, err := getUserIDFromContext(ctx)
if err != nil {
msg := fmt.Sprintf("failed to get user id from header: %v", err)
return nil, fmt.Errorf(ErrorCreateKnowledgeBaseMsg, msg)
}
// check name if it is empty
if req.Name == "" {
msg := "name is required"
return nil, fmt.Errorf(ErrorCreateKnowledgeBaseMsg, msg)
}
nameOk := isValidName(req.Name)
if !nameOk {
msg := "name is invalid: " + req.Name
return nil, fmt.Errorf(ErrorCreateKnowledgeBaseMsg, msg)
}
res, err := ph.service.Repository.CreateKnowledgeBase(ctx,
repository.KnowledgeBase{
Name: req.Name,
KbID: toIDStyle(req.Name),
Description: req.Description,
Tags: req.Tags,
Owner: uid,
},
)
if err != nil {
return nil, fmt.Errorf(fmt.Sprintf(ErrorCreateKnowledgeBaseMsg, err))
}
return &artifactpb.CreateKnowledgeBaseResponse{
Body: &artifactpb.KnowledgeBase{
Name: res.Name,
Id: res.KbID,
Description: res.Description,
Tags: res.Tags,
OwnerName: res.Owner,
CreateTime: res.CreateTime.String(),
UpdateTime: res.UpdateTime.String(),
}, ErrorMsg: "", StatusCode: 0,
}, nil
}
func (ph *PublicHandler) GetKnowledgeBases(ctx context.Context, req *artifactpb.GetKnowledgeBasesRequest) (*artifactpb.GetKnowledgeBasesResponse, error) {
fmt.Println("GetKnowledgeBases")
return nil, nil
func (ph *PublicHandler) ListKnowledgeBases(ctx context.Context, _ *artifactpb.ListKnowledgeBasesRequest) (*artifactpb.ListKnowledgeBasesResponse, error) {

// get user id from context
uid, err := getUserIDFromContext(ctx)
if err != nil {

return nil, fmt.Errorf(ErrorListKnowledgeBasesMsg, err)
}
res, err := ph.service.Repository.ListKnowledgeBases(ctx, uid)
if err != nil {
return nil, fmt.Errorf(ErrorListKnowledgeBasesMsg, err)
}

kbs := make([]*artifactpb.KnowledgeBase, len(res))
for i, kb := range res {
kbs[i] = &artifactpb.KnowledgeBase{
Name: kb.Name,
Id: kb.KbID,
Description: kb.Description,
Tags: kb.Tags,
CreateTime: kb.CreateTime.String(),
UpdateTime: kb.UpdateTime.String(),
OwnerName: kb.Owner,
}
}
return &artifactpb.ListKnowledgeBasesResponse{
Body: &artifactpb.KnowledgeBasesList{
KnowledgeBases: kbs,
},
ErrorMsg: "", StatusCode: 0,
}, nil
}
func (ph *PublicHandler) UpdateKnowledgeBase(ctx context.Context, req *artifactpb.UpdateKnowledgeBaseRequest) (*artifactpb.UpdateKnowledgeBaseResponse, error) {
fmt.Println("UpdateKnowledgeBase")
return nil, nil
uid, err := getUserIDFromContext(ctx)
if err != nil {

return nil, fmt.Errorf(ErrorUpdateKnowledgeBaseMsg, err)
}
// check name if it is empty
if req.Name == "" {
return nil, fmt.Errorf(ErrorUpdateKnowledgeBaseMsg, "name is required")
}
nameOk := isValidName(req.Name)
if !nameOk {
return nil, fmt.Errorf(ErrorUpdateKnowledgeBaseMsg, "name is invalid: "+req.Name)
}
// check if knowledge base exists
res, err := ph.service.Repository.UpdateKnowledgeBase(
ctx,
uid,
repository.KnowledgeBase{
Name: req.Name,
KbID: req.Id,
Description: req.Description,
Tags: req.Tags,
Owner: uid,
},
)
if err != nil {
return nil, fmt.Errorf(ErrorUpdateKnowledgeBaseMsg, err)
}
// populate response
return &artifactpb.UpdateKnowledgeBaseResponse{
Body: &artifactpb.KnowledgeBase{
Name: res.Name,
Id: res.KbID,
Description: res.Description,
Tags: res.Tags,
CreateTime: res.CreateTime.String(),
UpdateTime: res.UpdateTime.String(),
OwnerName: res.Owner,
}, ErrorMsg: "", StatusCode: 0,
}, nil
}
func (ph *PublicHandler) DeleteKnowledgeBase(ctx context.Context, req *artifactpb.DeleteKnowledgeBaseRequest) (*artifactpb.DeleteKnowledgeBaseResponse, error) {
// get header

uid, err := getUserIDFromContext(ctx)
if err != nil {

return nil, fmt.Errorf(ErrorDeleteKnowledgeBaseMsg, err)
}
err = ph.service.Repository.DeleteKnowledgeBase(ctx, uid, req.Id)
if err != nil {

return nil, fmt.Errorf(ErrorDeleteKnowledgeBaseMsg, err)
}
return &artifactpb.DeleteKnowledgeBaseResponse{
ErrorMsg: "", StatusCode: 0,
}, nil
}
func getUserIDFromContext(ctx context.Context) (string, error) {
md, _ := metadata.FromIncomingContext(ctx)
fmt.Println(md)
if v, ok := md[strings.ToLower(constant.HeaderUserUIDKey)]; ok {
return v[0], nil
}
return "", fmt.Errorf("user id not found in context")
}

func isValidName(name string) bool {
name = strings.ToLower(name) // Convert the name to lowercase for case-insensitive matching
// Define the regular expression pattern
pattern := `^[a-z0-9 _-]+$`
// Compile the regular expression
re := regexp.MustCompile(pattern)
// Match the name against the regular expression
return re.MatchString(name)
}

// toIDStyle converts a name to an ID style by replacing spaces with underscores
// and ensuring it only contains lowercase letters, underscores, and hyphens.
func toIDStyle(name string) string {

// Replace spaces with underscores
id := strings.ReplaceAll(name, " ", "_")

// Convert to lowercase
id = strings.ToLower(id)

fmt.Println("DeleteKnowledgeBase")
return nil, nil
return id
}
Loading
Loading