renames /internal/connector to /internal/m365. No logic changes in this PR. Only the dir rename, import renames, and one linter shadowing rename. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🧹 Tech Debt/Cleanup #### Issue(s) * #1996 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
208 lines
4.6 KiB
Go
208 lines
4.6 KiB
Go
// 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/ptr"
|
|
"github.com/alcionai/corso/src/internal/common/str"
|
|
"github.com/alcionai/corso/src/internal/m365/graph"
|
|
"github.com/alcionai/corso/src/pkg/account"
|
|
"github.com/alcionai/corso/src/pkg/credentials"
|
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
|
)
|
|
|
|
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 := str.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,
|
|
}
|
|
|
|
gr := graph.NewNoTimeoutHTTPWrapper()
|
|
|
|
ac, err := api.NewClient(creds)
|
|
if err != nil {
|
|
return Only(ctx, clues.Wrap(err, "getting api client"))
|
|
}
|
|
|
|
err = runDisplayM365JSON(ctx, ac, gr, 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,
|
|
ac api.Client,
|
|
gr graph.Requester,
|
|
creds account.M365Config,
|
|
userID, itemID string,
|
|
) error {
|
|
drive, err := ac.Users().GetDefaultDrive(ctx, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
driveID := ptr.Val(drive.GetId())
|
|
|
|
it := itemPrintable{}
|
|
|
|
item, err := ac.Drives().GetItem(ctx, driveID, itemID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if item != nil {
|
|
content, err := getDriveItemContent(ctx, gr, 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 := ac.Drives().GetItemPermission(ctx, 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(
|
|
ctx context.Context,
|
|
gr graph.Requester,
|
|
item models.DriveItemable,
|
|
) ([]byte, error) {
|
|
url, ok := item.GetAdditionalData()[downloadURLKey].(*string)
|
|
if !ok {
|
|
return nil, clues.New("retrieving download url")
|
|
}
|
|
|
|
resp, err := gr.Request(ctx, http.MethodGet, *url, nil, nil)
|
|
if err != nil {
|
|
return nil, clues.New("downloading item").With("error", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
content, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, clues.New("read downloaded item").With("error", err)
|
|
}
|
|
|
|
return content, nil
|
|
}
|