feat(app): add externally sourced support options (#3249)

* feat(app): add externally sourced support options

* refactor(api): rename struct fields
pull/3255/head
Anthony Lapenna 2019-10-10 10:59:27 +13:00 committed by GitHub
parent 521a36e629
commit ab2acea463
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 116 additions and 41 deletions

View File

@ -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"):

View File

@ -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
}

View File

@ -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)
}

View File

@ -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,

View File

@ -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

View File

@ -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')

View File

@ -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 },
});
}]);

View File

@ -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;
}]);

View File

@ -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');
});
};

View File

@ -6,23 +6,6 @@
</rd-header-content>
</rd-header>
<information-panel title-text="Information">
<span class="small text-muted">
<p>
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.
</p>
<p>
Once acquired through an in-app purchase, a support subscription will enable private access to the Portainer Support Portal at the appropriate service level.
</p>
<p>
Business support includes comprehensive assistance and issue resolution for Portainer software, as well as the ability to ask “how to” questions.
</p>
<p>
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.
</p>
</span>
</information-panel>
<div class="row">
<div class="col-sm-12">
<product-list

View File

@ -1,34 +1,20 @@
angular.module('portainer.app')
.controller('SupportController', ['$scope', '$state',
function($scope, $state) {
.controller('SupportController', ['$scope', '$state', 'SupportService', 'Notifications',
function($scope, $state, SupportService, Notifications) {
$scope.goToProductView = function(product) {
$state.go('portainer.support.product', { product: product });
};
function initView() {
var supportProducts = [
{
Id: 1,
Name: 'Business Support Standard',
ShortDescription: '11x5 support with 4 hour response',
Price: 'US$120.00',
PriceDescription: 'Price per month per host (minimum 10 hosts)',
Description: 'Portainer Business Support Standard:\n\n* 7am 6pm business days, local time.\n* 4 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
ProductId: '1163'
},
{
Id: 2,
Name: 'Business Support Critical',
ShortDescription: '24x7 support with 1 hour response',
Price: 'US$240.00',
PriceDescription: 'Price per month per host (minimum 10 hosts)',
Description: 'Portainer Business Support Critical:\n\n* 24x7\n* 1 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with advanced support for critical requirements. Business Support Critical is an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
ProductId: '1162'
}
];
$scope.products = supportProducts;
SupportService.supportProducts()
.then(function success(data){
$scope.products = data;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to fetch support options');
});
}
initView();