Skip to content

Commit

Permalink
add some error handling on the UI
Browse files Browse the repository at this point in the history
  • Loading branch information
LinuxSuRen committed Sep 9, 2023
1 parent 4a513d7 commit 97a5fbc
Show file tree
Hide file tree
Showing 11 changed files with 142 additions and 17 deletions.
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ build:
mkdir -p bin
rm -rf bin/atest
go build -o bin/atest main.go
build-ext-git:
CGO_ENABLED=0 go build -ldflags "-w -s" -o bin/atest-store-git extensions/store-git/main.go
embed-ui:
cd console/atest-ui && npm i && npm run build-only
cp console/atest-ui/dist/index.html cmd/data/index.html
Expand Down
31 changes: 28 additions & 3 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ package cmd

import (
"bytes"
"context"
"fmt"
"log"
"net"
"net/http"
"os"
"os/signal"
"path"
"strings"
"syscall"
"time"

_ "embed"
Expand Down Expand Up @@ -71,6 +74,9 @@ func (o *serverOption) preRunE(cmd *cobra.Command, args []string) (err error) {
}

func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()

if o.printProto {
for _, val := range server.GetProtos() {
cmd.Println(val)
Expand Down Expand Up @@ -109,7 +115,7 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
}

remoteServer := server.NewRemoteServer(loader, remote.NewGRPCloaderFromStore(), secretServer, o.configDir)
kinds, storeKindsErr := remoteServer.GetStoreKinds(nil, nil)
kinds, storeKindsErr := remoteServer.GetStoreKinds(ctx, nil)
if storeKindsErr != nil {
cmd.PrintErrf("failed to get store kinds, error: %p\n", storeKindsErr)
} else {
Expand All @@ -118,6 +124,9 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
}
}

clean := make(chan os.Signal, 1)
signal.Notify(clean, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

s := o.gRPCServer
go func() {
if gRPCServer, ok := s.(reflection.GRPCServer); ok {
Expand All @@ -128,8 +137,19 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
s.Serve(lis)
}()

go func() {
<-clean
_ = lis.Close()
_ = o.httpServer.Shutdown(ctx)
for _, file := range filesNeedToBeRemoved {
if err = os.RemoveAll(file); err != nil {
log.Printf("failed to remove %s, error: %v", file, err)
}
}
}()

mux := runtime.NewServeMux(runtime.WithMetadata(server.MetadataStoreFunc)) // runtime.WithIncomingHeaderMatcher(func(key string) (s string, b bool) {
err = server.RegisterRunnerHandlerServer(cmd.Context(), mux, remoteServer)
err = server.RegisterRunnerHandlerServer(ctx, mux, remoteServer)
if err == nil {
mux.HandlePath(http.MethodGet, "/", frontEndHandlerWithLocation(o.consolePath))
mux.HandlePath(http.MethodGet, "/assets/{asset}", frontEndHandlerWithLocation(o.consolePath))
Expand All @@ -139,6 +159,7 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
o.httpServer.WithHandler(mux)
log.Printf("HTTP server listening at %v", httplis.Addr())
err = o.httpServer.Serve(httplis)
err = util.IgnoreErrServerClosed(err)
}
return
}
Expand All @@ -153,7 +174,9 @@ func startPlugins(execer fakeruntime.Execer, kinds *server.StoreKinds) (err erro
log.Printf("failed to find %s, error: %v", kind.Name, lookErr)
} else {
go func(socketURL, plugin string) {
if err = execer.RunCommand(plugin, "--socket", strings.TrimPrefix(socketURL, socketPrefix)); err != nil {
socketFile := strings.TrimPrefix(socketURL, socketPrefix)
filesNeedToBeRemoved = append(filesNeedToBeRemoved, socketFile)
if err = execer.RunCommand(plugin, "--socket", socketFile); err != nil {
log.Printf("failed to start %s, error: %v", socketURL, err)
}
}(kind.Url, binaryPath)
Expand Down Expand Up @@ -216,6 +239,8 @@ func debugHandler(mux *runtime.ServeMux) {
})
}

var filesNeedToBeRemoved = []string{}

func (o *serverOption) getAtestBinary(w http.ResponseWriter, r *http.Request, pathParams map[string]string) {
w.Header().Set("Content-Disposition", "attachment; filename=atest")
w.Header().Set("Content-Type", "application/octet-stream")
Expand Down
21 changes: 15 additions & 6 deletions console/atest-ui/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import StoreManager from './views/StoreManager.vue'
import SecretManager from './views/SecretManager.vue'
import TemplateFunctions from './views/TemplateFunctions.vue'
import { reactive, ref, watch } from 'vue'
import { ElTree } from 'element-plus'
import { ElTree, ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Edit, Share } from '@element-plus/icons-vue'
import type { Suite } from './types'
import { GetLastTestCaseLocation, SetLastTestCaseLocation } from './views/cache'
import { DefaultResponseProcess } from './views/net'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
Expand Down Expand Up @@ -217,12 +218,20 @@ const submitForm = async (formEl: FormInstance | undefined) => {
}
fetch('/server.Runner/CreateTestSuite', requestOptions)
.then((response) => response.json())
.then(() => {
.then(DefaultResponseProcess())
.then((e) => {
suiteCreatingLoading.value = false
loadStores()
dialogVisible.value = false
formEl.resetFields()
if (e.error !== "") {
ElMessage.error('Oops, ' + e.error)
} else {
loadStores()
dialogVisible.value = false
formEl.resetFields()
}
})
.catch((e) => {
suiteCreatingLoading.value = false
ElMessage.error('Oops, ' + e)
})
}
})
Expand Down
5 changes: 4 additions & 1 deletion console/atest-ui/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"send": "Send",
"toolbox": "Tool Box",
"refresh": "Refresh",
"newtestcase": "New TestCase"
"newtestcase": "New TestCase",
"verify": "Verify"
},
"title": {
"createTestSuite": "Create Test Suite",
Expand All @@ -28,6 +29,8 @@
"password": "Password",
"properties": "Properties",
"plugin": "Plugin",
"pluginName": "Plugin Name",
"pluginURL": "Plugin URL",
"status": "Status",
"operations": "Operations",
"storageLocation": "Storage Location"
Expand Down
5 changes: 4 additions & 1 deletion console/atest-ui/src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"send": "发送",
"toolbox": "工具箱",
"refresh": "刷新",
"newtestcase": "新建测试用例"
"newtestcase": "新建测试用例",
"verify": "检查"
},
"title": {
"createTestSuite": "创建测试用例集",
Expand All @@ -28,6 +29,8 @@
"password": "密码",
"properties": "属性",
"plugin": "插件",
"pluginName": "插件名称",
"pluginURL": "插件地址",
"status": "状态",
"operations": "操作",
"storageLocation": "保存位置"
Expand Down
46 changes: 43 additions & 3 deletions console/atest-ui/src/views/StoreManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ const submitForm = async (formEl: FormInstance | undefined) => {
if (!response.ok) {
throw new Error(response.statusText)
} else {
response.json()
return response.json()
}
})
.then(() => {
Expand All @@ -145,6 +145,40 @@ const submitForm = async (formEl: FormInstance | undefined) => {
})
}
function storeVerify(formEl: FormInstance | undefined) {
if (!formEl) return
const requestOptions = {
method: 'POST',
body: JSON.stringify({
name: storeForm.name
})
}
let api = '/server.Runner/VerifyStore'
fetch(api, requestOptions)
.then((response) => {
if (!response.ok) {
throw new Error(response.statusText)
} else {
return response.json()
}
})
.then((e) => {
if (e.success) {
ElMessage({
message: 'Verified!',
type: 'success'
})
} else {
ElMessage.error(e.message)
}
})
.catch((e) => {
ElMessage.error('Oops, ' + e)
})
}
function updateKeys() {
const props = storeForm.properties
let lastItem = props[props.length - 1]
Expand Down Expand Up @@ -231,10 +265,10 @@ function updateKeys() {
<el-form-item :label="t('field.password')" prop="password">
<el-input v-model="storeForm.password" type="password" test-id="store-form-password" />
</el-form-item>
<el-form-item :label="t('field.plugin')" prop="pluginName">
<el-form-item :label="t('field.pluginName')" prop="pluginName">
<el-input v-model="storeForm.kind.name" test-id="store-form-plugin-name" />
</el-form-item>
<el-form-item :label="t('field.plugin')" prop="plugin">
<el-form-item :label="t('field.pluginURL')" prop="plugin">
<el-input v-model="storeForm.kind.url" test-id="store-form-plugin" />
</el-form-item>
<el-form-item :label="t('field.properties')" prop="properties">
Expand All @@ -254,6 +288,12 @@ function updateKeys() {
</el-table>
</el-form-item>
<el-form-item>
<el-button
type="primary"
@click="storeVerify(storeFormRef)"
test-id="store-form-verify"
>{{t('button.verify')}}</el-button
>
<el-button
type="primary"
@click="submitForm(storeFormRef)"
Expand Down
9 changes: 9 additions & 0 deletions console/atest-ui/src/views/net.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function DefaultResponseProcess() {
return (response: any) => {
if (!response.ok) {
throw new Error(response.statusText)
} else {
return response.json()
}
}
}
16 changes: 14 additions & 2 deletions pkg/server/http.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
context "context"
"net"
"net/http"
)
Expand All @@ -9,9 +10,11 @@ import (
type HTTPServer interface {
Serve(lis net.Listener) error
WithHandler(handler http.Handler)
Shutdown(ctx context.Context) error
}

type defaultHTTPServer struct {
server *http.Server
handler http.Handler
}

Expand All @@ -21,15 +24,19 @@ func NewDefaultHTTPServer() HTTPServer {
}

func (s *defaultHTTPServer) Serve(lis net.Listener) (err error) {
server := &http.Server{Handler: s.handler}
err = server.Serve(lis)
s.server = &http.Server{Handler: s.handler}
err = s.server.Serve(lis)
return
}

func (s *defaultHTTPServer) WithHandler(h http.Handler) {
s.handler = h
}

func (s *defaultHTTPServer) Shutdown(ctx context.Context) error {
return s.server.Shutdown(ctx)
}

type fakeHandler struct{}

// NewFakeHTTPServer creates a fake HTTP server
Expand All @@ -45,3 +52,8 @@ func (s *fakeHandler) Serve(lis net.Listener) (err error) {
func (s *fakeHandler) WithHandler(h http.Handler) {
// do nothing due to this is a fake method
}

func (s *fakeHandler) Shutdown(ctx context.Context) error {
// do nothing due to this is a fake method
return nil
}
7 changes: 6 additions & 1 deletion pkg/server/remote_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,13 @@ func (s *server) GetSuites(ctx context.Context, in *Empty) (reply *Suites, err e
}

func (s *server) CreateTestSuite(ctx context.Context, in *TestSuiteIdentity) (reply *HelloReply, err error) {
reply = &HelloReply{}
loader := s.getLoader(ctx)
err = loader.CreateSuite(in.Name, in.Api)
if loader == nil {
reply.Error = "no loader found"
} else {
err = loader.CreateSuite(in.Name, in.Api)
}
return
}

Expand Down
10 changes: 10 additions & 0 deletions pkg/util/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ SOFTWARE.

package util

import "net/http"

// OKOrErrorMessage returns OK or error message
func OKOrErrorMessage(err error) string {
return OrErrorMessage(err, "OK")
Expand All @@ -36,3 +38,11 @@ func OrErrorMessage(err error, message string) string {
}
return message
}

// IgnoreErrServerClosed ignores ErrServerClosed
func IgnoreErrServerClosed(err error) error {
if err == http.ErrServerClosed {
return nil
}
return err
}
7 changes: 7 additions & 0 deletions pkg/util/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ package util_test

import (
"errors"
"net/http"
"testing"

"github.com/linuxsuren/api-testing/pkg/util"
Expand All @@ -36,3 +37,9 @@ func TestOkOrErrorMessage(t *testing.T) {
assert.Equal(t, "OK", util.OKOrErrorMessage(nil))
assert.Equal(t, "test", util.OKOrErrorMessage(errors.New("test")))
}

func TestIgnoreErrServerClosed(t *testing.T) {
assert.Nil(t, util.IgnoreErrServerClosed(nil))
assert.Nil(t, util.IgnoreErrServerClosed(http.ErrServerClosed))
assert.Error(t, util.IgnoreErrServerClosed(errors.New("test")))
}

0 comments on commit 97a5fbc

Please sign in to comment.