From ab2acea463a474226317b203f25b4a288fc4b715 Mon Sep 17 00:00:00 2001 From: Anthony Lapenna Date: Thu, 10 Oct 2019 10:59:27 +1300 Subject: [PATCH] feat(app): add externally sourced support options (#3249) * feat(app): add externally sourced support options * refactor(api): rename struct fields --- api/http/handler/handler.go | 5 +++ api/http/handler/support/handler.go | 26 +++++++++++++ api/http/handler/support/support_list.go | 39 +++++++++++++++++++ api/http/server.go | 5 +++ api/portainer.go | 2 + app/constants.js | 1 + app/portainer/rest/support.js | 7 ++++ app/portainer/services/api/supportService.js | 21 ++++++++++ app/portainer/views/home/homeController.js | 2 +- app/portainer/views/support/support.html | 17 -------- .../views/support/supportController.js | 32 +++++---------- 11 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 api/http/handler/support/handler.go create mode 100644 api/http/handler/support/support_list.go create mode 100644 app/portainer/rest/support.js create mode 100644 app/portainer/services/api/supportService.js diff --git a/api/http/handler/handler.go b/api/http/handler/handler.go index 9dc541641..7bb72dbb9 100644 --- a/api/http/handler/handler.go +++ b/api/http/handler/handler.go @@ -4,6 +4,8 @@ import ( "net/http" "strings" + "github.com/portainer/portainer/api/http/handler/support" + "github.com/portainer/portainer/api/http/handler/schedules" "github.com/portainer/portainer/api/http/handler/roles" @@ -48,6 +50,7 @@ type Handler struct { SettingsHandler *settings.Handler StackHandler *stacks.Handler StatusHandler *status.Handler + SupportHandler *support.Handler TagHandler *tags.Handler TeamMembershipHandler *teammemberships.Handler TeamHandler *teams.Handler @@ -96,6 +99,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.StripPrefix("/api", h.StackHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/status"): http.StripPrefix("/api", h.StatusHandler).ServeHTTP(w, r) + case strings.HasPrefix(r.URL.Path, "/api/support"): + http.StripPrefix("/api", h.SupportHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/tags"): http.StripPrefix("/api", h.TagHandler).ServeHTTP(w, r) case strings.HasPrefix(r.URL.Path, "/api/templates"): diff --git a/api/http/handler/support/handler.go b/api/http/handler/support/handler.go new file mode 100644 index 000000000..1ac8de22a --- /dev/null +++ b/api/http/handler/support/handler.go @@ -0,0 +1,26 @@ +package support + +import ( + "net/http" + + httperror "github.com/portainer/libhttp/error" + + "github.com/gorilla/mux" + "github.com/portainer/portainer/api/http/security" +) + +// Handler is the HTTP handler used to handle support operations. +type Handler struct { + *mux.Router +} + +// NewHandler returns a new Handler +func NewHandler(bouncer *security.RequestBouncer) *Handler { + h := &Handler{ + Router: mux.NewRouter(), + } + h.Handle("/support", + bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.supportList))).Methods(http.MethodGet) + + return h +} diff --git a/api/http/handler/support/support_list.go b/api/http/handler/support/support_list.go new file mode 100644 index 000000000..a16d8dafd --- /dev/null +++ b/api/http/handler/support/support_list.go @@ -0,0 +1,39 @@ +package support + +import ( + "encoding/json" + + httperror "github.com/portainer/libhttp/error" + portainer "github.com/portainer/portainer/api" + + "net/http" + + "github.com/portainer/portainer/api/http/client" + + "github.com/portainer/libhttp/response" +) + +type supportProduct struct { + ID int `json:"Id"` + Name string `json:"Name"` + ShortDescription string `json:"ShortDescription"` + Price string `json:"Price"` + PriceDescription string `json:"PriceDescription"` + Description string `json:"Description"` + ProductID string `json:"ProductId"` +} + +func (handler *Handler) supportList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { + supportData, err := client.Get(portainer.SupportProductsURL, 30) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to fetch support options", err} + } + + var supportProducts []supportProduct + err = json.Unmarshal(supportData, &supportProducts) + if err != nil { + return &httperror.HandlerError{http.StatusInternalServerError, "Unable to fetch support options", err} + } + + return response.JSON(w, supportProducts) +} diff --git a/api/http/server.go b/api/http/server.go index 664c1989f..40c11ac2e 100644 --- a/api/http/server.go +++ b/api/http/server.go @@ -3,6 +3,8 @@ package http import ( "time" + "github.com/portainer/portainer/api/http/handler/support" + "github.com/portainer/portainer/api/http/handler/roles" "github.com/portainer/portainer/api" @@ -227,6 +229,8 @@ func (server *Server) Start() error { var statusHandler = status.NewHandler(requestBouncer, server.Status) + var supportHandler = support.NewHandler(requestBouncer) + var templatesHandler = templates.NewHandler(requestBouncer) templatesHandler.TemplateService = server.TemplateService templatesHandler.SettingsService = server.SettingsService @@ -268,6 +272,7 @@ func (server *Server) Start() error { SettingsHandler: settingsHandler, StatusHandler: statusHandler, StackHandler: stackHandler, + SupportHandler: supportHandler, TagHandler: tagHandler, TeamHandler: teamHandler, TeamMembershipHandler: teamMembershipHandler, diff --git a/api/portainer.go b/api/portainer.go index 7fdf39b5c..a50b0186a 100644 --- a/api/portainer.go +++ b/api/portainer.go @@ -914,6 +914,8 @@ const ( VersionCheckURL = "https://api.github.com/repos/portainer/portainer/releases/latest" // ExtensionDefinitionsURL represents the URL where Portainer extension definitions can be retrieved ExtensionDefinitionsURL = AssetsServerURL + "/extensions-1.22.0.json" + // SupportProductsURL represents the URL where Portainer support products can be retrieved + SupportProductsURL = AssetsServerURL + "/support.json" // PortainerAgentHeader represents the name of the header available in any agent response PortainerAgentHeader = "Portainer-Agent" // PortainerAgentEdgeIDHeader represent the name of the header containing the Edge ID associated to an agent/agent cluster diff --git a/app/constants.js b/app/constants.js index d741e312d..8cbcbb9e8 100644 --- a/app/constants.js +++ b/app/constants.js @@ -11,6 +11,7 @@ angular.module('portainer') .constant('API_ENDPOINT_SETTINGS', 'api/settings') .constant('API_ENDPOINT_STACKS', 'api/stacks') .constant('API_ENDPOINT_STATUS', 'api/status') +.constant('API_ENDPOINT_SUPPORT', 'api/support') .constant('API_ENDPOINT_USERS', 'api/users') .constant('API_ENDPOINT_TAGS', 'api/tags') .constant('API_ENDPOINT_TEAMS', 'api/teams') diff --git a/app/portainer/rest/support.js b/app/portainer/rest/support.js new file mode 100644 index 000000000..7fb3a5a90 --- /dev/null +++ b/app/portainer/rest/support.js @@ -0,0 +1,7 @@ +angular.module('portainer.app') +.factory('Support', ['$resource', 'API_ENDPOINT_SUPPORT', function SupportFactory($resource, API_ENDPOINT_SUPPORT) { + 'use strict'; + return $resource(API_ENDPOINT_SUPPORT, {}, { + get: { method: 'GET', isArray: true }, + }); +}]); diff --git a/app/portainer/services/api/supportService.js b/app/portainer/services/api/supportService.js new file mode 100644 index 000000000..9bbcb56aa --- /dev/null +++ b/app/portainer/services/api/supportService.js @@ -0,0 +1,21 @@ +angular.module('portainer.app') +.factory('SupportService', ['$q', 'Support', function SupportServiceFactory($q, Support) { + 'use strict'; + var service = {}; + + service.supportProducts = function() { + var deferred = $q.defer(); + + Support.get().$promise + .then(function success(data) { + deferred.resolve(data); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve support options', err: err }); + }); + + return deferred.promise; + }; + + return service; +}]); diff --git a/app/portainer/views/home/homeController.js b/app/portainer/views/home/homeController.js index 78dc765d2..003f96edb 100644 --- a/app/portainer/views/home/homeController.js +++ b/app/portainer/views/home/homeController.js @@ -22,7 +22,7 @@ angular.module('portainer.app') endpoint = data; return switchToDockerEndpoint(endpoint); }).catch(function error(err) { - Notifications.error('Failure', err, 'Unable to verify endpoint status'); + Notifications.error('Failure', err, 'Unable to verify endpoint status'); }); }; diff --git a/app/portainer/views/support/support.html b/app/portainer/views/support/support.html index 97e0e3a7e..8bae72b91 100644 --- a/app/portainer/views/support/support.html +++ b/app/portainer/views/support/support.html @@ -6,23 +6,6 @@ - - -

- Business support is a subscription service and is delivered by Portainer developers directly. Portainer Business Support is available in Standard and Critical levels, which offer a range of availability and response time options. -

-

- Once acquired through an in-app purchase, a support subscription will enable private access to the Portainer Support Portal at the appropriate service level. -

-

- Business support includes comprehensive assistance and issue resolution for Portainer software, as well as the ability to ask “how to” questions. -

-

- Issues outside Portainer (such as those relating to third party software or hardware) will be diagnosed, verified and the client will be referred to the relevant supplier for support. Portainer support will investigate and resolve any bugs that are identified as part of the support case. -

-
-
-