From 58df4a958d93cd8f4532bcebccd973b9816ec735 Mon Sep 17 00:00:00 2001 From: Victor Vrantchan Date: Mon, 16 May 2016 17:37:28 -0400 Subject: [PATCH] add list devices endpoint --- management/endpoint_dep.go | 21 ++++++++++++ management/endpoint_device.go | 37 ++++++++++++++++++++ management/endpoint_profile.go | 15 -------- management/service.go | 8 +++++ management/transport.go | 15 ++++++++ management/transport_test.go | 62 +++++++++++++++++++++++++++++++++- 6 files changed, 142 insertions(+), 16 deletions(-) create mode 100644 management/endpoint_dep.go create mode 100644 management/endpoint_device.go diff --git a/management/endpoint_dep.go b/management/endpoint_dep.go new file mode 100644 index 00000000..097e59c5 --- /dev/null +++ b/management/endpoint_dep.go @@ -0,0 +1,21 @@ +package management + +import ( + "github.com/go-kit/kit/endpoint" + "golang.org/x/net/context" +) + +type fetchDEPDevicesRequest struct{} + +type fetchDEPDevicesResponse struct { + Err error `json:"error,omitempty"` +} + +func (r fetchDEPDevicesResponse) error() error { return r.Err } + +func makeFetchDevicesEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + err := svc.FetchDEPDevices() + return fetchDEPDevicesResponse{Err: err}, nil + } +} diff --git a/management/endpoint_device.go b/management/endpoint_device.go new file mode 100644 index 00000000..f21b974d --- /dev/null +++ b/management/endpoint_device.go @@ -0,0 +1,37 @@ +package management + +import ( + "encoding/json" + "net/http" + + "golang.org/x/net/context" + + "github.com/go-kit/kit/endpoint" + "github.com/micromdm/micromdm/device" +) + +type listDevicesRequest struct{} + +type listDevicesResponse struct { + devices []device.Device + Err error `json:"error,omitempty"` +} + +func (r listDevicesResponse) error() error { return r.Err } + +func (r listDevicesResponse) encodeList(w http.ResponseWriter) error { + jsn, err := json.MarshalIndent(r.devices, "", " ") + if err != nil { + return err + } + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.Write(jsn) + return nil +} + +func makeListDevicesEndpoint(svc Service) endpoint.Endpoint { + return func(ctx context.Context, request interface{}) (interface{}, error) { + ds, err := svc.Devices() + return listDevicesResponse{Err: err, devices: ds}, nil + } +} diff --git a/management/endpoint_profile.go b/management/endpoint_profile.go index 125ddb23..2fc7732e 100644 --- a/management/endpoint_profile.go +++ b/management/endpoint_profile.go @@ -16,21 +16,6 @@ var ( errBadRouting = errors.New("inconsistent mapping between route and handler (programmer error)") ) -type fetchDEPDevicesRequest struct{} - -type fetchDEPDevicesResponse struct { - Err error `json:"error,omitempty"` -} - -func (r fetchDEPDevicesResponse) error() error { return r.Err } - -func makeFetchDevicesEndpoint(svc Service) endpoint.Endpoint { - return func(ctx context.Context, request interface{}) (interface{}, error) { - err := svc.FetchDEPDevices() - return fetchDEPDevicesResponse{Err: err}, nil - } -} - type addProfileRequest struct { *workflow.Profile } diff --git a/management/service.go b/management/service.go index 53aa498d..b90e3480 100644 --- a/management/service.go +++ b/management/service.go @@ -20,6 +20,9 @@ type Service interface { // workflows AddWorkflow(wf *workflow.Workflow) (*workflow.Workflow, error) Workflows() ([]workflow.Workflow, error) + + // Devices + Devices() ([]device.Device, error) // dep FetchDEPDevices() error } @@ -87,6 +90,11 @@ func (svc service) Workflows() ([]workflow.Workflow, error) { return svc.workflows.Workflows() } +// devices +func (svc service) Devices() ([]device.Device, error) { + return svc.devices.Devices() +} + // NewService creates a management service func NewService(ds device.Datastore, ws workflow.Datastore, dc dep.Client) Service { return &service{ diff --git a/management/transport.go b/management/transport.go index 01c29229..e4f03af0 100644 --- a/management/transport.go +++ b/management/transport.go @@ -74,10 +74,20 @@ func ServiceHandler(ctx context.Context, svc Service, logger kitlog.Logger) http opts..., ) + listDevicesHandler := kithttp.NewServer( + ctx, + makeListDevicesEndpoint(svc), + decodeListDevicesRequest, + encodeResponse, + opts..., + ) + r := mux.NewRouter() // dep r.Handle("/management/v1/devices/fetch", fetchDEPHandler).Methods("POST") + //devices + r.Handle("/management/v1/devices", listDevicesHandler).Methods("GET") // profiles r.Handle("/management/v1/profiles", addProfileHandler).Methods("POST") r.Handle("/management/v1/profiles", listProfilesHandler).Methods("GET") @@ -153,6 +163,11 @@ func decodeListWorkflowsRequest(_ context.Context, r *http.Request) (interface{} return listWorkflowsRequest{}, nil } +// devices +func decodeListDevicesRequest(_ context.Context, r *http.Request) (interface{}, error) { + return listDevicesRequest{}, nil +} + func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { if e, ok := response.(errorer); ok && e.error() != nil { encodeError(ctx, e.error(), w) diff --git a/management/transport_test.go b/management/transport_test.go index 994ded94..53f06f05 100644 --- a/management/transport_test.go +++ b/management/transport_test.go @@ -17,6 +17,15 @@ import ( "golang.org/x/net/context" ) +func TestListDevices(t *testing.T) { + server, svc := newServer(t) + defer teardown() + defer server.Close() + + fetchDEPDevices(t, server, svc) + testListDevicesHTTP(t, svc, server, http.StatusOK) +} + func TestAddWorkflowWithProfiles(t *testing.T) { server, svc := newServer(t) defer teardown() @@ -273,13 +282,47 @@ func newServer(t *testing.T) (*httptest.Server, Service) { if err != nil { t.Fatal(err) } + // dep client + config := &dep.Config{ + ConsumerKey: "CK_48dd68d198350f51258e885ce9a5c37ab7f98543c4a697323d75682a6c10a32501cb247e3db08105db868f73f2c972bdb6ae77112aea803b9219eb52689d42e6", + ConsumerSecret: "CS_34c7b2b531a600d99a0e4edcf4a78ded79b86ef318118c2f5bcfee1b011108c32d5302df801adbe29d446eb78f02b13144e323eb9aad51c79f01e50cb45c3a68", + AccessToken: "AT_927696831c59ba510cfe4ec1a69e5267c19881257d4bca2906a99d0785b785a6f6fdeb09774954fdd5e2d0ad952e3af52c6d8d2f21c924ba0caf4a031c158b89", + AccessSecret: "AS_c31afd7a09691d83548489336e8ff1cb11b82b6bca13f793344496a556b1f4972eaff4dde6deb5ac9cf076fdfa97ec97699c34d515947b9cf9ed31c99dded6ba", + } - svc := NewService(ds, ps, nil) + dc, err := dep.NewClient(config, dep.ServerURL("http://localhost:9000")) + if err != nil { + t.Fatal(err) + } + + svc := NewService(ds, ps, dc) handler := ServiceHandler(ctx, svc, logger) server := httptest.NewServer(handler) return server, svc } +func testListDevicesHTTP(t *testing.T, svc Service, server *httptest.Server, expectedStatus int) []device.Device { + client := http.DefaultClient + theURL := server.URL + "/management/v1/devices" + resp, err := client.Get(theURL) + if err != nil { + t.Fatal(err) + } + + if resp.StatusCode != expectedStatus { + io.Copy(os.Stdout, resp.Body) + t.Fatal("expected", expectedStatus, "got", resp.StatusCode) + } + + // test decoding the result into a struct + var devices []device.Device + if err := json.NewDecoder(resp.Body).Decode(&devices); err != nil { + t.Log("failed to decode profiles from list response") + t.Fatal(err) + } + return devices +} + func testListWorkflowsHTTP(t *testing.T, svc Service, server *httptest.Server, expectedStatus int) []workflow.Workflow { client := http.DefaultClient theURL := server.URL + "/management/v1/workflows" @@ -381,6 +424,23 @@ func TestFetchDEPDevices(t *testing.T) { } } +func fetchDEPDevices(t *testing.T, server *httptest.Server, svc Service) *http.Response { + client := http.DefaultClient + theURL := server.URL + "/management/v1/devices/fetch" + resp, err := client.Post(theURL, "application/json", nil) + if err != nil { + t.Fatal(err) + } + + if resp.StatusCode != http.StatusOK { + io.Copy(os.Stdout, resp.Body) + t.Fatal("expected", http.StatusOK, "got", resp.StatusCode) + } + + return resp + +} + // a face io.ReadCloser for constructing request Body type nopCloser struct { io.Reader