diff --git a/Device.go b/Device.go new file mode 100644 index 0000000..41c2512 --- /dev/null +++ b/Device.go @@ -0,0 +1,137 @@ +package msgraph + +import ( + "encoding/json" + "fmt" + "time" +) + +// Device represents one device of ms graph +type Device struct { + ID string `json:"id,omitempty"` + DeletedDateTime time.Time `json:"deletedDateTime,omitempty"` + AccountEnabled bool `json:"accountEnabled,omitempty"` + ApproximateLastSignInDateTime time.Time `json:"approximateLastSignInDateTime,omitempty"` + ComplianceExpirationDateTime time.Time `json:"complianceExpirationDateTime,omitempty"` + DeviceId string `json:"deviceId,omitempty"` + DisplayName string `json:"displayName,omitempty"` + IsCompliant bool `json:"isCompliant,omitempty"` + IsManaged bool `json:"isManaged,omitempty"` + Manufacturer string `json:"manufacturer,omitempty"` + MdmAppId string `json:"mdmAppId,omitempty"` + Model string `json:"model,omitempty"` + OnPremisesLastSyncDateTime time.Time `json:"onPremisesLastSyncDateTime,omitempty"` + OnPremisesSyncEnabled bool `json:"onPremisesSyncEnabled,omitempty"` + OperatingSystem string `json:"operatingSystem,omitempty"` + OperatingSystemVersion string `json:"operatingSystemVersion,omitempty"` + PhysicalIds []string `json:"physicalIds,omitempty"` + ProfileType string `json:"profileType,omitempty"` + RegistrationDateTime time.Time `json:"registrationDateTime,omitempty"` + SystemLabels []string `json:"systemLabels,omitempty"` + TrustType string `json:"trustType,omitempty"` + MemberOf []Member `json:"memberOf,omitempty"` + + graphClient *GraphClient // the graphClient that called the group +} + +func (d Device) String() string { + return fmt.Sprintf("Device(ID: \"%v\", DeletedDateTime: \"%v\" AccountEnabled: \"%v\", ApproximateLastSignInDateTime: \"%v\", ComplianceExpirationDateTime: \"%v\", "+ + "DeviceId: \"%v\", DisplayName: \"%v\", IsCompliant: \"%v\" IsManaged: \"%v\", Manufacturer: \"%v\", MdmAppId: \"%v\", Model: \"%v\", OnPremisesLastSyncDateTime: \"%v\", "+ + "OnPremisesSyncEnabled: \"%v\", OperatingSystem: \"%v\", OperatingSystemVersion: \"%v\", PhysicalIds: \"%v\", ProfileType: \"%v\", RegistrationDateTime: \"%v\", "+ + "SystemLabels: \"%v\", TrustType: \"%v\", DirectAPIConnection: %v)", + d.ID, d.DeletedDateTime, d.AccountEnabled, d.ApproximateLastSignInDateTime, d.ComplianceExpirationDateTime, d.DeviceId, d.DisplayName, d.IsCompliant, + d.IsManaged, d.Manufacturer, d.MdmAppId, d.Model, d.OnPremisesLastSyncDateTime, d.OnPremisesSyncEnabled, d.OperatingSystem, + d.OperatingSystemVersion, d.PhysicalIds, d.ProfileType, d.RegistrationDateTime, d.SystemLabels, d.TrustType, d.graphClient != nil) +} + +// setGraphClient sets the graphClient instance in this instance and all child-instances (if any) +func (d *Device) setGraphClient(gC *GraphClient) { + d.graphClient = gC +} + +// ListMembers - Get a list of the domain's direct members. +func (d Device) ListMembers(opts ...ListQueryOption) (Devices, error) { + if d.graphClient == nil { + return nil, ErrNotGraphClientSourced + } + resource := fmt.Sprintf("/devices/%v/members", d.ID) + + var marsh struct { + Devices Devices `json:"value"` + } + marsh.Devices.setGraphClient(d.graphClient) + return marsh.Devices, d.graphClient.makeGETAPICall(resource, compileListQueryOptions(opts), &marsh) +} + +// UnmarshalJSON implements the json unmarshal to be used by the json-library +func (d *Device) UnmarshalJSON(data []byte) error { + tmp := struct { + ID string `json:"id,omitempty"` + DeletedDateTime string `json:"deletedDateTime,omitempty"` + AccountEnabled bool `json:"accountEnabled,omitempty"` + ApproximateLastSignInDateTime string `json:"approximateLastSignInDateTime,omitempty"` + ComplianceExpirationDateTime string `json:"complianceExpirationDateTime,omitempty"` + DeviceId string `json:"deviceId,omitempty"` + DisplayName string `json:"displayName,omitempty"` + IsCompliant bool `json:"isCompliant,omitempty"` + IsManaged bool `json:"isManaged,omitempty"` + Manufacturer string `json:"manufacturer,omitempty"` + MdmAppId string `json:"mdmAppId,omitempty"` + Model string `json:"model,omitempty"` + OnPremisesLastSyncDateTime string `json:"onPremisesLastSyncDateTime,omitempty"` + OnPremisesSyncEnabled bool `json:"onPremisesSyncEnabled,omitempty"` + OperatingSystem string `json:"operatingSystem,omitempty"` + OperatingSystemVersion string `json:"operatingSystemVersion,omitempty"` + PhysicalIds []string `json:"physicalIds,omitempty"` + ProfileType string `json:"profileType,omitempty"` + RegistrationDateTime string `json:"registrationDateTime,omitempty"` + SystemLabels []string `json:"systemLabels,omitempty"` + TrustType string `json:"trustType,omitempty"` + MemberOf []Member `json:"memberOf,omitempty"` + }{} + + err := json.Unmarshal(data, &tmp) + if err != nil { + return err + } + + d.ID = tmp.ID + d.DeletedDateTime, err = time.Parse(time.RFC3339, tmp.DeletedDateTime) + if err != nil && tmp.DeletedDateTime != "" { + return fmt.Errorf("cannot parse DeletedDateTime %v with RFC3339: %v", tmp.DeletedDateTime, err) + } + d.AccountEnabled = tmp.AccountEnabled + d.ApproximateLastSignInDateTime, err = time.Parse(time.RFC3339, tmp.ApproximateLastSignInDateTime) + if err != nil && tmp.ApproximateLastSignInDateTime != "" { + return fmt.Errorf("cannot parse ApproximateLastSignInDateTime %v with RFC3339: %v", tmp.ApproximateLastSignInDateTime, err) + } + d.ComplianceExpirationDateTime, err = time.Parse(time.RFC3339, tmp.ComplianceExpirationDateTime) + if err != nil && tmp.ComplianceExpirationDateTime != "" { + return fmt.Errorf("cannot parse ApproximateLastSignInDateTime %v with RFC3339: %v", tmp.ComplianceExpirationDateTime, err) + } + d.DeviceId = tmp.DeviceId + d.OnPremisesSyncEnabled = tmp.OnPremisesSyncEnabled + d.DisplayName = tmp.DisplayName + d.IsCompliant = tmp.IsCompliant + d.IsManaged = tmp.IsManaged + d.Manufacturer = tmp.Manufacturer + d.MdmAppId = tmp.MdmAppId + d.Model = tmp.Model + d.OnPremisesLastSyncDateTime, err = time.Parse(time.RFC3339, tmp.OnPremisesLastSyncDateTime) + if err != nil && tmp.OnPremisesLastSyncDateTime != "" { + return fmt.Errorf("cannot parse OnPremisesLastSyncDateTime %v with RFC3339: %v", tmp.OnPremisesLastSyncDateTime, err) + } + d.OnPremisesSyncEnabled = tmp.OnPremisesSyncEnabled + d.OperatingSystem = tmp.OperatingSystem + d.OperatingSystemVersion = tmp.OperatingSystemVersion + d.PhysicalIds = tmp.PhysicalIds + d.ProfileType = tmp.ProfileType + d.RegistrationDateTime, err = time.Parse(time.RFC3339, tmp.RegistrationDateTime) + if err != nil && tmp.RegistrationDateTime != "" { + return fmt.Errorf("cannot parse OnPremisesLastSyncDateTime %v with RFC3339: %v", tmp.RegistrationDateTime, err) + } + d.SystemLabels = tmp.SystemLabels + d.TrustType = tmp.TrustType + d.MemberOf = tmp.MemberOf + return nil +} diff --git a/Device_test.go b/Device_test.go new file mode 100644 index 0000000..1ffefd2 --- /dev/null +++ b/Device_test.go @@ -0,0 +1,49 @@ +package msgraph + +import ( + "fmt" + "testing" +) + +func GetTestDevice(t *testing.T) Device { + t.Helper() + devices, err := graphClient.ListDevices() + if err != nil { + t.Fatalf("Cannot GraphClient.ListDevice(): %v", err) + } + deviceTest, err := devices.GetByDisplayName(msGraphExistingDeviceDisplayName) + if err != nil { + t.Fatalf("Cannot devices.GetByDisplayName(%v): %v", msGraphExistingDeviceDisplayName, err) + } + return deviceTest +} + +func TestDevice_String(t *testing.T) { + testDevice := GetTestDevice(t) + + tests := []struct { + name string + g Device + want string + }{ + { + name: "Test All Devices", + g: testDevice, + want: fmt.Sprintf("Device(ID: \"%v\", DeletedDateTime: \"%v\" AccountEnabled: \"%v\", ApproximateLastSignInDateTime: \"%v\", ComplianceExpirationDateTime: \"%v\", "+ + "DeviceId: \"%v\", DisplayName: \"%v\", IsCompliant: \"%v\" IsManaged: \"%v\", Manufacturer: \"%v\", MdmAppId: \"%v\", Model: \"%v\", OnPremisesLastSyncDateTime: \"%v\", "+ + "OnPremisesSyncEnabled: \"%v\", OperatingSystem: \"%v\", OperatingSystemVersion: \"%v\", PhysicalIds: \"%v\", ProfileType: \"%v\", RegistrationDateTime: \"%v\", "+ + "SystemLabels: \"%v\", TrustType: \"%v\", DirectAPIConnection: %v)", + testDevice.ID, testDevice.DeletedDateTime, testDevice.AccountEnabled, testDevice.ApproximateLastSignInDateTime, testDevice.ComplianceExpirationDateTime, testDevice.DeviceId, + testDevice.DisplayName, testDevice.IsCompliant, testDevice.IsManaged, testDevice.Manufacturer, testDevice.MdmAppId, testDevice.Model, testDevice.OnPremisesLastSyncDateTime, + testDevice.OnPremisesSyncEnabled, testDevice.OperatingSystem, testDevice.OperatingSystemVersion, testDevice.PhysicalIds, testDevice.ProfileType, testDevice.RegistrationDateTime, + testDevice.SystemLabels, testDevice.TrustType, testDevice.graphClient != nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.g.String(); got != tt.want { + t.Errorf("Device.String() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Devices.go b/Devices.go new file mode 100644 index 0000000..3f71230 --- /dev/null +++ b/Devices.go @@ -0,0 +1,36 @@ +package msgraph + +import ( + "strings" +) + +// Devices represents multiple Device-instances and provides funcs to work with them. +type Devices []Device + +func (d Devices) String() string { + var devices = make([]string, len(d)) + for i, calendar := range d { + devices[i] = calendar.String() + } + return "Devices(" + strings.Join(devices, " | ") + ")" +} + +// setGraphClient sets the GraphClient within that particular instance. Hence it's directly created by GraphClient +func (d Devices) setGraphClient(gC *GraphClient) Devices { + for i := range d { + d[i].setGraphClient(gC) + } + return d +} + +// GetByDisplayName returns the Device obj of that array whose DisplayName matches +// the given name. Returns an ErrFindDevice if no device exists that matches the given +// DisplayName. +func (d Devices) GetByDisplayName(displayName string) (Device, error) { + for _, device := range d { + if device.DisplayName == displayName { + return device, nil + } + } + return Device{}, ErrFindDevice +} diff --git a/Domain.go b/Domain.go new file mode 100644 index 0000000..82e35b6 --- /dev/null +++ b/Domain.go @@ -0,0 +1,48 @@ +package msgraph + +import ( + "fmt" +) + +// Domain represents one domain of ms graph +type Domain struct { + ID string `json:"id,omitempty"` + AuthenticationType string `json:"authenticationType,omitempty"` + IsAdminManaged bool `json:"isAdminManaged,omitempty"` + IsDefault bool `json:"isDefault,omitempty"` + IsInitial bool `json:"isInitial,omitempty"` + IsRoot bool `json:"isRoot,omitempty"` + IsVerified bool `json:"isVerified,omitempty"` + SupportedServices []string `json:"supportedServices,omitempty"` + PasswordValidityPeriodInDays *int `json:"passwordValidityPeriodInDays,omitempty"` + PasswordNotificationWindowInDays *int `json:"passwordNotificationWindowInDays,omitempty"` + State interface{} `json:"state,omitempty"` + + graphClient *GraphClient // the graphClient that called the group +} + +func (d Domain) String() string { + return fmt.Sprintf("Domain(ID: \"%v\", AuthenticationType: \"%v\" IsAdminManaged: \"%v\", IsDefault: \"%v\", IsInitial: \"%v\", IsRoot: \"%v\", "+ + "IsVerified: \"%v\", SupportedServices: \"%v\", PasswordValidityPeriodInDays: \"%v\", PasswordNotificationWindowInDays: \"%v\", DirectAPIConnection: %v)", + d.ID, d.AuthenticationType, d.IsAdminManaged, d.IsDefault, d.IsInitial, d.IsRoot, d.IsVerified, d.SupportedServices, d.PasswordValidityPeriodInDays, + d.PasswordNotificationWindowInDays, d.graphClient != nil) +} + +// setGraphClient sets the graphClient instance in this instance and all child-instances (if any) +func (d *Domain) setGraphClient(gC *GraphClient) { + d.graphClient = gC +} + +// ListMembers - Get a list of the domain's direct members. +func (d Domain) ListMembers(opts ...ListQueryOption) (Users, error) { + if d.graphClient == nil { + return nil, ErrNotGraphClientSourced + } + resource := fmt.Sprintf("/domains/%v/members", d.ID) + + var marsh struct { + Users Users `json:"value"` + } + marsh.Users.setGraphClient(d.graphClient) + return marsh.Users, d.graphClient.makeGETAPICall(resource, compileListQueryOptions(opts), &marsh) +} diff --git a/Domain_test.go b/Domain_test.go new file mode 100644 index 0000000..3c4ca8a --- /dev/null +++ b/Domain_test.go @@ -0,0 +1,43 @@ +package msgraph + +import ( + "fmt" + "testing" +) + +func GetTestDomain(t *testing.T) Domain { + t.Helper() + domains, err := graphClient.ListDomains() + if err != nil { + t.Fatalf("Cannot GraphClient.ListDomains(): %v", err) + } + domainTest, err := domains.GetById(msGraphExistingDomainId) + if err != nil { + t.Fatalf("Cannot domains.GetById(%v): %v", msGraphExistingDomainId, err) + } + return domainTest +} + +func TestDomain_String(t *testing.T) { + testDomain := GetTestDomain(t) + + tests := []struct { + name string + g Domain + want string + }{ + { + name: "Test All Domains", + g: testDomain, + want: fmt.Sprintf("Domain(ID: \"%v\", AuthenticationType: \"%v\" IsAdminManaged: \"%v\", IsDefault: \"%v\", IsInitial: \"%v\", IsRoot: \"%v\", IsVerified: \"%v\", SupportedServices: \"%v\", PasswordValidityPeriodInDays: \"%v\", PasswordNotificationWindowInDays: \"%v\", DirectAPIConnection: %v)", + testDomain.ID, testDomain.AuthenticationType, testDomain.IsAdminManaged, testDomain.IsDefault, testDomain.IsInitial, testDomain.IsRoot, testDomain.IsVerified, testDomain.SupportedServices, testDomain.PasswordValidityPeriodInDays, testDomain.PasswordNotificationWindowInDays, testDomain.graphClient != nil), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.g.String(); got != tt.want { + t.Errorf("Domain.String() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/Domains.go b/Domains.go new file mode 100644 index 0000000..f42de5d --- /dev/null +++ b/Domains.go @@ -0,0 +1,36 @@ +package msgraph + +import ( + "strings" +) + +// Domains represents multiple Domains-instances and provides funcs to work with them. +type Domains []Domain + +func (d Domains) String() string { + var domains = make([]string, len(d)) + for i, calendar := range d { + domains[i] = calendar.String() + } + return "Domains(" + strings.Join(domains, " | ") + ")" +} + +// setGraphClient sets the GraphClient within that particular instance. Hence it's directly created by GraphClient +func (d Domains) setGraphClient(gC *GraphClient) Domains { + for i := range d { + d[i].setGraphClient(gC) + } + return d +} + +// GetById returns the Domain obj of that array whose ID matches +// the given id. Returns an ErrFindDomain if no domain exists that matches the given +// DisplayName. +func (d Domains) GetById(id string) (Domain, error) { + for _, domain := range d { + if domain.ID == id { + return domain, nil + } + } + return Domain{}, ErrFindDomain +} diff --git a/GraphClient.go b/GraphClient.go index d60720e..f4ca4f6 100644 --- a/GraphClient.go +++ b/GraphClient.go @@ -20,6 +20,7 @@ const ( odataSearchParamKey = "$search" odataFilterParamKey = "$filter" odataSelectParamKey = "$select" + odataExpandParamKey = "$expand" ) // GraphClient represents a msgraph API connection instance. @@ -325,6 +326,17 @@ func (g *GraphClient) performRequest(req *http.Request, v interface{}) error { return json.Unmarshal(toReturn, &v) // return the error of the json unmarshal } +// ListDomains returns a list of all domains +func (g *GraphClient) ListDomains(opts ...ListQueryOption) (Domains, error) { + resource := "/domains" + var marsh struct { + Domains Domains `json:"value"` + } + err := g.makeGETAPICall(resource, compileListQueryOptions(opts), &marsh) + marsh.Domains.setGraphClient(g) + return marsh.Domains, err +} + // ListUsers returns a list of all users // Supports optional OData query parameters https://docs.microsoft.com/en-us/graph/query-parameters // @@ -345,17 +357,25 @@ func (g *GraphClient) ListUsers(opts ...ListQueryOption) (Users, error) { // Reference: https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/group_list func (g *GraphClient) ListGroups(opts ...ListQueryOption) (Groups, error) { resource := "/groups" - - var reqParams = compileListQueryOptions(opts) - var marsh struct { Groups Groups `json:"value"` } - err := g.makeGETAPICall(resource, reqParams, &marsh) + err := g.makeGETAPICall(resource, compileListQueryOptions(opts), &marsh) marsh.Groups.setGraphClient(g) return marsh.Groups, err } +// ListDevices returns a list of all devices +func (g *GraphClient) ListDevices(opts ...ListQueryOption) (Devices, error) { + resource := "/devices" + var marsh struct { + Devices Devices `json:"value"` + } + err := g.makeGETAPICall(resource, compileListQueryOptions(opts), &marsh) + marsh.Devices.setGraphClient(g) + return marsh.Devices, err +} + // GetUser returns the user object associated to the given user identified by either // the given ID or userPrincipalName // Supports optional OData query parameters https://docs.microsoft.com/en-us/graph/query-parameters diff --git a/GraphClient_queryOptions.go b/GraphClient_queryOptions.go index d9d3bb3..be4ecdb 100644 --- a/GraphClient_queryOptions.go +++ b/GraphClient_queryOptions.go @@ -51,6 +51,13 @@ var ( } } + // ListWithExpand - $expand - Filters properties (columns) - https://docs.microsoft.com/en-us/graph/query-parameters#expand-parameter + ListWithExpand = func(expandParam string) ListQueryOption { + return func(opts *listQueryOptions) { + opts.queryValues.Add(odataExpandParamKey, expandParam) + } + } + // ListWithFilter - $filter - Filters results (rows) - https://docs.microsoft.com/en-us/graph/query-parameters#filter-parameter ListWithFilter = func(filterParam string) ListQueryOption { return func(opts *listQueryOptions) { diff --git a/GraphClient_test.go b/GraphClient_test.go index 04430aa..fdce96f 100644 --- a/GraphClient_test.go +++ b/GraphClient_test.go @@ -34,8 +34,10 @@ var ( msGraphExistingCalendarsOfUser []string // the number of expected results when searching for the msGraphExistingGroupDisplayName with $search or $filter msGraphExistingGroupDisplayNameNumRes uint64 - // a domain-name for unit tests to create a user or other objects, e.g. contoso.com - omit the @ - msGraphDomainNameForCreateTests string + // a valid domain id from msgraph, e.g. technicians@contoso.com + msGraphExistingDomainId string + // a valid devicedisplayname from msgraph + msGraphExistingDeviceDisplayName string // the graphclient used to perform all tests graphClient *GraphClient // marker if the calendar tests should be skipped - set if msGraphExistingCalendarsOfUser is empty @@ -56,7 +58,8 @@ func TestMain(m *testing.M) { msGraphClientSecret = getEnvOrPanic("MSGraphClientSecret") msGraphExistingGroupDisplayName = getEnvOrPanic("MSGraphExistingGroupDisplayName") msGraphExistingUserPrincipalInGroup = getEnvOrPanic("MSGraphExistingUserPrincipalInGroup") - msGraphDomainNameForCreateTests = getEnvOrPanic("MSGraphDomainNameForCreateTests") + msGraphExistingDomainId = getEnvOrPanic("MSGraphExistingDomainId") + msGraphExistingDeviceDisplayName = getEnvOrPanic("MSGraphExistingDeviceDisplayName") if msGraphAzureADAuthEndpoint = os.Getenv("MSGraphAzureADAuthEndpoint"); msGraphAzureADAuthEndpoint == "" { msGraphAzureADAuthEndpoint = AzureADAuthEndpointGlobal @@ -101,7 +104,7 @@ func createUnitTestUser(t *testing.T) User { AccountEnabled: true, DisplayName: "go-msgraph unit-test generated user " + time.Now().Format("2006-01-02") + " - random " + rndstring, MailNickname: "go-msgraph.unit-test.generated." + rndstring, - UserPrincipalName: "go-msgraph.unit-test.generated." + rndstring + "@" + msGraphDomainNameForCreateTests, + UserPrincipalName: "go-msgraph.unit-test.generated." + rndstring + "@" + msGraphExistingDomainId, PasswordProfile: PasswordProfile{Password: randomString(32)}, }) if err != nil { @@ -603,7 +606,7 @@ func TestGraphClient_CreateAndDeleteUser(t *testing.T) { AccountEnabled: true, DisplayName: "go-msgraph unit-test generated user " + time.Now().Format("2006-01-02") + " - random " + rndstring, MailNickname: "go-msgraph.unit-test.generated." + rndstring, - UserPrincipalName: "go-msgraph.unit-test.generated." + rndstring + "@" + msGraphDomainNameForCreateTests, + UserPrincipalName: "go-msgraph.unit-test.generated." + rndstring + "@" + msGraphExistingDomainId, PasswordProfile: PasswordProfile{Password: randomString(32)}, }, wantErr: false, diff --git a/Group.go b/Group.go index f6ab86a..2698115 100644 --- a/Group.go +++ b/Group.go @@ -10,20 +10,21 @@ import ( // // See: https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/group_get type Group struct { - ID string - Description string - DisplayName string - CreatedDateTime time.Time - GroupTypes []string - Mail string - MailEnabled bool - MailNickname string - OnPremisesLastSyncDateTime time.Time // defaults to 0001-01-01 00:00:00 +0000 UTC if there's none - OnPremisesSecurityIdentifier string - OnPremisesSyncEnabled bool - ProxyAddresses []string - SecurityEnabled bool - Visibility string + ID string `json:"id,omitempty"` + Description string `json:"description,omitempty"` + DisplayName string `json:"displayName,omitempty"` + CreatedDateTime time.Time `json:"createdDateTime,omitempty"` + GroupTypes []string `json:"groupTypes,omitempty"` + Mail string `json:"mail,omitempty"` + MailEnabled bool `json:"mailEnabled,omitempty"` + MailNickname string `json:"mailNickname,omitempty"` + OnPremisesLastSyncDateTime time.Time `json:"onPremisesLastSyncDateTime,omitempty"` // defaults to 0001-01-01 00:00:00 +0000 UTC if there's none + OnPremisesSecurityIdentifier string `json:"onPremisesSecurityIdentifier,omitempty"` + OnPremisesSyncEnabled bool `json:"onPremisesSyncEnabled,omitempty"` + ProxyAddresses []string `json:"proxyAddresses,omitempty"` + SecurityEnabled bool `json:"securityEnabled,omitempty"` + Visibility string `json:"visibility,omitempty"` + MemberOf []Member `json:"memberOf,omitempty"` graphClient *GraphClient // the graphClient that called the group } @@ -74,6 +75,7 @@ func (g *Group) UnmarshalJSON(data []byte) error { ProxyAddresses []string `json:"proxyAddresses"` SecurityEnabled bool `json:"securityEnabled"` Visibility string `json:"visibility"` + MemberOf []Member `json:"memberOf"` }{} err := json.Unmarshal(data, &tmp) @@ -101,6 +103,7 @@ func (g *Group) UnmarshalJSON(data []byte) error { g.ProxyAddresses = tmp.ProxyAddresses g.SecurityEnabled = tmp.SecurityEnabled g.Visibility = tmp.Visibility + g.MemberOf = tmp.MemberOf return nil } diff --git a/Member.go b/Member.go new file mode 100644 index 0000000..e9e618c --- /dev/null +++ b/Member.go @@ -0,0 +1,9 @@ +package msgraph + +// Member represents one member of ms graph +type Member struct { + Type string `json:"@odata.type,omitempty"` + Id string `json:"id,omitempty"` + Description string `json:"description,omitempty"` + DisplayName string `json:"displayName,omitempty"` +} diff --git a/README.md b/README.md index be2e4a2..d882363 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,11 @@ This implementation has been written to get various user, group and calendar det working & tested: -- list users, groups, calendars, calendarevents +- list users, groups, calendars, calendarevents, domains, devices - automatically grab & refresh token for API-access - json-load the GraphClient struct & initialize it - set timezone for full-day CalendarEvent -- use `$select`, `$search` and `$filter` when querying data +- use `$select`, `$search`, `$filter` and `$expand` when querying data - `context`-aware API calls, can be cancelled. - loading huge data sets with paging, thanks to PR #20 - [@Goorsky123](https://github.com/Goorsky123) @@ -54,6 +54,10 @@ groups, err := graphClient.ListGroups() groupMembers, err := groups[0].ListMembers() // Lists all Calendars of a user calendars, err := user.ListCalendars() +// List all domains +domains, err := graphClient.ListDomains() +// List all devices +devices, err := graphClient.ListDevices() // Let all full-day calendar events that are loaded from ms graph be set to timezone Europe/Vienna: // Standard is time.Local diff --git a/User.go b/User.go index a23b92c..24fb82f 100644 --- a/User.go +++ b/User.go @@ -25,6 +25,17 @@ type User struct { Department string `json:"department,omitempty"` MailNickname string `json:"mailNickname,omitempty"` PasswordProfile PasswordProfile `json:"passwordProfile,omitempty"` + OtherMails []string `json:"otherMails,omitempty"` + Country string `json:"country,omitempty"` + State string `json:"state,omitempty"` + City string `json:"city,omitempty"` + StreetAddress string `json:"streetAddress"` + OfficeLocation string `json:"officeLocation,omitempty"` + JobTitle string `json:"jobTitle,omitempty"` + PostalCode string `json:"postalCode,omitempty"` + UsageLocation string `json:"usageLocation,omitempty"` + ProxyAddresses []string `json:"proxyAddresses,omitempty"` + MemberOf []Member `json:"memberOf,omitempty"` activePhone string // private cache for the active phone number graphClient *GraphClient // the graphClient that called the user diff --git a/constants.go b/constants.go index d753861..fa73b82 100644 --- a/constants.go +++ b/constants.go @@ -69,10 +69,14 @@ const APIVersion string = "v1.0" const MaxPageSize int = 999 var ( + // ErrFindDomain is returned on any func that tries to find a domain with the given parameters that cannot be found + ErrFindDomain = errors.New("unable to find domain") // ErrFindUser is returned on any func that tries to find a user with the given parameters that cannot be found ErrFindUser = errors.New("unable to find user") // ErrFindGroup is returned on any func that tries to find a group with the given parameters that cannot be found ErrFindGroup = errors.New("unable to find group") + // ErrFindDevice is returned on any func that tries to find a device with the given parameters that cannot be found + ErrFindDevice = errors.New("unable to find device") // ErrFindCalendar is returned on any func that tries to find a calendar with the given parameters that cannot be found ErrFindCalendar = errors.New("unable to find calendar") // ErrNotGraphClientSourced is returned if e.g. a ListMembers() is called but the Group has not been created by a graphClient query