Get OneDrive item with getM365 (#2791)
Add support for onedrive in getM365 --- #### 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: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Test - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * #<issue> #### Test Plan <!-- How will this be tested prior to merging.--> - [x] 💪 Manual - [ ] ⚡ Unit test - [ ] 💚 E2E
This commit is contained in:
parent
bebd83474a
commit
baddd9fc83
@ -79,4 +79,7 @@ load-test:
|
|||||||
-mutexprofile=mutex.prof \
|
-mutexprofile=mutex.prof \
|
||||||
-trace=trace.out \
|
-trace=trace.out \
|
||||||
-outputdir=test_results \
|
-outputdir=test_results \
|
||||||
./pkg/repository/loadtest/repository_load_test.go
|
./pkg/repository/loadtest/repository_load_test.go
|
||||||
|
|
||||||
|
getM365:
|
||||||
|
go build -o getM365 cmd/getM365/main.go
|
||||||
@ -62,7 +62,7 @@ func StderrWriter(ctx context.Context) io.Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------
|
||||||
// Helper funcs
|
// Exported interface
|
||||||
// ---------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Only tells the CLI to only display this error, preventing the usage
|
// Only tells the CLI to only display this error, preventing the usage
|
||||||
@ -110,6 +110,15 @@ func Infof(ctx context.Context, t string, s ...any) {
|
|||||||
outf(getRootCmd(ctx).ErrOrStderr(), t, s...)
|
outf(getRootCmd(ctx).ErrOrStderr(), t, s...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrettyJSON prettifies and prints the value.
|
||||||
|
func PrettyJSON(ctx context.Context, p minimumPrintabler) {
|
||||||
|
if p == nil {
|
||||||
|
Err(ctx, "<nil>")
|
||||||
|
}
|
||||||
|
|
||||||
|
outputJSON(getRootCmd(ctx).ErrOrStderr(), p, outputAsJSONDebug)
|
||||||
|
}
|
||||||
|
|
||||||
// out is the testable core of exported print funcs
|
// out is the testable core of exported print funcs
|
||||||
func out(w io.Writer, s ...any) {
|
func out(w io.Writer, s ...any) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
@ -135,8 +144,7 @@ func outf(w io.Writer, t string, s ...any) {
|
|||||||
// ---------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
type Printable interface {
|
type Printable interface {
|
||||||
// reduces the struct to a minimized format for easier human consumption
|
minimumPrintabler
|
||||||
MinimumPrintable() any
|
|
||||||
// should list the property names of the values surfaced in Values()
|
// should list the property names of the values surfaced in Values()
|
||||||
Headers() []string
|
Headers() []string
|
||||||
// list of values for tabular or csv formatting
|
// list of values for tabular or csv formatting
|
||||||
@ -145,6 +153,11 @@ type Printable interface {
|
|||||||
Values() []string
|
Values() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type minimumPrintabler interface {
|
||||||
|
// reduces the struct to a minimized format for easier human consumption
|
||||||
|
MinimumPrintable() any
|
||||||
|
}
|
||||||
|
|
||||||
// Item prints the printable, according to the caller's requested format.
|
// Item prints the printable, according to the caller's requested format.
|
||||||
func Item(ctx context.Context, p Printable) {
|
func Item(ctx context.Context, p Printable) {
|
||||||
printItem(getRootCmd(ctx).OutOrStdout(), p)
|
printItem(getRootCmd(ctx).OutOrStdout(), p)
|
||||||
@ -216,13 +229,17 @@ func outputTable(w io.Writer, ps []Printable) {
|
|||||||
// JSON
|
// JSON
|
||||||
// ------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
func outputJSON(w io.Writer, p Printable, debug bool) {
|
func outputJSON(w io.Writer, p minimumPrintabler, debug bool) {
|
||||||
if debug {
|
if debug {
|
||||||
printJSON(w, p)
|
printJSON(w, p)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
printJSON(w, p.MinimumPrintable())
|
if debug {
|
||||||
|
printJSON(w, p)
|
||||||
|
} else {
|
||||||
|
printJSON(w, p.MinimumPrintable())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputJSONArr(w io.Writer, ps []Printable, debug bool) {
|
func outputJSONArr(w io.Writer, ps []Printable, debug bool) {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
// getItem.go is a source file designed to retrieve an m365 object from an
|
// get_item.go is a source file designed to retrieve an m365 object from an
|
||||||
// existing M365 account. Data displayed is representative of the current
|
// existing M365 account. Data displayed is representative of the current
|
||||||
// serialization abstraction versioning used by Microsoft Graph and stored by Corso.
|
// serialization abstraction versioning used by Microsoft Graph and stored by Corso.
|
||||||
|
|
||||||
package main
|
package exchange
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -14,76 +14,65 @@ import (
|
|||||||
kw "github.com/microsoft/kiota-serialization-json-go"
|
kw "github.com/microsoft/kiota-serialization-json-go"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
. "github.com/alcionai/corso/src/cli/print"
|
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
"github.com/alcionai/corso/src/internal/common"
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/connector"
|
|
||||||
"github.com/alcionai/corso/src/internal/connector/exchange/api"
|
"github.com/alcionai/corso/src/internal/connector/exchange/api"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
|
||||||
"github.com/alcionai/corso/src/pkg/account"
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
"github.com/alcionai/corso/src/pkg/credentials"
|
"github.com/alcionai/corso/src/pkg/credentials"
|
||||||
"github.com/alcionai/corso/src/pkg/fault"
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
"github.com/alcionai/corso/src/pkg/logger"
|
|
||||||
"github.com/alcionai/corso/src/pkg/path"
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
)
|
)
|
||||||
|
|
||||||
var getCmd = &cobra.Command{
|
|
||||||
Use: "get",
|
|
||||||
Short: "Get a M365ID item JSON",
|
|
||||||
RunE: handleGetCommand,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required inputs from user for command execution
|
// Required inputs from user for command execution
|
||||||
var (
|
var (
|
||||||
tenant, user, m365ID, category string
|
user, tenant, m365ID, category string
|
||||||
)
|
)
|
||||||
|
|
||||||
// main function will produce the JSON String for a given m365 object of a
|
func AddCommands(parent *cobra.Command) {
|
||||||
// user. Displayed Objects can be used as inputs for Mockable data
|
exCmd := &cobra.Command{
|
||||||
// Supports:
|
Use: "exchange",
|
||||||
// - exchange (contacts, email, and events)
|
Short: "Get an M365ID item JSON",
|
||||||
// Input: go run ./getItem.go --user <user>
|
RunE: handleExchangeCmd,
|
||||||
//
|
|
||||||
// --m365ID <m365ID> --category <oneof: contacts, email, events>
|
|
||||||
func main() {
|
|
||||||
ctx, _ := logger.SeedLevel(context.Background(), logger.Development)
|
|
||||||
ctx = SetRootCmd(ctx, getCmd)
|
|
||||||
|
|
||||||
defer logger.Flush(ctx)
|
|
||||||
|
|
||||||
fs := getCmd.PersistentFlags()
|
|
||||||
fs.StringVar(&user, "user", "", "m365 user id of M365 user")
|
|
||||||
fs.StringVar(&tenant, "tenant", "",
|
|
||||||
"m365 Tenant: m365 identifier for the tenant, not required if active in OS Environment")
|
|
||||||
fs.StringVar(&m365ID, "m365ID", "", "m365 identifier for object to be created")
|
|
||||||
fs.StringVar(&category, "category", "", "type of M365 data (contacts, email, events or files)") // files not supported
|
|
||||||
|
|
||||||
cobra.CheckErr(getCmd.MarkPersistentFlagRequired("user"))
|
|
||||||
cobra.CheckErr(getCmd.MarkPersistentFlagRequired("m365ID"))
|
|
||||||
cobra.CheckErr(getCmd.MarkPersistentFlagRequired("category"))
|
|
||||||
|
|
||||||
if err := getCmd.ExecuteContext(ctx); err != nil {
|
|
||||||
logger.Flush(ctx)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs := exCmd.PersistentFlags()
|
||||||
|
fs.StringVar(&m365ID, "id", "", "m365 identifier for object")
|
||||||
|
fs.StringVar(&category, "category", "", "type of M365 data (contacts, email, events)")
|
||||||
|
fs.StringVar(&user, "user", "", "m365 user id of M365 user")
|
||||||
|
fs.StringVar(&tenant, "tenant", "", "m365 identifier for the tenant")
|
||||||
|
|
||||||
|
cobra.CheckErr(exCmd.MarkPersistentFlagRequired("user"))
|
||||||
|
cobra.CheckErr(exCmd.MarkPersistentFlagRequired("id"))
|
||||||
|
cobra.CheckErr(exCmd.MarkPersistentFlagRequired("category"))
|
||||||
|
|
||||||
|
parent.AddCommand(exCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleGetCommand(cmd *cobra.Command, args []string) error {
|
func handleExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||||
ctx := cmd.Context()
|
|
||||||
|
|
||||||
if utils.HasNoFlagsAndShownHelp(cmd) {
|
if utils.HasNoFlagsAndShownHelp(cmd) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, creds, err := getGC(ctx)
|
tid := common.First(tenant, os.Getenv(account.AzureTenantID))
|
||||||
if err != nil {
|
|
||||||
return err
|
ctx := clues.Add(
|
||||||
|
cmd.Context(),
|
||||||
|
"item_id", m365ID,
|
||||||
|
"resource_owner", user,
|
||||||
|
"tenant", tid)
|
||||||
|
|
||||||
|
creds := account.M365Config{
|
||||||
|
M365: credentials.GetM365(),
|
||||||
|
AzureTenantID: tid,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = runDisplayM365JSON(ctx, creds, user, m365ID, fault.New(true))
|
err := runDisplayM365JSON(ctx, creds, user, m365ID, fault.New(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, clues.Wrap(err, "Error displaying item: "+m365ID))
|
cmd.SilenceUsage = true
|
||||||
|
cmd.SilenceErrors = true
|
||||||
|
|
||||||
|
return clues.Wrap(err, "getting item")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -165,30 +154,3 @@ func getItem(
|
|||||||
|
|
||||||
return itm.Serialize(ctx, sp, user, itemID)
|
return itm.Serialize(ctx, sp, user, itemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
// Helpers
|
|
||||||
//-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func getGC(ctx context.Context) (*connector.GraphConnector, account.M365Config, error) {
|
|
||||||
// get account info
|
|
||||||
m365Cfg := account.M365Config{
|
|
||||||
M365: credentials.GetM365(),
|
|
||||||
AzureTenantID: common.First(tenant, os.Getenv(account.AzureTenantID)),
|
|
||||||
}
|
|
||||||
|
|
||||||
acct, err := account.NewAccount(account.ProviderM365, m365Cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, m365Cfg, Only(ctx, clues.Wrap(err, "finding m365 account details"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: log/print recoverable errors
|
|
||||||
errs := fault.New(false)
|
|
||||||
|
|
||||||
gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), acct, connector.Users, errs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, m365Cfg, Only(ctx, clues.Wrap(err, "connecting to graph API"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return gc, m365Cfg, nil
|
|
||||||
}
|
|
||||||
32
src/cmd/getM365/main.go
Normal file
32
src/cmd/getM365/main.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
. "github.com/alcionai/corso/src/cli/print"
|
||||||
|
"github.com/alcionai/corso/src/cmd/getM365/exchange"
|
||||||
|
"github.com/alcionai/corso/src/cmd/getM365/onedrive"
|
||||||
|
"github.com/alcionai/corso/src/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "getM365",
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, _ := logger.SeedLevel(context.Background(), logger.Development)
|
||||||
|
|
||||||
|
ctx = SetRootCmd(ctx, rootCmd)
|
||||||
|
defer logger.Flush(ctx)
|
||||||
|
|
||||||
|
exchange.AddCommands(rootCmd)
|
||||||
|
onedrive.AddCommands(rootCmd)
|
||||||
|
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
Err(ctx, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
207
src/cmd/getM365/onedrive/get_item.go
Normal file
207
src/cmd/getM365/onedrive/get_item.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
// get_item.go is a source file designed to retrieve an m365 object from an
|
||||||
|
// existing M365 account. Data displayed is representative of the current
|
||||||
|
// serialization abstraction versioning used by Microsoft Graph and stored by Corso.
|
||||||
|
|
||||||
|
package onedrive
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/alcionai/clues"
|
||||||
|
"github.com/microsoft/kiota-abstractions-go/serialization"
|
||||||
|
kjson "github.com/microsoft/kiota-serialization-json-go"
|
||||||
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
. "github.com/alcionai/corso/src/cli/print"
|
||||||
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
|
"github.com/alcionai/corso/src/internal/connector/onedrive/api"
|
||||||
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
|
"github.com/alcionai/corso/src/pkg/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
const downloadURLKey = "@microsoft.graph.downloadUrl"
|
||||||
|
|
||||||
|
// Required inputs from user for command execution
|
||||||
|
var (
|
||||||
|
user, tenant, m365ID string
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddCommands(parent *cobra.Command) {
|
||||||
|
exCmd := &cobra.Command{
|
||||||
|
Use: "onedrive",
|
||||||
|
Short: "Get an M365ID item",
|
||||||
|
RunE: handleOneDriveCmd,
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := exCmd.PersistentFlags()
|
||||||
|
fs.StringVar(&m365ID, "id", "", "m365 identifier for object")
|
||||||
|
fs.StringVar(&user, "user", "", "m365 user id of M365 user")
|
||||||
|
fs.StringVar(&tenant, "tenant", "", "m365 identifier for the tenant")
|
||||||
|
|
||||||
|
cobra.CheckErr(exCmd.MarkPersistentFlagRequired("user"))
|
||||||
|
cobra.CheckErr(exCmd.MarkPersistentFlagRequired("id"))
|
||||||
|
|
||||||
|
parent.AddCommand(exCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleOneDriveCmd(cmd *cobra.Command, args []string) error {
|
||||||
|
if utils.HasNoFlagsAndShownHelp(cmd) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tid := common.First(tenant, os.Getenv(account.AzureTenantID))
|
||||||
|
|
||||||
|
ctx := clues.Add(
|
||||||
|
cmd.Context(),
|
||||||
|
"item_id", m365ID,
|
||||||
|
"resource_owner", user,
|
||||||
|
"tenant", tid)
|
||||||
|
|
||||||
|
// get account info
|
||||||
|
creds := account.M365Config{
|
||||||
|
M365: credentials.GetM365(),
|
||||||
|
AzureTenantID: tid,
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: swap to drive api client, when finished.
|
||||||
|
adpt, err := graph.CreateAdapter(tid, creds.AzureClientID, creds.AzureClientSecret)
|
||||||
|
if err != nil {
|
||||||
|
return Only(ctx, clues.Wrap(err, "creating graph adapter"))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runDisplayM365JSON(ctx, graph.NewService(adpt), creds, user, m365ID)
|
||||||
|
if err != nil {
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
cmd.SilenceErrors = true
|
||||||
|
|
||||||
|
return Only(ctx, clues.Wrap(err, "getting item"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type itemData struct {
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type itemPrintable struct {
|
||||||
|
Info json.RawMessage `json:"info"`
|
||||||
|
Permissions json.RawMessage `json:"permissions"`
|
||||||
|
Data itemData `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i itemPrintable) MinimumPrintable() any {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDisplayM365JSON(
|
||||||
|
ctx context.Context,
|
||||||
|
srv graph.Servicer,
|
||||||
|
creds account.M365Config,
|
||||||
|
user, itemID string,
|
||||||
|
) error {
|
||||||
|
drive, err := api.GetDriveByID(ctx, srv, user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
driveID := ptr.Val(drive.GetId())
|
||||||
|
|
||||||
|
it := itemPrintable{}
|
||||||
|
|
||||||
|
item, err := api.GetDriveItem(ctx, srv, driveID, itemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if item != nil {
|
||||||
|
content, err := getDriveItemContent(item)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We could get size from item.GetSize(), but the
|
||||||
|
// getDriveItemContent call is to ensure that we are able to
|
||||||
|
// download the file.
|
||||||
|
it.Data.Size = len(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
sInfo, err := serializeObject(item)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(sInfo), &it.Info)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
perms, err := api.GetItemPermission(ctx, srv, driveID, itemID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sPerms, err := serializeObject(perms)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal([]byte(sPerms), &it.Permissions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
PrettyJSON(ctx, it)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func serializeObject(data serialization.Parsable) (string, error) {
|
||||||
|
sw := kjson.NewJsonSerializationWriter()
|
||||||
|
|
||||||
|
err := sw.WriteObjectValue("", data)
|
||||||
|
if err != nil {
|
||||||
|
return "", clues.Wrap(err, "writing serializing info")
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := sw.GetSerializedContent()
|
||||||
|
if err != nil {
|
||||||
|
return "", clues.Wrap(err, "getting serializing info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDriveItemContent(item models.DriveItemable) ([]byte, error) {
|
||||||
|
url, ok := item.GetAdditionalData()[downloadURLKey].(*string)
|
||||||
|
if !ok {
|
||||||
|
return nil, clues.New("get download url")
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, *url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.New("create download request").With("error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hc := graph.HTTPClient(graph.NoTimeout())
|
||||||
|
|
||||||
|
resp, err := hc.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.New("download item").With("error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, clues.New("read downloaded item").With("error", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return content, nil
|
||||||
|
}
|
||||||
@ -29,6 +29,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
logGraphRequestsEnvKey = "LOG_GRAPH_REQUESTS"
|
logGraphRequestsEnvKey = "LOG_GRAPH_REQUESTS"
|
||||||
log2xxGraphRequestsEnvKey = "LOG_2XX_GRAPH_REQUESTS"
|
log2xxGraphRequestsEnvKey = "LOG_2XX_GRAPH_REQUESTS"
|
||||||
|
log2xxGraphResponseEnvKey = "LOG_2XX_GRAPH_RESPONSES"
|
||||||
retryAttemptHeader = "Retry-Attempt"
|
retryAttemptHeader = "Retry-Attempt"
|
||||||
retryAfterHeader = "Retry-After"
|
retryAfterHeader = "Retry-After"
|
||||||
defaultMaxRetries = 3
|
defaultMaxRetries = 3
|
||||||
@ -368,7 +369,7 @@ func (handler *LoggingMiddleware) Intercept(
|
|||||||
// If api logging is toggled, log a body-less dump of the request/resp.
|
// If api logging is toggled, log a body-less dump of the request/resp.
|
||||||
if (resp.StatusCode / 100) == 2 {
|
if (resp.StatusCode / 100) == 2 {
|
||||||
if logger.DebugAPI || os.Getenv(log2xxGraphRequestsEnvKey) != "" {
|
if logger.DebugAPI || os.Getenv(log2xxGraphRequestsEnvKey) != "" {
|
||||||
log.Debugw("2xx graph api resp", "response", getRespDump(ctx, resp, false))
|
log.Debugw("2xx graph api resp", "response", getRespDump(ctx, resp, os.Getenv(log2xxGraphResponseEnvKey) != ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
|
|||||||
@ -203,10 +203,6 @@ func (p *siteDrivePager) ValuesIn(l api.PageLinker) ([]models.Driveable, error)
|
|||||||
return getValues[models.Driveable](l)
|
return getValues[models.Driveable](l)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// Drive Paging
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
// DrivePager pages through different types of drive owners
|
// DrivePager pages through different types of drive owners
|
||||||
type DrivePager interface {
|
type DrivePager interface {
|
||||||
GetPage(context.Context) (api.PageLinker, error)
|
GetPage(context.Context) (api.PageLinker, error)
|
||||||
@ -275,3 +271,55 @@ func GetAllDrives(
|
|||||||
|
|
||||||
return ds, nil
|
return ds, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generic drive item getter
|
||||||
|
func GetDriveItem(
|
||||||
|
ctx context.Context,
|
||||||
|
srv graph.Servicer,
|
||||||
|
driveID, itemID string,
|
||||||
|
) (models.DriveItemable, error) {
|
||||||
|
di, err := srv.Client().
|
||||||
|
DrivesById(driveID).
|
||||||
|
ItemsById(itemID).
|
||||||
|
Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "getting item")
|
||||||
|
}
|
||||||
|
|
||||||
|
return di, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetItemPermission(
|
||||||
|
ctx context.Context,
|
||||||
|
service graph.Servicer,
|
||||||
|
driveID, itemID string,
|
||||||
|
) (models.PermissionCollectionResponseable, error) {
|
||||||
|
perm, err := service.
|
||||||
|
Client().
|
||||||
|
DrivesById(driveID).
|
||||||
|
ItemsById(itemID).
|
||||||
|
Permissions().
|
||||||
|
Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "getting item metadata").With("item_id", itemID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return perm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDriveByID(
|
||||||
|
ctx context.Context,
|
||||||
|
srv graph.Servicer,
|
||||||
|
userID string,
|
||||||
|
) (models.Driveable, error) {
|
||||||
|
//revive:enable:context-as-argument
|
||||||
|
d, err := srv.Client().
|
||||||
|
UsersById(userID).
|
||||||
|
Drive().
|
||||||
|
Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "getting drive")
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
|
"github.com/alcionai/corso/src/internal/connector/onedrive/api"
|
||||||
"github.com/alcionai/corso/src/internal/connector/support"
|
"github.com/alcionai/corso/src/internal/connector/support"
|
||||||
"github.com/alcionai/corso/src/internal/data"
|
"github.com/alcionai/corso/src/internal/data"
|
||||||
"github.com/alcionai/corso/src/internal/observe"
|
"github.com/alcionai/corso/src/internal/observe"
|
||||||
@ -166,11 +167,11 @@ func NewCollection(
|
|||||||
// Allows tests to set a mock populator
|
// Allows tests to set a mock populator
|
||||||
switch source {
|
switch source {
|
||||||
case SharePointSource:
|
case SharePointSource:
|
||||||
c.itemGetter = getDriveItem
|
c.itemGetter = api.GetDriveItem
|
||||||
c.itemReader = sharePointItemReader
|
c.itemReader = sharePointItemReader
|
||||||
c.itemMetaReader = sharePointItemMetaReader
|
c.itemMetaReader = sharePointItemMetaReader
|
||||||
default:
|
default:
|
||||||
c.itemGetter = getDriveItem
|
c.itemGetter = api.GetDriveItem
|
||||||
c.itemReader = oneDriveItemReader
|
c.itemReader = oneDriveItemReader
|
||||||
c.itemMetaReader = oneDriveItemMetaReader
|
c.itemMetaReader = oneDriveItemMetaReader
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
|
"github.com/alcionai/corso/src/internal/connector/onedrive/api"
|
||||||
"github.com/alcionai/corso/src/internal/connector/uploadsession"
|
"github.com/alcionai/corso/src/internal/connector/uploadsession"
|
||||||
"github.com/alcionai/corso/src/internal/version"
|
"github.com/alcionai/corso/src/internal/version"
|
||||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
@ -26,20 +27,6 @@ const (
|
|||||||
downloadURLKey = "@microsoft.graph.downloadUrl"
|
downloadURLKey = "@microsoft.graph.downloadUrl"
|
||||||
)
|
)
|
||||||
|
|
||||||
// generic drive item getter
|
|
||||||
func getDriveItem(
|
|
||||||
ctx context.Context,
|
|
||||||
srv graph.Servicer,
|
|
||||||
driveID, itemID string,
|
|
||||||
) (models.DriveItemable, error) {
|
|
||||||
di, err := srv.Client().DrivesById(driveID).ItemsById(itemID).Get(ctx, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, graph.Wrap(ctx, err, "getting item")
|
|
||||||
}
|
|
||||||
|
|
||||||
return di, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// sharePointItemReader will return a io.ReadCloser for the specified item
|
// sharePointItemReader will return a io.ReadCloser for the specified item
|
||||||
// It crafts this by querying M365 for a download URL for the item
|
// It crafts this by querying M365 for a download URL for the item
|
||||||
// and using a http client to initialize a reader
|
// and using a http client to initialize a reader
|
||||||
@ -229,14 +216,9 @@ func driveItemPermissionInfo(
|
|||||||
driveID string,
|
driveID string,
|
||||||
itemID string,
|
itemID string,
|
||||||
) ([]UserPermission, error) {
|
) ([]UserPermission, error) {
|
||||||
perm, err := service.
|
perm, err := api.GetItemPermission(ctx, service, driveID, itemID)
|
||||||
Client().
|
|
||||||
DrivesById(driveID).
|
|
||||||
ItemsById(itemID).
|
|
||||||
Permissions().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, graph.Wrap(ctx, err, "fetching item permissions").With("item_id", itemID)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
uperms := filterUserPermissions(ctx, perm.GetValue())
|
uperms := filterUserPermissions(ctx, perm.GetValue())
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user