From 83ea8491aaa64d60b0bc99c8288a3e6886a02cb6 Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Sat, 9 Dec 2017 16:50:10 -0500 Subject: [PATCH] reorganize profile service (#347) Moved all the endpoints into the profile package. Defined a Store interface which includes all the used BoltDB methods. Moved the BoltDB implementation into a subpackage. --- cmd/mdmctl/apply.go | 12 ++- cmd/mdmctl/get.go | 11 +-- cmd/mdmctl/remove.go | 10 +-- cmd/mdmctl/remove_profiles.go | 2 +- cmd/mdmctl/setup.go | 59 +++++++++++++++ cmd/micromdm/serve.go | 29 ++++---- mdm/enroll/service.go | 4 +- platform/api/server/apply/client.go | 11 --- platform/api/server/apply/endpoint.go | 31 -------- platform/api/server/apply/service.go | 7 -- platform/api/server/apply/transport_http.go | 24 ------ platform/api/server/list/client.go | 11 --- platform/api/server/list/endpoint.go | 30 -------- platform/api/server/list/service.go | 19 +---- platform/api/server/list/transport_http.go | 26 ------- platform/api/server/remove/client.go | 11 --- platform/api/server/remove/endpoint.go | 21 ------ platform/api/server/remove/service.go | 15 ---- platform/api/server/remove/transport_http.go | 6 -- platform/blueprint/db.go | 4 +- platform/profile/apply_profile.go | 59 +++++++++++++++ platform/profile/{ => builtin}/db.go | 28 ++++--- platform/profile/client.go | 70 ++++++++++++++++++ platform/profile/get_profiles.go | 70 ++++++++++++++++++ platform/profile/remove_profiles.go | 65 +++++++++++++++++ platform/profile/server.go | 77 ++++++++++++++++++++ platform/profile/service.go | 40 ++++++++++ 27 files changed, 487 insertions(+), 265 deletions(-) create mode 100644 cmd/mdmctl/setup.go create mode 100644 platform/profile/apply_profile.go rename platform/profile/{ => builtin}/db.go (79%) create mode 100644 platform/profile/client.go create mode 100644 platform/profile/get_profiles.go create mode 100644 platform/profile/remove_profiles.go create mode 100644 platform/profile/server.go create mode 100644 platform/profile/service.go diff --git a/cmd/mdmctl/apply.go b/cmd/mdmctl/apply.go index 2715fadb..d260af21 100644 --- a/cmd/mdmctl/apply.go +++ b/cmd/mdmctl/apply.go @@ -13,18 +13,16 @@ import ( "strings" "github.com/go-kit/kit/log" - httptransport "github.com/go-kit/kit/transport/http" "github.com/pkg/errors" uuid "github.com/satori/go.uuid" - "github.com/micromdm/micromdm/platform/api/server/apply" "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/profile" ) type applyCommand struct { - config *ServerConfig - applysvc apply.Service + config *ServerConfig + *remoteServices } func (cmd *applyCommand) setup() error { @@ -34,11 +32,11 @@ func (cmd *applyCommand) setup() error { } cmd.config = cfg logger := log.NewLogfmtLogger(os.Stderr) - applysvc, err := apply.NewClient(cfg.ServerURL, logger, cfg.APIToken, httptransport.SetClient(skipVerifyHTTPClient(cmd.config.SkipVerify))) + remote, err := setupClient(logger) if err != nil { return err } - cmd.applysvc = applysvc + cmd.remoteServices = remote return nil } @@ -256,7 +254,7 @@ func (cmd *applyCommand) applyProfile(args []string) error { } ctx := context.Background() - err = cmd.applysvc.ApplyProfile(ctx, &p) + err = cmd.profilesvc.ApplyProfile(ctx, &p) if err != nil { return err } diff --git a/cmd/mdmctl/get.go b/cmd/mdmctl/get.go index 66413d8f..1ab0f036 100644 --- a/cmd/mdmctl/get.go +++ b/cmd/mdmctl/get.go @@ -13,16 +13,16 @@ import ( "crypto/x509" "github.com/go-kit/kit/log" - httptransport "github.com/go-kit/kit/transport/http" "github.com/pkg/errors" "github.com/micromdm/micromdm/pkg/crypto" "github.com/micromdm/micromdm/platform/api/server/list" + "github.com/micromdm/micromdm/platform/profile" ) type getCommand struct { config *ServerConfig - list list.Service + *remoteServices } func (cmd *getCommand) setup() error { @@ -32,11 +32,12 @@ func (cmd *getCommand) setup() error { } cmd.config = cfg logger := log.NewLogfmtLogger(os.Stderr) - listsvc, err := list.NewClient(cfg.ServerURL, logger, cfg.APIToken, httptransport.SetClient(skipVerifyHTTPClient(cmd.config.SkipVerify))) + + remote, err := setupClient(logger) if err != nil { return err } - cmd.list = listsvc + cmd.remoteServices = remote return nil } @@ -304,7 +305,7 @@ func (cmd *getCommand) getProfiles(args []string) error { } ctx := context.Background() - profiles, err := cmd.list.GetProfiles(ctx, list.GetProfilesOption{Identifier: *flIdentifier}) + profiles, err := cmd.profilesvc.GetProfiles(ctx, profile.GetProfilesOption{Identifier: *flIdentifier}) if err != nil { return err } diff --git a/cmd/mdmctl/remove.go b/cmd/mdmctl/remove.go index 6b7a39c2..4279d32e 100644 --- a/cmd/mdmctl/remove.go +++ b/cmd/mdmctl/remove.go @@ -6,15 +6,11 @@ import ( "strings" "github.com/go-kit/kit/log" - - httptransport "github.com/go-kit/kit/transport/http" - - "github.com/micromdm/micromdm/platform/api/server/remove" ) type removeCommand struct { config *ServerConfig - remove remove.Service + *remoteServices } func (cmd *removeCommand) setup() error { @@ -24,11 +20,11 @@ func (cmd *removeCommand) setup() error { } cmd.config = cfg logger := log.NewLogfmtLogger(os.Stderr) - rmsvc, err := remove.NewClient(cfg.ServerURL, logger, cfg.APIToken, httptransport.SetClient(skipVerifyHTTPClient(cmd.config.SkipVerify))) + remote, err := setupClient(logger) if err != nil { return err } - cmd.remove = rmsvc + cmd.remoteServices = remote return nil } diff --git a/cmd/mdmctl/remove_profiles.go b/cmd/mdmctl/remove_profiles.go index fb0a68bb..003b447e 100644 --- a/cmd/mdmctl/remove_profiles.go +++ b/cmd/mdmctl/remove_profiles.go @@ -18,7 +18,7 @@ func (cmd *removeCommand) removeProfiles(args []string) error { } ctx := context.Background() - err := cmd.remove.RemoveProfiles(ctx, strings.Split(*flIdentifier, ",")) + err := cmd.profilesvc.RemoveProfiles(ctx, strings.Split(*flIdentifier, ",")) if err != nil { return err } diff --git a/cmd/mdmctl/setup.go b/cmd/mdmctl/setup.go new file mode 100644 index 00000000..821f6d9c --- /dev/null +++ b/cmd/mdmctl/setup.go @@ -0,0 +1,59 @@ +package main + +import ( + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" + + "github.com/micromdm/micromdm/platform/api/server/apply" + "github.com/micromdm/micromdm/platform/api/server/list" + "github.com/micromdm/micromdm/platform/api/server/remove" + "github.com/micromdm/micromdm/platform/profile" +) + +type remoteServices struct { + profilesvc profile.Service + applysvc apply.Service + list list.Service + remove remove.Service +} + +func setupClient(logger log.Logger) (*remoteServices, error) { + cfg, err := LoadServerConfig() + if err != nil { + return nil, err + } + applysvc, err := apply.NewClient( + cfg.ServerURL, logger, cfg.APIToken, + httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) + if err != nil { + return nil, err + } + + profilesvc, err := profile.NewHTTPClient( + cfg.ServerURL, cfg.APIToken, logger, + httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) + if err != nil { + return nil, err + } + + listsvc, err := list.NewClient( + cfg.ServerURL, logger, cfg.APIToken, + httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) + if err != nil { + return nil, err + } + + rmsvc, err := remove.NewClient( + cfg.ServerURL, logger, cfg.APIToken, + httptransport.SetClient(skipVerifyHTTPClient(cfg.SkipVerify))) + if err != nil { + return nil, err + } + + return &remoteServices{ + profilesvc: profilesvc, + applysvc: applysvc, + list: listsvc, + remove: rmsvc, + }, nil +} diff --git a/cmd/micromdm/serve.go b/cmd/micromdm/serve.go index 705400ee..eb50b011 100644 --- a/cmd/micromdm/serve.go +++ b/cmd/micromdm/serve.go @@ -54,6 +54,7 @@ import ( "github.com/micromdm/micromdm/platform/deptoken" "github.com/micromdm/micromdm/platform/device" "github.com/micromdm/micromdm/platform/profile" + profilebuiltin "github.com/micromdm/micromdm/platform/profile/builtin" "github.com/micromdm/micromdm/platform/pubsub" "github.com/micromdm/micromdm/platform/pubsub/inmem" "github.com/micromdm/micromdm/platform/queue" @@ -175,7 +176,7 @@ func serve(args []string) error { stdlog.Fatal(err) } - sm.profileDB, err = profile.NewDB(sm.db) + sm.profileDB, err = profilebuiltin.NewDB(sm.db) if err != nil { stdlog.Fatal(err) } @@ -269,6 +270,13 @@ func serve(args []string) error { tokenDB := &deptoken.DB{DB: sm.db, Publisher: sm.pubclient} appDB := &appstore.Repo{Path: *flRepoPath} + var profilesvc profile.Service + { + profilesvc = profile.New(sm.profileDB) + } + + profileEndpoints := profile.MakeServerEndpoints(profilesvc) + var listsvc list.Service { l := &list.ListService{ @@ -276,7 +284,6 @@ func serve(args []string) error { Devices: devDB, Tokens: tokenDB, Blueprints: bpDB, - Profiles: sm.profileDB, Apps: appDB, Users: userDB, } @@ -295,7 +302,6 @@ func serve(args []string) error { ListDevicesEndpoint: listDevicesEndpoint, GetDEPTokensEndpoint: list.MakeGetDEPTokensEndpoint(listsvc), GetBlueprintsEndpoint: list.MakeGetBlueprintsEndpoint(listsvc), - GetProfilesEndpoint: list.MakeGetProfilesEndpoint(listsvc), GetDEPAccountInfoEndpoint: list.MakeGetDEPAccountInfoEndpoint(listsvc), GetDEPProfileEndpoint: list.MakeGetDEPProfileEndpoint(listsvc), GetDEPDeviceEndpoint: list.MakeGetDEPDeviceDetailsEndpoint(listsvc), @@ -309,7 +315,6 @@ func serve(args []string) error { DEPClient: dc, Blueprints: bpDB, Tokens: tokenDB, - Profiles: sm.profileDB, Apps: appDB, Users: userDB, RemoveService: removeService, @@ -325,11 +330,6 @@ func serve(args []string) error { applyBlueprintEndpoint = apply.MakeApplyBlueprintEndpoint(applysvc) } - var applyProfileEndpoint endpoint.Endpoint - { - applyProfileEndpoint = apply.MakeApplyProfileEndpoint(applysvc) - } - var defineDEPProfileEndpoint endpoint.Endpoint { defineDEPProfileEndpoint = apply.MakeDefineDEPProfile(applysvc) @@ -348,7 +348,6 @@ func serve(args []string) error { applyEndpoints := apply.Endpoints{ ApplyBlueprintEndpoint: applyBlueprintEndpoint, ApplyDEPTokensEndpoint: apply.MakeApplyDEPTokensEndpoint(applysvc), - ApplyProfileEndpoint: applyProfileEndpoint, DefineDEPProfileEndpoint: defineDEPProfileEndpoint, AppUploadEndpoint: appUploadEndpoint, ApplyUserEndpoint: applyUserEndpoint, @@ -359,7 +358,7 @@ func serve(args []string) error { listAPIHandlers := list.MakeHTTPHandlers(ctx, listEndpoints, connectOpts...) - rmsvc := &remove.RemoveService{Blueprints: bpDB, Profiles: sm.profileDB, RemoveService: removeService} + rmsvc := &remove.RemoveService{Blueprints: bpDB, RemoveService: removeService} removeAPIHandlers := remove.MakeHTTPHandlers(ctx, remove.MakeEndpoints(rmsvc), connectOpts...) connectHandlers := connect.MakeHTTPHandlers(ctx, connectEndpoints, connectOpts...) @@ -378,8 +377,11 @@ func serve(args []string) error { io.WriteString(w, homePage) }) + profilesHandler := profile.MakeHTTPHandler(profileEndpoints, logger) + // API commands. Only handled if the user provides an api key. if *flAPIKey != "" { + r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, profilesHandler)) r.Handle("/push/{udid}", apiAuthMiddleware(*flAPIKey, pushHandlers.PushHandler)) r.Handle("/v1/commands", apiAuthMiddleware(*flAPIKey, commandHandlers.NewCommandHandler)).Methods("POST") r.Handle("/v1/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.ListDevicesHandler)).Methods("GET") @@ -390,9 +392,6 @@ func serve(args []string) error { r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetBlueprintsHandler)).Methods("GET") r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.BlueprintHandler)).Methods("PUT") r.Handle("/v1/blueprints", apiAuthMiddleware(*flAPIKey, removeAPIHandlers.BlueprintHandler)).Methods("DELETE") - r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetProfilesHandler)).Methods("GET") - r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, applyAPIHandlers.ProfileHandler)).Methods("PUT") - r.Handle("/v1/profiles", apiAuthMiddleware(*flAPIKey, removeAPIHandlers.ProfileHandler)).Methods("DELETE") r.Handle("/v1/dep/devices", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPDeviceDetailsHandler)).Methods("GET") r.Handle("/v1/dep/account", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPAccountInfoHandler)).Methods("GET") r.Handle("/v1/dep/profiles", apiAuthMiddleware(*flAPIKey, listAPIHandlers.GetDEPProfileHandler)).Methods("GET") @@ -496,7 +495,7 @@ type server struct { APNSPrivateKeyPass string tlsCertPath string scepDepot *boltdepot.Depot - profileDB *profile.DB + profileDB profile.Store configDB *config.DB removeDB *block.DB CommandWebhookURL string diff --git a/mdm/enroll/service.go b/mdm/enroll/service.go index 1bac3f77..76d7c07d 100644 --- a/mdm/enroll/service.go +++ b/mdm/enroll/service.go @@ -29,7 +29,7 @@ type Service interface { OTAPhase3(ctx context.Context) (profile.Mobileconfig, error) } -func NewService(topic TopicProvider, sub pubsub.Subscriber, caCertPath, scepURL, scepChallenge, url, tlsCertPath, scepSubject string, profileDB *profile.DB) (Service, error) { +func NewService(topic TopicProvider, sub pubsub.Subscriber, caCertPath, scepURL, scepChallenge, url, tlsCertPath, scepSubject string, profileDB profile.Store) (Service, error) { var caCert, tlsCert []byte var err error @@ -120,7 +120,7 @@ type service struct { SCEPSubject [][][]string CACert []byte TLSCert []byte - ProfileDB *profile.DB + ProfileDB profile.Store topicProvier TopicProvider diff --git a/platform/api/server/apply/client.go b/platform/api/server/apply/client.go index 7c34b698..9dc3dcdb 100644 --- a/platform/api/server/apply/client.go +++ b/platform/api/server/apply/client.go @@ -36,16 +36,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra opts..., ).Endpoint() } - var applyProfileEndpoint endpoint.Endpoint - { - applyProfileEndpoint = httptransport.NewClient( - "PUT", - copyURL(u, "/v1/profiles"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeProfileResponse, - opts..., - ).Endpoint() - } var defineDEPProfileEndpoint endpoint.Endpoint { @@ -94,7 +84,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra return Endpoints{ ApplyBlueprintEndpoint: applyBlueprintEndpoint, ApplyDEPTokensEndpoint: applyDEPTokensEndpoint, - ApplyProfileEndpoint: applyProfileEndpoint, DefineDEPProfileEndpoint: defineDEPProfileEndpoint, AppUploadEndpoint: uploadAppEndpoint, ApplyUserEndpoint: applyUserEndpoint, diff --git a/platform/api/server/apply/endpoint.go b/platform/api/server/apply/endpoint.go index ee00ff6d..0d2eeb11 100644 --- a/platform/api/server/apply/endpoint.go +++ b/platform/api/server/apply/endpoint.go @@ -8,14 +8,12 @@ import ( "github.com/micromdm/dep" "github.com/micromdm/micromdm/platform/blueprint" - "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/user" ) type Endpoints struct { ApplyBlueprintEndpoint endpoint.Endpoint ApplyDEPTokensEndpoint endpoint.Endpoint - ApplyProfileEndpoint endpoint.Endpoint DefineDEPProfileEndpoint endpoint.Endpoint AppUploadEndpoint endpoint.Endpoint ApplyUserEndpoint endpoint.Endpoint @@ -87,15 +85,6 @@ func (e Endpoints) ApplyDEPToken(ctx context.Context, P7MContent []byte) error { return resp.(depTokensResponse).Err } -func (e Endpoints) ApplyProfile(ctx context.Context, p *profile.Profile) error { - request := profileRequest{Profile: p} - resp, err := e.ApplyProfileEndpoint(ctx, request) - if err != nil { - return err - } - return resp.(profileResponse).Err -} - func MakeApplyBlueprintEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(blueprintRequest) @@ -116,16 +105,6 @@ func MakeApplyDEPTokensEndpoint(svc Service) endpoint.Endpoint { } } -func MakeApplyProfileEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(profileRequest) - err = svc.ApplyProfile(ctx, req.Profile) - return profileResponse{ - Err: err, - }, nil - } -} - func MakeDefineDEPProfile(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(depProfileRequest) @@ -192,16 +171,6 @@ type blueprintResponse struct { func (r blueprintResponse) error() error { return r.Err } -type profileRequest struct { - Profile *profile.Profile `json:"profile"` -} - -type profileResponse struct { - Err error `json:"err,omitempty"` -} - -func (r profileResponse) error() error { return r.Err } - type depTokensRequest struct { P7MContent []byte `json:"p7m_content"` } diff --git a/platform/api/server/apply/service.go b/platform/api/server/apply/service.go index 95772392..56574d6e 100644 --- a/platform/api/server/apply/service.go +++ b/platform/api/server/apply/service.go @@ -19,7 +19,6 @@ import ( "github.com/micromdm/micromdm/platform/appstore" "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/deptoken" - "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/pubsub" "github.com/micromdm/micromdm/platform/remove" "github.com/micromdm/micromdm/platform/user" @@ -28,7 +27,6 @@ import ( type Service interface { ApplyBlueprint(ctx context.Context, bp *blueprint.Blueprint) error ApplyDEPToken(ctx context.Context, P7MContent []byte) error - ApplyProfile(ctx context.Context, p *profile.Profile) error UploadApp(ctx context.Context, manifestName string, manifest io.Reader, pkgName string, pkg io.Reader) error ApplyUser(ctx context.Context, u user.User) (*user.User, error) DEPService @@ -40,7 +38,6 @@ type ApplyService struct { DEPClient dep.Client Blueprints *blueprint.DB - Profiles *profile.DB Tokens *deptoken.DB Apps appstore.AppStore Users *user.DB @@ -180,7 +177,3 @@ func (svc *ApplyService) ApplyDEPToken(ctx context.Context, P7MContent []byte) e log.Println("stored DEP token with ck", depToken.ConsumerKey) return nil } - -func (svc *ApplyService) ApplyProfile(ctx context.Context, p *profile.Profile) error { - return svc.Profiles.Save(p) -} diff --git a/platform/api/server/apply/transport_http.go b/platform/api/server/apply/transport_http.go index a29cdfee..3cac1657 100644 --- a/platform/api/server/apply/transport_http.go +++ b/platform/api/server/apply/transport_http.go @@ -18,7 +18,6 @@ import ( type HTTPHandlers struct { BlueprintHandler http.Handler DEPTokensHandler http.Handler - ProfileHandler http.Handler DefineDEPProfileHandler http.Handler AppUploadHandler http.Handler ApplyUserhandler http.Handler @@ -39,12 +38,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran encodeResponse, opts..., ), - ProfileHandler: httptransport.NewServer( - endpoints.ApplyProfileEndpoint, - decodeProfileRequest, - encodeResponse, - opts..., - ), DefineDEPProfileHandler: httptransport.NewServer( endpoints.DefineDEPProfileEndpoint, decodeDEPProfileRequest, @@ -101,14 +94,6 @@ func decodeBlueprintRequest(ctx context.Context, r *http.Request) (interface{}, return bpReq, nil } -func decodeProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var req profileRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - return nil, err - } - return req, nil -} - func decodeDEPProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) { var req depProfileRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { @@ -254,15 +239,6 @@ func DecodeDEPTokensResponse(_ context.Context, r *http.Response) (interface{}, return resp, err } -func DecodeProfileResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp profileResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - func DecodeDEPProfileResponse(_ context.Context, r *http.Response) (interface{}, error) { if r.StatusCode != http.StatusOK { return nil, errorDecoder(r) diff --git a/platform/api/server/list/client.go b/platform/api/server/list/client.go index 0283e096..a3f8d070 100644 --- a/platform/api/server/list/client.go +++ b/platform/api/server/list/client.go @@ -46,16 +46,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra opts..., ).Endpoint() } - var getProfilesEndpoint endpoint.Endpoint - { - getProfilesEndpoint = httptransport.NewClient( - "GET", - copyURL(u, "/v1/profiles"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeGetProfilesResponse, - opts..., - ).Endpoint() - } var getDEPAccountInfoEndpoint endpoint.Endpoint { @@ -116,7 +106,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra ListDevicesEndpoint: listDevicesEndpoint, GetDEPTokensEndpoint: getDEPTokensEndpoint, GetBlueprintsEndpoint: getBlueprintsEndpoint, - GetProfilesEndpoint: getProfilesEndpoint, GetDEPAccountInfoEndpoint: getDEPAccountInfoEndpoint, GetDEPDeviceEndpoint: getDEPDeviceDetailsEndpoint, GetDEPProfileEndpoint: getDEPProfilesEndpoint, diff --git a/platform/api/server/list/endpoint.go b/platform/api/server/list/endpoint.go index 89d62ccf..d7b9c35d 100644 --- a/platform/api/server/list/endpoint.go +++ b/platform/api/server/list/endpoint.go @@ -9,7 +9,6 @@ import ( "github.com/micromdm/micromdm/platform/blueprint" "github.com/micromdm/micromdm/platform/deptoken" - "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/user" ) @@ -17,7 +16,6 @@ type Endpoints struct { ListDevicesEndpoint endpoint.Endpoint GetDEPTokensEndpoint endpoint.Endpoint GetBlueprintsEndpoint endpoint.Endpoint - GetProfilesEndpoint endpoint.Endpoint GetDEPAccountInfoEndpoint endpoint.Endpoint GetDEPDeviceEndpoint endpoint.Endpoint GetDEPProfileEndpoint endpoint.Endpoint @@ -69,15 +67,6 @@ func (e Endpoints) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ( return response.(blueprintsResponse).Blueprints, response.(blueprintsResponse).Err } -func (e Endpoints) GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error) { - request := profilesRequest{opt} - response, err := e.GetProfilesEndpoint(ctx, request.Opts) - if err != nil { - return nil, err - } - return response.(profilesResponse).Profiles, response.(profilesResponse).Err -} - func (e Endpoints) GetDEPAccountInfo(ctx context.Context) (*dep.Account, error) { request := depAccountInforequest{} response, err := e.GetDEPAccountInfoEndpoint(ctx, request) @@ -160,17 +149,6 @@ func MakeGetBlueprintsEndpoint(svc Service) endpoint.Endpoint { } } -func MakeGetProfilesEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(profilesRequest) - profiles, err := svc.GetProfiles(ctx, req.Opts) - return profilesResponse{ - Profiles: profiles, - Err: err, - }, nil - } -} - func MakeGetDEPAccountInfoEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { account, err := svc.GetDEPAccountInfo(ctx) @@ -229,14 +207,6 @@ type blueprintsResponse struct { func (r blueprintsResponse) error() error { return r.Err } -type profilesRequest struct{ Opts GetProfilesOption } -type profilesResponse struct { - Profiles []profile.Profile `json:"profiles"` - Err error `json:"err,omitempty"` -} - -func (r profilesResponse) error() error { return r.Err } - type depAccountInforequest struct{} type depAccountInfoResponse struct { *dep.Account diff --git a/platform/api/server/list/service.go b/platform/api/server/list/service.go index d5f44f3a..e7949ec0 100644 --- a/platform/api/server/list/service.go +++ b/platform/api/server/list/service.go @@ -39,10 +39,6 @@ type GetBlueprintsOption struct { FilterName string } -type GetProfilesOption struct { - Identifier string `json:"id"` -} - type ListAppsOption struct { FilterName []string `json:"filter_name"` } @@ -52,7 +48,6 @@ type Service interface { ListUsers(ctx context.Context, opt ListUsersOption) ([]user.User, error) GetDEPTokens(ctx context.Context) ([]deptoken.DEPToken, []byte, error) GetBlueprints(ctx context.Context, opt GetBlueprintsOption) ([]blueprint.Blueprint, error) - GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error) ListApplications(ctx context.Context, opt ListAppsOption) ([]AppDTO, error) DEPService } @@ -63,7 +58,7 @@ type ListService struct { Devices *device.DB Blueprints *blueprint.DB - Profiles *profile.DB + Profiles profile.Store Tokens *deptoken.DB Apps appstore.AppStore Users *user.DB @@ -176,15 +171,3 @@ func (svc *ListService) GetBlueprints(ctx context.Context, opt GetBlueprintsOpti return bps, nil } } - -func (svc *ListService) GetProfiles(ctx context.Context, opt GetProfilesOption) ([]profile.Profile, error) { - if opt.Identifier != "" { - foundProf, err := svc.Profiles.ProfileById(opt.Identifier) - if err != nil { - return nil, err - } - return []profile.Profile{*foundProf}, nil - } else { - return svc.Profiles.List() - } -} diff --git a/platform/api/server/list/transport_http.go b/platform/api/server/list/transport_http.go index 1e53073f..6903e569 100644 --- a/platform/api/server/list/transport_http.go +++ b/platform/api/server/list/transport_http.go @@ -15,7 +15,6 @@ type HTTPHandlers struct { ListDevicesHandler http.Handler GetDEPTokensHandler http.Handler GetBlueprintsHandler http.Handler - GetProfilesHandler http.Handler GetDEPAccountInfoHandler http.Handler GetDEPProfileHandler http.Handler GetDEPDeviceDetailsHandler http.Handler @@ -41,11 +40,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoints Endpoints, opts ...httptran decodeGetBlueprintsRequest, encodeResponse, opts...), - GetProfilesHandler: httptransport.NewServer( - endpoints.GetProfilesEndpoint, - decodeGetProfilesRequest, - encodeResponse, - opts...), GetDEPAccountInfoHandler: httptransport.NewServer( endpoints.GetDEPAccountInfoEndpoint, decodeDepAccountInfoRequest, @@ -107,17 +101,6 @@ func decodeGetBlueprintsRequest(ctx context.Context, r *http.Request) (interface return req, nil } -func decodeGetProfilesRequest(ctx context.Context, r *http.Request) (interface{}, error) { - var opts GetProfilesOption - if err := json.NewDecoder(r.Body).Decode(&opts); err != nil { - return nil, err - } - req := profilesRequest{ - Opts: opts, - } - return req, nil -} - func decodeDepAccountInfoRequest(ctx context.Context, r *http.Request) (interface{}, error) { return nil, nil } @@ -218,15 +201,6 @@ func DecodeGetBlueprintsResponse(_ context.Context, r *http.Response) (interface return resp, err } -func DecodeGetProfilesResponse(_ context.Context, r *http.Response) (interface{}, error) { - if r.StatusCode != http.StatusOK { - return nil, errorDecoder(r) - } - var resp profilesResponse - err := json.NewDecoder(r.Body).Decode(&resp) - return resp, err -} - func DecodeDEPAccountInfoResponse(_ context.Context, r *http.Response) (interface{}, error) { if r.StatusCode != http.StatusOK { return nil, errorDecoder(r) diff --git a/platform/api/server/remove/client.go b/platform/api/server/remove/client.go index 1fb0967e..756e16c4 100644 --- a/platform/api/server/remove/client.go +++ b/platform/api/server/remove/client.go @@ -26,16 +26,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra opts..., ).Endpoint() } - var removeProfilesEndpoint endpoint.Endpoint - { - removeProfilesEndpoint = httptransport.NewClient( - "DELETE", - copyURL(u, "/v1/profiles"), - encodeRequestWithToken(token, EncodeHTTPGenericRequest), - DecodeProfileResponse, - opts..., - ).Endpoint() - } var unblockDeviceEndpoint endpoint.Endpoint { @@ -50,7 +40,6 @@ func NewClient(instance string, logger log.Logger, token string, opts ...httptra return Endpoints{ RemoveBlueprintsEndpoint: removeBlueprintsEndpoint, - RemoveProfilesEndpoint: removeProfilesEndpoint, UnblockDeviceEndpoint: unblockDeviceEndpoint, }, nil } diff --git a/platform/api/server/remove/endpoint.go b/platform/api/server/remove/endpoint.go index 0fc47e7b..57e259b9 100644 --- a/platform/api/server/remove/endpoint.go +++ b/platform/api/server/remove/endpoint.go @@ -8,14 +8,12 @@ import ( type Endpoints struct { RemoveBlueprintsEndpoint endpoint.Endpoint - RemoveProfilesEndpoint endpoint.Endpoint UnblockDeviceEndpoint endpoint.Endpoint } func MakeEndpoints(svc Service) Endpoints { e := Endpoints{ RemoveBlueprintsEndpoint: MakeRemoveBlueprintsEndpoint(svc), - RemoveProfilesEndpoint: MakeRemoveProfilesEndpoint(svc), UnblockDeviceEndpoint: MakeUnblockDeviceEndpoint(svc), } return e @@ -39,15 +37,6 @@ func (e Endpoints) RemoveBlueprints(ctx context.Context, names []string) error { return resp.(blueprintResponse).Err } -func (e Endpoints) RemoveProfiles(ctx context.Context, ids []string) error { - request := profileRequest{Identifiers: ids} - resp, err := e.RemoveProfilesEndpoint(ctx, request) - if err != nil { - return err - } - return resp.(profileResponse).Err -} - func MakeRemoveBlueprintsEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(blueprintRequest) @@ -58,16 +47,6 @@ func MakeRemoveBlueprintsEndpoint(svc Service) endpoint.Endpoint { } } -func MakeRemoveProfilesEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (response interface{}, err error) { - req := request.(profileRequest) - err = svc.RemoveProfiles(ctx, req.Identifiers) - return profileResponse{ - Err: err, - }, nil - } -} - func MakeUnblockDeviceEndpoint(svc Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (response interface{}, err error) { req := request.(unblockDeviceRequest) diff --git a/platform/api/server/remove/service.go b/platform/api/server/remove/service.go index f3a0e814..d551b42d 100644 --- a/platform/api/server/remove/service.go +++ b/platform/api/server/remove/service.go @@ -4,19 +4,16 @@ import ( "context" "github.com/micromdm/micromdm/platform/blueprint" - "github.com/micromdm/micromdm/platform/profile" "github.com/micromdm/micromdm/platform/remove" ) type Service interface { RemoveBlueprints(ctx context.Context, names []string) error - RemoveProfiles(ctx context.Context, ids []string) error UnblockDevice(ctx context.Context, udid string) error } type RemoveService struct { Blueprints *blueprint.DB - Profiles *profile.DB *remove.RemoveService } @@ -31,15 +28,3 @@ func (svc *RemoveService) RemoveBlueprints(ctx context.Context, names []string) } return nil } - -func (svc *RemoveService) RemoveProfiles(ctx context.Context, ids []string) error { - // TODO: Wrap deletion(s) in transactions so as to not have - // incomplete removals? - for _, id := range ids { - err := svc.Profiles.Delete(id) - if err != nil { - return err - } - } - return nil -} diff --git a/platform/api/server/remove/transport_http.go b/platform/api/server/remove/transport_http.go index 981e5e0c..fc5a7a41 100644 --- a/platform/api/server/remove/transport_http.go +++ b/platform/api/server/remove/transport_http.go @@ -27,12 +27,6 @@ func MakeHTTPHandlers(ctx context.Context, endpoint Endpoints, opts ...httptrans encodeResponse, opts..., ), - ProfileHandler: httptransport.NewServer( - endpoint.RemoveProfilesEndpoint, - decodeProfileRequest, - encodeResponse, - opts..., - ), UnblockDeviceHandler: httptransport.NewServer( endpoint.UnblockDeviceEndpoint, decodeUnblockDeviceRequest, diff --git a/platform/blueprint/db.go b/platform/blueprint/db.go index b52b799b..cfe3234b 100644 --- a/platform/blueprint/db.go +++ b/platform/blueprint/db.go @@ -18,13 +18,13 @@ const ( type DB struct { *bolt.DB - profDB *profile.DB + profDB profile.Store userDB *user.DB } func NewDB( db *bolt.DB, - profileDB *profile.DB, + profileDB profile.Store, userDB *user.DB, ) (*DB, error) { err := db.Update(func(tx *bolt.Tx) error { diff --git a/platform/profile/apply_profile.go b/platform/profile/apply_profile.go new file mode 100644 index 00000000..cf3b5a6c --- /dev/null +++ b/platform/profile/apply_profile.go @@ -0,0 +1,59 @@ +package profile + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *ProfileService) ApplyProfile(ctx context.Context, p *Profile) error { + return svc.store.Save(p) +} + +type applyProfileRequest struct { + Profile *Profile `json:"profile"` +} + +type applyProfileResponse struct { + Err error `json:"err,omitempty"` +} + +func (r applyProfileResponse) error() error { return r.Err } + +func decodeApplyProfileRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var req applyProfileRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, err + } + return req, nil +} + +func decodeApplyProfileResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp applyProfileResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeApplyProfileEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(applyProfileRequest) + err = svc.ApplyProfile(ctx, req.Profile) + return applyProfileResponse{ + Err: err, + }, nil + } +} + +func (e Endpoints) ApplyProfile(ctx context.Context, p *Profile) error { + request := applyProfileRequest{Profile: p} + resp, err := e.ApplyProfileEndpoint(ctx, request) + if err != nil { + return err + } + return resp.(applyProfileResponse).Err +} diff --git a/platform/profile/db.go b/platform/profile/builtin/db.go similarity index 79% rename from platform/profile/db.go rename to platform/profile/builtin/db.go index ddfd1887..83023a1b 100644 --- a/platform/profile/db.go +++ b/platform/profile/builtin/db.go @@ -1,9 +1,10 @@ -package profile +package builtin import ( "fmt" "github.com/boltdb/bolt" + "github.com/micromdm/micromdm/platform/profile" "github.com/pkg/errors" ) @@ -29,15 +30,15 @@ func NewDB(db *bolt.DB) (*DB, error) { return datastore, nil } -func (db *DB) List() ([]Profile, error) { +func (db *DB) List() ([]profile.Profile, error) { // TODO add filter/limit with ForEach - var list []Profile + var list []profile.Profile err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(ProfileBucket)) c := b.Cursor() for k, v := c.First(); k != nil; k, v = c.Next() { - var p Profile - if err := UnmarshalProfile(v, &p); err != nil { + var p profile.Profile + if err := profile.UnmarshalProfile(v, &p); err != nil { return err } list = append(list, p) @@ -47,7 +48,7 @@ func (db *DB) List() ([]Profile, error) { return list, err } -func (db *DB) Save(p *Profile) error { +func (db *DB) Save(p *profile.Profile) error { err := p.Validate() if err != nil { return err @@ -60,7 +61,7 @@ func (db *DB) Save(p *Profile) error { if bkt == nil { return fmt.Errorf("bucket %q not found!", ProfileBucket) } - pproto, err := MarshalProfile(p) + pproto, err := profile.MarshalProfile(p) if err != nil { return errors.Wrap(err, "marshalling profile") } @@ -70,15 +71,15 @@ func (db *DB) Save(p *Profile) error { return tx.Commit() } -func (db *DB) ProfileById(id string) (*Profile, error) { - var p Profile +func (db *DB) ProfileById(id string) (*profile.Profile, error) { + var p profile.Profile err := db.View(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(ProfileBucket)) v := b.Get([]byte(id)) if v == nil { return ¬Found{"Profile", fmt.Sprintf("id %s", id)} } - return UnmarshalProfile(v, &p) + return profile.UnmarshalProfile(v, &p) }) return &p, err } @@ -104,9 +105,6 @@ func (e *notFound) Error() string { return fmt.Sprintf("not found: %s %s", e.ResourceType, e.Message) } -func IsNotFound(err error) bool { - if _, ok := err.(*notFound); ok { - return true - } - return false +func (e *notFound) NotFound() bool { + return true } diff --git a/platform/profile/client.go b/platform/profile/client.go new file mode 100644 index 00000000..938a2437 --- /dev/null +++ b/platform/profile/client.go @@ -0,0 +1,70 @@ +package profile + +import ( + "context" + "net/http" + "net/url" + + "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" +) + +func NewHTTPClient(instance, token string, logger log.Logger, opts ...httptransport.ClientOption) (Service, error) { + u, err := url.Parse(instance) + if err != nil { + return nil, err + } + + var applyProfileEndpoint endpoint.Endpoint + { + applyProfileEndpoint = httptransport.NewClient( + "PUT", + copyURL(u, "/v1/profiles"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeApplyProfileResponse, + opts..., + ).Endpoint() + } + + var getProfilesEndpoint endpoint.Endpoint + { + getProfilesEndpoint = httptransport.NewClient( + "GET", + copyURL(u, "/v1/profiles"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeGetProfilesResponse, + opts..., + ).Endpoint() + } + + var removeProfilesEndpoint endpoint.Endpoint + { + removeProfilesEndpoint = httptransport.NewClient( + "DELETE", + copyURL(u, "/v1/profiles"), + encodeRequestWithToken(token, httptransport.EncodeJSONRequest), + decodeRemoveProfileResponse, + opts..., + ).Endpoint() + } + + return Endpoints{ + ApplyProfileEndpoint: applyProfileEndpoint, + GetProfilesEndpoint: getProfilesEndpoint, + RemoveProfilesEndpoint: removeProfilesEndpoint, + }, nil +} + +func encodeRequestWithToken(token string, next httptransport.EncodeRequestFunc) httptransport.EncodeRequestFunc { + return func(ctx context.Context, r *http.Request, request interface{}) error { + r.SetBasicAuth("micromdm", token) + return next(ctx, r, request) + } +} + +func copyURL(base *url.URL, path string) *url.URL { + next := *base + next.Path = path + return &next +} diff --git a/platform/profile/get_profiles.go b/platform/profile/get_profiles.go new file mode 100644 index 00000000..3d84cc94 --- /dev/null +++ b/platform/profile/get_profiles.go @@ -0,0 +1,70 @@ +package profile + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *ProfileService) GetProfiles(ctx context.Context, opt GetProfilesOption) ([]Profile, error) { + if opt.Identifier != "" { + foundProf, err := svc.store.ProfileById(opt.Identifier) + if err != nil { + return nil, err + } + return []Profile{*foundProf}, nil + } else { + return svc.store.List() + } +} + +type getProfilesRequest struct{ Opts GetProfilesOption } + +type getProfilesResponse struct { + Profiles []Profile `json:"profiles"` + Err error `json:"err,omitempty"` +} + +func (r getProfilesResponse) error() error { return r.Err } + +func decodeGetProfilesRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var opts GetProfilesOption + if err := json.NewDecoder(r.Body).Decode(&opts); err != nil { + return nil, err + } + req := getProfilesRequest{ + Opts: opts, + } + return req, nil +} + +func decodeGetProfilesResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp getProfilesResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeGetProfilesEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(getProfilesRequest) + profiles, err := svc.GetProfiles(ctx, req.Opts) + return getProfilesResponse{ + Profiles: profiles, + Err: err, + }, nil + } +} + +func (e Endpoints) GetProfiles(ctx context.Context, opt GetProfilesOption) ([]Profile, error) { + request := getProfilesRequest{opt} + response, err := e.GetProfilesEndpoint(ctx, request.Opts) + if err != nil { + return nil, err + } + return response.(getProfilesResponse).Profiles, response.(getProfilesResponse).Err +} diff --git a/platform/profile/remove_profiles.go b/platform/profile/remove_profiles.go new file mode 100644 index 00000000..5677d88a --- /dev/null +++ b/platform/profile/remove_profiles.go @@ -0,0 +1,65 @@ +package profile + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/go-kit/kit/endpoint" +) + +func (svc *ProfileService) RemoveProfiles(ctx context.Context, ids []string) error { + for _, id := range ids { + err := svc.store.Delete(id) + if err != nil { + return err + } + } + return nil +} + +type removeProfileRequest struct { + Identifiers []string `json:"ids"` +} + +type removeProfileResponse struct { + Err error `json:"err,omitempty"` +} + +func (r removeProfileResponse) error() error { return r.Err } + +func decodeRemoveProfilesRequest(ctx context.Context, r *http.Request) (interface{}, error) { + var req removeProfileRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + return nil, err + } + return req, nil +} + +func decodeRemoveProfileResponse(_ context.Context, r *http.Response) (interface{}, error) { + if r.StatusCode != http.StatusOK { + return nil, errorDecoder(r) + } + var resp removeProfileResponse + err := json.NewDecoder(r.Body).Decode(&resp) + return resp, err +} + +func MakeRemoveProfilesEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (response interface{}, err error) { + req := request.(removeProfileRequest) + err = svc.RemoveProfiles(ctx, req.Identifiers) + return removeProfileResponse{ + Err: err, + }, nil + } +} + +func (e Endpoints) RemoveProfiles(ctx context.Context, ids []string) error { + request := removeProfileRequest{Identifiers: ids} + resp, err := e.RemoveProfilesEndpoint(ctx, request) + if err != nil { + return err + } + return resp.(removeProfileResponse).Err +} diff --git a/platform/profile/server.go b/platform/profile/server.go new file mode 100644 index 00000000..2abd6ec0 --- /dev/null +++ b/platform/profile/server.go @@ -0,0 +1,77 @@ +package profile + +import ( + "encoding/json" + "errors" + "net/http" + + "github.com/go-kit/kit/endpoint" + "github.com/go-kit/kit/log" + httptransport "github.com/go-kit/kit/transport/http" + "github.com/gorilla/mux" +) + +type Endpoints struct { + ApplyProfileEndpoint endpoint.Endpoint + GetProfilesEndpoint endpoint.Endpoint + RemoveProfilesEndpoint endpoint.Endpoint +} + +func MakeServerEndpoints(s Service) Endpoints { + return Endpoints{ + ApplyProfileEndpoint: MakeApplyProfileEndpoint(s), + GetProfilesEndpoint: MakeGetProfilesEndpoint(s), + RemoveProfilesEndpoint: MakeRemoveProfilesEndpoint(s), + } +} + +func MakeHTTPHandler(e Endpoints, logger log.Logger) http.Handler { + options := []httptransport.ServerOption{ + httptransport.ServerErrorLogger(logger), + } + + r := mux.NewRouter() + + // GET /v1/profiles get a list of profiles managed by the server + // PUT /v1/profiles create or replace a profile on the server + // DELETE /v1/profiles remove one or more profiles from the server + + r.Methods("GET").Path("/v1/profiles").Handler(httptransport.NewServer( + e.GetProfilesEndpoint, + decodeGetProfilesRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + r.Methods("PUT").Path("/v1/profiles").Handler(httptransport.NewServer( + e.ApplyProfileEndpoint, + decodeApplyProfileRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + r.Methods("DELETE").Path("/v1/profiles").Handler(httptransport.NewServer( + e.RemoveProfilesEndpoint, + decodeRemoveProfilesRequest, + httptransport.EncodeJSONResponse, + options..., + )) + + return r +} + +type errorWrapper struct { + Error string `json:"error"` +} + +type errorer interface { + error() error +} + +func errorDecoder(r *http.Response) error { + var w errorWrapper + if err := json.NewDecoder(r.Body).Decode(&w); err != nil { + return err + } + return errors.New(w.Error) +} diff --git a/platform/profile/service.go b/platform/profile/service.go new file mode 100644 index 00000000..613f10a4 --- /dev/null +++ b/platform/profile/service.go @@ -0,0 +1,40 @@ +package profile + +import ( + "context" +) + +type Service interface { + ApplyProfile(ctx context.Context, p *Profile) error + GetProfiles(ctx context.Context, opt GetProfilesOption) ([]Profile, error) + RemoveProfiles(ctx context.Context, ids []string) error +} + +type GetProfilesOption struct { + Identifier string `json:"id"` +} + +type Store interface { + ProfileById(id string) (*Profile, error) + Save(p *Profile) error + List() ([]Profile, error) + Delete(id string) error +} + +func New(store Store) *ProfileService { + return &ProfileService{store: store} +} + +type ProfileService struct { + store Store +} + +func IsNotFound(err error) bool { + type notFoundError interface { + error + NotFound() bool + } + + _, ok := err.(notFoundError) + return ok +}