This is an automated email from the ASF dual-hosted git repository. linkinstar pushed a commit to branch dev in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit 5aad9e95b572ae82ec12cfe9e2bab9dd52d7e51d Author: Anan <[email protected]> AuthorDate: Tue Oct 15 04:49:08 2024 +0000 Add importer service --- cmd/wire_gen.go | 4 +- internal/router/plugin_api_router.go | 5 - .../importer/importer_service.go} | 390 ++++++++++----------- .../service/plugin_common/plugin_common_service.go | 11 + plugin/importer.go | 10 +- 5 files changed, 203 insertions(+), 217 deletions(-) diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index 537ece84..1ffa75ca 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -111,6 +111,7 @@ import ( "github.com/apache/incubator-answer/internal/service/user_common" user_external_login2 "github.com/apache/incubator-answer/internal/service/user_external_login" user_notification_config2 "github.com/apache/incubator-answer/internal/service/user_notification_config" + "github.com/apache/incubator-answer/internal/service/importer" "github.com/segmentfault/pacman" "github.com/segmentfault/pacman/log" ) @@ -253,7 +254,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database, roleController := controller_admin.NewRoleController(roleService) pluginConfigRepo := plugin_config.NewPluginConfigRepo(dataData) pluginUserConfigRepo := plugin_config.NewPluginUserConfigRepo(dataData) - pluginCommonService := plugin_common.NewPluginCommonService(pluginConfigRepo, pluginUserConfigRepo, configService, dataData) + importerService := importer.NewImporterService(questionService, rankService, userCommon) + pluginCommonService := plugin_common.NewPluginCommonService(pluginConfigRepo, pluginUserConfigRepo, configService, dataData, importerService) pluginController := controller_admin.NewPluginController(pluginCommonService) permissionController := controller.NewPermissionController(rankService) userPluginController := controller.NewUserPluginController(pluginCommonService) diff --git a/internal/router/plugin_api_router.go b/internal/router/plugin_api_router.go index 22a13b85..e69e0b6b 100644 --- a/internal/router/plugin_api_router.go +++ b/internal/router/plugin_api_router.go @@ -30,7 +30,6 @@ type PluginAPIRouter struct { captchaController *controller.CaptchaController embedController *controller.EmbedController renderController *controller.RenderController - importerController *controller.ImporterController } func NewPluginAPIRouter( @@ -39,7 +38,6 @@ func NewPluginAPIRouter( captchaController *controller.CaptchaController, embedController *controller.EmbedController, renderController *controller.RenderController, - importerController *controller.ImporterController, ) *PluginAPIRouter { return &PluginAPIRouter{ connectorController: connectorController, @@ -70,9 +68,6 @@ func (pr *PluginAPIRouter) RegisterUnAuthConnectorRouter(r *gin.RouterGroup) { r.GET("/captcha/config", pr.captchaController.GetCaptchaConfig) r.GET("/embed/config", pr.embedController.GetEmbedConfig) r.GET("/render/config", pr.renderController.GetRenderConfig) - - // importer plugin - r.POST("/importer/answer", pr.importerController.ImportCommand) } func (pr *PluginAPIRouter) RegisterAuthUserConnectorRouter(r *gin.RouterGroup) { diff --git a/internal/controller/plugin_importer_controller.go b/internal/service/importer/importer_service.go similarity index 54% rename from internal/controller/plugin_importer_controller.go rename to internal/service/importer/importer_service.go index 90532acf..668a2bf9 100644 --- a/internal/controller/plugin_importer_controller.go +++ b/internal/service/importer/importer_service.go @@ -1,209 +1,181 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package controller - -import ( - "bytes" - "io" - - "github.com/apache/incubator-answer/internal/base/handler" - "github.com/apache/incubator-answer/internal/base/middleware" - "github.com/apache/incubator-answer/internal/base/reason" - "github.com/apache/incubator-answer/internal/base/translator" - "github.com/apache/incubator-answer/internal/base/validator" - "github.com/apache/incubator-answer/internal/schema" - "github.com/apache/incubator-answer/internal/service/content" - "github.com/apache/incubator-answer/internal/service/permission" - "github.com/apache/incubator-answer/internal/service/rank" - usercommon "github.com/apache/incubator-answer/internal/service/user_common" - "github.com/apache/incubator-answer/plugin" - "github.com/gin-gonic/gin" - "github.com/segmentfault/pacman/errors" - "github.com/segmentfault/pacman/log" -) - -type ImporterController struct { - // userRepo *usercommon.UserRepo - questionService *content.QuestionService - rankService *rank.RankService - rateLimitMiddleware *middleware.RateLimitMiddleware - userCommon *usercommon.UserCommon -} - -func NewImporterController( - questionService *content.QuestionService, - rankService *rank.RankService, - rateLimitMiddleware *middleware.RateLimitMiddleware, - userCommon *usercommon.UserCommon, -) *ImporterController { - return &ImporterController{ - questionService: questionService, - rankService: rankService, - rateLimitMiddleware: rateLimitMiddleware, - userCommon: userCommon, - } -} - -// ImportCommand godoc -// @Summary ImportCommand -// @Description ImportCommand -// @Tags PluginImporter -// @Accept json -// @Produce json -// @Router /answer/api/v1/importer/command [post] -// @Success 200 {object} handler.RespBody{data=[]plugin.importStatus} -func (ipc *ImporterController) ImportCommand(ctx *gin.Context) { - questionInfo := &plugin.QuestionImporterInfo{} - if !plugin.ImporterEnabled() { - log.Errorf("error: plugin is not enabled") - ctx.JSON(200, gin.H{"text": "plugin is not enabled"}) - return - } - body, err := io.ReadAll(ctx.Request.Body) - ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body)) - cmd := ctx.PostForm("command") - if cmd != "/ask" { - log.Errorf("error: Invalid command") - ctx.JSON(200, gin.H{"text": "Invalid command"}) - return - } - _ = plugin.CallImporter(func(importer plugin.Importer) error { - ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body)) - questionInfo, err = importer.GetQuestion(ctx) - return nil - }) - if err != nil { - log.Errorf("error: %v", err) - ctx.JSON(200, gin.H{"text": err.Error()}) - return - } - - req := &schema.QuestionAdd{} - errFields := make([]*validator.FormErrorField, 0) - // errFields := handler.BindAndCheckReturnErr(ctx, req) - if ctx.IsAborted() { - return - } - - // reject, rejectKey := ipc.rateLimitMiddleware.DuplicateRequestRejection(ctx, req) - // if reject { - // return - // } - user_info, exist, err := ipc.userCommon.GetByEmail(ctx, questionInfo.UserEmail) - if err != nil { - log.Errorf("error: %v", err) - ctx.JSON(200, gin.H{"text": err.Error()}) - return - } - if !exist { - log.Errorf("error: User Email not found") - ctx.JSON(200, gin.H{"text": "User Email not found"}) - return - } - - // defer func() { - // // If status is not 200 means that the bad request has been returned, so the record should be cleared - // if ctx.Writer.Status() != http.StatusOK { - // ipc.rateLimitMiddleware.DuplicateRequestClear(ctx, rejectKey) - // } - // }() - req.UserID = user_info.ID - req.Title = questionInfo.Title - req.Content = questionInfo.Content - req.HTML = "<p>" + questionInfo.Content + "</p>" - req.Tags = make([]*schema.TagItem, len(questionInfo.Tags)) - for i, tag := range questionInfo.Tags { - req.Tags[i] = &schema.TagItem{ - SlugName: tag, - DisplayName: tag, - } - } - canList, requireRanks, err := ipc.rankService.CheckOperationPermissionsForRanks(ctx, req.UserID, []string{ - permission.QuestionAdd, - permission.QuestionEdit, - permission.QuestionDelete, - permission.QuestionClose, - permission.QuestionReopen, - permission.TagUseReservedTag, - permission.TagAdd, - permission.LinkUrlLimit, - }) - if err != nil { - log.Errorf("error: %v", err) - ctx.JSON(200, gin.H{"text": err.Error()}) - return - } - req.CanAdd = canList[0] - req.CanEdit = canList[1] - req.CanDelete = canList[2] - req.CanClose = canList[3] - req.CanReopen = canList[4] - req.CanUseReservedTag = canList[5] - req.CanAddTag = canList[6] - if !req.CanAdd { - ctx.JSON(200, gin.H{"text": "No permission to add question"}) - log.Errorf("error: %v", err) - return - } - hasNewTag, err := ipc.questionService.HasNewTag(ctx, req.Tags) - if err != nil { - log.Errorf("error: %v", err) - ctx.JSON(200, gin.H{"text": err.Error()}) - return - } - if !req.CanAddTag && hasNewTag { - lang := handler.GetLang(ctx) - msg := translator.TrWithData(lang, reason.NoEnoughRankToOperate, &schema.PermissionTrTplData{Rank: requireRanks[6]}) - log.Errorf("error: %v", msg) - ctx.JSON(200, gin.H{"text": msg}) - return - } - - errList, err := ipc.questionService.CheckAddQuestion(ctx, req) - if err != nil { - errlist, ok := errList.([]*validator.FormErrorField) - if ok { - errFields = append(errFields, errlist...) - } - } - if len(errFields) > 0 { - handler.HandleResponse(ctx, errors.BadRequest(reason.RequestFormatError), errFields) - log.Errorf("error: RequestFormatError") - ctx.JSON(200, gin.H{"text": "RequestFormatError"}) - return - } - req.UserAgent = ctx.GetHeader("User-Agent") - req.IP = ctx.ClientIP() - resp, err := ipc.questionService.AddQuestion(ctx, req) - if err != nil { - errlist, ok := resp.([]*validator.FormErrorField) - if ok { - errFields = append(errFields, errlist...) - } - } - - if len(errFields) > 0 { - log.Errorf("error: RequestFormatError") - ctx.JSON(200, gin.H{"text": "RequestFormatError"}) - return - } - - ctx.JSON(200, gin.H{"text": "Add Question Successfully"}) -} +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package importer + +import ( + "context" + "fmt" + + "github.com/apache/incubator-answer/internal/base/handler" + "github.com/apache/incubator-answer/internal/base/reason" + "github.com/apache/incubator-answer/internal/base/translator" + "github.com/apache/incubator-answer/internal/base/validator" + "github.com/apache/incubator-answer/internal/schema" + "github.com/apache/incubator-answer/internal/service/content" + "github.com/apache/incubator-answer/internal/service/permission" + "github.com/apache/incubator-answer/internal/service/rank" + usercommon "github.com/apache/incubator-answer/internal/service/user_common" + "github.com/apache/incubator-answer/plugin" + "github.com/gin-gonic/gin" + "github.com/segmentfault/pacman/errors" + "github.com/segmentfault/pacman/log" +) + +// ImporterService importer service +type ImporterService struct { + questionService *content.QuestionService + rankService *rank.RankService + userCommon *usercommon.UserCommon +} + +// NewRankService new rank service +func NewImporterService( + questionService *content.QuestionService, + rankService *rank.RankService, + userCommon *usercommon.UserCommon) *ImporterService { + return &ImporterService{ + questionService: questionService, + rankService: rankService, + userCommon: userCommon, + } +} + +type ImporterFunc struct { + importerService *ImporterService +} + +func (ipfunc *ImporterFunc) AddQuestion(ctx context.Context, questionInfo plugin.QuestionImporterInfo) { + println("Hello, World123!") + ipfunc.importerService.AddQuestion123(ctx, questionInfo) +} + +func (ip *ImporterService) NewImporterFunc() plugin.ImporterFunc { + return &ImporterFunc{importerService: ip} +} + +func (ip *ImporterService) AddQuestion123(ctx context.Context, questionInfo plugin.QuestionImporterInfo) { + fmt.Println("Call from func (ip *ImporterService) AddQuestion123(*****)") + // body, err := io.ReadAll(ctx.Request.Body) + // ctx.Request.Body = io.NopCloser(bytes.NewBuffer(body)) + fmt.Println("ImportPush") + + // if err != nil { + // log.Errorf("error: %v", err) + // return + // } + req := &schema.QuestionAdd{} + errFields := make([]*validator.FormErrorField, 0) + // reject, rejectKey := ipc.rateLimitMiddleware.DuplicateRequestRejection(ctx, req) + // if reject { + // return + // } + user_info, exist, err := ip.userCommon.GetByEmail(ctx, questionInfo.UserEmail) + if err != nil { + log.Errorf("error: %v", err) + return + } + if !exist { + log.Errorf("error: User Email not found") + return + } + + // defer func() { + // // If status is not 200 means that the bad request has been returned, so the record should be cleared + // if ctx.Writer.Status() != http.StatusOK { + // ipc.rateLimitMiddleware.DuplicateRequestClear(ctx, rejectKey) + // } + // }() + req.UserID = user_info.ID + req.Title = questionInfo.Title + req.Content = questionInfo.Content + req.HTML = "<p>" + questionInfo.Content + "</p>" + req.Tags = make([]*schema.TagItem, len(questionInfo.Tags)) + for i, tag := range questionInfo.Tags { + req.Tags[i] = &schema.TagItem{ + SlugName: tag, + DisplayName: tag, + } + } + canList, requireRanks, err := ip.rankService.CheckOperationPermissionsForRanks(ctx, req.UserID, []string{ + permission.QuestionAdd, + permission.QuestionEdit, + permission.QuestionDelete, + permission.QuestionClose, + permission.QuestionReopen, + permission.TagUseReservedTag, + permission.TagAdd, + permission.LinkUrlLimit, + }) + if err != nil { + log.Errorf("error: %v", err) + return + } + req.CanAdd = canList[0] + req.CanEdit = canList[1] + req.CanDelete = canList[2] + req.CanClose = canList[3] + req.CanReopen = canList[4] + req.CanUseReservedTag = canList[5] + req.CanAddTag = canList[6] + if !req.CanAdd { + log.Errorf("error: %v", err) + return + } + hasNewTag, err := ip.questionService.HasNewTag(ctx.(*gin.Context), req.Tags) + if err != nil { + log.Errorf("error: %v", err) + return + } + if !req.CanAddTag && hasNewTag { + lang := handler.GetLang(ctx.(*gin.Context)) + msg := translator.TrWithData(lang, reason.NoEnoughRankToOperate, &schema.PermissionTrTplData{Rank: requireRanks[6]}) + log.Errorf("error: %v", msg) + return + } + + errList, err := ip.questionService.CheckAddQuestion(ctx, req) + if err != nil { + errlist, ok := errList.([]*validator.FormErrorField) + if ok { + errFields = append(errFields, errlist...) + } + } + if len(errFields) > 0 { + handler.HandleResponse(ctx.(*gin.Context), errors.BadRequest(reason.RequestFormatError), errFields) + log.Errorf("error: RequestFormat Error") + return + } + ginCtx := ctx.(*gin.Context) + req.UserAgent = ginCtx.GetHeader("User-Agent") + req.IP = ginCtx.ClientIP() + resp, err := ip.questionService.AddQuestion(ctx, req) + if err != nil { + errlist, ok := resp.([]*validator.FormErrorField) + if ok { + errFields = append(errFields, errlist...) + } + } + + if len(errFields) > 0 { + log.Errorf("error: RequestFormatError") + return + } + log.Info("Add Question Successfully") +} diff --git a/internal/service/plugin_common/plugin_common_service.go b/internal/service/plugin_common/plugin_common_service.go index d064fa99..4f7cb30a 100644 --- a/internal/service/plugin_common/plugin_common_service.go +++ b/internal/service/plugin_common/plugin_common_service.go @@ -22,6 +22,7 @@ package plugin_common import ( "context" "encoding/json" + "fmt" "github.com/apache/incubator-answer/internal/base/data" "github.com/apache/incubator-answer/internal/repo/search_sync" @@ -34,6 +35,7 @@ import ( "github.com/apache/incubator-answer/internal/entity" "github.com/apache/incubator-answer/internal/schema" "github.com/apache/incubator-answer/internal/service/config" + "github.com/apache/incubator-answer/internal/service/importer" "github.com/apache/incubator-answer/plugin" ) @@ -56,6 +58,7 @@ type PluginCommonService struct { pluginConfigRepo PluginConfigRepo pluginUserConfigRepo PluginUserConfigRepo data *data.Data + importerService *importer.ImporterService } // NewPluginCommonService new report service @@ -64,6 +67,7 @@ func NewPluginCommonService( pluginUserConfigRepo PluginUserConfigRepo, configService *config.ConfigService, data *data.Data, + importerService *importer.ImporterService, ) *PluginCommonService { p := &PluginCommonService{ @@ -71,6 +75,7 @@ func NewPluginCommonService( pluginConfigRepo: pluginConfigRepo, pluginUserConfigRepo: pluginUserConfigRepo, data: data, + importerService: importerService, } p.initPluginData() return p @@ -99,6 +104,11 @@ func (ps *PluginCommonService) UpdatePluginConfig(ctx context.Context, req *sche } return nil }) + _ = plugin.CallImporter(func(importer plugin.Importer) error { + fmt.Println("更新时注册Importer") + importer.RegisterImporterFunc(ctx, ps.importerService.NewImporterFunc()) + return nil + }) return nil } @@ -126,6 +136,7 @@ func (ps *PluginCommonService) GetUserPluginConfig(ctx context.Context, req *sch } func (ps *PluginCommonService) initPluginData() { + fmt.Println("init plugin data") // init plugin status pluginStatus, err := ps.configService.GetStringValue(context.TODO(), constant.PluginStatus) if err != nil { diff --git a/plugin/importer.go b/plugin/importer.go index 79a84c50..4c25d72b 100644 --- a/plugin/importer.go +++ b/plugin/importer.go @@ -19,7 +19,9 @@ package plugin -import "github.com/gin-gonic/gin" +import ( + "context" +) type QuestionImporterInfo struct { Title string `json:"title"` @@ -30,7 +32,11 @@ type QuestionImporterInfo struct { type Importer interface { Base - GetQuestion(ctx *gin.Context) (questionInfo *QuestionImporterInfo, err error) + RegisterImporterFunc(ctx context.Context, importer ImporterFunc) +} + +type ImporterFunc interface { + AddQuestion(ctx context.Context, questionInfo QuestionImporterInfo) } var (
