From 98e28208141a4c9fa7975e01e1a97258a82b4caa Mon Sep 17 00:00:00 2001 From: Chetanya Kandhari Date: Thu, 20 Dec 2018 21:00:21 +0530 Subject: [PATCH] Update boilerplate code --- .editorconfig | 13 +++- .gitignore | 30 ++++---- Makefile | 93 ++++++++++++++++++++----- server/config/main.go | 1 - server/config/manifest.go | 5 ++ server/controller/main.go | 2 +- server/plugin.go | 10 +-- server/util/util.go | 2 +- webapp/.eslintrc.json | 113 ++++++++++++++----------------- webapp/src/client/client.js | 72 ++++++++++++++++++++ webapp/src/client/index.js | 5 ++ webapp/src/constants/index.js | 13 ++++ webapp/src/constants/manifest.js | 1 + webapp/src/index.jsx | 12 +++- 14 files changed, 267 insertions(+), 105 deletions(-) create mode 100644 server/config/manifest.go create mode 100644 webapp/src/client/client.js create mode 100644 webapp/src/client/index.js create mode 100644 webapp/src/constants/manifest.js diff --git a/.editorconfig b/.editorconfig index 1f73ddd..a8c675f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,19 +8,26 @@ insert_final_newline = true trim_trailing_whitespace = true charset = utf-8 -[*.{go,scss,js,jsx,json,html}] +[*.go] indent_style = tab -[webapp/package.json] +[*.{js,jsx,json,html}] indent_style = space +indent_size = 4 + +[webapp/package.json] indent_size = 2 [webapp/.eslintrc.json] -indent_style = tab +indent_size = 2 [Makefile] indent_style = tab +[*.scss] +indent_style = space +indent_size = 4 + [*.md] trim_trailing_whitespace = false indent_style = space diff --git a/.gitignore b/.gitignore index 53d1699..c38af30 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,20 @@ +# Binaries for programs and plugins +*.exe +*.dll +*.so +*.dylib + +# Test binary, build with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 +.glide/ + # Logs logs -*.log npm-debug.log* yarn-debug.log* yarn-error.log* @@ -14,12 +28,6 @@ pids # Directory for instrumented libs generated by jscoverage/JSCover lib-cov -# Coverage directory used by tools like istanbul -coverage - -# nyc test coverage -.nyc_output - # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt @@ -36,9 +44,6 @@ build/Release node_modules/ jspm_packages/ -# TypeScript v1 declaration files -typings/ - # Optional npm cache directory .npm @@ -62,8 +67,9 @@ typings/ # Plugin generated files dist -webapp/dist -webapp/node_modules +plugin.exe +.npminstall +*.tar.gz server/vendor/ .history/ coverage.txt diff --git a/Makefile b/Makefile index 12de31e..edd5e8a 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,19 @@ -GO := go -GOARCH=amd64 +GO:=go +GOARCH:=amd64 GOOS=$(shell uname -s | tr '[:upper:]' '[:lower:]') GOPATH ?= $(GOPATH:) -PLUGINNAME=boilerplate -PLUGINVERSION=v$(shell echo `cat plugin.json | grep -Po '"'"version"'"\s*:\s*"\K([^"]*)'`) +MANIFEST_FILE ?= plugin.json + +PLUGINNAME=$(shell echo `grep -Po '"'"id"'"\s*:\s*"\K([^"]*)' $(MANIFEST_FILE)`) +PLUGINVERSION=v$(shell echo `grep -Po '"'"version"'"\s*:\s*"\K([^"]*)' $(MANIFEST_FILE)`) PACKAGENAME=mattermost-plugin-$(PLUGINNAME)-$(PLUGINVERSION) +HAS_WEBAPP=$(shell if [ "$(shell grep -E '[\^"]webapp["][ ]*[:]' $(MANIFEST_FILE) | wc -l)" -gt "0" ]; then echo "true"; fi) +HAS_SERVER=$(shell if [ "$(shell grep -E '[\^"]server["][ ]*[:]' $(MANIFEST_FILE) | wc -l)" -gt "0" ]; then echo "true"; fi) + +TMPFILEGOLINT=golint.tmp + BLACK=`tput setaf 0` RED=`tput setaf 1` GREEN=`tput setaf 2` @@ -20,30 +27,44 @@ BOLD=`tput bold` INVERSE=`tput rev` RESET=`tput sgr0` -.PHONY: default test clean check-style checkjs checkgo govet golint gofmt .distclean dist fix fixjs fixgo github-release +.PHONY: default pre-run test clean check-style check-js check-go govet golint gofmt .distclean dist fix format fix-js fix-go github-release default: check-style test dist +pre-run: +ifneq ($(HAS_WEBAPP),) + @echo "export const PLUGIN_NAME = '`echo $(PLUGINNAME)`';\ + " > webapp/src/constants/manifest.js +endif +ifneq ($(HAS_SERVER),) + @echo "package config\n\nconst (\n\ + PluginName = \""`echo $(PLUGINNAME)`"\"\n)" > server/config/manifest.go + +endif + github-release: @if [ $$(git status --porcelain | wc -l) != "0" -o $$(git rev-list HEAD@{upstream}..HEAD | wc -l) != "0" ]; \ then echo ${RED}"local repo is not clean"${RESET}; exit 1; fi; - @echo ${BOLD}"Creating a tag to trigger circleci github release\n"${RESET} + @echo ${BOLD}"Creating a tag to trigger circleci build-and-release job\n"${RESET} git tag $(PLUGINVERSION) git push origin $(PLUGINVERSION) check-style: .npminstall vendor @echo ${BOLD}"Checking for style guide compliance\n"${RESET} - @make checkjs - @make checkgo + @make check-js + @make check-go -checkjs: webapp +check-js: +ifneq ($(HAS_WEBAPP),) @echo ${BOLD}Running ESLINT${RESET} @cd webapp && npm run lint @echo ${GREEN}"eslint success\n"${RESET} +endif -checkgo: server govet golint gofmt +check-go: server govet golint gofmt govet: +ifneq ($(HAS_SERVER),) @go tool vet 2>/dev/null ; if [ $$? -eq 3 ]; then \ echo "--> installing govet"; \ go get golang.org/x/tools/cmd/vet; \ @@ -53,8 +74,10 @@ govet: $(eval PKGS := $(shell go list ./... | grep -v /vendor/)) @$(GO) vet $(PKGS) @echo ${GREEN}"govet success\n"${RESET} +endif golint: +ifneq ($(HAS_SERVER),) @command -v golint >/dev/null ; if [ $$? -ne 0 ]; then \ echo "--> installing golint"; \ go get -u golang.org/x/lint/golint; \ @@ -62,19 +85,30 @@ golint: @echo ${BOLD}Running GOLINT${RESET}${RED} @cd server $(eval PKGS := $(shell go list ./... | grep -v /vendor/)) - @# grep -v is used to ignore specific linting rules and not echo their failure terminal + @touch $(TMPFILEGOLINT) -@for pkg in $(PKGS) ; do \ - golint $$pkg | grep -v "have comment" | grep -v "comment on exported" | grep -v "lint suggestions" ; \ - done && echo ${RED}"golint failure\n"${RESET} || echo ${GREEN}"golint success\n"${RESET} + echo `golint $$pkg | grep -v "have comment" | grep -v "comment on exported" | grep -v "lint suggestions"` >> $(TMPFILEGOLINT) ; \ + done + @grep -Ev "^$$" $(TMPFILEGOLINT) || true + @if [ "$$(grep -Ev "^$$" $(TMPFILEGOLINT) | wc -l)" -gt "0" ]; then \ + rm -f $(TMPFILEGOLINT); echo "golint failure\n"${RESET}; exit 1; else \ + rm -f $(TMPFILEGOLINT); echo ${GREEN}"golint success\n"${RESET}; \ + fi +endif + +format: fix -fix: fixjs fixgo +fix: fix-js fix-go -fixjs: +fix-js: +ifneq ($(HAS_WEBAPP),) @echo ${BOLD}Formatting js giles${RESET} @cd webapp && npm run fix @echo "formatted js files\n" +endif -fixgo: +fix-go: +ifneq ($(HAS_SERVER),) @command -v goimports >/dev/null ; if [ $$? -ne 0 ]; then \ echo "--> installing goimports"; \ go get golang.org/x/tools/cmd/goimports; \ @@ -83,8 +117,10 @@ fixgo: @cd server @find ./ -type f -name "*.go" -not -path "./server/vendor/*" -exec goimports -w {} \; @echo "formatted go files\n" +endif gofmt: +ifneq ($(HAS_SERVER),) @echo ${BOLD}Running GOFMT${RESET}${RED} @for package in $$(go list ./server/...); do \ files=$$(go list -f '{{range .GoFiles}}{{$$.Dir}}/{{.}} {{end}}' $$package); \ @@ -98,36 +134,50 @@ gofmt: fi; \ done @echo ${GREEN}"gofmt success\n"${RESET} +endif test: +ifneq ($(HAS_SERVER),) go test -v -coverprofile=coverage.txt ./... +endif .npminstall: webapp/package-lock.json +ifneq ($(HAS_WEBAPP),) @echo ${BOLD}"Getting dependencies using npm\n"${RESET} cd webapp && npm install @echo "\n" +endif vendor: server/glide.lock +ifneq ($(HAS_SERVER),) @echo ${BOLD}"Getting dependencies using glide\n"${RESET} cd server && go get github.com/Masterminds/glide cd server && $(shell go env GOPATH)/bin/glide install @echo "\n" +endif -dist: .distclean check-style plugin.json +dist: .distclean check-style $(MANIFEST_FILE) @echo ${BOLD}"Building plugin\n"${RESET} +ifneq ($(HAS_WEBAPP),) # Build and copy files from webapp cd webapp && npm run build mkdir -p dist/$(PLUGINNAME)/webapp cp -r webapp/dist/* dist/$(PLUGINNAME)/webapp/ +else ifneq ($(HAS_SERVER),) + mkdir -p dist/$(PLUGINNAME)/ +endif +ifneq ($(HAS_SERVER),) # Build files from server cd server && go get github.com/mitchellh/gox $(shell go env GOPATH)/bin/gox -osarch='darwin/amd64 linux/amd64 windows/amd64' -output 'dist/intermediate/plugin_{{.OS}}_{{.Arch}}' ./server +endif # Copy plugin files - cp plugin.json dist/$(PLUGINNAME)/ + cp $(MANIFEST_FILE) dist/$(PLUGINNAME)/ +ifneq ($(HAS_SERVER),) # Copy server executables & compress plugin mkdir -p dist/$(PLUGINNAME)/server @@ -139,14 +189,21 @@ dist: .distclean check-style plugin.json mv dist/intermediate/plugin_windows_amd64.exe dist/$(PLUGINNAME)/server/plugin.exe cd dist && tar -zcvf $(PACKAGENAME)-windows-amd64.tar.gz $(PLUGINNAME)/* +else ifneq ($(HAS_WEBAPP),) + cd dist && tar -zcvf $(PACKAGENAME).tar.gz $(PLUGINNAME)/* +endif # Clean up temp files rm -rf dist/$(PLUGINNAME) rm -rf dist/intermediate +ifneq ($(HAS_SERVER),) @echo Linux plugin built at: dist/$(PACKAGENAME)-linux-amd64.tar.gz @echo MacOS X plugin built at: dist/$(PACKAGENAME)-darwin-amd64.tar.gz @echo Windows plugin built at: dist/$(PACKAGENAME)-windows-amd64.tar.gz +else ifneq ($(HAS_WEBAPP),) + @echo Cross-platform plugin built at: dist/$(PACKAGENAME)-amd64.tar.gz +endif .distclean: @echo ${BOLD}"Cleaning dist files\n"${RESET} diff --git a/server/config/main.go b/server/config/main.go index ab1b1c2..57ab095 100644 --- a/server/config/main.go +++ b/server/config/main.go @@ -7,7 +7,6 @@ import ( ) const ( - PluginName = "boilerplate" CommandPrefix = PluginName URLMappingKeyPrefix = "url_" ServerExeToWebappRootPath = "/../webapp" diff --git a/server/config/manifest.go b/server/config/manifest.go new file mode 100644 index 0000000..71a53b6 --- /dev/null +++ b/server/config/manifest.go @@ -0,0 +1,5 @@ +package config + +const ( + PluginName = "boilerplate" +) diff --git a/server/controller/main.go b/server/controller/main.go index eb7c50e..d2fd3db 100644 --- a/server/controller/main.go +++ b/server/controller/main.go @@ -21,10 +21,10 @@ var Endpoints = map[string]*Endpoint{ // Authenticated verifies if provided request is performed by a logged-in Mattermost user. func Authenticated(w http.ResponseWriter, r *http.Request) bool { userID := r.Header.Get(config.HeaderMattermostUserID) - if userID == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return false } + return true } diff --git a/server/plugin.go b/server/plugin.go index cbad67b..94f8114 100644 --- a/server/plugin.go +++ b/server/plugin.go @@ -42,7 +42,7 @@ func (p *Plugin) OnActivate() error { } func (p *Plugin) setupStaticFileServer() error { - var exe, err = os.Executable() + exe, err := os.Executable() if err != nil { return err } @@ -52,7 +52,6 @@ func (p *Plugin) setupStaticFileServer() error { func (p *Plugin) OnConfigurationChange() error { if config.Mattermost != nil { - var configuration config.Configuration if err := config.Mattermost.LoadPluginConfiguration(&configuration); err != nil { @@ -120,15 +119,16 @@ func (p *Plugin) prepareContext(args *model.CommandArgs) command.Context { } func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) { - var conf = config.GetConfig() + conf := config.GetConfig() + if err := conf.IsValid(); err != nil { p.API.LogError("This plugin is not configured: " + err.Error()) http.Error(w, "This plugin is not configured.", http.StatusNotImplemented) return } - var path = r.URL.Path - var endpoint = controller.Endpoints[path] + path := r.URL.Path + endpoint := controller.Endpoints[path] if endpoint == nil { p.handler.ServeHTTP(w, r) diff --git a/server/util/util.go b/server/util/util.go index 267e71e..43e247b 100644 --- a/server/util/util.go +++ b/server/util/util.go @@ -56,7 +56,7 @@ func Min(a, b int) int { return b } -// CommandError can be used to return an empheral message as the response for a slash command +// CommandError can be used to return an ephemeral message as the response for a slash command func CommandError(msg string) (*model.CommandResponse, *model.AppError) { return &model.CommandResponse{ Type: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, diff --git a/webapp/.eslintrc.json b/webapp/.eslintrc.json index 0c85f04..073bc7f 100644 --- a/webapp/.eslintrc.json +++ b/webapp/.eslintrc.json @@ -1,77 +1,64 @@ { "extends": [ - "./node_modules/eslint-config-mattermost/.eslintrc.json", - "./node_modules/eslint-config-mattermost/.eslintrc-react.json" + "./node_modules/eslint-config-mattermost/.eslintrc.json", + "./node_modules/eslint-config-mattermost/.eslintrc-react.json" ], "globals": { - "window": true, - "document": true + "window": true, + "document": true }, "plugins": [ - "header", - "import", - "react" + "header", + "import", + "react" ], "env": { - "browser": true, - "node": true, - "jquery": true, - "es6": true + "browser": true, + "node": true, + "jquery": true, + "es6": true }, "settings": { - "import/resolver": "webpack", - "react": { - "pragma": "React", - "version": "16.4.1" - } + "import/resolver": "webpack", + "react": { + "pragma": "React", + "version": "16.4.1" + } }, "rules": { - "header/header": 0, - "import/no-unresolved": 2, - "import/order": [ - "error", - { - "newlines-between": "always-and-inside-groups", - "groups": [ - "builtin", - "external", - [ - "internal", - "parent" - ], - "sibling", - "index" - ] - } - ], - "no-magic-numbers": [ - 1, - { - "ignore": [ - -1, - 0, - 1, - 2 - ], - "enforceConst": true, - "detectObjects": true - } - ], - "react/jsx-filename-extension": [ - 1, - { - "extensions": [".js", ".jsx"] - } - ], - "react/prop-types": [ - 2, - { - "ignore": [ - "location", - "history", - "component" - ] - } - ] + "header/header": 0, + "import/no-unresolved": 2, + "import/order": [ + 2, + { + "newlines-between": "always-and-inside-groups", + "groups": [ + + "builtin", + "external", + [ + "internal", + "parent" + ], + "sibling", + "index" + ] + } + ], + "no-magic-numbers": [ + 1, + { + "ignore": [-1, 0, 1, 2], + "enforceConst": true, + "ignoreArrayIndexes": true + } + ], + "react/jsx-filename-extension": [ + 1, + { + "extensions": [".js", ".jsx"] + } + ], + "react/prop-types": 2 } } diff --git a/webapp/src/client/client.js b/webapp/src/client/client.js new file mode 100644 index 0000000..6c3975e --- /dev/null +++ b/webapp/src/client/client.js @@ -0,0 +1,72 @@ +import request from 'superagent'; + +import Constants from '../constants'; // eslint-disable-line no-unused-vars + +export default class Client { + doGet = async (url, headers = {}) => { + headers['X-Requested-With'] = 'XMLHttpRequest'; + + try { + const response = await request. + get(url). + set(headers). + type('application/json'). + accept('application/json'); + + return response.body; + } catch (err) { + throw err; + } + }; + + doPost = async (url, body, headers = {}) => { + headers['X-Requested-With'] = 'XMLHttpRequest'; + + try { + const response = await request. + post(url). + send(body). + set(headers). + type('application/json'). + accept('application/json'); + + return response.body; + } catch (err) { + throw err; + } + }; + + doDelete = async (url, body, headers = {}) => { + headers['X-Requested-With'] = 'XMLHttpRequest'; + + try { + const response = await request. + delete(url). + send(body). + set(headers). + type('application/json'). + accept('application/json'); + + return response.body; + } catch (err) { + throw err; + } + }; + + doPut = async (url, body, headers = {}) => { + headers['X-Requested-With'] = 'XMLHttpRequest'; + + try { + const response = await request. + put(url). + send(body). + set(headers). + type('application/json'). + accept('application/json'); + + return response.body; + } catch (err) { + throw err; + } + } +} diff --git a/webapp/src/client/index.js b/webapp/src/client/index.js new file mode 100644 index 0000000..9406f34 --- /dev/null +++ b/webapp/src/client/index.js @@ -0,0 +1,5 @@ +import ClientClass from './client.js'; + +const Client = new ClientClass(); + +export default Client; diff --git a/webapp/src/constants/index.js b/webapp/src/constants/index.js index 6950f0e..74bf79e 100644 --- a/webapp/src/constants/index.js +++ b/webapp/src/constants/index.js @@ -1,5 +1,18 @@ +import Utils from '../utils'; + +import {PLUGIN_NAME} from './manifest'; import SVGS from './svgs'; +// +// Define our URL constants +// +const URL_BASE = `${Utils.getBaseURL()}/plugins/${PLUGIN_NAME}`; + +// +// Export the constants +// export default { + PLUGIN_NAME, + URL_BASE, SVGS, }; diff --git a/webapp/src/constants/manifest.js b/webapp/src/constants/manifest.js new file mode 100644 index 0000000..d9f2fce --- /dev/null +++ b/webapp/src/constants/manifest.js @@ -0,0 +1 @@ +export const PLUGIN_NAME = 'boilerplate'; diff --git a/webapp/src/index.jsx b/webapp/src/index.jsx index 58e1e12..e1350fd 100644 --- a/webapp/src/index.jsx +++ b/webapp/src/index.jsx @@ -2,6 +2,12 @@ import React from 'react'; import {ChannelHeaderButtonIcon} from 'components/icons'; +import Constants from './constants'; + +// +// Define the plugin class that will register +// our plugin components. +// class PluginClass { initialize(registry) { registry.registerChannelHeaderButtonAction( @@ -21,4 +27,8 @@ class PluginClass { } } -window.registerPlugin('boilerplate', new PluginClass()); +// +// To register your plugin you must expose it +// on window. +// +window.registerPlugin(Constants.PLUGIN_NAME, new PluginClass());