From 247e8cfa14910b70720e2c4763ebb75eec083f31 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Fri, 29 May 2020 14:52:56 +0300 Subject: [PATCH 1/8] feat: list hubs feat: list projects by hub id --- .gitignore | 2 + dm/bucket.go | 2 +- dm/bucket_test.go | 2 +- dm/hubs.go | 249 +++++++++++++++++++++++++++++++++++++--------- dm/hubs_test.go | 37 +++++++ go.mod | 8 ++ go.sum | 4 + recap/recap.go | 2 +- 8 files changed, 257 insertions(+), 49 deletions(-) create mode 100644 dm/hubs_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 7338427..8b34e0a 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,5 @@ crashlytics.properties crashlytics-build.properties fabric.properties .idea/ + +*.cmd \ No newline at end of file diff --git a/dm/bucket.go b/dm/bucket.go index 5d5c627..be02103 100644 --- a/dm/bucket.go +++ b/dm/bucket.go @@ -8,7 +8,7 @@ import ( "net/http" "strconv" - "github.com/apprentice3d/forge-api-go-client/oauth" + "forge-api-go-client/oauth" ) // BucketAPI holds the necessary data for making Bucket related calls to Forge Data Management service diff --git a/dm/bucket_test.go b/dm/bucket_test.go index 6cb05eb..d4a9c6c 100644 --- a/dm/bucket_test.go +++ b/dm/bucket_test.go @@ -2,7 +2,7 @@ package dm_test import ( "fmt" - "github.com/apprentice3d/forge-api-go-client/dm" + "forge-api-go-client/dm" "log" "os" "testing" diff --git a/dm/hubs.go b/dm/hubs.go index 97b5cb7..87e8b08 100644 --- a/dm/hubs.go +++ b/dm/hubs.go @@ -1,47 +1,204 @@ package dm -// -//type Hubs struct { -// Data []Content `json:"data,omitempty"` -// JsonApi JsonAPI `json:"jsonapi,omitempty"` -// Links Link `json:"links,omitempty"` -//} -// -//type JsonAPI struct { -// Version string `json:"version,omitempty"` -//} -// -//type Link struct { -// Self struct { -// Href string `json:"href,omitempty"` -// } `json:"self,omitempty"` -//} -// -//type Content struct { -// Relationships struct { -// Projects Project `json:"projects,omitempty"` -// } `json:"relationships,omitempty"` -// Attributes Attribute `json:"attributes,omitempty"` -// Type string `json:"type,omitempty"` -// Id string `json:"id,omitempty"` -// Links Link `json:"links,omitempty"` -//} -// -//type Project struct { -// Links struct { -// Related struct { -// Href string `json:"href,omitempty"` -// } `json:"related,omitempty"` -// } `json:"links,omitempty"` -//} -// -//type Attribute struct { -// Name string `json:"name,omitempty"` -// Extension struct { -// Data map[string]interface{} `json:"data,omitempty"` -// Version string `json:"version,omitempty"` -// Type string `json:"type,omitempty"` -// Schema struct { -// Href string `json:"href,omitempty"` -// } `json:"schema,omitempty"` -// } `json:"extension,omitempty"` -//} + +import ( + "encoding/json" + "errors" + "fmt" + "forge-api-go-client/oauth" + "io/ioutil" + "net/http" + "strconv" +) + +type HubsAPI struct { + oauth.TwoLeggedAuth + HubsAPIPath string +} + +func NewHubsAPIWithCredentials(ClientID, ClientSecret string) HubsAPI { + return HubsAPI{ + TwoLeggedAuth: oauth.NewTwoLeggedClient(ClientID, ClientSecret), + HubsAPIPath: "/project/v1/hubs", + } +} + +type Hubs struct { + Data []Content `json:"data,omitempty"` + JsonApi JsonAPI `json:"jsonapi,omitempty"` + Links Link `json:"links,omitempty"` +} + +type JsonAPI struct { + Version string `json:"version,omitempty"` +} + +type Link struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` +} + +type Content struct { + Relationships struct { + Projects Project `json:"projects,omitempty"` + } `json:"relationships,omitempty"` + Attributes Attribute `json:"attributes,omitempty"` + Type string `json:"type,omitempty"` + Id string `json:"id,omitempty"` + Links Link `json:"links,omitempty"` +} + +type Project struct { + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` +} + +type Attribute struct { + Name string `json:"name,omitempty"` + Extension struct { + Data map[string]interface{} `json:"data,omitempty"` + Version string `json:"version,omitempty"` + Type string `json:"type,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + } `json:"extension,omitempty"` +} + +type Projects struct { + Jsonapi struct { + Version string `json:"version,omitempty"` + } `json:"jsonapi,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Data []struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Attributes struct { + Name string `json:"name,omitempty"` + Extension struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + Data struct { + } `json:"data,omitempty"` + } `json:"extension,omitempty"` + } `json:"attributes,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Relationships struct { + Hub struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"hub,omitempty"` + RootFolder struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"rootFolder,omitempty"` + } `json:"relationships,omitempty"` + } `json:"data,omitempty"` +} + + +func (api *HubsAPI) ListHubs() (result Hubs, err error){ + bearer, err := api.Authenticate("data:read") + if err != nil { + return Hubs{}, err + } + + path := api.Host + api.HubsAPIPath + result, err = listHubs(path, bearer.AccessToken) + return result, err +} + +func listHubs(path string, token string) (result Hubs, err error) { + task := http.Client{} + + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return Hubs{}, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + response, err := task.Do(req) + if err != nil { + return Hubs{}, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + content, _ := ioutil.ReadAll(response.Body) + err = errors.New("[" + strconv.Itoa(response.StatusCode) + "] " + string(content)) + return Hubs{}, err + } + + decoder := json.NewDecoder(response.Body) + err = decoder.Decode(&result) + return result, nil +} + +func (api *HubsAPI) GetHubProjects(hubId string) (result Projects, err error){ + bearer, err := api.Authenticate("data:read") + if err != nil { + return Projects{}, err + } + + path := fmt.Sprintf("%s/%s/%s/projects", api.Host, api.HubsAPIPath, hubId) + result, err = getHubProjects(path, bearer.AccessToken) + return result, err +} + +func getHubProjects(path string, token string) (result Projects, err error) { + task := http.Client{} + + req, err := http.NewRequest("GET", path, nil) + if err != nil { + return Projects{}, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + response, err := task.Do(req) + if err != nil { + return Projects{}, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + content, _ := ioutil.ReadAll(response.Body) + err = errors.New("[" + strconv.Itoa(response.StatusCode) + "] " + string(content)) + return Projects{}, err + } + + decoder := json.NewDecoder(response.Body) + err = decoder.Decode(&result) + return result, nil +} diff --git a/dm/hubs_test.go b/dm/hubs_test.go new file mode 100644 index 0000000..763c486 --- /dev/null +++ b/dm/hubs_test.go @@ -0,0 +1,37 @@ +package dm_test + +import ( + "fmt" + "forge-api-go-client/dm" + "os" + "testing" +) + +func TestHubsAPI_ListHubs(t *testing.T) { + clientID := os.Getenv("FORGE_CLIENT_ID") + clientSecret := os.Getenv("FORGE_CLIENT_SECRET") + + fmt.Printf("Using envs: %s\n%s\n", clientID, clientSecret) + + hubsAPI := dm.NewHubsAPIWithCredentials(clientID, clientSecret) + + t.Run("List Hubs", func(t *testing.T) { + hubs, err := hubsAPI.ListHubs() + if err!=nil{ + t.Fatalf("Failed to list hubs: %s\n", err.Error()) + } + + if len(hubs.Data) == 0 { + t.Fatalf("Failed to list hubs. No hubs retreived.") + } + + projects, err := hubsAPI.GetHubProjects(hubs.Data[0].Id) + if err!=nil{ + t.Fatalf("Failed to list hub '%s' projects: %s\n", hubs.Data[0].Id, err.Error()) + } + + if len(projects.Data) == 0 { + t.Fatalf("Failed to list hub projects. No projects retreived or failed to unmarshal.") + } + }) +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0cd53b5 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module forge-api-go-client + +go 1.14 + +require ( + github.com/BUCKU/forge-api-go-client v0.1.0 + github.com/apprentice3d/forge-api-go-client v0.1.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2371161 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/BUCKU/forge-api-go-client v0.1.0 h1:7iMtb0gzMgxPiIfog1L5O8DcVZJvXUmmC8/liSDytyg= +github.com/BUCKU/forge-api-go-client v0.1.0/go.mod h1:5/HRneXXt+Y/sSqrZu/414f6y+O9Tr8Arnfz2D5gP9w= +github.com/apprentice3d/forge-api-go-client v0.1.0 h1:BI6U0GUIbp53r8WMgQ3qt5R6cbUvUfTCjWKFf3KdcvI= +github.com/apprentice3d/forge-api-go-client v0.1.0/go.mod h1:zco+aF8YQfGGXy2anwF73XS9u0THEFgW57i4I3bF9ic= diff --git a/recap/recap.go b/recap/recap.go index 927623d..cdf22fd 100644 --- a/recap/recap.go +++ b/recap/recap.go @@ -9,7 +9,7 @@ package recap import ( - "github.com/apprentice3d/forge-api-go-client/oauth" + "github.com/BUCKU/forge-api-go-client/oauth" ) // API struct holds all paths necessary to access ReCap API From 07e62bf90482782e8b9524075c9f815ac2fc2c91 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Fri, 29 May 2020 15:15:33 +0300 Subject: [PATCH 2/8] removed unused imports --- go.mod | 1 - 1 file changed, 1 deletion(-) diff --git a/go.mod b/go.mod index 0cd53b5..2631e43 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,4 @@ go 1.14 require ( github.com/BUCKU/forge-api-go-client v0.1.0 - github.com/apprentice3d/forge-api-go-client v0.1.0 ) From 0d536260ebe38e7dc38d195156028079da9a5736 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Wed, 3 Jun 2020 09:53:48 +0300 Subject: [PATCH 3/8] added GET Hubs support, GET Projects support, GET Folders and GET Item --- .gitignore | 2 + dm/hubs.go | 2 +- dm/hubs_test.go | 2 +- dm/object_test.go | 3 +- dm/projects.go | 440 ++++++++++++++++++++++++++++++++++++ dm/projects_test.go | 50 ++++ oauth/informational_test.go | 2 +- oauth/three_legged_test.go | 2 +- oauth/two_legged.go | 31 ++- oauth/two_legged_test.go | 2 +- 10 files changed, 528 insertions(+), 8 deletions(-) create mode 100644 dm/projects.go create mode 100644 dm/projects_test.go diff --git a/.gitignore b/.gitignore index 8b34e0a..835bb66 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff: +.idea +.idea/ .idea/**/workspace.xml .idea/**/tasks.xml .idea/dictionaries diff --git a/dm/hubs.go b/dm/hubs.go index 87e8b08..3b9a3e5 100644 --- a/dm/hubs.go +++ b/dm/hubs.go @@ -125,7 +125,7 @@ type Projects struct { } -func (api *HubsAPI) ListHubs() (result Hubs, err error){ +func (api *HubsAPI) GetHubs() (result Hubs, err error){ bearer, err := api.Authenticate("data:read") if err != nil { return Hubs{}, err diff --git a/dm/hubs_test.go b/dm/hubs_test.go index 763c486..3baeee1 100644 --- a/dm/hubs_test.go +++ b/dm/hubs_test.go @@ -16,7 +16,7 @@ func TestHubsAPI_ListHubs(t *testing.T) { hubsAPI := dm.NewHubsAPIWithCredentials(clientID, clientSecret) t.Run("List Hubs", func(t *testing.T) { - hubs, err := hubsAPI.ListHubs() + hubs, err := hubsAPI.GetHubs() if err!=nil{ t.Fatalf("Failed to list hubs: %s\n", err.Error()) } diff --git a/dm/object_test.go b/dm/object_test.go index d048196..6af8e2d 100644 --- a/dm/object_test.go +++ b/dm/object_test.go @@ -4,8 +4,7 @@ import ( "io/ioutil" "os" "testing" - - "github.com/apprentice3d/forge-api-go-client/dm" + "forge-api-go-client/dm" ) func TestBucketAPI_ListObjects(t *testing.T) { diff --git a/dm/projects.go b/dm/projects.go new file mode 100644 index 0000000..19adc97 --- /dev/null +++ b/dm/projects.go @@ -0,0 +1,440 @@ +package dm + +import ( + "encoding/json" + "errors" + "fmt" + "forge-api-go-client/oauth" + "log" + "net/http" + "strconv" + "time" +) + +type FolderContents struct { + Jsonapi struct { + Version string `json:"version,omitempty"` + } `json:"jsonapi,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Data []struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Attributes struct { + DisplayName string `json:"displayName,omitempty"` + CreateTime time.Time `json:"createTime,omitempty"` + CreateUserID string `json:"createUserId,omitempty"` + CreateUserName string `json:"createUserName,omitempty"` + LastModifiedTime time.Time `json:"lastModifiedTime,omitempty"` + LastModifiedUserID string `json:"lastModifiedUserId,omitempty"` + LastModifiedUserName string `json:"lastModifiedUserName,omitempty"` + Extension struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + Data struct { + } `json:"data,omitempty"` + } `json:"extension,omitempty"` + } `json:"attributes,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Relationships struct { + Tip struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"tip,omitempty"` + Versions struct { + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"versions,omitempty"` + Parent struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"parent,omitempty"` + Refs struct { + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"refs,omitempty"` + } `json:"relationships,omitempty"` + } `json:"data,omitempty"` + Included []struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Attributes struct { + Name string `json:"name,omitempty"` + DisplayName string `json:"displayName,omitempty"` + CreateTime time.Time `json:"createTime,omitempty"` + CreateUserID string `json:"createUserId,omitempty"` + CreateUserName string `json:"createUserName,omitempty"` + LastModifiedTime time.Time `json:"lastModifiedTime,omitempty"` + LastModifiedUserID string `json:"lastModifiedUserId,omitempty"` + LastModifiedUserName string `json:"lastModifiedUserName,omitempty"` + VersionNumber int `json:"versionNumber,omitempty"` + MimeType string `json:"mimeType,omitempty"` + FileType string `json:"fileType,omitempty"` + StorageSize int `json:"storageSize,omitempty"` + Extension struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + Data struct { + } `json:"data,omitempty"` + } `json:"extension,omitempty"` + } `json:"attributes,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Relationships struct { + Item struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"item,omitempty"` + Refs struct { + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"refs,omitempty"` + Derivatives struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"derivatives,omitempty"` + Thumbnails struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"thumbnails,omitempty"` + Storage struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"storage,omitempty"` + } `json:"relationships,omitempty"` + } `json:"included,omitempty"` +} + +type ItemData struct { + Jsonapi struct { + Version string `json:"version,omitempty"` + } `json:"jsonapi,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Attributes struct { + DisplayName string `json:"displayName,omitempty"` + CreateTime time.Time `json:"createTime,omitempty"` + CreateUserID string `json:"createUserId,omitempty"` + CreateUserName string `json:"createUserName,omitempty"` + LastModifiedTime time.Time `json:"lastModifiedTime,omitempty"` + LastModifiedUserID string `json:"lastModifiedUserId,omitempty"` + LastModifiedUserName string `json:"lastModifiedUserName,omitempty"` + Extension struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + Data struct { + } `json:"data,omitempty"` + } `json:"extension,omitempty"` + } `json:"attributes,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Relationships struct { + Tip struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"tip,omitempty"` + Versions struct { + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"versions,omitempty"` + Parent struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"parent,omitempty"` + Refs struct { + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"refs,omitempty"` + } `json:"relationships,omitempty"` + } `json:"data,omitempty"` + Included []struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + Attributes struct { + Name string `json:"name,omitempty"` + DisplayName string `json:"displayName,omitempty"` + CreateTime time.Time `json:"createTime,omitempty"` + CreateUserID string `json:"createUserId,omitempty"` + CreateUserName string `json:"createUserName,omitempty"` + LastModifiedTime time.Time `json:"lastModifiedTime,omitempty"` + LastModifiedUserID string `json:"lastModifiedUserId,omitempty"` + LastModifiedUserName string `json:"lastModifiedUserName,omitempty"` + VersionNumber int `json:"versionNumber,omitempty"` + MimeType string `json:"mimeType,omitempty"` + FileType string `json:"fileType,omitempty"` + StorageSize int `json:"storageSize,omitempty"` + Extension struct { + Type string `json:"type,omitempty"` + Version string `json:"version,omitempty"` + Schema struct { + Href string `json:"href,omitempty"` + } `json:"schema,omitempty"` + Data struct { + } `json:"data,omitempty"` + } `json:"extension,omitempty"` + } `json:"attributes,omitempty"` + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + } `json:"links,omitempty"` + Relationships struct { + Item struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Links struct { + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"item,omitempty"` + Refs struct { + Links struct { + Self struct { + Href string `json:"href,omitempty"` + } `json:"self,omitempty"` + Related struct { + Href string `json:"href,omitempty"` + } `json:"related,omitempty"` + } `json:"links,omitempty"` + } `json:"refs,omitempty"` + Derivatives struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"derivatives,omitempty"` + Thumbnails struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"thumbnails,omitempty"` + Storage struct { + Data struct { + Type string `json:"type,omitempty"` + ID string `json:"id,omitempty"` + } `json:"data,omitempty"` + Meta struct { + Link struct { + Href string `json:"href,omitempty"` + } `json:"link,omitempty"` + } `json:"meta,omitempty"` + } `json:"storage,omitempty"` + } `json:"relationships,omitempty"` + } `json:"included,omitempty"` +} + +type ProjectsAPI struct { + oauth.TwoLeggedAuth + ProjectsAPIPath string +} + +func NewProjectsAPIWithCredentials(ClientID, ClientSecret, ProjectId string) ProjectsAPI { + return ProjectsAPI{ + TwoLeggedAuth: oauth.NewTwoLeggedClient(ClientID, ClientSecret), + ProjectsAPIPath: fmt.Sprintf("/data/v1/projects/%s/", ProjectId), // projects/:project_id/folders/:folder_id/ + } +} + +func (api *ProjectsAPI) GetFolderContents(FolderId string) (result FolderContents, err error){ + //bearer, err := api.Authenticate("data:read") + bearer, err := api.AuthenticateIfNecessary("data:read") + if err != nil { + return FolderContents{}, err + } + + path := fmt.Sprintf("%s%sfolders/%s", api.Host, api.ProjectsAPIPath, FolderId) + result, err = getFolderContents(path, bearer.AccessToken) + return result, err +} + +func getFolderContents(path string, token string) (result FolderContents, err error) { + task := http.Client{} + + req, err := http.NewRequest("GET", path+"/contents", nil) + + if err != nil { + return FolderContents{}, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + response, err := task.Do(req) + if err != nil { + return FolderContents{}, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + err = errors.New(strconv.Itoa(response.StatusCode)) + return FolderContents{}, err + } + + decoder := json.NewDecoder(response.Body) + err = decoder.Decode(&result) + return result, nil +} + +func (api *ProjectsAPI) GetItemData(itemId string) (result ItemData, err error){ + //bearer, err := api.Authenticate("data:read") + bearer, err := api.AuthenticateIfNecessary("data:read") + if err != nil { + return ItemData{}, err + } + + path := fmt.Sprintf("%s%sitems/%s", api.Host, api.ProjectsAPIPath, itemId) + result, err = getItemData(path, bearer.AccessToken) + return result, err +} + +func getItemData(path string, token string) (result ItemData, err error) { + task := http.Client{} + + req, err := http.NewRequest("GET", path, nil) + + if err != nil { + return ItemData{}, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + response, err := task.Do(req) + if err != nil { + log.Printf("Error at request: %v", err.Error()) + return ItemData{}, err + } + + if response.StatusCode != http.StatusOK { + err = errors.New(strconv.Itoa(response.StatusCode)) + log.Printf("Error at request: %v", err.Error()) + return ItemData{}, err + } + + defer response.Body.Close() + decoder := json.NewDecoder(response.Body) + err = decoder.Decode(&result) + return result, nil +} \ No newline at end of file diff --git a/dm/projects_test.go b/dm/projects_test.go new file mode 100644 index 0000000..0409e91 --- /dev/null +++ b/dm/projects_test.go @@ -0,0 +1,50 @@ +package dm + +import ( + "fmt" + "os" + "testing" +) + +func TestProjectsAPI_GetFolderContents(t *testing.T) { + clientID := os.Getenv("FORGE_CLIENT_ID") + clientSecret := os.Getenv("FORGE_CLIENT_SECRET") + + fmt.Printf("Using envs: %s\n%s\n", clientID, clientSecret) + + t.Run("List Hubs", func(t *testing.T) { + hubsAPI := NewHubsAPIWithCredentials(clientID, clientSecret) + hubs, err := hubsAPI.GetHubs() + if err!=nil{ + t.Fatalf("Failed to list hubs: %s\n", err.Error()) + } + + if len(hubs.Data) == 0 { + t.Fatalf("Failed to list hubs. No hubs retreived.") + } + + projects, err := hubsAPI.GetHubProjects(hubs.Data[0].Id) + if err!=nil{ + t.Fatalf("Failed to list hub '%s' projects: %s\n", hubs.Data[0].Id, err.Error()) + } + + if len(projects.Data) == 0 { + t.Fatalf("Failed to list hub projects. No projects retreived or failed to unmarshal.") + } + + projectsAPI := NewProjectsAPIWithCredentials(clientID, clientSecret, projects.Data[0].ID) + + _,err = projectsAPI.GetFolderContents(projects.Data[0].Relationships.RootFolder.Data.ID) + if err != nil { + t.Fatalf("Failed to get folders: %v", err.Error()) + } + _,err = projectsAPI.GetFolderContents(projects.Data[0].Relationships.RootFolder.Data.ID) + if err != nil { + t.Fatalf("Failed to get folders: %v", err.Error()) + } + _,err = projectsAPI.GetFolderContents(projects.Data[0].Relationships.RootFolder.Data.ID) + if err != nil { + t.Fatalf("Failed to get folders: %v", err.Error()) + } + }) +} diff --git a/oauth/informational_test.go b/oauth/informational_test.go index deef933..00eb283 100644 --- a/oauth/informational_test.go +++ b/oauth/informational_test.go @@ -2,7 +2,7 @@ package oauth_test import ( "fmt" - "github.com/apprentice3d/forge-api-go-client/oauth" + "forge-api-go-client/oauth" ) //TODO: enable it after having set up a pipeline for auto-creating a 3-legged oauth token diff --git a/oauth/three_legged_test.go b/oauth/three_legged_test.go index 2c5084d..971b8d6 100644 --- a/oauth/three_legged_test.go +++ b/oauth/three_legged_test.go @@ -1,7 +1,7 @@ package oauth_test import ( - "github.com/apprentice3d/forge-api-go-client/oauth" + "forge-api-go-client/oauth" "os" "testing" ) diff --git a/oauth/two_legged.go b/oauth/two_legged.go index 07755a3..fcb0c5d 100644 --- a/oauth/two_legged.go +++ b/oauth/two_legged.go @@ -5,14 +5,18 @@ import ( "encoding/json" "errors" "io/ioutil" + "log" "net/http" "net/url" "strconv" + "time" ) // TwoLeggedAuth struct holds data necessary for making requests in 2-legged context type TwoLeggedAuth struct { AuthData + token Bearer + expires time.Time } // TwoLeggedAuthenticator interface defines the method necessary to qualify as 2-legged authenticator @@ -29,11 +33,30 @@ func NewTwoLeggedClient(clientID, clientSecret string) TwoLeggedAuth { "https://developer.api.autodesk.com", "/authentication/v1", }, + Bearer{}, + time.Time{}, } } +func (a *TwoLeggedAuth) AuthenticateIfNecessary(scope string) (bearer Bearer, err error){ + if a.token.AccessToken != "" { + now := time.Now() + + if now.After(a.expires){ + log.Printf("Authenticate cause expired") + return a.Authenticate(scope) + } else { + //log.Printf("Return exist auth token") + return a.token, nil + } + } else { + log.Printf("Authenticate cause no token") + return a.Authenticate(scope) + } + +} // Authenticate allows getting a token with a given scope -func (a TwoLeggedAuth) Authenticate(scope string) (bearer Bearer, err error) { +func (a *TwoLeggedAuth) Authenticate(scope string) (bearer Bearer, err error) { task := http.Client{} @@ -68,5 +91,11 @@ func (a TwoLeggedAuth) Authenticate(scope string) (bearer Bearer, err error) { decoder := json.NewDecoder(response.Body) err = decoder.Decode(&bearer) + if err == nil { + a.token = bearer + exp := time.Duration(a.token.ExpiresIn)*time.Second + a.expires = time.Now().Add(exp) + } + return } diff --git a/oauth/two_legged_test.go b/oauth/two_legged_test.go index 3e8bf56..131f42a 100644 --- a/oauth/two_legged_test.go +++ b/oauth/two_legged_test.go @@ -2,7 +2,7 @@ package oauth_test import ( "fmt" - "github.com/apprentice3d/forge-api-go-client/oauth" + "forge-api-go-client/oauth" "log" "os" "testing" From fa207cac45034a64f4d09760cc9ea2e1b291e681 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Wed, 3 Jun 2020 10:03:15 +0300 Subject: [PATCH 4/8] Setup go modules properly --- go.mod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2631e43..0edc028 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,4 @@ module forge-api-go-client go 1.14 -require ( - github.com/BUCKU/forge-api-go-client v0.1.0 -) +replace forge-api-go-client => ./ From a8039fae04e41a2a8b5e0556534bc3ae359e420e Mon Sep 17 00:00:00 2001 From: BUCKU Date: Wed, 3 Jun 2020 12:54:03 +0300 Subject: [PATCH 5/8] Changed return values to pointers --- dm/projects.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/dm/projects.go b/dm/projects.go index 19adc97..0810137 100644 --- a/dm/projects.go +++ b/dm/projects.go @@ -357,11 +357,11 @@ func NewProjectsAPIWithCredentials(ClientID, ClientSecret, ProjectId string) Pro } } -func (api *ProjectsAPI) GetFolderContents(FolderId string) (result FolderContents, err error){ +func (api *ProjectsAPI) GetFolderContents(FolderId string) (result *FolderContents, err error){ //bearer, err := api.Authenticate("data:read") bearer, err := api.AuthenticateIfNecessary("data:read") if err != nil { - return FolderContents{}, err + return nil, err } path := fmt.Sprintf("%s%sfolders/%s", api.Host, api.ProjectsAPIPath, FolderId) @@ -369,13 +369,13 @@ func (api *ProjectsAPI) GetFolderContents(FolderId string) (result FolderContent return result, err } -func getFolderContents(path string, token string) (result FolderContents, err error) { +func getFolderContents(path string, token string) (result *FolderContents, err error) { task := http.Client{} req, err := http.NewRequest("GET", path+"/contents", nil) if err != nil { - return FolderContents{}, err + return nil, err } req.Header.Set("Content-Type", "application/json") @@ -383,13 +383,13 @@ func getFolderContents(path string, token string) (result FolderContents, err er response, err := task.Do(req) if err != nil { - return FolderContents{}, err + return nil, err } defer response.Body.Close() if response.StatusCode != http.StatusOK { err = errors.New(strconv.Itoa(response.StatusCode)) - return FolderContents{}, err + return nil, err } decoder := json.NewDecoder(response.Body) @@ -397,11 +397,11 @@ func getFolderContents(path string, token string) (result FolderContents, err er return result, nil } -func (api *ProjectsAPI) GetItemData(itemId string) (result ItemData, err error){ +func (api *ProjectsAPI) GetItemData(itemId string) (result *ItemData, err error){ //bearer, err := api.Authenticate("data:read") bearer, err := api.AuthenticateIfNecessary("data:read") if err != nil { - return ItemData{}, err + return nil, err } path := fmt.Sprintf("%s%sitems/%s", api.Host, api.ProjectsAPIPath, itemId) @@ -409,13 +409,13 @@ func (api *ProjectsAPI) GetItemData(itemId string) (result ItemData, err error){ return result, err } -func getItemData(path string, token string) (result ItemData, err error) { +func getItemData(path string, token string) (result *ItemData, err error) { task := http.Client{} req, err := http.NewRequest("GET", path, nil) if err != nil { - return ItemData{}, err + return nil, err } req.Header.Set("Content-Type", "application/json") @@ -424,13 +424,13 @@ func getItemData(path string, token string) (result ItemData, err error) { response, err := task.Do(req) if err != nil { log.Printf("Error at request: %v", err.Error()) - return ItemData{}, err + return nil, err } if response.StatusCode != http.StatusOK { err = errors.New(strconv.Itoa(response.StatusCode)) log.Printf("Error at request: %v", err.Error()) - return ItemData{}, err + return nil, err } defer response.Body.Close() From 033999453cc4c2880590e5d5cb24a0c408f0e7f4 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Sun, 21 Jun 2020 17:31:57 +0300 Subject: [PATCH 6/8] StorageSize type from int to uint64 --- dm/projects.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dm/projects.go b/dm/projects.go index 0810137..5008c94 100644 --- a/dm/projects.go +++ b/dm/projects.go @@ -103,7 +103,7 @@ type FolderContents struct { VersionNumber int `json:"versionNumber,omitempty"` MimeType string `json:"mimeType,omitempty"` FileType string `json:"fileType,omitempty"` - StorageSize int `json:"storageSize,omitempty"` + StorageSize uint64 `json:"storageSize,omitempty"` Extension struct { Type string `json:"type,omitempty"` Version string `json:"version,omitempty"` From fbf7d9736bee75d4c3675a5e0adefbb64461fc33 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Sun, 21 Jun 2020 17:34:27 +0300 Subject: [PATCH 7/8] project api attributes section StorageSize int -> uint64 --- dm/projects.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dm/projects.go b/dm/projects.go index 5008c94..372a7f5 100644 --- a/dm/projects.go +++ b/dm/projects.go @@ -270,7 +270,7 @@ type ItemData struct { VersionNumber int `json:"versionNumber,omitempty"` MimeType string `json:"mimeType,omitempty"` FileType string `json:"fileType,omitempty"` - StorageSize int `json:"storageSize,omitempty"` + StorageSize uint64 `json:"storageSize,omitempty"` Extension struct { Type string `json:"type,omitempty"` Version string `json:"version,omitempty"` From 9d05c780aaeba61fdb149f1a0087e5ad1ce489d3 Mon Sep 17 00:00:00 2001 From: BUCKU Date: Mon, 22 Jun 2020 15:37:58 +0300 Subject: [PATCH 8/8] get item reader --- dm/projects.go | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/dm/projects.go b/dm/projects.go index 372a7f5..462dd8c 100644 --- a/dm/projects.go +++ b/dm/projects.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "forge-api-go-client/oauth" + "io" "log" "net/http" "strconv" @@ -437,4 +438,41 @@ func getItemData(path string, token string) (result *ItemData, err error) { decoder := json.NewDecoder(response.Body) err = decoder.Decode(&result) return result, nil +} + +func (api *ProjectsAPI) GetItemReader(itemStorageLink string) (result *io.ReadCloser, err error) { + bearer, err := api.AuthenticateIfNecessary("data:read") + if err != nil { + return nil, err + } + + result, err = getItemReader(itemStorageLink, bearer.AccessToken) + return result, err +} + +func getItemReader(link string, token string) (*io.ReadCloser, error) { + task := http.Client{} + + req, err := http.NewRequest("GET", link, nil) + + if err != nil { + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+token) + + response, err := task.Do(req) + if err != nil { + log.Printf("Error at request: %v", err.Error()) + return nil, err + } + + if response.StatusCode != http.StatusOK { + err = errors.New(strconv.Itoa(response.StatusCode)) + log.Printf("Error at request: %v", err.Error()) + return nil, err + } + + return &response.Body, nil } \ No newline at end of file