Add common code to check if services are enabled (#4127)

<!-- PR description-->

Currently corso CLI & SDK have different checks for services enabled. These checks have diverged which can lead to bugs. For e.g. SDK users use [code](3e43028a88/src/pkg/services/m365/users.go (L92)) to check if OD is enabled, while corso uses [this code](https://github.com/alcionai/corso/blob/main/src/pkg/services/m365/api/users.go#L174). These funcs have different checks. This PR introduces common helpers to consolidate these checks in one place.


- Note: I decided against absorbing these helpers into api/users.go, because separation of concerns -  getters shouldn't arbitrate on return vals. 
- Note that this code is not yet wired up to SDK/CLI. It'll be added in a following [PR](https://github.com/alcionai/corso/pull/4096). 


**Changes**
1. `Is*ServiceEnabled` helpers in internal/m365/common.go. 

2. Add `GetMailboxInfo` to common code. This is currently a duplicate of code in `GetInfo`. That function will be removed once we port SDK users to `GetMailboxInfo`. 

3. Unit tests for common code. Integration tests will be added in a later PR
- Note:`TestIsOneDriveServiceEnabled` is copied from `TestCheckUserHasDrives`. The older test will be removed in a later PR.


---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [x] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

<!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. -->
* https://github.com/alcionai/corso/issues/3844

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
Abhishek Pandey 2023-09-05 00:29:03 +05:30 committed by GitHub
parent b7598d1ebe
commit c1c4218994
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 780 additions and 14 deletions

View File

@ -34,7 +34,7 @@ const (
// failed for an attachment." // failed for an attachment."
cannotOpenFileAttachment errorCode = "ErrorCannotOpenFileAttachment" cannotOpenFileAttachment errorCode = "ErrorCannotOpenFileAttachment"
emailFolderNotFound errorCode = "ErrorSyncFolderNotFound" emailFolderNotFound errorCode = "ErrorSyncFolderNotFound"
errorAccessDenied errorCode = "ErrorAccessDenied" ErrorAccessDenied errorCode = "ErrorAccessDenied"
errorItemNotFound errorCode = "ErrorItemNotFound" errorItemNotFound errorCode = "ErrorItemNotFound"
// This error occurs when an attempt is made to create a folder that has // This error occurs when an attempt is made to create a folder that has
// the same name as another folder in the same parent. Such duplicate folder // the same name as another folder in the same parent. Such duplicate folder
@ -49,7 +49,7 @@ const (
// nameAlreadyExists occurs when a request with // nameAlreadyExists occurs when a request with
// @microsoft.graph.conflictBehavior=fail finds a conflicting file. // @microsoft.graph.conflictBehavior=fail finds a conflicting file.
nameAlreadyExists errorCode = "nameAlreadyExists" nameAlreadyExists errorCode = "nameAlreadyExists"
quotaExceeded errorCode = "ErrorQuotaExceeded" QuotaExceeded errorCode = "ErrorQuotaExceeded"
RequestResourceNotFound errorCode = "Request_ResourceNotFound" RequestResourceNotFound errorCode = "Request_ResourceNotFound"
// Returned when we try to get the inbox of a user that doesn't exist. // Returned when we try to get the inbox of a user that doesn't exist.
ResourceNotFound errorCode = "ResourceNotFound" ResourceNotFound errorCode = "ResourceNotFound"
@ -137,7 +137,7 @@ func IsErrInvalidDelta(err error) bool {
} }
func IsErrQuotaExceeded(err error) bool { func IsErrQuotaExceeded(err error) bool {
return hasErrorCode(err, quotaExceeded) return hasErrorCode(err, QuotaExceeded)
} }
func IsErrExchangeMailFolderNotFound(err error) bool { func IsErrExchangeMailFolderNotFound(err error) bool {
@ -170,7 +170,7 @@ func IsErrCannotOpenFileAttachment(err error) bool {
} }
func IsErrAccessDenied(err error) bool { func IsErrAccessDenied(err error) bool {
return hasErrorCode(err, errorAccessDenied) || clues.HasLabel(err, LabelStatus(http.StatusForbidden)) return hasErrorCode(err, ErrorAccessDenied) || clues.HasLabel(err, LabelStatus(http.StatusForbidden))
} }
func IsErrTimeout(err error) bool { func IsErrTimeout(err error) bool {

View File

@ -0,0 +1,100 @@
package exchange
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/pkg/logger"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type getMailInboxer interface {
GetMailInbox(ctx context.Context, userID string) (models.MailFolderable, error)
}
func IsServiceEnabled(
ctx context.Context,
gmi getMailInboxer,
resource string,
) (bool, error) {
_, err := gmi.GetMailInbox(ctx, resource)
if err != nil {
if err := api.EvaluateMailboxError(err); err != nil {
logger.CtxErr(ctx, err).Error("getting user's mail folder")
return false, clues.Stack(err)
}
logger.Ctx(ctx).Info("resource owner does not have a mailbox enabled")
return false, nil
}
return true, nil
}
type getMailboxer interface {
GetMailInbox(ctx context.Context, userID string) (models.MailFolderable, error)
GetMailboxSettings(ctx context.Context, userID string) (models.Userable, error)
GetFirstInboxMessage(ctx context.Context, userID, inboxID string) error
}
func GetMailboxInfo(
ctx context.Context,
gmb getMailboxer,
userID string,
) (api.MailboxInfo, error) {
mi := api.MailboxInfo{
ErrGetMailBoxSetting: []error{},
}
// First check whether the user is able to access their inbox.
inbox, err := gmb.GetMailInbox(ctx, userID)
if err != nil {
if err := api.EvaluateMailboxError(graph.Stack(ctx, err)); err != nil {
logger.CtxErr(ctx, err).Error("getting user's mail folder")
return mi, err
}
logger.Ctx(ctx).Info("resource owner does not have a mailbox enabled")
mi.ErrGetMailBoxSetting = append(
mi.ErrGetMailBoxSetting,
api.ErrMailBoxSettingsNotFound)
return mi, nil
}
mboxSettings, err := gmb.GetMailboxSettings(ctx, userID)
if err != nil {
logger.CtxErr(ctx, err).Info("err getting user's mailbox settings")
if !graph.IsErrAccessDenied(err) {
return mi, graph.Wrap(ctx, err, "getting user's mailbox settings")
}
logger.CtxErr(ctx, err).Info("mailbox settings access denied")
mi.ErrGetMailBoxSetting = append(
mi.ErrGetMailBoxSetting,
api.ErrMailBoxSettingsAccessDenied,
)
} else {
mi = api.ParseMailboxSettings(mboxSettings, mi)
}
err = gmb.GetFirstInboxMessage(ctx, userID, ptr.Val(inbox.GetId()))
if err != nil {
if !graph.IsErrQuotaExceeded(err) {
return mi, clues.Stack(err)
}
mi.QuotaExceeded = graph.IsErrQuotaExceeded(err)
}
return mi, nil
}

View File

@ -0,0 +1,271 @@
package exchange
import (
"context"
"testing"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
"github.com/alcionai/corso/src/pkg/services/m365/api"
"github.com/alcionai/corso/src/pkg/services/m365/api/mock"
)
type EnabledUnitSuite struct {
tester.Suite
}
func TestEnabledUnitSuite(t *testing.T) {
suite.Run(t, &EnabledUnitSuite{Suite: tester.NewUnitSuite(t)})
}
var _ getMailboxer = mockGMB{}
type mockGMB struct {
mailbox models.MailFolderable
mailboxErr error
settings models.Userable
settingsErr error
inboxMessageErr error
}
func (m mockGMB) GetMailInbox(context.Context, string) (models.MailFolderable, error) {
return m.mailbox, m.mailboxErr
}
func (m mockGMB) GetMailboxSettings(context.Context, string) (models.Userable, error) {
return m.settings, m.settingsErr
}
func (m mockGMB) GetFirstInboxMessage(context.Context, string, string) error {
return m.inboxMessageErr
}
// TODO(pandeyabs): Duplicate of graph/errors_test.go. Remove
// this and identical funcs in od/sp and use the one in graph/errors_test.go
// instead.
func odErrMsg(code, message string) *odataerrors.ODataError {
odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError()
merr.SetCode(&code)
merr.SetMessage(&message)
odErr.SetErrorEscaped(merr)
return odErr
}
func (suite *EnabledUnitSuite) TestIsServiceEnabled() {
table := []struct {
name string
mock func(context.Context) getMailInboxer
expect assert.BoolAssertionFunc
expectErr func(*testing.T, error)
}{
{
name: "ok",
mock: func(ctx context.Context) getMailInboxer {
return mockGMB{
mailbox: models.NewMailFolder(),
}
},
expect: assert.True,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "user has no mailbox",
mock: func(ctx context.Context) getMailInboxer {
odErr := odErrMsg(string(graph.ResourceNotFound), "message")
return mockGMB{
mailboxErr: graph.Stack(ctx, odErr),
}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "user not found",
mock: func(ctx context.Context) getMailInboxer {
odErr := odErrMsg(string(graph.RequestResourceNotFound), "message")
return mockGMB{
mailboxErr: graph.Stack(ctx, odErr),
}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
{
name: "overlapping resourcenotfound",
mock: func(ctx context.Context) getMailInboxer {
odErr := odErrMsg(string(graph.ResourceNotFound), "User not found")
return mockGMB{
mailboxErr: graph.Stack(ctx, odErr),
}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
{
name: "arbitrary error",
mock: func(ctx context.Context) getMailInboxer {
odErr := odErrMsg("code", "message")
return mockGMB{
mailboxErr: graph.Stack(ctx, odErr),
}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
gmi := test.mock(ctx)
ok, err := IsServiceEnabled(ctx, gmi, "resource_id")
test.expect(t, ok, "has mailbox flag")
test.expectErr(t, err)
})
}
}
func (suite *EnabledUnitSuite) TestGetMailboxInfo() {
table := []struct {
name string
mock func(context.Context) getMailboxer
expectErr func(*testing.T, error)
expect func(*testing.T) api.MailboxInfo
}{
{
name: "ok",
mock: func(ctx context.Context) getMailboxer {
return mockGMB{
mailbox: models.NewMailFolder(),
settings: mock.UserSettings(),
}
},
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
expect: func(t *testing.T) api.MailboxInfo {
return mock.UserMailboxInfo()
},
},
{
name: "user has no mailbox",
mock: func(ctx context.Context) getMailboxer {
err := odErrMsg(string(graph.ResourceNotFound), "message")
return mockGMB{
mailboxErr: graph.Stack(ctx, err),
}
},
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
expect: func(t *testing.T) api.MailboxInfo {
mi := api.MailboxInfo{}
mi.ErrGetMailBoxSetting = append(
mi.ErrGetMailBoxSetting,
api.ErrMailBoxSettingsNotFound)
return mi
},
},
{
name: "settings access denied",
mock: func(ctx context.Context) getMailboxer {
err := odErrMsg(string(graph.ErrorAccessDenied), "message")
return mockGMB{
mailbox: models.NewMailFolder(),
settingsErr: err,
}
},
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
expect: func(t *testing.T) api.MailboxInfo {
mi := api.MailboxInfo{}
mi.ErrGetMailBoxSetting = append(
mi.ErrGetMailBoxSetting,
api.ErrMailBoxSettingsAccessDenied)
return mi
},
},
{
name: "error fetching settings",
mock: func(ctx context.Context) getMailboxer {
return mockGMB{
mailbox: models.NewMailFolder(),
settingsErr: assert.AnError,
}
},
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
expect: func(t *testing.T) api.MailboxInfo {
return api.MailboxInfo{
ErrGetMailBoxSetting: []error{},
}
},
},
{
name: "mailbox quota exceeded",
mock: func(ctx context.Context) getMailboxer {
err := odErrMsg(string(graph.QuotaExceeded), "message")
return mockGMB{
mailbox: models.NewMailFolder(),
settings: mock.UserSettings(),
inboxMessageErr: graph.Stack(ctx, err),
}
},
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
expect: func(t *testing.T) api.MailboxInfo {
mi := mock.UserMailboxInfo()
mi.QuotaExceeded = true
return mi
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
gmi := test.mock(ctx)
mi, err := GetMailboxInfo(ctx, gmi, "resource_id")
test.expectErr(t, err)
assert.Equal(t, test.expect(t), mi)
})
}
}

View File

@ -0,0 +1,37 @@
package onedrive
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/m365/graph"
)
type getDefaultDriver interface {
GetDefaultDrive(ctx context.Context, userID string) (models.Driveable, error)
}
func IsServiceEnabled(
ctx context.Context,
gdd getDefaultDriver,
resource string,
) (bool, error) {
_, err := gdd.GetDefaultDrive(ctx, resource)
if err != nil {
// we consider this a non-error case, since it
// answers the question the caller is asking.
if clues.HasLabel(err, graph.LabelsMysiteNotFound) || clues.HasLabel(err, graph.LabelsNoSharePointLicense) {
return false, nil
}
if graph.IsErrUserNotFound(err) {
return false, clues.Stack(graph.ErrResourceOwnerNotFound, err)
}
return false, clues.Stack(err)
}
return true, nil
}

View File

@ -0,0 +1,134 @@
package onedrive
import (
"context"
"testing"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
)
type EnabledUnitSuite struct {
tester.Suite
}
func TestEnabledUnitSuite(t *testing.T) {
suite.Run(t, &EnabledUnitSuite{Suite: tester.NewUnitSuite(t)})
}
var _ getDefaultDriver = mockDGDD{}
type mockDGDD struct {
response models.Driveable
err error
}
func (m mockDGDD) GetDefaultDrive(context.Context, string) (models.Driveable, error) {
return m.response, m.err
}
// Copied from src/internal/m365/graph/errors_test.go
func odErrMsg(code, message string) *odataerrors.ODataError {
odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError()
merr.SetCode(&code)
merr.SetMessage(&message)
odErr.SetErrorEscaped(merr)
return odErr
}
func (suite *EnabledUnitSuite) TestIsServiceEnabled() {
table := []struct {
name string
mock func(context.Context) getDefaultDriver
expect assert.BoolAssertionFunc
expectErr func(*testing.T, error)
}{
{
name: "ok",
mock: func(ctx context.Context) getDefaultDriver {
return mockDGDD{models.NewDrive(), nil}
},
expect: assert.True,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "mysite not found",
mock: func(ctx context.Context) getDefaultDriver {
odErr := odErrMsg("code", string(graph.MysiteNotFound))
return mockDGDD{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "mysite URL not found",
mock: func(ctx context.Context) getDefaultDriver {
odErr := odErrMsg("code", string(graph.MysiteURLNotFound))
return mockDGDD{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "no sharepoint license",
mock: func(ctx context.Context) getDefaultDriver {
odErr := odErrMsg("code", string(graph.NoSPLicense))
return mockDGDD{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "user not found",
mock: func(ctx context.Context) getDefaultDriver {
odErr := odErrMsg(string(graph.RequestResourceNotFound), "message")
return mockDGDD{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
{
name: "arbitrary error",
mock: func(ctx context.Context) getDefaultDriver {
odErr := odErrMsg("code", "message")
return mockDGDD{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
dgdd := test.mock(ctx)
ok, err := IsServiceEnabled(ctx, dgdd, "resource_id")
test.expect(t, ok, "has drives flag")
test.expectErr(t, err)
})
}
}

View File

@ -0,0 +1,31 @@
package sharepoint
import (
"context"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/m365/graph"
)
type getSiteRooter interface {
GetRoot(ctx context.Context) (models.Siteable, error)
}
func IsServiceEnabled(
ctx context.Context,
gsr getSiteRooter,
resource string,
) (bool, error) {
_, err := gsr.GetRoot(ctx)
if err != nil {
if clues.HasLabel(err, graph.LabelsNoSharePointLicense) {
return false, nil
}
return false, clues.Stack(err)
}
return true, nil
}

View File

@ -0,0 +1,102 @@
package sharepoint
import (
"context"
"testing"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/internal/tester"
)
type EnabledUnitSuite struct {
tester.Suite
}
func TestEnabledUnitSuite(t *testing.T) {
suite.Run(t, &EnabledUnitSuite{Suite: tester.NewUnitSuite(t)})
}
var _ getSiteRooter = mockGSR{}
type mockGSR struct {
response models.Siteable
err error
}
func (m mockGSR) GetRoot(context.Context) (models.Siteable, error) {
return m.response, m.err
}
func odErrMsg(code, message string) *odataerrors.ODataError {
odErr := odataerrors.NewODataError()
merr := odataerrors.NewMainError()
merr.SetCode(&code)
merr.SetMessage(&message)
odErr.SetErrorEscaped(merr)
return odErr
}
func (suite *EnabledUnitSuite) TestIsServiceEnabled() {
table := []struct {
name string
mock func(context.Context) getSiteRooter
expect assert.BoolAssertionFunc
expectErr func(*testing.T, error)
}{
{
name: "ok",
mock: func(ctx context.Context) getSiteRooter {
return mockGSR{models.NewSite(), nil}
},
expect: assert.True,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "no sharepoint license",
mock: func(ctx context.Context) getSiteRooter {
odErr := odErrMsg("code", string(graph.NoSPLicense))
return mockGSR{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.NoError(t, err, clues.ToCore(err))
},
},
{
name: "arbitrary error",
mock: func(ctx context.Context) getSiteRooter {
odErr := odErrMsg("code", "message")
return mockGSR{nil, graph.Stack(ctx, odErr)}
},
expect: assert.False,
expectErr: func(t *testing.T, err error) {
assert.Error(t, err, clues.ToCore(err))
},
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
ctx, flush := tester.NewContext(t)
defer flush()
gsr := test.mock(ctx)
ok, err := IsServiceEnabled(ctx, gsr, "resource_id")
test.expect(t, ok, "has sites flag")
test.expectErr(t, err)
})
}
}

View File

@ -0,0 +1,90 @@
package mock
import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
func UserSettings() models.Userable {
u := models.NewUser()
u.SetAdditionalData(
map[string]any{
"archiveFolder": "archive",
"timeZone": "UTC",
"dateFormat": "MM/dd/yyyy",
"timeFormat": "hh:mm tt",
"userPurpose": "user",
"delegateMeetingMessageDeliveryOptions": "test",
"automaticRepliesSetting": map[string]any{
"status": "foo",
"externalAudience": "bar",
"externalReplyMessage": "baz",
"internalReplyMessage": "qux",
"scheduledStartDateTime": map[string]any{
"dateTime": "2020-01-01T00:00:00Z",
"timeZone": "UTC",
},
"scheduledEndDateTime": map[string]any{
"dateTime": "2020-01-01T00:00:00Z",
"timeZone": "UTC",
},
},
"language": map[string]any{
"displayName": "en-US",
"locale": "US",
},
"workingHours": map[string]any{
"daysOfWeek": []any{"monday"},
"startTime": "08:00:00.0000000",
"endTime": "17:00:00.0000000",
"timeZone": map[string]any{
"name": "UTC",
},
},
})
return u
}
func UserMailboxInfo() api.MailboxInfo {
return api.MailboxInfo{
Purpose: "user",
ArchiveFolder: "archive",
DateFormat: "MM/dd/yyyy",
TimeFormat: "hh:mm tt",
DelegateMeetMsgDeliveryOpt: "test",
Timezone: "UTC",
AutomaticRepliesSetting: api.AutomaticRepliesSettings{
Status: "foo",
ExternalAudience: "bar",
ExternalReplyMessage: "baz",
InternalReplyMessage: "qux",
ScheduledStartDateTime: api.TimeInfo{
DateTime: "2020-01-01T00:00:00Z",
Timezone: "UTC",
},
ScheduledEndDateTime: api.TimeInfo{
DateTime: "2020-01-01T00:00:00Z",
Timezone: "UTC",
},
},
Language: api.Language{
DisplayName: "en-US",
Locale: "US",
},
WorkingHours: api.WorkingHours{
DaysOfWeek: []string{"monday"},
StartTime: "08:00:00.0000000",
EndTime: "17:00:00.0000000",
TimeZone: struct {
Name string
}{
Name: "UTC",
},
},
ErrGetMailBoxSetting: []error{},
}
}

View File

@ -35,12 +35,12 @@ type AutomaticRepliesSettings struct {
ExternalAudience string ExternalAudience string
ExternalReplyMessage string ExternalReplyMessage string
InternalReplyMessage string InternalReplyMessage string
ScheduledEndDateTime timeInfo ScheduledEndDateTime TimeInfo
ScheduledStartDateTime timeInfo ScheduledStartDateTime TimeInfo
Status string Status string
} }
type timeInfo struct { type TimeInfo struct {
DateTime string DateTime string
Timezone string Timezone string
} }
@ -86,7 +86,7 @@ func (ui *UserInfo) CanMakeDeltaQueries() bool {
return !ui.Mailbox.QuotaExceeded return !ui.Mailbox.QuotaExceeded
} }
func parseMailboxSettings( func ParseMailboxSettings(
settings models.Userable, settings models.Userable,
mi MailboxInfo, mi MailboxInfo,
) MailboxInfo { ) MailboxInfo {

View File

@ -21,6 +21,7 @@ import (
// Variables // Variables
var ( var (
ErrMailBoxSettingsNotFound = clues.New("mailbox settings not found") ErrMailBoxSettingsNotFound = clues.New("mailbox settings not found")
ErrMailBoxSettingsAccessDenied = clues.New("mailbox settings access denied")
) )
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -219,7 +220,7 @@ func (c Users) GetInfo(ctx context.Context, userID string) (*UserInfo, error) {
return userInfo, nil return userInfo, nil
} }
mboxSettings, err := c.getMailboxSettings(ctx, userID) mboxSettings, err := c.GetMailboxSettings(ctx, userID)
if err != nil { if err != nil {
logger.CtxErr(ctx, err).Info("err getting user's mailbox settings") logger.CtxErr(ctx, err).Info("err getting user's mailbox settings")
@ -229,10 +230,10 @@ func (c Users) GetInfo(ctx context.Context, userID string) (*UserInfo, error) {
mi.ErrGetMailBoxSetting = append(mi.ErrGetMailBoxSetting, clues.New("access denied")) mi.ErrGetMailBoxSetting = append(mi.ErrGetMailBoxSetting, clues.New("access denied"))
} else { } else {
mi = parseMailboxSettings(mboxSettings, mi) mi = ParseMailboxSettings(mboxSettings, mi)
} }
err = c.getFirstInboxMessage(ctx, userID, ptr.Val(inbx.GetId())) err = c.GetFirstInboxMessage(ctx, userID, ptr.Val(inbx.GetId()))
if err != nil { if err != nil {
if !graph.IsErrQuotaExceeded(err) { if !graph.IsErrQuotaExceeded(err) {
return nil, clues.Stack(err) return nil, clues.Stack(err)
@ -266,7 +267,7 @@ func EvaluateMailboxError(err error) error {
return err return err
} }
func (c Users) getMailboxSettings( func (c Users) GetMailboxSettings(
ctx context.Context, ctx context.Context,
userID string, userID string,
) (models.Userable, error) { ) (models.Userable, error) {
@ -323,7 +324,7 @@ func (c Users) GetDefaultDrive(
// exceeded error. Ideally(if available) we should convert this to // exceeded error. Ideally(if available) we should convert this to
// pull the user's usage via an api and compare if they have used // pull the user's usage via an api and compare if they have used
// up their quota. // up their quota.
func (c Users) getFirstInboxMessage( func (c Users) GetFirstInboxMessage(
ctx context.Context, ctx context.Context,
userID, inboxID string, userID, inboxID string,
) error { ) error {