You've already forked focalboard
							
							
				mirror of
				https://github.com/mattermost/focalboard.git
				synced 2025-10-31 00:17:42 +02:00 
			
		
		
		
	Add API client, integration tests structure and server lifecycle changes
This commit is contained in:
		| @@ -1,13 +1,13 @@ | ||||
| package app | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/utils" | ||||
| ) | ||||
|  | ||||
| func (a *App) SaveFile(reader io.Reader, filename string) (string, error) { | ||||
| @@ -17,7 +17,7 @@ func (a *App) SaveFile(reader io.Reader, filename string) (string, error) { | ||||
| 		fileExtension = ".jpg" | ||||
| 	} | ||||
|  | ||||
| 	createdFilename := fmt.Sprintf(`%s%s`, createGUID(), fileExtension) | ||||
| 	createdFilename := fmt.Sprintf(`%s%s`, utils.CreateGUID(), fileExtension) | ||||
|  | ||||
| 	_, appErr := a.filesBackend.WriteFile(reader, createdFilename) | ||||
| 	if appErr != nil { | ||||
| @@ -32,15 +32,3 @@ func (a *App) GetFilePath(filename string) string { | ||||
|  | ||||
| 	return filepath.Join(folderPath, filename) | ||||
| } | ||||
|  | ||||
| // CreateGUID returns a random GUID. | ||||
| func createGUID() string { | ||||
| 	b := make([]byte, 16) | ||||
| 	_, err := rand.Read(b) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) | ||||
|  | ||||
| 	return uuid | ||||
| } | ||||
|   | ||||
							
								
								
									
										186
									
								
								server/client/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								server/client/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| package client | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/model" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	API_URL_SUFFIX = "/api/v1" | ||||
| ) | ||||
|  | ||||
| type Response struct { | ||||
| 	StatusCode int | ||||
| 	Error      error | ||||
| 	Header     http.Header | ||||
| } | ||||
|  | ||||
| func BuildResponse(r *http.Response) *Response { | ||||
| 	return &Response{ | ||||
| 		StatusCode: r.StatusCode, | ||||
| 		Header:     r.Header, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func BuildErrorResponse(r *http.Response, err error) *Response { | ||||
| 	statusCode := 0 | ||||
| 	header := make(http.Header) | ||||
| 	if r != nil { | ||||
| 		statusCode = r.StatusCode | ||||
| 		header = r.Header | ||||
| 	} | ||||
|  | ||||
| 	return &Response{ | ||||
| 		StatusCode: statusCode, | ||||
| 		Error:      err, | ||||
| 		Header:     header, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func closeBody(r *http.Response) { | ||||
| 	if r.Body != nil { | ||||
| 		_, _ = io.Copy(ioutil.Discard, r.Body) | ||||
| 		_ = r.Body.Close() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func toJSON(v interface{}) string { | ||||
| 	b, _ := json.Marshal(v) | ||||
| 	return string(b) | ||||
| } | ||||
|  | ||||
| type Client struct { | ||||
| 	Url        string | ||||
| 	ApiUrl     string | ||||
| 	HttpClient *http.Client | ||||
| 	HttpHeader map[string]string | ||||
| } | ||||
|  | ||||
| func NewClient(url string) *Client { | ||||
| 	url = strings.TrimRight(url, "/") | ||||
| 	return &Client{url, url + API_URL_SUFFIX, &http.Client{}, map[string]string{}} | ||||
| } | ||||
|  | ||||
| func (c *Client) DoApiGet(url string, etag string) (*http.Response, error) { | ||||
| 	return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag) | ||||
| } | ||||
|  | ||||
| func (c *Client) DoApiPost(url string, data string) (*http.Response, error) { | ||||
| 	return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "") | ||||
| } | ||||
|  | ||||
| func (c *Client) doApiPostBytes(url string, data []byte) (*http.Response, error) { | ||||
| 	return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "") | ||||
| } | ||||
|  | ||||
| func (c *Client) DoApiPut(url string, data string) (*http.Response, error) { | ||||
| 	return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "") | ||||
| } | ||||
|  | ||||
| func (c *Client) doApiPutBytes(url string, data []byte) (*http.Response, error) { | ||||
| 	return c.doApiRequestBytes(http.MethodPut, c.ApiUrl+url, data, "") | ||||
| } | ||||
|  | ||||
| func (c *Client) DoApiDelete(url string) (*http.Response, error) { | ||||
| 	return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "") | ||||
| } | ||||
|  | ||||
| func (c *Client) DoApiRequest(method, url, data, etag string) (*http.Response, error) { | ||||
| 	return c.doApiRequestReader(method, url, strings.NewReader(data), etag) | ||||
| } | ||||
|  | ||||
| func (c *Client) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, error) { | ||||
| 	return c.doApiRequestReader(method, url, bytes.NewReader(data), etag) | ||||
| } | ||||
|  | ||||
| func (c *Client) doApiRequestReader(method, url string, data io.Reader, etag string) (*http.Response, error) { | ||||
| 	rq, err := http.NewRequest(method, url, data) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if c.HttpHeader != nil && len(c.HttpHeader) > 0 { | ||||
| 		for k, v := range c.HttpHeader { | ||||
| 			rq.Header.Set(k, v) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	rp, err := c.HttpClient.Do(rq) | ||||
| 	if err != nil || rp == nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	if rp.StatusCode == 304 { | ||||
| 		return rp, nil | ||||
| 	} | ||||
|  | ||||
| 	if rp.StatusCode >= 300 { | ||||
| 		defer closeBody(rp) | ||||
| 		b, err := ioutil.ReadAll(rp.Body) | ||||
| 		if err != nil { | ||||
| 			return rp, fmt.Errorf("error when parsing response with code %d: %w", rp.StatusCode, err) | ||||
| 		} | ||||
| 		return rp, fmt.Errorf(string(b)) | ||||
| 	} | ||||
|  | ||||
| 	return rp, nil | ||||
| } | ||||
|  | ||||
| func (c *Client) GetBlocksRoute() string { | ||||
| 	return "/blocks" | ||||
| } | ||||
|  | ||||
| func (c *Client) GetBlockRoute(id string) string { | ||||
| 	return fmt.Sprintf("%s/%s", c.GetBlocksRoute(), id) | ||||
| } | ||||
|  | ||||
| func (c *Client) GetSubtreeRoute(id string) string { | ||||
| 	return fmt.Sprintf("%s/subtree", c.GetBlockRoute(id)) | ||||
| } | ||||
|  | ||||
| func (c *Client) GetBlocks() ([]model.Block, *Response) { | ||||
| 	r, err := c.DoApiGet(c.GetBlocksRoute(), "") | ||||
| 	if err != nil { | ||||
| 		return nil, BuildErrorResponse(r, err) | ||||
| 	} | ||||
| 	defer closeBody(r) | ||||
|  | ||||
| 	return model.BlocksFromJSON(r.Body), BuildResponse(r) | ||||
| } | ||||
|  | ||||
| func (c *Client) InsertBlocks(blocks []model.Block) (bool, *Response) { | ||||
| 	r, err := c.DoApiPost(c.GetBlocksRoute(), toJSON(blocks)) | ||||
| 	if err != nil { | ||||
| 		return false, BuildErrorResponse(r, err) | ||||
| 	} | ||||
| 	defer closeBody(r) | ||||
|  | ||||
| 	return true, BuildResponse(r) | ||||
| } | ||||
|  | ||||
| func (c *Client) DeleteBlock(blockID string) (bool, *Response) { | ||||
| 	r, err := c.DoApiDelete(c.GetBlockRoute(blockID)) | ||||
| 	if err != nil { | ||||
| 		return false, BuildErrorResponse(r, err) | ||||
| 	} | ||||
| 	defer closeBody(r) | ||||
|  | ||||
| 	return true, BuildResponse(r) | ||||
| } | ||||
|  | ||||
| func (c *Client) GetSubtree(blockID string) ([]model.Block, *Response) { | ||||
| 	r, err := c.DoApiGet(c.GetSubtreeRoute(blockID), "") | ||||
| 	if err != nil { | ||||
| 		return nil, BuildErrorResponse(r, err) | ||||
| 	} | ||||
| 	defer closeBody(r) | ||||
|  | ||||
| 	return model.BlocksFromJSON(r.Body), BuildResponse(r) | ||||
| } | ||||
							
								
								
									
										218
									
								
								server/integrationtests/blocks_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								server/integrationtests/blocks_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| package integrationtests | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/model" | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/utils" | ||||
|  | ||||
| 	"github.com/stretchr/testify/require" | ||||
| ) | ||||
|  | ||||
| func TestGetBlocks(t *testing.T) { | ||||
| 	th := SetupTestHelper().InitBasic() | ||||
| 	defer th.TearDown() | ||||
|  | ||||
| 	blockID1 := utils.CreateGUID() | ||||
| 	blockID2 := utils.CreateGUID() | ||||
| 	newBlocks := []model.Block{ | ||||
| 		{ | ||||
| 			ID:       blockID1, | ||||
| 			CreateAt: 1, | ||||
| 			UpdateAt: 1, | ||||
| 			Type:     "board", | ||||
| 		}, | ||||
| 		{ | ||||
| 			ID:       blockID2, | ||||
| 			CreateAt: 1, | ||||
| 			UpdateAt: 1, | ||||
| 			Type:     "board", | ||||
| 		}, | ||||
| 	} | ||||
| 	_, resp := th.Client.InsertBlocks(newBlocks) | ||||
| 	require.NoError(t, resp.Error) | ||||
|  | ||||
| 	blocks, resp := th.Client.GetBlocks() | ||||
| 	require.NoError(t, resp.Error) | ||||
| 	require.Len(t, blocks, 2) | ||||
|  | ||||
| 	blockIDs := make([]string, len(blocks)) | ||||
| 	for i, b := range blocks { | ||||
| 		blockIDs[i] = b.ID | ||||
| 	} | ||||
| 	require.Contains(t, blockIDs, blockID1) | ||||
| 	require.Contains(t, blockIDs, blockID2) | ||||
| } | ||||
|  | ||||
| func TestPostBlock(t *testing.T) { | ||||
| 	th := SetupTestHelper().InitBasic() | ||||
| 	defer th.TearDown() | ||||
|  | ||||
| 	blockID1 := utils.CreateGUID() | ||||
| 	blockID2 := utils.CreateGUID() | ||||
| 	blockID3 := utils.CreateGUID() | ||||
|  | ||||
| 	t.Run("Create a single block", func(t *testing.T) { | ||||
| 		block := model.Block{ | ||||
| 			ID:       blockID1, | ||||
| 			CreateAt: 1, | ||||
| 			UpdateAt: 1, | ||||
| 			Type:     "board", | ||||
| 			Title:    "New title", | ||||
| 		} | ||||
|  | ||||
| 		_, resp := th.Client.InsertBlocks([]model.Block{block}) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 1) | ||||
| 		require.Equal(t, blockID1, blocks[0].ID) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Create a couple of blocks in the same call", func(t *testing.T) { | ||||
| 		newBlocks := []model.Block{ | ||||
| 			{ | ||||
| 				ID:       blockID2, | ||||
| 				CreateAt: 1, | ||||
| 				UpdateAt: 1, | ||||
| 				Type:     "board", | ||||
| 			}, | ||||
| 			{ | ||||
| 				ID:       blockID3, | ||||
| 				CreateAt: 1, | ||||
| 				UpdateAt: 1, | ||||
| 				Type:     "board", | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		_, resp := th.Client.InsertBlocks(newBlocks) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 3) | ||||
|  | ||||
| 		blockIDs := make([]string, len(blocks)) | ||||
| 		for i, b := range blocks { | ||||
| 			blockIDs[i] = b.ID | ||||
| 		} | ||||
| 		require.Contains(t, blockIDs, blockID1) | ||||
| 		require.Contains(t, blockIDs, blockID2) | ||||
| 		require.Contains(t, blockIDs, blockID3) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Update a block", func(t *testing.T) { | ||||
| 		block := model.Block{ | ||||
| 			ID:       blockID1, | ||||
| 			CreateAt: 1, | ||||
| 			UpdateAt: 20, | ||||
| 			Type:     "board", | ||||
| 			Title:    "Updated title", | ||||
| 		} | ||||
|  | ||||
| 		_, resp := th.Client.InsertBlocks([]model.Block{block}) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 3) | ||||
|  | ||||
| 		var updatedBlock model.Block | ||||
| 		for _, b := range blocks { | ||||
| 			if b.ID == blockID1 { | ||||
| 				updatedBlock = b | ||||
| 			} | ||||
| 		} | ||||
| 		require.NotNil(t, updatedBlock) | ||||
| 		require.Equal(t, "Updated title", updatedBlock.Title) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestDeleteBlock(t *testing.T) { | ||||
| 	th := SetupTestHelper().InitBasic() | ||||
| 	defer th.TearDown() | ||||
|  | ||||
| 	blockID := utils.CreateGUID() | ||||
| 	t.Run("Create a block", func(t *testing.T) { | ||||
| 		block := model.Block{ | ||||
| 			ID:       blockID, | ||||
| 			CreateAt: 1, | ||||
| 			UpdateAt: 1, | ||||
| 			Type:     "board", | ||||
| 			Title:    "New title", | ||||
| 		} | ||||
|  | ||||
| 		_, resp := th.Client.InsertBlocks([]model.Block{block}) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 1) | ||||
| 		require.Equal(t, blockID, blocks[0].ID) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Delete a block", func(t *testing.T) { | ||||
| 		_, resp := th.Client.DeleteBlock(blockID) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 0) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestGetSubtree(t *testing.T) { | ||||
| 	th := SetupTestHelper().InitBasic() | ||||
| 	defer th.TearDown() | ||||
|  | ||||
| 	parentBlockID := utils.CreateGUID() | ||||
| 	childBlockID1 := utils.CreateGUID() | ||||
| 	childBlockID2 := utils.CreateGUID() | ||||
| 	t.Run("Create the block structure", func(t *testing.T) { | ||||
| 		newBlocks := []model.Block{ | ||||
| 			{ | ||||
| 				ID:       parentBlockID, | ||||
| 				CreateAt: 1, | ||||
| 				UpdateAt: 1, | ||||
| 				Type:     "board", | ||||
| 			}, | ||||
| 			{ | ||||
| 				ID:       childBlockID1, | ||||
| 				ParentID: parentBlockID, | ||||
| 				CreateAt: 2, | ||||
| 				UpdateAt: 2, | ||||
| 				Type:     "card", | ||||
| 			}, | ||||
| 			{ | ||||
| 				ID:       childBlockID2, | ||||
| 				ParentID: parentBlockID, | ||||
| 				CreateAt: 2, | ||||
| 				UpdateAt: 2, | ||||
| 				Type:     "card", | ||||
| 			}, | ||||
| 		} | ||||
|  | ||||
| 		_, resp := th.Client.InsertBlocks(newBlocks) | ||||
| 		require.NoError(t, resp.Error) | ||||
|  | ||||
| 		blocks, resp := th.Client.GetBlocks() | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 1) | ||||
| 		require.Equal(t, parentBlockID, blocks[0].ID) | ||||
| 	}) | ||||
|  | ||||
| 	t.Run("Get subtree for parent ID", func(t *testing.T) { | ||||
| 		blocks, resp := th.Client.GetSubtree(parentBlockID) | ||||
| 		require.NoError(t, resp.Error) | ||||
| 		require.Len(t, blocks, 3) | ||||
|  | ||||
| 		blockIDs := make([]string, len(blocks)) | ||||
| 		for i, b := range blocks { | ||||
| 			blockIDs[i] = b.ID | ||||
| 		} | ||||
| 		require.Contains(t, blockIDs, parentBlockID) | ||||
| 		require.Contains(t, blockIDs, childBlockID1) | ||||
| 		require.Contains(t, blockIDs, childBlockID2) | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										54
									
								
								server/integrationtests/clienttestlib.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								server/integrationtests/clienttestlib.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| package integrationtests | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
|  | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/client" | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/server" | ||||
| 	"github.com/mattermost/mattermost-octo-tasks/server/services/config" | ||||
| ) | ||||
|  | ||||
| type TestHelper struct { | ||||
| 	Server *server.Server | ||||
| 	Client *client.Client | ||||
| } | ||||
|  | ||||
| func getTestConfig() *config.Configuration { | ||||
| 	return &config.Configuration{ | ||||
| 		ServerRoot:     "http://localhost:8888", | ||||
| 		Port:           8888, | ||||
| 		DBType:         "sqlite3", | ||||
| 		DBConfigString: ":memory:", | ||||
| 		WebPath:        "./pack", | ||||
| 		FilesPath:      "./files", | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func SetupTestHelper() *TestHelper { | ||||
| 	th := &TestHelper{} | ||||
| 	srv, err := server.New(getTestConfig()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	th.Server = srv | ||||
| 	th.Client = client.NewClient(srv.Config().ServerRoot) | ||||
|  | ||||
| 	return th | ||||
| } | ||||
|  | ||||
| func (th *TestHelper) InitBasic() *TestHelper { | ||||
| 	go func() { | ||||
| 		if err := th.Server.Start(); err != http.ErrServerClosed { | ||||
| 			panic(err) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return th | ||||
| } | ||||
|  | ||||
| func (th *TestHelper) TearDown() { | ||||
| 	err := th.Server.Shutdown() | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| } | ||||
| @@ -1,5 +1,10 @@ | ||||
| package model | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"io" | ||||
| ) | ||||
|  | ||||
| // Block is the basic data unit. | ||||
| type Block struct { | ||||
| 	ID       string                 `json:"id"` | ||||
| @@ -12,3 +17,9 @@ type Block struct { | ||||
| 	UpdateAt int64                  `json:"updateAt"` | ||||
| 	DeleteAt int64                  `json:"deleteAt"` | ||||
| } | ||||
|  | ||||
| func BlocksFromJSON(data io.Reader) []Block { | ||||
| 	var blocks []Block | ||||
| 	json.NewDecoder(data).Decode(&blocks) | ||||
| 	return blocks | ||||
| } | ||||
|   | ||||
| @@ -135,5 +135,13 @@ func (s *Server) Start() error { | ||||
| } | ||||
|  | ||||
| func (s *Server) Shutdown() error { | ||||
| 	if err := s.webServer.Shutdown(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return s.store.Shutdown() | ||||
| } | ||||
|  | ||||
| func (s *Server) Config() *config.Configuration { | ||||
| 	return s.config | ||||
| } | ||||
|   | ||||
							
								
								
									
										19
									
								
								server/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								server/utils/utils.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"fmt" | ||||
| 	"log" | ||||
| ) | ||||
|  | ||||
| // CreateGUID returns a random GUID. | ||||
| func CreateGUID() string { | ||||
| 	b := make([]byte, 16) | ||||
| 	_, err := rand.Read(b) | ||||
| 	if err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| 	uuid := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) | ||||
|  | ||||
| 	return uuid | ||||
| } | ||||
| @@ -20,6 +20,8 @@ type RoutedService interface { | ||||
|  | ||||
| // Server is the structure responsible for managing our http web server. | ||||
| type Server struct { | ||||
| 	http.Server | ||||
|  | ||||
| 	router   *mux.Router | ||||
| 	rootPath string | ||||
| 	port     int | ||||
| @@ -31,7 +33,10 @@ func NewServer(rootPath string, port int, ssl bool) *Server { | ||||
| 	r := mux.NewRouter() | ||||
|  | ||||
| 	ws := &Server{ | ||||
| 		router:   r, | ||||
| 		Server: http.Server{ | ||||
| 			Addr:    fmt.Sprintf(`:%d`, port), | ||||
| 			Handler: r, | ||||
| 		}, | ||||
| 		rootPath: rootPath, | ||||
| 		port:     port, | ||||
| 		ssl:      ssl, | ||||
| @@ -40,14 +45,18 @@ func NewServer(rootPath string, port int, ssl bool) *Server { | ||||
| 	return ws | ||||
| } | ||||
|  | ||||
| func (ws *Server) Router() *mux.Router { | ||||
| 	return ws.Server.Handler.(*mux.Router) | ||||
| } | ||||
|  | ||||
| // AddRoutes allows services to register themself in the webserver router and provide new endpoints. | ||||
| func (ws *Server) AddRoutes(rs RoutedService) { | ||||
| 	rs.RegisterRoutes(ws.router) | ||||
| 	rs.RegisterRoutes(ws.Router()) | ||||
| } | ||||
|  | ||||
| func (ws *Server) registerRoutes() { | ||||
| 	ws.router.PathPrefix("/static").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(filepath.Join(ws.rootPath, "static"))))) | ||||
| 	ws.router.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 	ws.Router().PathPrefix("/static").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir(filepath.Join(ws.rootPath, "static"))))) | ||||
| 	ws.Router().PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "text/html; charset=utf-8") | ||||
| 		http.ServeFile(w, r, path.Join(ws.rootPath, "index.html")) | ||||
| 	}) | ||||
| @@ -56,14 +65,11 @@ func (ws *Server) registerRoutes() { | ||||
| // Start runs the web server and start listening for charsetnnections. | ||||
| func (ws *Server) Start() error { | ||||
| 	ws.registerRoutes() | ||||
| 	http.Handle("/", ws.router) | ||||
|  | ||||
| 	urlPort := fmt.Sprintf(`:%d`, ws.port) | ||||
| 	isSSL := ws.ssl && fileExists("./cert/cert.pem") && fileExists("./cert/key.pem") | ||||
|  | ||||
| 	if isSSL { | ||||
| 		log.Println("https server started on ", urlPort) | ||||
| 		err := http.ListenAndServeTLS(urlPort, "./cert/cert.pem", "./cert/key.pem", nil) | ||||
| 		log.Printf("https server started on :%d\n", ws.port) | ||||
| 		err := ws.ListenAndServeTLS("./cert/cert.pem", "./cert/key.pem") | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -71,8 +77,8 @@ func (ws *Server) Start() error { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	log.Println("http server started on ", urlPort) | ||||
| 	err := http.ListenAndServe(urlPort, nil) | ||||
| 	log.Println("http server started on :%d\n", ws.port) | ||||
| 	err := ws.ListenAndServe() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -80,6 +86,10 @@ func (ws *Server) Start() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (ws *Server) Shutdown() error { | ||||
| 	return ws.Close() | ||||
| } | ||||
|  | ||||
| // fileExists returns true if a file exists at the path. | ||||
| func fileExists(path string) bool { | ||||
| 	_, err := os.Stat(path) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user