Add importer service

This commit is contained in:
Anan
2024-10-15 04:49:08 +00:00
committed by LinkinStars
parent 4c9e37fa6e
commit 5aad9e95b5
5 changed files with 203 additions and 217 deletions
+3 -1
View File
@@ -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)
-5
View File
@@ -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) {
@@ -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")
}
@@ -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 {
+8 -2
View File
@@ -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 (