Files
gocommon/azure/graph.go

357 lines
8.0 KiB
Go

package azure
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"time"
)
type Graph struct {
*accessToken
tenantId string
groupId string
}
func NewGraphByEnv() *Graph {
tenantId := os.Getenv("GRAPH_TENANT_ID")
groupId := os.Getenv("GRAPH_GROUP_ID")
return NewGraph(tenantId, groupId)
}
func NewGraph(tenantId string, groupId string) *Graph {
return &Graph{
accessToken: &accessToken{
url: fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantId),
values: url.Values{
"grant_type": {"client_credentials"},
"client_id": {microsoftAppId()},
"scope": {"https://graph.microsoft.com/.default"},
"client_secret": {microsoftAppPassword()},
},
},
tenantId: tenantId,
groupId: groupId,
}
}
func (s Graph) GroupId() string {
return s.groupId
}
var errGroupIdMissing = errors.New("GRAPH_GROUP_ID is missing")
func (s Graph) ReadFile(relativePath string) (io.ReadCloser, error) {
if len(s.groupId) == 0 {
return nil, errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return nil, err
}
requrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/groups/%s/drive/root:/%s:/content", s.groupId, relativePath)
req, _ := http.NewRequest("GET", requrl, nil)
req.Header.Add("Authorization", token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, errors.New(resp.Status)
}
return resp.Body, nil
}
func (s Graph) GetWebURLOfFile(relativePath string) (string, error) {
if len(s.groupId) == 0 {
return "", errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return "", err
}
requrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/groups/%s/drive/root:/%s:", s.groupId, relativePath)
req, _ := http.NewRequest("GET", requrl, nil)
req.Header.Add("Authorization", token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
itemdoc, _ := io.ReadAll(resp.Body)
var item map[string]interface{}
json.Unmarshal(itemdoc, &item)
url, ok := item["webUrl"]
if ok {
return url.(string), nil
}
return "", nil
}
func (s Graph) DownloadFile(relativePath string, outputFile string) error {
body, err := s.ReadFile(relativePath)
if err != nil {
return err
}
defer body.Close()
// Create the file
out, err := os.Create(outputFile)
if err != nil {
return err
}
defer out.Close()
// Write the body to file
_, err = io.Copy(out, body)
return err
}
func (s Graph) UploadStream(relativePath string, reader io.Reader) (string, error) {
if len(s.groupId) == 0 {
return "", errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return "", err
}
requrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/groups/%s/drive/root:/%s:/content", s.groupId, relativePath)
req, _ := http.NewRequest("PUT", requrl, reader)
req.Header.Add("Authorization", token)
req.Header.Add("Content-Type", "text/plain")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
body, _ := io.ReadAll(resp.Body)
unquot, _ := unescapeUnicodeCharactersInJSON(body)
var drive map[string]interface{}
err = json.Unmarshal([]byte(unquot), &drive)
if err != nil {
return "", err
}
if errnode, ok := drive["error"]; ok {
errmsg := errnode.(map[string]interface{})["message"].(string)
return "", errors.New(errmsg)
}
return drive["webUrl"].(string), nil
}
func (s Graph) UploadBytes(relativePath string, content []byte) (string, error) {
return s.UploadStream(relativePath, bytes.NewReader(content))
}
func (s Graph) GetChannelFilesFolderName(channel string) (string, error) {
if len(s.groupId) == 0 {
return "", errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return "", err
}
requrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/teams/%s/channels/%s/filesFolder", s.groupId, channel)
req, _ := http.NewRequest("GET", requrl, nil)
req.Header.Add("Authorization", token)
req.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
body, _ := io.ReadAll(resp.Body)
unquot, _ := unescapeUnicodeCharactersInJSON(body)
var drive map[string]interface{}
err = json.Unmarshal([]byte(unquot), &drive)
if err != nil {
return "", err
}
if errnode, ok := drive["error"]; ok {
errmsg := errnode.(map[string]interface{})["message"].(string)
return "", errors.New(errmsg)
}
return drive["name"].(string), nil
}
func (s Graph) listChildren(path string) (map[string]interface{}, error) {
if len(s.groupId) == 0 {
return nil, errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return nil, err
}
// groupId := "612620b7-cd90-4b3f-a9bd-d34cddf52517"
var requrl string
if len(path) == 0 {
requrl = fmt.Sprintf("https://graph.microsoft.com/v1.0/groups/%s/drive/root/children", s.groupId)
} else {
requrl = fmt.Sprintf("https://graph.microsoft.com/v1.0/groups/%s/drive/root:/%s:/children", s.groupId, path)
}
req, _ := http.NewRequest("GET", requrl, nil)
req.Header.Add("Authorization", token)
req.Header.Add("Accept", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
body, _ := io.ReadAll(resp.Body)
unquot, _ := unescapeUnicodeCharactersInJSON(body)
var drive map[string]interface{}
err = json.Unmarshal([]byte(unquot), &drive)
if err != nil {
return nil, err
}
if errnode, ok := drive["error"]; ok {
errmsg := errnode.(map[string]interface{})["message"].(string)
return nil, errors.New(errmsg)
}
return drive, nil
}
func (s Graph) ListFolders(parent string) ([]string, error) {
drive, err := s.listChildren(parent)
if err != nil {
return nil, err
}
var output []string
items := drive["value"].([]interface{})
for _, item := range items {
itemobj := item.(map[string]interface{})
if _, ok := itemobj["file"]; !ok {
output = append(output, itemobj["name"].(string))
}
}
return output, nil
}
type FileMeta struct {
Path string `json:"path"`
LastModified time.Time `json:"lastModified"`
}
func (s Graph) ListFiles(parent string) ([]FileMeta, error) {
drive, err := s.listChildren(parent)
if err != nil {
return nil, err
}
var output []FileMeta
items := drive["value"].([]interface{})
for _, item := range items {
itemobj := item.(map[string]interface{})
if _, ok := itemobj["file"]; ok {
modTime, _ := time.Parse(time.RFC3339, itemobj["lastModifiedDateTime"].(string))
output = append(output, FileMeta{
Path: path.Join(parent, itemobj["name"].(string)),
LastModified: modTime,
})
}
}
return output, nil
}
func (s Graph) GetChannels() (map[string]string, error) {
if len(s.groupId) == 0 {
return nil, errGroupIdMissing
}
token, err := s.getAuthoizationToken()
if err != nil {
return nil, err
}
requrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/teams/%s/channels", s.groupId)
req, _ := http.NewRequest("GET", requrl, nil)
req.Header.Add("Authorization", token)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var raw map[string]interface{}
if err = json.Unmarshal(body, &raw); err != nil {
return nil, err
}
if errnode, ok := raw["error"]; ok {
return nil, errors.New(errnode.(map[string]interface{})["message"].(string))
}
if r, ok := raw["value"]; ok {
valuenode := r.([]interface{})
output := make(map[string]string)
for _, v := range valuenode {
data := v.(map[string]interface{})
id := data["id"].(string)
displayname := data["displayName"].(string)
output[id] = displayname
}
return output, nil
}
return nil, errors.New("error")
}