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 4c9e37fa6e2af4a6cd6bf41ccea0cdb0634210ec
Author: Anan <[email protected]>
AuthorDate: Sat Sep 28 05:07:54 2024 +0000

    Add importer plugin type
---
 internal/controller/plugin_importer_controller.go | 209 ++++++++++++++++++++++
 internal/router/plugin_api_router.go              |   5 +
 plugin/importer.go                                |  56 ++++++
 plugin/plugin.go                                  |   4 +
 4 files changed, 274 insertions(+)

diff --git a/internal/controller/plugin_importer_controller.go 
b/internal/controller/plugin_importer_controller.go
new file mode 100644
index 00000000..90532acf
--- /dev/null
+++ b/internal/controller/plugin_importer_controller.go
@@ -0,0 +1,209 @@
+/*
+ * 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"})
+}
diff --git a/internal/router/plugin_api_router.go 
b/internal/router/plugin_api_router.go
index e69e0b6b..22a13b85 100644
--- a/internal/router/plugin_api_router.go
+++ b/internal/router/plugin_api_router.go
@@ -30,6 +30,7 @@ type PluginAPIRouter struct {
        captchaController    *controller.CaptchaController
        embedController      *controller.EmbedController
        renderController     *controller.RenderController
+       importerController   *controller.ImporterController
 }
 
 func NewPluginAPIRouter(
@@ -38,6 +39,7 @@ func NewPluginAPIRouter(
        captchaController *controller.CaptchaController,
        embedController *controller.EmbedController,
        renderController *controller.RenderController,
+       importerController *controller.ImporterController,
 ) *PluginAPIRouter {
        return &PluginAPIRouter{
                connectorController:  connectorController,
@@ -68,6 +70,9 @@ 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/plugin/importer.go b/plugin/importer.go
new file mode 100644
index 00000000..79a84c50
--- /dev/null
+++ b/plugin/importer.go
@@ -0,0 +1,56 @@
+/*
+ * 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 plugin
+
+import "github.com/gin-gonic/gin"
+
+type QuestionImporterInfo struct {
+       Title     string   `json:"title"`
+       Content   string   `json:"content"`
+       Tags      []string `json:"tags"`
+       UserEmail string   `json:"user_email"`
+}
+
+type Importer interface {
+       Base
+       GetQuestion(ctx *gin.Context) (questionInfo *QuestionImporterInfo, err 
error)
+}
+
+var (
+       // CallImporter is a function that calls all registered parsers
+       CallImporter,
+       registerImporter = MakePlugin[Importer](false)
+)
+
+func ImporterEnabled() (enabled bool) {
+       _ = CallImporter(func(fn Importer) error {
+               enabled = true
+               return nil
+       })
+       return
+}
+func GetImporter() (ip Importer, ok bool) {
+       _ = CallImporter(func(fn Importer) error {
+               ip = fn
+               ok = true
+               return nil
+       })
+       return
+}
diff --git a/plugin/plugin.go b/plugin/plugin.go
index 9c144346..2e4bf749 100644
--- a/plugin/plugin.go
+++ b/plugin/plugin.go
@@ -110,6 +110,10 @@ func Register(p Base) {
        if _, ok := p.(CDN); ok {
                registerCDN(p.(CDN))
        }
+
+       if _, ok := p.(Importer); ok {
+               registerImporter(p.(Importer))
+       }
 }
 
 type Stack[T Base] struct {

Reply via email to