mirror of
https://github.com/micromdm/micromdm/
synced 2026-08-08 10:45:34 +08:00
Adding a VPP Package (#538)
This commit is contained in:
111
vpp/client.go
Normal file
111
vpp/client.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package vpp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
serverURL = "https://your.server.com" // This needs to be modified to be imported from server
|
||||
version = "" // This needs to be modified to be imported from server
|
||||
|
||||
defaultBaseURL = "https://vpp.itunes.apple.com/WebObjects/MZFinance.woa/wa/VPPServiceConfigSrv"
|
||||
mediaType = "application/json;charset=UTF8"
|
||||
XServerProtocolVersionHeader = "X-Server-Protocol-Version"
|
||||
XServerProtocolVersion = "3"
|
||||
)
|
||||
|
||||
type HTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Contains the sToken string used to authenticate to the various VPP services
|
||||
// Contains the return VPPServiceConfigSrv information
|
||||
type Client struct {
|
||||
SToken string
|
||||
VPPServiceConfigSrv *VPPServiceConfigSrv
|
||||
UserAgent string
|
||||
Client HTTPClient
|
||||
BaseURL *url.URL
|
||||
}
|
||||
|
||||
func NewClient(sToken string) (*Client, error) {
|
||||
baseURL, _ := url.Parse(defaultBaseURL)
|
||||
c := Client{
|
||||
SToken: sToken,
|
||||
UserAgent: path.Join("micromdm", version),
|
||||
Client: http.DefaultClient,
|
||||
BaseURL: baseURL,
|
||||
}
|
||||
|
||||
// Get VPPServiceConfigSrv Data
|
||||
VPPServiceConfigSrv, err := c.GetVPPServiceConfigSrv()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create VPPServiceConfigSrv request")
|
||||
}
|
||||
c.VPPServiceConfigSrv = VPPServiceConfigSrv
|
||||
|
||||
// Set Client Context If Needed
|
||||
context, err := c.GetClientContext()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "GetClientContext request")
|
||||
}
|
||||
|
||||
if context.HostName != serverURL {
|
||||
_, err := c.SetClientContext(serverURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "SetClientContext request")
|
||||
}
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(method, URLStr string, body interface{}) (*http.Request, error) {
|
||||
rel, err := url.Parse(URLStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "parse vpp request url %s", URLStr)
|
||||
}
|
||||
|
||||
u := c.BaseURL.ResolveReference(rel)
|
||||
var buf bytes.Buffer
|
||||
if body != nil {
|
||||
if err := json.NewEncoder(&buf).Encode(body); err != nil {
|
||||
return nil, errors.Wrap(err, "encode http body for VPP request")
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, u.String(), &buf)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "creating %s request to vpp %s", method, u.String())
|
||||
}
|
||||
|
||||
req.Header.Add("User-Agent", c.UserAgent)
|
||||
req.Header.Add("Content-Type", mediaType)
|
||||
req.Header.Add("Accept", mediaType)
|
||||
req.Header.Add(XServerProtocolVersionHeader, XServerProtocolVersion)
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (c *Client) do(req *http.Request, into interface{}) error {
|
||||
resp, err := c.Client.Do(req)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "perform vpp request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
return errors.Errorf("unexpected vpp response. status=%d VPP API Error: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
err = json.NewDecoder(resp.Body).Decode(into)
|
||||
|
||||
return errors.Wrap(err, "decode VPP response body")
|
||||
}
|
||||
152
vpp/clientconfigsrv.go
Normal file
152
vpp/clientconfigsrv.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package vpp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/satori/go.uuid"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Contains information that associates your particular mdm server to a VPP account token
|
||||
type ClientContext struct {
|
||||
HostName string `json:"hostname"`
|
||||
GUID string `json:"guid"`
|
||||
}
|
||||
|
||||
// Contains location information associated with a VPP account token
|
||||
type Location struct {
|
||||
LocationName string `json:"locationName"`
|
||||
LocationID int `json:"locationId"`
|
||||
}
|
||||
|
||||
// Contains org information associated with a VPP account token
|
||||
type ClientConfigSrv struct {
|
||||
ClientContext string `json:"clientContext"`
|
||||
AppleID string `json:"appleId,omitempty"`
|
||||
OrganizationIDHash string `json:"organizationIdHash"`
|
||||
Status int `json:"status"`
|
||||
OrganizationID int `json:"organizationId"`
|
||||
UID string `json:"uId"`
|
||||
CountryCode string `json:"countryCode"`
|
||||
Location Location `json:"location"`
|
||||
APNToken string `json:"apnToken"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
// These specify options for the ClientConfigSrv
|
||||
type GetClientConfigSrvOptions func(*getClientConfigSrvOpts) error
|
||||
|
||||
type getClientConfigSrvOpts struct {
|
||||
SToken string `json:"sToken"`
|
||||
Verbose bool `json:"verbose,omitempty"`
|
||||
ClientContext string `json:"clientContext,omitempty"`
|
||||
}
|
||||
|
||||
// Verbose is an optional argument that can be added to GetClientConfigSrv
|
||||
func VerboseOption(verbose bool) GetClientConfigSrvOptions {
|
||||
return func(opts *getClientConfigSrvOpts) error {
|
||||
opts.Verbose = verbose
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ClientContext is an optional argument that can be added to GetClientConfigSrv
|
||||
func ClientContextOption(context string) GetClientConfigSrvOptions {
|
||||
return func(opts *getClientConfigSrvOpts) error {
|
||||
opts.ClientContext = context
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Gets ClientConfigSrv information
|
||||
func (c *Client) GetClientConfigSrv(opts ...GetClientConfigSrvOptions) (*ClientConfigSrv, error) {
|
||||
// Set required and optional arguments
|
||||
request := &getClientConfigSrvOpts{SToken: c.SToken}
|
||||
for _, option := range opts {
|
||||
if err := option(request); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Get the ClientConfigSrvURL
|
||||
clientConfigSrvURL := c.VPPServiceConfigSrv.ClientConfigSrvURL
|
||||
|
||||
// Create the ClientConfigSrvURL request
|
||||
req, err := c.newRequest("POST", clientConfigSrvURL, request)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create ClientConfigSrv request")
|
||||
}
|
||||
|
||||
// Make the request
|
||||
var response ClientConfigSrv
|
||||
err = c.do(req, &response)
|
||||
|
||||
return &response, errors.Wrap(err, "make ClientConfigSrv request")
|
||||
}
|
||||
|
||||
// Gets the appleID field along with the standard information
|
||||
func (c *Client) GetClientConfigSrvVerbose() (*ClientConfigSrv, error) {
|
||||
options := VerboseOption(true)
|
||||
response, err := c.GetClientConfigSrv(options)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "using verbose option")
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// Gets the values that determine which mdm server is associated with a VPP account token
|
||||
func (c *Client) GetClientContext() (*ClientContext, error) {
|
||||
// Get the ClientConfigSrv info
|
||||
clientConfigSrv, err := c.GetClientConfigSrv()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get ClientContext request")
|
||||
}
|
||||
|
||||
// Get the ClientContext string
|
||||
var context = clientConfigSrv.ClientContext
|
||||
|
||||
// Convert the string to a ClientContext type
|
||||
var clientContext ClientContext
|
||||
err = json.NewDecoder(strings.NewReader(context)).Decode(&clientContext)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode ClientContext")
|
||||
}
|
||||
|
||||
return &clientContext, nil
|
||||
}
|
||||
|
||||
// Sets the values that determine which mdm server is associated with a VPP account token
|
||||
func (c *Client) SetClientContext(serverURL string) (*ClientContext, error) {
|
||||
// Generate a UUID that is tracked to ensure VPP licenses are up to date
|
||||
uuid := uuid.NewV4().String()
|
||||
|
||||
// Generate a ClientContext string with the new UUID and the current serverURL
|
||||
context := ClientContext{serverURL, uuid}
|
||||
data, err := json.Marshal(context)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create new ClientContext")
|
||||
}
|
||||
newContext := string(data)
|
||||
|
||||
// Enter the new ClientContext string into the ClientConfigSrv options
|
||||
options := ClientContextOption(newContext)
|
||||
|
||||
// Set the new ClientContext into the VPP account token
|
||||
response, err := c.GetClientConfigSrv(options)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "set ClientContext request")
|
||||
}
|
||||
|
||||
// Get the new ClientContext string
|
||||
var contextString = response.ClientContext
|
||||
|
||||
// Convert the string to a ClientContext type
|
||||
var clientContext ClientContext
|
||||
err = json.NewDecoder(strings.NewReader(contextString)).Decode(&clientContext)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decode new ClientContext")
|
||||
}
|
||||
|
||||
return &clientContext, nil
|
||||
}
|
||||
89
vpp/getlicensessrv.go
Normal file
89
vpp/getlicensessrv.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package vpp
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// Contains information about the VPP Licenses associated with a VPP account token
|
||||
type LicensesSrv struct {
|
||||
IfModifiedSinceMillisOrig string `json:"ifModifiedSinceMillisOrig"`
|
||||
TotalCount int `json:"totalCount"`
|
||||
Status int `json:"status"`
|
||||
TotalBatchCount string `json:"totalBatchCount"`
|
||||
Licenses []License `json:"licenses"`
|
||||
BatchToken string `json:"batchToken"`
|
||||
BatchCount int `json:"batchCount"`
|
||||
ClientContext string `json:"clientContext"`
|
||||
UID string `json:"uId"`
|
||||
Location Location `json:"location"`
|
||||
}
|
||||
|
||||
// Contains information about VPP Licenses
|
||||
type License struct {
|
||||
LicenseID int `json:"licenseId"`
|
||||
ProductTypeID int `json:"productTypeId"`
|
||||
IsIrrevocable bool `json:"isIrrevocable"`
|
||||
Status string `json:"status"`
|
||||
PricingParam string `json:"pricingParam"`
|
||||
AdamIDStr string `json:"adamIdStr"`
|
||||
LicenseIDStr string `json:"licenseIdStr"`
|
||||
ProductTypeName string `json:"productTypeName"`
|
||||
AdamID int `json:"adamId"`
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
}
|
||||
|
||||
// Options for the LicensesSrv
|
||||
type GetLicensesSrvOptions struct {
|
||||
SToken string `json:"sToken"`
|
||||
SerialNumber string `json:"serialNumber,omitempty"`
|
||||
}
|
||||
|
||||
// Gets the LicensesSrv information
|
||||
func (c *Client) GetLicensesSrv(options GetLicensesSrvOptions) (*LicensesSrv, error) {
|
||||
// Sends the sToken string
|
||||
options.SToken = c.SToken
|
||||
|
||||
// Get the LicensesSrvURL
|
||||
licensesSrvURL := c.VPPServiceConfigSrv.GetLicensesSrvURL
|
||||
|
||||
// Create the LicensesSrv request
|
||||
req, err := c.newRequest("POST", licensesSrvURL, options)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create LicensesSrv request")
|
||||
}
|
||||
|
||||
// Make the request
|
||||
var response LicensesSrv
|
||||
err = c.do(req, &response)
|
||||
|
||||
return &response, errors.Wrap(err, "make LicensesSrv request")
|
||||
}
|
||||
|
||||
// Gets licenses with specified serial associated
|
||||
func (c *Client) GetLicensesForSerial(serial string) ([]License, error) {
|
||||
options := GetLicensesSrvOptions{
|
||||
SerialNumber: serial,
|
||||
}
|
||||
|
||||
response, err := c.GetLicensesSrv(options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
licenses := response.Licenses
|
||||
return licenses, err
|
||||
}
|
||||
|
||||
// Checks if a particular serial is associated with an appID
|
||||
func (c *Client) CheckAssignedLicense(serial string, appID string) (bool, error) {
|
||||
// Get all licenses with serial associated
|
||||
licenses, err := c.GetLicensesForSerial(serial)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check for the particular appID
|
||||
for _, lic := range licenses {
|
||||
if lic.AdamIDStr == appID {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
74
vpp/getvppassetssrv.go
Normal file
74
vpp/getvppassetssrv.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package vpp
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// Contains information about the VPP Assets associated with a VPP account token
|
||||
type VPPAssetsSrv struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
Status int `json:"status"`
|
||||
Assets []Asset `json:"assets"`
|
||||
ClientContext string `json:"clientContext"`
|
||||
UID string `json:"uId"`
|
||||
Location Location `json:"location"`
|
||||
}
|
||||
|
||||
// Contains information about VPP Assets
|
||||
type Asset struct {
|
||||
ProductTypeID int `json:"productTypeId"`
|
||||
IsIrrevocable bool `json:"isIrrevocable"`
|
||||
PricingParam string `json:"pricingParam"`
|
||||
AdamIDStr string `json:"adamIdStr"`
|
||||
ProductTypeName string `json:"productTypeName"`
|
||||
DeviceAssignable bool `json:"deviceAssignable"`
|
||||
}
|
||||
|
||||
// Gets information about the VPP Assets associated with a VPP Account token
|
||||
func (c *Client) GetVPPAssetsSrv() (*VPPAssetsSrv, error) {
|
||||
// Send the sToken string
|
||||
request := struct {
|
||||
SToken string `json:"sToken"`
|
||||
}{
|
||||
SToken: c.SToken,
|
||||
}
|
||||
|
||||
// Get the VPPAssetsSrvURL
|
||||
VPPAssetsSrvURL := c.VPPServiceConfigSrv.GetVPPAssetsSrvURL
|
||||
|
||||
// Create the VPPAssetsSrv request
|
||||
req, err := c.newRequest("POST", VPPAssetsSrvURL, request)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create VPPAssetsSrv request")
|
||||
}
|
||||
|
||||
// Make the request
|
||||
var response VPPAssetsSrv
|
||||
err = c.do(req, &response)
|
||||
|
||||
return &response, errors.Wrap(err, "get VPPAssetsSrv request")
|
||||
}
|
||||
|
||||
// Gets the pricing param for a particular VPP asset
|
||||
func (c *Client) GetPricingParamForApp(appID string) (string, error) {
|
||||
// Get a list of assets
|
||||
response, err := c.GetVPPAssetsSrv()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var assets = response.Assets
|
||||
|
||||
// Find the pricing param for the asset with matching appId
|
||||
var pricing string
|
||||
for _, asset := range assets {
|
||||
if asset.AdamIDStr == appID {
|
||||
pricing = asset.PricingParam
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check for err finding Pricing Param
|
||||
if pricing == "" {
|
||||
err := errors.New("Unable to find Pricing Param")
|
||||
return pricing, err
|
||||
}
|
||||
return pricing, nil
|
||||
}
|
||||
86
vpp/managevpplicensesbyadamidsrv.go
Normal file
86
vpp/managevpplicensesbyadamidsrv.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package vpp
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// Contains information about a managed license
|
||||
type ManageVPPLicensesByAdamIdSrv struct {
|
||||
ProductTypeID int `json:"productTypeId,omitempty"`
|
||||
ProductTypeName string `json:"productTypeName,omitempty"`
|
||||
IsIrrevocable bool `json:"isIrrevocable,omitempty"`
|
||||
PricingParam string `json:"pricingParam,omitempty"`
|
||||
UID string `json:"uId,omitempty,omitempty"`
|
||||
AdamIdStr string `json:"adamIdStr,omitempty"`
|
||||
Status int `json:"status"`
|
||||
ClientContext string `json:"clientContext,omitempty"`
|
||||
Location *Location `json:"location,omitempty"`
|
||||
Associations []Association `json:"associations,omitempty"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
ErrorNumber int `json:"errorNumber,omitempty"`
|
||||
}
|
||||
|
||||
// Contains information about an app association
|
||||
type Association struct {
|
||||
SerialNumber string `json:"serialNumber"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
ErrorCode int `json:"errorCode,omitempty"`
|
||||
ErrorNumber int `json:"errorNumber,omitempty"`
|
||||
LicenseIDStr string `json:"licenseIdStr,omitempty"`
|
||||
LicenseAlreadyAssigned *License `json:"licenseAlreadyAssigned,omitempty"`
|
||||
}
|
||||
|
||||
// Contains options to pass to the ManageVPPLicensesByAdamIdSrv
|
||||
type ManageVPPLicensesByAdamIdSrvOptions struct {
|
||||
SToken string `json:"sToken"`
|
||||
AdamIDStr string `json:"adamIdStr"`
|
||||
PricingParam string `json:"pricingParam"`
|
||||
AssociateSerialNumbers []string `json:"associateSerialNumbers,omitempty"`
|
||||
DisassociateSerialNumbers []string `json:"disassociateSerialNumbers,omitempty"`
|
||||
}
|
||||
|
||||
// Associates a list of serials to a VPP app license
|
||||
func (c *Client) AssociateSerialsToApp(appID string, serials []string) (*ManageVPPLicensesByAdamIdSrv, error) {
|
||||
options := ManageVPPLicensesByAdamIdSrvOptions{
|
||||
AssociateSerialNumbers: serials,
|
||||
}
|
||||
|
||||
response, err := c.ManageVPPLicensesByAdamIdSrv(appID, options)
|
||||
return &response, err
|
||||
}
|
||||
|
||||
// Disssociates a list of serials to a VPP app license
|
||||
func (c *Client) DisassociateSerialsToApp(appID string, serials []string) (*ManageVPPLicensesByAdamIdSrv, error) {
|
||||
options := ManageVPPLicensesByAdamIdSrvOptions{
|
||||
DisassociateSerialNumbers: serials,
|
||||
}
|
||||
|
||||
response, err := c.ManageVPPLicensesByAdamIdSrv(appID, options)
|
||||
return &response, err
|
||||
}
|
||||
|
||||
// Interfaces with the ManageVPPLicensesByAdamIdSrv to managed VPP licenses
|
||||
func (c *Client) ManageVPPLicensesByAdamIdSrv(appID string, options ManageVPPLicensesByAdamIdSrvOptions) (ManageVPPLicensesByAdamIdSrv, error) {
|
||||
options.SToken = c.SToken
|
||||
options.AdamIDStr = appID
|
||||
|
||||
// Get the pricing param required to manage a vpp license
|
||||
pricing, err := c.GetPricingParamForApp(appID)
|
||||
if err != nil {
|
||||
return ManageVPPLicensesByAdamIdSrv{}, errors.Wrap(err, "get PricingParam request")
|
||||
}
|
||||
options.PricingParam = pricing
|
||||
|
||||
// Get the ManageVPPLicensesByAdamIdSrvURL
|
||||
manageVPPLicensesByAdamIdSrvUrl := c.VPPServiceConfigSrv.ManageVPPLicensesByAdamIdSrvURL
|
||||
|
||||
// Create the ManageVPPLicensesByAdamIdSrv request
|
||||
req, err := c.newRequest("POST", manageVPPLicensesByAdamIdSrvUrl, options)
|
||||
if err != nil {
|
||||
return ManageVPPLicensesByAdamIdSrv{}, errors.Wrap(err, "create ManageVPPLicensesByAdamIdSrv request")
|
||||
}
|
||||
|
||||
// Make the Request
|
||||
var response ManageVPPLicensesByAdamIdSrv
|
||||
err = c.do(req, &response)
|
||||
|
||||
return response, errors.Wrap(err, "ManageVPPLicensesByAdamIdSrv request")
|
||||
}
|
||||
41
vpp/vppserviceconfigsrv.go
Normal file
41
vpp/vppserviceconfigsrv.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package vpp
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// Contains the most recent data from Apple for configuring vpp
|
||||
type VPPServiceConfigSrv struct {
|
||||
EditUserSrvURL string `json:"editUserSrvUrl"`
|
||||
DisassociateLicenseSrvURL string `json:"disassociateLicenseSrvUrl"`
|
||||
ContentMetadataLookupURL string `json:"contentMetadataLookupUrl"`
|
||||
ClientConfigSrvURL string `json:"clientConfigSrvUrl"`
|
||||
GetUserSrvURL string `json:"getUserSrvUrl"`
|
||||
GetUsersSrvURL string `json:"getUsersSrvUrl"`
|
||||
GetLicensesSrvURL string `json:"getLicensesSrvUrl"`
|
||||
GetVPPAssetsSrvURL string `json:"getVPPAssetsSrvUrl"`
|
||||
VppWebsiteURL string `json:"vppWebsiteUrl"`
|
||||
InvitationEmailURL string `json:"invitationEmailUrl"`
|
||||
RetireUserSrvURL string `json:"retireUserSrvUrl"`
|
||||
AssociateLicenseSrvURL string `json:"associateLicenseSrvUrl"`
|
||||
ManageVPPLicensesByAdamIdSrvURL string `json:"manageVPPLicensesByAdamIdSrvUrl"`
|
||||
RegisterUserSrvURL string `json:"registerUserSrvUrl"`
|
||||
MaxBatchAssociateLicenseCount int `json:"maxBatchAssociateLicenseCount"`
|
||||
MaxBatchDisassociateLicenseCount int `json:"maxBatchDisassociateLicenseCount"`
|
||||
Status int `json:"status"`
|
||||
ErrorCodes []Error `json:"errorCodes"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
ErrorMessage string `json:"errorMessage"`
|
||||
ErrorNumber int `json:"errorNumber"`
|
||||
}
|
||||
|
||||
func (c *Client) GetVPPServiceConfigSrv() (*VPPServiceConfigSrv, error) {
|
||||
var response VPPServiceConfigSrv
|
||||
req, err := c.newRequest("GET", c.BaseURL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "create VPPServiceConfigSrv request")
|
||||
}
|
||||
|
||||
err = c.do(req, &response)
|
||||
return &response, errors.Wrap(err, "VPPServiceConfigSrv request")
|
||||
}
|
||||
Reference in New Issue
Block a user