From 4dfeaf531ea192c7089769d800e4fd1e52c59548 Mon Sep 17 00:00:00 2001 From: Maksym Novozhylov Date: Tue, 6 Nov 2018 13:22:47 +0100 Subject: [PATCH] Initial commit --- .docgen | 2 + .gitignore | 1 + .travis.yml | 16 + CHANGES.md | 24 ++ LICENSE | 176 +++++++++++ README.md | 72 +++++ api/client.go | 294 ++++++++++++++++++ api/client_test.go | 30 ++ api/config.go | 129 ++++++++ api/config_test.go | 53 ++++ .../activities/engagement/engagement.go | 50 +++ api/routers/activities/team/team.go | 88 ++++++ api/routers/auth/auth.go | 40 +++ api/routers/freelancers/profile/profile.go | 45 +++ api/routers/freelancers/search/search.go | 40 +++ .../hr/clients/applications/applications.go | 45 +++ api/routers/hr/clients/offers/offers.go | 50 +++ api/routers/hr/contracts/contracts.go | 50 +++ api/routers/hr/engagements/engagements.go | 45 +++ .../freelancers/applications/applications.go | 45 +++ api/routers/hr/freelancers/offers/offers.go | 50 +++ api/routers/hr/interviews/interviews.go | 40 +++ api/routers/hr/jobs/jobs.go | 60 ++++ api/routers/hr/milestones/milestones.go | 70 +++++ api/routers/hr/roles/roles.go | 45 +++ api/routers/hr/submissions/submissions.go | 50 +++ api/routers/jobs/profile/profile.go | 40 +++ api/routers/jobs/search/search.go | 40 +++ api/routers/messages/messages.go | 80 +++++ api/routers/metadata/metadata.go | 60 ++++ .../organization/companies/companies.go | 55 ++++ api/routers/organization/teams/teams.go | 49 +++ api/routers/organization/users/users.go | 45 +++ api/routers/payments/payments.go | 40 +++ .../reports/finance/accounts/accounts.go | 45 +++ .../reports/finance/billings/billings.go | 60 ++++ .../reports/finance/earnings/earnings.go | 60 ++++ api/routers/reports/time/time.go | 80 +++++ api/routers/snapshot/snapshot.go | 50 +++ api/routers/workdays/workdays.go | 45 +++ api/routers/workdiary/workdiary.go | 40 +++ example/config.json | 9 + example/example.go | 118 +++++++ 43 files changed, 2526 insertions(+) create mode 100644 .docgen create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 CHANGES.md create mode 100644 LICENSE create mode 100644 README.md create mode 100644 api/client.go create mode 100644 api/client_test.go create mode 100644 api/config.go create mode 100644 api/config_test.go create mode 100644 api/routers/activities/engagement/engagement.go create mode 100644 api/routers/activities/team/team.go create mode 100644 api/routers/auth/auth.go create mode 100644 api/routers/freelancers/profile/profile.go create mode 100644 api/routers/freelancers/search/search.go create mode 100644 api/routers/hr/clients/applications/applications.go create mode 100644 api/routers/hr/clients/offers/offers.go create mode 100644 api/routers/hr/contracts/contracts.go create mode 100644 api/routers/hr/engagements/engagements.go create mode 100644 api/routers/hr/freelancers/applications/applications.go create mode 100644 api/routers/hr/freelancers/offers/offers.go create mode 100644 api/routers/hr/interviews/interviews.go create mode 100644 api/routers/hr/jobs/jobs.go create mode 100644 api/routers/hr/milestones/milestones.go create mode 100644 api/routers/hr/roles/roles.go create mode 100644 api/routers/hr/submissions/submissions.go create mode 100644 api/routers/jobs/profile/profile.go create mode 100644 api/routers/jobs/search/search.go create mode 100644 api/routers/messages/messages.go create mode 100644 api/routers/metadata/metadata.go create mode 100644 api/routers/organization/companies/companies.go create mode 100644 api/routers/organization/teams/teams.go create mode 100644 api/routers/organization/users/users.go create mode 100644 api/routers/payments/payments.go create mode 100644 api/routers/reports/finance/accounts/accounts.go create mode 100644 api/routers/reports/finance/billings/billings.go create mode 100644 api/routers/reports/finance/earnings/earnings.go create mode 100644 api/routers/reports/time/time.go create mode 100644 api/routers/snapshot/snapshot.go create mode 100644 api/routers/workdays/workdays.go create mode 100644 api/routers/workdiary/workdiary.go create mode 100644 example/config.json create mode 100644 example/example.go diff --git a/.docgen b/.docgen new file mode 100644 index 0000000..dbba8e1 --- /dev/null +++ b/.docgen @@ -0,0 +1,2 @@ +#trick +godoc -http=:6060 & wget -e robots=off -p -k -r --no-parent -P doc -m "http://localhost:6060/pkg/github.com/upwork/golang-upwork-oauth2/" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b25c15b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2c7df67 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: go +go: + - 1.7 + - 1.8 + - 1.9 + - tip +env: + global: + - IGNORE_PATHS: example +script: + - go test -v ./... +notifications: + email: + recipients: + - apisupport@upwork.com + on_failure: change diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..1b3ea0a --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,24 @@ +## 2.0.0 +* OAuth2 release + +## 1.2.1 +* Applications API has moved from v3 to v4 + +## 1.2.0 +* Added Messages API (new) +* Message API (V1) is now fully depricated + +## 1.1.1 +* Add optional parameter to support pagination in GetTrayByType + +## 1.1.0 +* Get Categories (V1) is now fully depricated +* Added new Activities API - Assign to specific engagement the list of activities + +## 1.0.1 +* added a possibility of usage a custom http client +* added basic tests +* bug fixes + +## 1.0.0 +* welcome to Upwork! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..3e37a9f --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +GO bindings for Upwork API (OAuth2) +============ + +[![License](http://img.shields.io/packagist/l/upwork/php-upwork.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) +[![GitHub release](https://img.shields.io/github/release/upwork/golang-upwork-oauth2.svg)](https://github.com/upwork/golang-upwork-oauth2/releases) +[![Build status](https://travis-ci.org/upwork/golang-upwork-oauth2.svg)](http://travis-ci.org/upwork/golang-upwork-oauth2) + +# Introduction +This project provides a set of resources of Upwork API from http://developers.upwork.com + based on OAuth 2.0. + +# Features +These are the supported API resources: + +* My Info +* Custom Payments +* Hiring +* Job and Freelancer Profile +* Search Jobs and Freelancers +* Organization +* Messages +* Time and Financial Reporting +* Metadata +* Snapshot +* Team +* Workd Diary +* Activities + +# License + +Copyright 2018 Upwork Corporation. All Rights Reserved. + +perl-upwork is licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +## SLA +The usage of this API is ruled by the Terms of Use at: + + https://developers.upwork.com/api-tos.html + +# Application Integration +To integrate this library you need to have: + +* GO >= 1.7 + +## Example +In addition to this, a full example is available in the `example` directory. +This includes `example.go` that gets an access/refresh token pair and requests +the data for the application. + +## Installation +1. +Get `go get -u github.com/upwork/golang-upwork-oauth2/api` + +2. +Open `example.go` and type the clientId (a.k.a. consumer key), clientSecret (a.k.a. client secret) +and `redirectUri` that you previously got from the API Center, or use `config.json` file +to configure your application. + +3. +Compile using GO compilator. (for more details visit http://golang.org) + +***That's all. Run your app as `example` and have fun.*** diff --git a/api/client.go b/api/client.go new file mode 100644 index 0000000..a1fe35a --- /dev/null +++ b/api/client.go @@ -0,0 +1,294 @@ +// Package implements access to Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2018(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package api + +import ( + "log" + "strings" + "fmt" + "bytes" + "context" + "net/http" + "net/url" + "io/ioutil" + + "golang.org/x/oauth2" +) + +// Define end points +const ( + // oauth2 and api flow + BaseHost = "https://www.upwork.com/" + DefaultEpoint = "api" + AuthorizationEP = BaseHost + "ab/account-security/oauth2/authorize" + AccessTokenEP = BaseHost + DefaultEpoint + "/v3/oauth2/token" + DataFormat = "json" + OverloadParam = "http_method" + + // response types + ByteResponse = "[]byte" + ErrorResponse = "error" +) + +// Api client +type ApiClient struct { + // oauth2 + oconf *oauth2.Config + token *oauth2.Token + oclient *http.Client + + // refresh token notify function + rnfunc TokenNotifyFunc + + // client + ep string + respType string + sendPostAsJson bool +} + +// TokenNotifyFunc is a function that accepts an oauth2 Token upon refresh, and +// returns an error if it should not be used. +type TokenNotifyFunc func(*oauth2.Token) error + +// NotifyingTokenSource is an oauth2.TokenSource that calls a function when a +// new token is obtained. +type NotifyingTokenSource struct { + f TokenNotifyFunc + src oauth2.TokenSource +} + +// Setup client using specific config +func Setup(config *Config) (client ApiClient) { + var c ApiClient + + c.oconf = &oauth2.Config{ + ClientID: config.ClientId, + ClientSecret: config.ClientSecret, + RedirectURL: config.RedirectUri, + Endpoint: oauth2.Endpoint{ + TokenURL: AccessTokenEP, + AuthURL: AuthorizationEP, + }, + } + + // Force setup of client_id as a parameter + oauth2.RegisterBrokenAuthHeaderProvider(BaseHost) + + c.token = new(oauth2.Token) + c.token.TokenType = "Bearer" + c.token.AccessToken = config.AccessToken + c.token.RefreshToken = config.RefreshToken + c.token.Expiry = config.ExpiresAt + + c.SetApiResponseType(ByteResponse) + c.SetPostAsJson(false) // send by default using PostForm + + return c +} + +// NewNotifyingTokenSource creates a NotifyingTokenSource from an underlying src +// and calls f when a new token is obtained. +func NewNotifyingTokenSource(src oauth2.TokenSource, f TokenNotifyFunc) *NotifyingTokenSource { + return &NotifyingTokenSource{f: f, src: src} +} + +// Token fetches a new token from the underlying source. +func (s *NotifyingTokenSource) Token() (*oauth2.Token, error) { + t, err := s.src.Token() + if err != nil { + return nil, err + } + if s.f == nil { + return t, nil + } + return t, s.f(t) +} + +// Set refresh token notify function +func (c *ApiClient) SetRefreshTokenNotifyFunc(f TokenNotifyFunc) () { + c.rnfunc = f +} + +// Set request type for non-Get requests +func (c *ApiClient) SetPostAsJson(t bool) { + c.sendPostAsJson = t; +} + +// Set API response type +func (c *ApiClient) SetApiResponseType(t string) { + c.respType = t; +} + +// Set entry point, e.g requested from a router +func (c *ApiClient) SetEntryPoint(ep string) { + c.ep = ep +} + +// Receive an authorization URL +func (c *ApiClient) GetAuthorizationUrl(stateString string) (authzUrl string) { + url := c.oconf.AuthCodeURL(stateString) + if url == "" { + log.Fatal("Can not get authorization URL using OAuth2 library") + } + + return url +} + +// Get access token using a specific authorization code +func (c *ApiClient) GetToken(ctx context.Context, authzCode string) (*oauth2.Token) { + accessToken, err := c.oconf.Exchange(ctx, strings.Trim(authzCode, "\n")) + if err != nil { + log.Fatal(err) + } + + c.token = accessToken + c.setupOauth2Client(ctx) + + return accessToken +} + +// Check if client contains already a access/refresh token pair +func (c *ApiClient) HasAccessToken(ctx context.Context) (bool) { + has := (c.token != nil && (c.token.AccessToken != "" && c.token.RefreshToken != "")) + if has { + c.setupOauth2Client(ctx) + } + return has +} + +// GET method for client +func (c *ApiClient) Get(uri string, params map[string]string) (r *http.Response, re interface{}) { + // parameters must be encoded according to RFC 3986 + // hmmm, it seems to be the easiest trick? + qstr := "" + if params != nil { + for k, v := range params { + qstr += fmt.Sprintf("%s=%s&", k, v) + } + qstr = qstr[0:len(qstr)-1] + } + u := &url.URL{Path: qstr} + // https://github.com/mrjones/oauth/issues/34 + encQuery := strings.Replace(u.String(), ";", "%3B", -1) + + response, err := c.oclient.Get(formatUri(uri, c.ep) + "?" + encQuery) + + return c.getTypedResponse(response, err) +} + +// POST method for client +func (c *ApiClient) Post(uri string, params map[string]string) (r *http.Response, re interface{}) { + return c.sendPostRequest(uri, params) +} + +// PUT method for client +func (c *ApiClient) Put(uri string, params map[string]string) (r *http.Response, re interface{}) { + return c.sendPostRequest(uri, addOverloadParam(params, "put")) +} + +// DELETE method for client +func (c *ApiClient) Delete(uri string, params map[string]string) (r *http.Response, re interface{}) { + return c.sendPostRequest(uri, addOverloadParam(params, "delete")) +} + +// setup/save authorized oauth2 client, based on received or provided access/refresh token pair +func (c *ApiClient) setupOauth2Client(ctx context.Context) { + if c.rnfunc != nil { + // setup notifier for token-refresh workflow - https://github.com/golang/oauth2/issues/84 + realSource := c.oconf.TokenSource(context.Background(), c.token) + notifyingSrc := NewNotifyingTokenSource(realSource, c.rnfunc) + notifyingWithInitialSrc := oauth2.ReuseTokenSource(c.token, notifyingSrc) + // setup authorized oauth2 client + c.oclient = oauth2.NewClient(ctx, notifyingWithInitialSrc) + } else { + c.oclient = c.oconf.Client(ctx, c.token) + } +} + +// run post/put/delete requests +func (c *ApiClient) sendPostRequest(uri string, params map[string]string) (*http.Response, interface{}) { + if c.sendPostAsJson == true { + // old style for backward compatibility with the old library + var jsonStr = []byte("{}") + if params != nil { + str := "" + for k, v := range params { + str += fmt.Sprintf("\"%s\": \"%s\",", k, v) + } + jsonStr = []byte(fmt.Sprintf("{%s}", str[0:len(str)-1])) + } + + response, err := c.oclient.Post(formatUri(uri, c.ep), "application/json", bytes.NewBuffer(jsonStr)) + + return c.getTypedResponse(response, err) + } else { + // prefered + urlValues := url.Values{} + if params != nil { + for k, v := range params { + urlValues.Add(k, v) + } + } + + response, err := c.oclient.PostForm(formatUri(uri, c.ep), urlValues) + + return c.getTypedResponse(response, err) + } +} + +// return proper response type +func (c *ApiClient) getTypedResponse(resp *http.Response, re error) (*http.Response, interface{}) { + if c.respType == ByteResponse { + r, b := formatResponse(resp, re) + return r, b.([]byte) + } else { + return resp, re + } +} + +// Check and format (preparate a byte body) http response routine +func formatResponse(resp *http.Response, err error) (*http.Response, interface{}) { + if err != nil { + log.Fatal("Can not execute the request, " + err.Error()) + } + + defer resp.Body.Close() + if resp.StatusCode != 200 { + // do not exit, it can be a normal response + // it's up to client/requester's side decide what to do + } + // read json http response + jsonDataFromHttp, _ := ioutil.ReadAll(resp.Body) + + return resp, jsonDataFromHttp +} + +// Create a path to a specific resource +func formatUri(uri string, ep string) (string) { + format := "" + if ep == DefaultEpoint { + format += "." + DataFormat + } + return BaseHost + ep + uri + format +} + +// add overload parameter to the map of parameters +func addOverloadParam(params map[string]string, op string) map[string]string { + if params == nil { + params = make(map[string]string) + } + params[OverloadParam] = op + return params +} diff --git a/api/client_test.go b/api/client_test.go new file mode 100644 index 0000000..9f628c2 --- /dev/null +++ b/api/client_test.go @@ -0,0 +1,30 @@ +package api + +import ( + "testing" + "github.com/stretchr/testify/assert" +) + +func TestClientConstants(t *testing.T) { + assert.Equal(t, "https://www.upwork.com/", BaseHost) + assert.Equal(t, "api", DefaultEpoint) + assert.Equal(t, "https://www.upwork.com/ab/account-security/oauth2/authorize", AuthorizationEP) + assert.Equal(t, "https://www.upwork.com/api/v3/oauth2/token", AccessTokenEP) + assert.Equal(t, "json", DataFormat) + assert.Equal(t, "http_method", OverloadParam) +} + +func TestSetup(t *testing.T) { + client := Setup(ReadConfig("../example/config.json")) + if assert.NotNil(t, client) { + assert.NotNil(t, client.oconf) + assert.NotNil(t, client.token) + } +} + +func TestSetEntryPoint(t *testing.T) { + client := Setup(ReadConfig("../example/config.json")) + client.SetEntryPoint("gds") + + assert.Equal(t, "gds", client.ep) +} diff --git a/api/config.go b/api/config.go new file mode 100644 index 0000000..d16deb1 --- /dev/null +++ b/api/config.go @@ -0,0 +1,129 @@ +// Package implements access to Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2018(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package api + +import ( + "log" + "fmt" + "time" + "context" + "encoding/json" + "io/ioutil" + "net/http" + + "golang.org/x/oauth2" +) + +const ( + TIMEFORMAT = "2006-01-02T15:04:05.000Z" // NOTE: time.RFC3339 does not work for unclear reason? +) + +// Config +type Config struct { + ClientId string + ClientSecret string + AccessToken string + RefreshToken string + RedirectUri string + ExpiresIn string + ExpiresAt time.Time + State string + Debug bool +} + +// List of required configuration keys +var requiredKeys = [3]string{"client_id", "client_secret", "redirect_uri"} + +// Create a new config +func NewConfig(data map[string]string) (settings *Config) { + cfg := &Config{ + ClientId: data["client_id"], + ClientSecret: data["client_secret"], + RedirectUri: data["redirect_uri"], + } + + // save access token if defined + if val, ok := data["access_token"]; ok { + cfg.AccessToken = val + } + + // save refresh token if defined + if val, ok := data["refresh_token"]; ok { + cfg.RefreshToken = val + } + + // save expires_in if defined + if val, ok := data["expires_in"]; ok { + cfg.ExpiresIn = val + } + + // save expiresat if defined + if val, ok := data["expires_at"]; ok { + cfg.ExpiresAt, _ = time.Parse(TIMEFORMAT, val) + } + + // save state if defined + if val, ok := data["state"]; ok { + cfg.State = val + } + + // save debug flag if defined + if debug, ok := data["debug"]; ok && debug == "on" { + cfg.Debug = true + } + + return cfg +} + +// Read a specific configuration (json) file +func ReadConfig(fn string) (settings *Config) { + // read from config file if exists + b, err := ioutil.ReadFile(fn) + if err != nil { + log.Fatal("config file: ", err) + } + + // parse json config + var data map[string]interface{} + if err := json.Unmarshal(b, &data); err != nil { + log.Fatal("config file: ", err) + } + + // test required properties + for _, v := range requiredKeys { + _, ok := data[v] + if !ok { + log.Fatal("config file: " + v + " is missing in " + fn) + } + } + + // convert + config := make(map[string]string) + for k, v := range data { + config[k] = v.(string) + } + + return NewConfig(config) +} + +// Configure a context with the custom http client +func (cfg *Config) SetCustomHttpClient(ctx context.Context, httpClient *http.Client) context.Context { + return context.WithValue(ctx, oauth2.HTTPClient, httpClient) +} + +// Test print of found/assigned key +func (cfg *Config) Print() { + fmt.Println("assigned client id (key):", cfg.ClientId) +} diff --git a/api/config_test.go b/api/config_test.go new file mode 100644 index 0000000..91b1308 --- /dev/null +++ b/api/config_test.go @@ -0,0 +1,53 @@ +package api + +import ( + "time" + "testing" + "github.com/stretchr/testify/assert" +) + +func TestImports(t *testing.T) { + if assert.Equal(t, 1, 1) != true { + t.Error("Something is wrong.") + } +} + +func TestNewConfig(t *testing.T) { + settings := map[string]string{ + "client_id": "consumerkey", + "client_secret": "consumersecret", + "access_token": "accesstoken", + "refresh_token": "refreshtoken", + "redirect_uri": "http://a.redirect.uri", + "expires_at": "2018-01-01T01:00:00.000Z", + "expires_in": "100", + "debug": "on", + } + config := NewConfig(settings) + + if assert.NotNil(t, config) { + assert.Equal(t, "consumerkey", config.ClientId) + assert.Equal(t, "consumersecret", config.ClientSecret) + assert.Equal(t, "accesstoken", config.AccessToken) + assert.Equal(t, "refreshtoken", config.RefreshToken) + assert.Equal(t, "http://a.redirect.uri", config.RedirectUri) + ttime, _ := time.Parse(TIMEFORMAT, "2018-01-01T01:00:00.000Z") + assert.Equal(t, ttime, config.ExpiresAt) + assert.Equal(t, "100", config.ExpiresIn) + assert.True(t, true, config.Debug) + } +} + +func TestReadConfig(t *testing.T) { + config := ReadConfig("../example/config.json") + + if assert.NotNil(t, config) { + assert.Equal(t, "YOUR_CONSUMER_KEY", config.ClientId) + assert.Equal(t, "YOUR_CONSUMER_SECRET", config.ClientSecret) + assert.Equal(t, "access-token-if-known-otherwise-remove", config.AccessToken) + assert.Equal(t, "access-token-if-known-otherwise-remove", config.RefreshToken) + assert.Equal(t, "https://a.callback.url", config.RedirectUri) + ttime, _ := time.Parse(TIMEFORMAT, "2018-01-01T01:00:00.000Z") + assert.Equal(t, ttime, config.ExpiresAt) + } +} diff --git a/api/routers/activities/engagement/engagement.go b/api/routers/activities/engagement/engagement.go new file mode 100644 index 0000000..4a5008e --- /dev/null +++ b/api/routers/activities/engagement/engagement.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package engagement + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// List activities for specific engagement +func (r a) GetSpecific(engagementRef string) (*http.Response, interface{}) { + return r.client.Get("/tasks/v2/tasks/contracts/" + engagementRef, nil) +} + +// Assign engagements to the list of activities +func (r a) Assign(company string, team string, engagement string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/engagements/" + engagement + "/tasks", params) +} + +// Assign to specific engagement the list of activities +func (r a) AssignToEngagement(engagementRef string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/tasks/v2/tasks/contracts/" + engagementRef, params) +} diff --git a/api/routers/activities/team/team.go b/api/routers/activities/team/team.go new file mode 100644 index 0000000..268e20a --- /dev/null +++ b/api/routers/activities/team/team.go @@ -0,0 +1,88 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package team + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// List all oTask/Activity records within a team +func (r a) GetList(company string, team string, params ...map[string]string) (*http.Response, interface{}) { + var p map[string]string + if params != nil { + p = params[0] + } + return r.getByType(company, team, "", p) +} + +// List all oTask/Activity records within a team by specified code(s) +func (r a) GetSpecificList(company string, team string, code string, params ...map[string]string) (*http.Response, interface{}) { + var p map[string]string + if params != nil { + p = params[0] + } + return r.getByType(company, team, code, p) +} + +// Create an oTask/Activity record within a team +func (r a) AddActivity(company string, team string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/tasks", params) +} + +// Update specific oTask/Activity record within a team +func (r a) UpdateActivity(company string, team string, code string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/tasks/" + code, params) +} + +// Archive specific oTask/Activity record within a team +func (r a) ArchiveActivity(company string, team string, code string) (*http.Response, interface{}) { + return r.client.Put("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/archive/" + code, nil) +} + +// Unarchive specific oTask/Activity record within a team +func (r a) UnarchiveActivity(company string, team string, code string) (*http.Response, interface{}) { + return r.client.Put("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/unarchive/" + code, nil) +} + +// Update a group of oTask/Activity records +func (r a) UpdateBatch(company string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/otask/v1/tasks/companies/" + company + "/tasks/batch", params) +} + +// Get by type +func (r a) getByType(company string, team string, code string, params map[string]string) (*http.Response, interface{}) { + url := "" + if code != "" { + url = "/" + code; + } + + return r.client.Get("/otask/v1/tasks/companies/" + company + "/teams/" + team + "/tasks" + url, params) +} diff --git a/api/routers/auth/auth.go b/api/routers/auth/auth.go new file mode 100644 index 0000000..1a8277a --- /dev/null +++ b/api/routers/auth/auth.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package auth + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get user info +func (r a) GetUserInfo() (*http.Response, interface{}) { + return r.client.Get("/auth/v1/info", nil) +} diff --git a/api/routers/freelancers/profile/profile.go b/api/routers/freelancers/profile/profile.go new file mode 100644 index 0000000..6989fcd --- /dev/null +++ b/api/routers/freelancers/profile/profile.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package profile + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get specific Freelancer's Profile +func (r a) GetSpecific(key string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/providers/" + key, nil) +} + +// Get brief info for the specific Freelancer's Profile +func (r a) GetSpecificBrief(key string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/providers/" + key + "/brief", nil) +} diff --git a/api/routers/freelancers/search/search.go b/api/routers/freelancers/search/search.go new file mode 100644 index 0000000..152ace8 --- /dev/null +++ b/api/routers/freelancers/search/search.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package search + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Search freelancers +func (r a) Find(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v2/search/providers", params) +} diff --git a/api/routers/hr/clients/applications/applications.go b/api/routers/hr/clients/applications/applications.go new file mode 100644 index 0000000..f4279b8 --- /dev/null +++ b/api/routers/hr/clients/applications/applications.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package applications + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of applications +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/hr/v4/clients/applications", params) +} + +// Get specific application +func (r a) GetSpecific(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/hr/v4/clients/applications/" + reference, params) +} diff --git a/api/routers/hr/clients/offers/offers.go b/api/routers/hr/clients/offers/offers.go new file mode 100644 index 0000000..717cf91 --- /dev/null +++ b/api/routers/hr/clients/offers/offers.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package offers + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of offers +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/offers/v1/clients/offers", params) +} + +// Get specific offer +func (r a) GetSpecific(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/offers/v1/clients/offers/" + reference, params) +} + +// Send offer +func (r a) MakeOffer(params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/offers/v1/clients/offers", params) +} diff --git a/api/routers/hr/contracts/contracts.go b/api/routers/hr/contracts/contracts.go new file mode 100644 index 0000000..63d6bf7 --- /dev/null +++ b/api/routers/hr/contracts/contracts.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package contracts + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Suspend Contract +func (r a) SuspendContract(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v2/contracts/" + reference + "/suspend", params) +} + +// Restart Contract +func (r a) RestartContract(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v2/contracts/" + reference + "/restart", params) +} + +// End Contract +func (r a) EndContract(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Delete("/hr/v2/contracts/" + reference, params) +} diff --git a/api/routers/hr/engagements/engagements.go b/api/routers/hr/engagements/engagements.go new file mode 100644 index 0000000..8269068 --- /dev/null +++ b/api/routers/hr/engagements/engagements.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package engagements + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of engagements +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/engagements", params) +} + +// Get specific engagement +func (r a) GetSpecific(reference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/engagements/" + reference, nil) +} diff --git a/api/routers/hr/freelancers/applications/applications.go b/api/routers/hr/freelancers/applications/applications.go new file mode 100644 index 0000000..4e18c99 --- /dev/null +++ b/api/routers/hr/freelancers/applications/applications.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package applications + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of applications +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/hr/v4/contractors/applications", params) +} + +// Get specific application +func (r a) GetSpecific(reference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v4/contractors/applications/" + reference, nil) +} diff --git a/api/routers/hr/freelancers/offers/offers.go b/api/routers/hr/freelancers/offers/offers.go new file mode 100644 index 0000000..d8af798 --- /dev/null +++ b/api/routers/hr/freelancers/offers/offers.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package offers + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of offers +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/offers/v1/contractors/offers", params) +} + +// Get specific offer +func (r a) GetSpecific(reference string) (*http.Response, interface{}) { + return r.client.Get("/offers/v1/contractors/offers/" + reference, nil) +} + +// Run a specific action +func (r a) MakeOffer(reference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/offers/v1/contractors/offers/" + reference, params) +} diff --git a/api/routers/hr/interviews/interviews.go b/api/routers/hr/interviews/interviews.go new file mode 100644 index 0000000..7f7ea4f --- /dev/null +++ b/api/routers/hr/interviews/interviews.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package interviews + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Invite to Interview +func (r a) Invite(jobKey string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/hr/v1/jobs/" + jobKey + "/candidates", params) +} diff --git a/api/routers/hr/jobs/jobs.go b/api/routers/hr/jobs/jobs.go new file mode 100644 index 0000000..3c9ae51 --- /dev/null +++ b/api/routers/hr/jobs/jobs.go @@ -0,0 +1,60 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package jobs + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get list of jobs +func (r a) GetList(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/jobs", params) +} + +// Get specific job by key +func (r a) GetSpecific(key string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/jobs/" + key, nil) +} + +// Post a new job +func (r a) PostJob(params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/hr/v2/jobs", params) +} + +// Edit existent job +func (r a) EditJob(key string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v2/jobs/" + key, params) +} + +// Delete existent job +func (r a) DeleteJob(key string, params map[string]string) (*http.Response, interface{}) { + return r.client.Delete("/hr/v2/jobs/" + key, params) +} diff --git a/api/routers/hr/milestones/milestones.go b/api/routers/hr/milestones/milestones.go new file mode 100644 index 0000000..a2dcb01 --- /dev/null +++ b/api/routers/hr/milestones/milestones.go @@ -0,0 +1,70 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package milestones + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get active Milestone for the Contract +func (r a) GetActiveMilestone(contractId string) (*http.Response, interface{}) { + return r.client.Get("/hr/v3/fp/milestones/statuses/active/contracts/" + contractId, nil) +} + +// Get all submissions for the active Milestone +func (r a) GetSubmissions(milestoneId string) (*http.Response, interface{}) { + return r.client.Get("/hr/v3/fp/milestones/" + milestoneId + "/submissions", nil) +} + +// Create a new Milestone +func (r a) Create(params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/hr/v3/fp/milestones", params) +} + +// Edit an existing Milestone +func (r a) Edit(milestoneId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v3/fp/milestones/" + milestoneId, params) +} + +// Activate an existing Milestone +func (r a) Activate(milestoneId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v3/fp/milestones/" + milestoneId + "/activate", params) +} + +// Approve an existing Milestone +func (r a) Approve(milestoneId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v3/fp/milestones/" + milestoneId + "/approve", params) +} + +// Delete an existing Milestone +func (r a) Delete(milestoneId string) (*http.Response, interface{}) { + return r.client.Delete("/hr/v3/fp/milestones/" + milestoneId , nil) +} diff --git a/api/routers/hr/roles/roles.go b/api/routers/hr/roles/roles.go new file mode 100644 index 0000000..0c0c94f --- /dev/null +++ b/api/routers/hr/roles/roles.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package roles + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get user roles +func (r a) GetAll() (*http.Response, interface{}) { + return r.client.Get("/hr/v2/userroles", nil) +} + +// Get by specific user +func (r a) GetBySpecificUser(reference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/userroles/" + reference, nil) +} diff --git a/api/routers/hr/submissions/submissions.go b/api/routers/hr/submissions/submissions.go new file mode 100644 index 0000000..ec3eb3d --- /dev/null +++ b/api/routers/hr/submissions/submissions.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package submissions + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Freelancer submits work for the client to approve +func (r a) RequestApproval(params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/hr/v3/fp/submissions", params) +} + +// Approve an existing Submission +func (r a) Approve(submissionId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v3/fp/submissions/" + submissionId + "/approve", params) +} + +// Reject an existing Submission +func (r a) Reject(submissionId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/hr/v3/fp/submissions/" + submissionId + "/reject", params) +} diff --git a/api/routers/jobs/profile/profile.go b/api/routers/jobs/profile/profile.go new file mode 100644 index 0000000..709a762 --- /dev/null +++ b/api/routers/jobs/profile/profile.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package profile + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get specific Job's Profile +func (r a) GetSpecific(key string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/jobs/" + key, nil) +} diff --git a/api/routers/jobs/search/search.go b/api/routers/jobs/search/search.go new file mode 100644 index 0000000..00a76c3 --- /dev/null +++ b/api/routers/jobs/search/search.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package search + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Search jobs +func (r a) Find(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v2/search/jobs", params) +} diff --git a/api/routers/messages/messages.go b/api/routers/messages/messages.go new file mode 100644 index 0000000..c5d1d74 --- /dev/null +++ b/api/routers/messages/messages.go @@ -0,0 +1,80 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2016(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package messages + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Retrieve rooms information +func (r a) GetRooms(company string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/messages/v3/" + company + "/rooms", params) +} + +// Get a specific room information +func (r a) GetRoomDetails(company string, roomId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/messages/v3/" + company + "/rooms/" + roomId, params) +} + +// Get a specific room by offer ID +func (r a) GetRoomByOffer(company string, offerId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/messages/v3/" + company + "/rooms/offers/" + offerId, params) +} + +// Get a specific room by application ID +func (r a) GetRoomByApplication(company string, applicationId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/messages/v3/" + company + "/rooms/applications/" + applicationId, params) +} + +// Get a specific room by contract ID +func (r a) GetRoomByContract(company string, contractId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/messages/v3/" + company + "/rooms/contracts/" + contractId, params) +} + +// Create a new room +func (r a) CreateRoom(company string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/messages/v3/" + company + "/rooms", params) +} + +// Send a message to a room +func (r a) SendMessageToRoom(company string, roomId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/messages/v3/" + company + "/rooms/" + roomId + "/stories", params) +} + +// Update a room settings +func (r a) UpdateRoomSettings(company string, roomId string, username string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/messages/v3/" + company + "/rooms/" + roomId + "/users/" + username, params) +} + +// Update the metadata of a room +func (r a) UpdateRoomMetadata(company string, roomId string, params map[string]string) (*http.Response, interface{}) { + return r.client.Put("/messages/v3/" + company + "/rooms/" + roomId, params) +} diff --git a/api/routers/metadata/metadata.go b/api/routers/metadata/metadata.go new file mode 100644 index 0000000..b3da190 --- /dev/null +++ b/api/routers/metadata/metadata.go @@ -0,0 +1,60 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package metadata + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get categories (V2) +func (r a) GetCategoriesV2() (*http.Response, interface{}) { + return r.client.Get("/profiles/v2/metadata/categories", nil) +} + +// Get skills +func (r a) GetSkills() (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/metadata/skills", nil) +} + +// Get regions +func (r a) GetRegions() (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/metadata/regions", nil) +} + +// Get tests +func (r a) GetTests() (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/metadata/tests", nil) +} + +// Get reasons +func (r a) GetReasons(params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/profiles/v1/metadata/reasons", params) +} diff --git a/api/routers/organization/companies/companies.go b/api/routers/organization/companies/companies.go new file mode 100644 index 0000000..f428dc7 --- /dev/null +++ b/api/routers/organization/companies/companies.go @@ -0,0 +1,55 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package companies + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get Companies List +func (r a) GetList() (*http.Response, interface{}) { + return r.client.Get("/hr/v2/companies", nil) +} + +// Get Specific Company +func (r a) GetSpecific(cmpReference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/companies/" + cmpReference, nil) +} + +// Get Teams in Company +func (r a) GetTeams(cmpReference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/companies/" + cmpReference + "/teams", nil) +} + +// Get Users in Company +func (r a) GetUsers(cmpReference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/companies/" + cmpReference + "/users", nil) +} diff --git a/api/routers/organization/teams/teams.go b/api/routers/organization/teams/teams.go new file mode 100644 index 0000000..7bc15fb --- /dev/null +++ b/api/routers/organization/teams/teams.go @@ -0,0 +1,49 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package teams + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get Teams info +func (r a) GetList(params ...map[string]string) (*http.Response, interface{}) { + var p map[string]string + if params != nil { + p = params[0] + } + return r.client.Get("/hr/v2/teams", p) +} + +// Get Users in Team +func (r a) GetUsersInTeam(teamReference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/teams/" + teamReference + "/users", nil) +} diff --git a/api/routers/organization/users/users.go b/api/routers/organization/users/users.go new file mode 100644 index 0000000..5d87fb4 --- /dev/null +++ b/api/routers/organization/users/users.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package users + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get Auth User Info +func (r a) GetMyInfo() (*http.Response, interface{}) { + return r.client.Get("/hr/v2/users/me", nil) +} + +// Get Specific User Info +func (r a) GetSpecific(userReference string) (*http.Response, interface{}) { + return r.client.Get("/hr/v2/users/" + userReference, nil) +} diff --git a/api/routers/payments/payments.go b/api/routers/payments/payments.go new file mode 100644 index 0000000..67d6cc1 --- /dev/null +++ b/api/routers/payments/payments.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package payments + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Submit a Custom Payment +func (r a) SubmitBonus(teamReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/hr/v2/teams/" + teamReference + "/adjustments", params) +} diff --git a/api/routers/reports/finance/accounts/accounts.go b/api/routers/reports/finance/accounts/accounts.go new file mode 100644 index 0000000..79456ee --- /dev/null +++ b/api/routers/reports/finance/accounts/accounts.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package accounts + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "gds" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Generate Financial Reports for an owned Account +func (r a) GetOwned(freelancerReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/financial_account_owner/" + freelancerReference, params) +} + +// Generate Financial Reports for a Specific Account +func (r a) GetSpecific(entityReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/financial_accounts/" + entityReference, params) +} diff --git a/api/routers/reports/finance/billings/billings.go b/api/routers/reports/finance/billings/billings.go new file mode 100644 index 0000000..00c4500 --- /dev/null +++ b/api/routers/reports/finance/billings/billings.go @@ -0,0 +1,60 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package billings + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "gds" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Generate Billing Reports for a Specific Freelancer +func (r a) GetByFreelancer(freelancerReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/providers/" + freelancerReference + "/billings", params) +} + +// Generate Billing Reports for a Specific Freelancer's Team +func (r a) GetByFreelancersTeam(freelancerTeamReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/provider_teams/" + freelancerTeamReference + "/billings", params) +} + +// Generate Billing Reports for a Specific Freelancer's Company +func (r a) GetByFreelancersCompany(freelancerCompanyReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/provider_companies/" + freelancerCompanyReference + "/billings", params) +} + +// Generate Billing Reports for a Specific Buyer's Team +func (r a) GetByBuyersTeam(buyerTeamReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/buyer_teams/" + buyerTeamReference + "/billings", params) +} + +// Generate Billing Reports for a Specific Buyer's Company +func (r a) GetByBuyersCompany(buyerCompanyReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/buyer_companies/" + buyerCompanyReference + "/billings", params) +} diff --git a/api/routers/reports/finance/earnings/earnings.go b/api/routers/reports/finance/earnings/earnings.go new file mode 100644 index 0000000..985604d --- /dev/null +++ b/api/routers/reports/finance/earnings/earnings.go @@ -0,0 +1,60 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package earnings + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "gds" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Generate Earning Reports for a Specific Freelancer +func (r a) GetByFreelancer(freelancerReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/providers/" + freelancerReference + "/earnings", params) +} + +// Generate Earning Reports for a Specific Freelancer's Team +func (r a) GetByFreelancersTeam(freelancerTeamReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/provider_teams/" + freelancerTeamReference + "/earnings", params) +} + +// Generate Earning Reports for a Specific Freelancer's Company +func (r a) GetByFreelancersCompany(freelancerCompanyReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/provider_companies/" + freelancerCompanyReference + "/earnings", params) +} + +// Generate Earning Reports for a Specific Buyer's Team +func (r a) GetByBuyersTeam(buyerTeamReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/buyer_teams/" + buyerTeamReference + "/earnings", params) +} + +// Generate Earning Reports for a Specific Buyer's Company +func (r a) GetByBuyersCompany(buyerCompanyReference string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/finreports/v2/buyer_companies/" + buyerCompanyReference + "/earnings", params) +} diff --git a/api/routers/reports/time/time.go b/api/routers/reports/time/time.go new file mode 100644 index 0000000..9d3188b --- /dev/null +++ b/api/routers/reports/time/time.go @@ -0,0 +1,80 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package time + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "gds" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Generate Time Reports for a Specific Team (with financial info) +func (r a) GetByTeamFull(company string, team string, params map[string]string) (*http.Response, interface{}) { + return r.getByType(company, team, "", params, false) +} + +// Generate Time Reports for a Specific Team (hide financial info) +func (r a) GetByTeamFullLimited(company string, team string, params map[string]string) (*http.Response, interface{}) { + return r.getByType(company, team, "", params, true) +} + +// Generating Agency Specific Reports +func (r a) GetByAgency(company string, agency string, params map[string]string) (*http.Response, interface{}) { + return r.getByType(company, "", agency, params, false) +} + +// Generating Company Wide Reports +func (r a) GetByCompany(company string, params map[string]string) (*http.Response, interface{}) { + return r.getByType(company, "", "", params, false) +} + +// Generating Freelancer's Specific Reports (with financial info) +func (r a) GetByFreelancerFull(freelancerId string, agency string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/timereports/v1/providers/" + freelancerId, params) +} + +// Generating Freelancer's Specific Reports (hide financial info) +func (r a) GetByFreelancerLimited(freelancerId string, agency string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/timereports/v1/providers/" + freelancerId + "/hours", params) +} + +// Get by type +func (r a) getByType(company string, team string, agency string, params map[string]string, hideFinDetails bool) (*http.Response, interface{}) { + url := "" + if team != "" { + url = "/teams/" + team + if hideFinDetails { + url = url + "/hours" + } + } else if agency != "" { + url = "/agencies/" + agency + } + + return r.client.Get("/timereports/v1/companies/" + company + url, params) +} diff --git a/api/routers/snapshot/snapshot.go b/api/routers/snapshot/snapshot.go new file mode 100644 index 0000000..3bc2d73 --- /dev/null +++ b/api/routers/snapshot/snapshot.go @@ -0,0 +1,50 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package snapshot + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get snapshot info by specific contract +func (r a) GetByContract(contractId string, ts string) (*http.Response, interface{}) { + return r.client.Get("/team/v2/snapshots/contracts/" + contractId + "/" + ts, nil) +} + +// Update snapshot by specific contract +func (r a) UpdateByContract(contractId string, ts string, params map[string]string) (*http.Response, interface{}) { + return r.client.Post("/team/v2/snapshots/contracts/" + contractId + "/" + ts, params) +} + +// Delete snapshot by specific contract +func (r a) DeleteByContract(contractId string, ts string) (*http.Response, interface{}) { + return r.client.Delete("/team/v2/snapshots/contracts/" + contractId + "/" + ts, nil) +} diff --git a/api/routers/workdays/workdays.go b/api/routers/workdays/workdays.go new file mode 100644 index 0000000..3cedb34 --- /dev/null +++ b/api/routers/workdays/workdays.go @@ -0,0 +1,45 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package workdays + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get Workdays by Company +func (r a) GetByCompany(company string, fromDate string, tillDate string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/team/v2/workdays/companies/" + company + "/" + fromDate + "," + tillDate, params) +} + +// Get Workdays by Contract +func (r a) GetByContract(contract string, fromDate string, tillDate string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/team/v2/workdays/contracts/" + contract + "/" + fromDate + "," + tillDate, params) +} diff --git a/api/routers/workdiary/workdiary.go b/api/routers/workdiary/workdiary.go new file mode 100644 index 0000000..b0be7db --- /dev/null +++ b/api/routers/workdiary/workdiary.go @@ -0,0 +1,40 @@ +// Router for Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2015(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package workdiary + +import ( + "net/http" + "github.com/upwork/golang-upwork-oauth2/api" +) + +const ( + EntryPoint = "api" +) + +type a struct { + client *api.ApiClient +} + +// Constructor +func New(c *api.ApiClient) *a { + c.SetEntryPoint(EntryPoint) + + return &a{c} +} + +// Get Workdiary by Contract +func (r a) GetByContract(contract string, date string, params map[string]string) (*http.Response, interface{}) { + return r.client.Get("/team/v2/workdiaries/contracts/" + contract + "/" + date, params) +} diff --git a/example/config.json b/example/config.json new file mode 100644 index 0000000..4229b5e --- /dev/null +++ b/example/config.json @@ -0,0 +1,9 @@ +{ +"client_id": "YOUR_CONSUMER_KEY", +"client_secret": "YOUR_CONSUMER_SECRET", +"redirect_uri": "https://a.callback.url", +"access_token": "access-token-if-known-otherwise-remove", +"refresh_token": "access-token-if-known-otherwise-remove", +"expires_at": "2018-01-01T01:00:00.000Z", +"debug": "off" +} diff --git a/example/example.go b/example/example.go new file mode 100644 index 0000000..bb5b50b --- /dev/null +++ b/example/example.go @@ -0,0 +1,118 @@ +// Example shows how to work with Upwork API +// +// Licensed under the Upwork's API Terms of Use; +// you may not use this file except in compliance with the Terms. +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author:: Maksym Novozhylov (mnovozhilov@upwork.com) +// Copyright:: Copyright 2018(c) Upwork.com +// License:: See LICENSE.txt and TOS - https://developers.upwork.com/api-tos.html +package main + +import ( + "fmt" + "bufio" + "os" + "context" // uncomment if you need to setup a custom http client + _ "net/http" // uncomment if you need to setup a custom http client + + _ "golang.org/x/oauth2" // uncomment if you need to work with oauth2.Token or other object, e.g. to store or re-cache token pair + + "github.com/upwork/golang-upwork-oauth2/api" + "github.com/upwork/golang-upwork-oauth2/api/routers/auth" + _ "github.com/upwork/golang-upwork-oauth2/api/routers/messages" // uncomment to test messages examples +) + +const cfgFile = "config.json" // update the path to your config file, or provide properties directly in your code + +func main() { + // init context + ctx := context.Background() + +/* it is possible to set up properties from code + settings := map[string]string{ + "client_id": "clientid", + "client_secret": "clientsecret", + } + config := api.NewConfig(settings) + + //or read them from a specific configuration file + config := api.ReadConfig(cfgFile) + config.Print() +*/ + +/* it is possible to setup a custom http client if needed + httpClient := &http.Client{Timeout: 2} + config := api.ReadConfig(cfgFile) + ctx = config.SetCustomHttpClient(ctx, httpClient) + client := api.Setup(config) +*/ + //client.SetPostAsJson(true) + + ctx = context.TODO() // define NoContext if you do not use a custom client, otherwise use earlier defined context + client := api.Setup(api.ReadConfig(cfgFile)) + // client.SetPostAsJson(true) // you can configure the package send the requests as application/json, by default PostForm is used +/* + // WARNING: oauth2 library will refresh the access token for you + // Setup notify function for refresh-token-workflow + // type Token, see https://godoc.org/golang.org/x/oauth2#Token + f := func(t *oauth2.Token) error { + // re-cache refreshed token + _, err := fmt.Printf("The token has been refreshed, here is a new one: %#v\n", t) + return err + } + client.SetRefreshTokenNotifyFunc(f) +*/ + // we need an access/refresh token pair in case we haven't received it yet + if !client.HasAccessToken(ctx) { + // required to authorize the application. Once you have an access/refresh token pair associated with + // the user, no need to redirect to the authorization screen. + aurl := client.GetAuthorizationUrl("random-state") + + // read code + reader := bufio.NewReader(os.Stdin) + fmt.Println("Visit the authorization url and provide oauth_verifier for further authorization") + fmt.Println(aurl) + authzCode, _ := reader.ReadString('\n') + + // WARNING: be sure to validate FormValue("state") before getting access token + + // get access token + token := client.GetToken(ctx, authzCode) + fmt.Println(token) // type Token, see https://godoc.org/golang.org/x/oauth2#Token + } + + // http.Response and specified type will be returned, you can use any + // use client.SetApiResponseType to specify the response type: use api.ByteResponse + // or api.ErrorResponse, see usage example below + // by default api.ByteResponse is used, i.e. []byte is returned as second value + _, jsonDataFromHttp1 := auth.New(&client).GetUserInfo() + + // here you can Unmarshal received json string, or do any other action(s) if you used ByteResponse + fmt.Println(string(jsonDataFromHttp1.([]byte))) // []byte + + // if you used ErrorResponse, like + // client.SetApiResponseType(api.ErrorResponse) + // httpResponse, err := auth.New(&client).GetUserInfo() + // if err == nil { + // ... do smth with http.Response + // } + + // run a post request using parameters as an example + // params := make(map[string]string) + // params["story"] = `{"message": "test message", "userId": "~017xxxxx"}` + // _, jsonDataFromHttp2 := messages.New(&client).SendMessageToRoom("company_id", "room_id", params) + // fmt.Println(string(jsonDataFromHttp2.([]byte))) + + // getting reports example + // params := make(map[string]string) + // params["tq"] = "select memo where worked_on >= '05-08-2015'" + // params["tqx"] = "out:json" + // _, jsonDataFromHttp4 := timereports.New(&client).GetByFreelancerFull(params) + // fmt.Println(string(jsonDataFromHttp4.([]byte))) +}