Skip to content

Commit

Permalink
Implement Projects Resource (#4)
Browse files Browse the repository at this point in the history
* Implement new resource: asana_project

* add env variables to tests.yml

* generate accessors
  • Loading branch information
davidji99 authored Nov 3, 2020
1 parent 512bd79 commit 4a3ebc2
Show file tree
Hide file tree
Showing 23 changed files with 1,196 additions and 148 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ jobs:

- name: Run make test
run: make testacc TEST="./asana/"
env:
ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }}
ASANA_WORKSPACE_ID: ${{ secrets.ASANA_WORKSPACE_ID }}
3 changes: 3 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ The following parameters are available for running the test. The absence of some
will cause certain tests to be skipped.

* **TF_ACC** (`integer`) **Required** - must be set to `1`.
* **ASANA_ACCESS_TOKEN** (`string`) **Required** - A valid Asana access token.
* **ASANA_WORKSPACE_ID** (`string`) **Required** - A valid Asana workspace ID.

**For example:**
```bash
export TF_ACC=1
export ASANA_ACCESS_TOKEN=...
$ make testacc TEST="./NAME_OF_TEST/" 2>&1 | tee test.log
```
89 changes: 89 additions & 0 deletions api/api-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"fmt"
"github.com/davidji99/simpleresty"
"github.com/go-resty/resty/v2"
"net/http"
Expand Down Expand Up @@ -127,6 +128,7 @@ func (c *Client) setupClient() {
c.http.SetHeader("Content-type", DefaultContentTypeHeader).
SetHeader("Accept", DefaultAcceptHeader).
SetHeader("User-Agent", c.userAgent).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", c.accessToken)).
SetTimeout(1 * time.Minute).
SetAllowGetMethodPayload(true)

Expand Down
11 changes: 11 additions & 0 deletions api/opts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package api

// InputOutputOpts represents the options available for POST or PUT request
// or when the `application/json` content type.
type InputOutputOpts struct {
// Provides “pretty” output.
Pretty bool `json:"opt_pretty"`

// Defines fields to return.
Fields []string `json:"opt_fields"`
}
28 changes: 28 additions & 0 deletions api/params.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package api

// InputOutputParams specify query parameters to control how your request is interpreted and how the response is generated.
// This struct is used for any GET requests or any PUT/POST requests that use the
// `application/x-www-form-urlencoded` content type.
//
// Reference: https://developers.asana.com/docs/input-output-options
type InputOutputParams struct {
// Provides the response in "pretty" output. In the case of JSON this means doing proper line breaking
// and indentation to make it readable. This will take extra time and increase the response size
// so it is advisable only to use this during debugging.
Pretty bool `url:"opt_pretty"`

// Some requests return compact representations of objects, to conserve resources and complete
// the request more efficiently. Other times requests return more information than you may need.
// This option allows you to list the exact set of fields that the API should be sure to return for the objects.
// The field names should be provided as paths, described below.
Fields []string `url:"opt_fields"`
}

// ListParams represents the query parameters available to most (if not all) list methods.
type ListParams struct {
// Results per page.
Limit int `url:"opt_fields"`

// Offset token.
Offset string `url:"offset"`
}
132 changes: 131 additions & 1 deletion api/projects.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
package api

import "time"
import (
"fmt"
"github.com/davidji99/simpleresty"
"time"
)

// ProjectsService handles communication with the project related
// methods of the Asana API.
//
// Asana API docs: https://developers.asana.com/docs/projects
type ProjectsService service

type ProjectResponse struct {
Data *Project `json:"data,omitempty"`
}

type ProjectsResponse struct {
Data []*Project `json:"data,omitempty"`
}

// Project represents a prioritized list of tasks in Asana or a board with columns of tasks represented as cards.
type Project struct {
CommonResourceFields
Expand Down Expand Up @@ -93,3 +105,121 @@ type Project struct {
// Create-only. The team that this project is shared with. This field only exists for projects in organizations.
Team *Team `json:"team,omitempty"`
}

// ProjectListParams represents the query parameters available when retrieving all projects.
type ProjectListParams struct {
// The workspace or organization to filter projects on.
Workspace string `url:"workspace"`

// The team to filter projects on.
Team string `url:"team"`

// Only return projects whose archived field takes on the value of this parameter.
Archived bool `url:"archived"`
}

// ProjectRequestOpts represents the options available when creating or updating a Project.
type ProjectRequestOpts struct {
Archived bool `json:"archived"`
Color *string `json:"color"`
CurrentStatus *ProjectStatus `json:"current_status,omitempty"`
CustomFields map[string]interface{} `json:"custom_fields,omitempty"`
DefaultView string `json:"default_view,omitempty"`
DueOn *string `json:"due_on"`
Followers string `json:"followers,omitempty"`
HTMLNotes string `json:"html_notes,omitempty"`
IsTemplate *bool `json:"is_template,omitempty"`
Name string `json:"name,omitempty"`
Notes string `json:"notes,omitempty"`
Owner *string `json:"owner"`
Public bool `json:"public"`
StartOn *string `json:"start_on"`
Team string `json:"team,omitempty"`
Workspace string `json:"workspace,omitempty"`
}

// List returns the compact project records for some filtered set of projects. Use one or more of the parameters
// provided to filter the projects returned.
//
// Asana API docs: https://developers.asana.com/docs/get-multiple-projects
func (p *ProjectsService) List(params ...interface{}) (*ProjectsResponse, *simpleresty.Response, error) {
result := new(ProjectsResponse)
urlStr, urlStrErr := p.client.http.RequestURLWithQueryParams(
fmt.Sprintf("/projects"), params...)
if urlStrErr != nil {
return nil, nil, urlStrErr
}

response, err := p.client.http.Get(urlStr, result, nil)

return result, response, err
}

// Create a new project in a workspace or team.
//
// Every project is required to be created in a specific workspace or organization, and this cannot be changed once set.
// Note that you can use the workspace parameter regardless of whether or not it is an organization.
// If the workspace for your project is an organization, you must also supply a team to share the project with.
// Returns the full record of the newly created project.
//
// Asana API docs: https://developers.asana.com/docs/create-a-project
func (p *ProjectsService) Create(createOpts *ProjectRequestOpts, ioOpts *InputOutputOpts) (*ProjectResponse, *simpleresty.Response, error) {
result := new(ProjectResponse)
urlStr := p.client.http.RequestURL("/projects")

body := struct {
Data *ProjectRequestOpts `json:"data"`
Options *InputOutputOpts `json:"options,omitempty"`
}{Data: createOpts, Options: ioOpts}

response, err := p.client.http.Post(urlStr, result, body)
return result, response, err
}

// Get returns the complete project record for a single project.
//
// Asana API docs: https://developers.asana.com/docs/get-a-project
func (p *ProjectsService) Get(id string, params ...interface{}) (*ProjectResponse, *simpleresty.Response, error) {
result := new(ProjectResponse)
urlStr, urlStrErr := p.client.http.RequestURLWithQueryParams(
fmt.Sprintf("/projects/%s", id), params...)
if urlStrErr != nil {
return nil, nil, urlStrErr
}

response, err := p.client.http.Get(urlStr, result, nil)

return result, response, err
}

// Update a project.
//
// A specific, existing project can be updated by making a PUT request on the URL for that project.
// Only the fields provided in the data block will be updated; any unspecified fields will remain unchanged.
// When using this method, it is best to specify only those fields you wish to change, or else you may overwrite changes made by another user since you last retrieved the task.
//
// Returns the complete updated project record.
//
// Asana API docs: https://developers.asana.com/docs/update-a-project
func (p *ProjectsService) Update(id string, updateOpts *ProjectRequestOpts, ioOpts *InputOutputOpts) (*ProjectResponse, *simpleresty.Response, error) {
result := new(ProjectResponse)
urlStr := p.client.http.RequestURL("/projects/%s", id)

body := struct {
Data *ProjectRequestOpts `json:"data"`
Options *InputOutputOpts `json:"options,omitempty"`
}{Data: updateOpts, Options: ioOpts}

response, err := p.client.http.Put(urlStr, result, body)
return result, response, err
}

// Delete a project.
//
// Asana API docs: https://developers.asana.com/docs/delete-a-project
func (p *ProjectsService) Delete(id string) (*simpleresty.Response, error) {
urlStr := p.client.http.RequestURL("/projects/%s", id)

response, err := p.client.http.Delete(urlStr, nil, nil)
return response, err
}
58 changes: 58 additions & 0 deletions asana/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package asana

import (
"fmt"
"github.com/davidji99/terraform-provider-asana/api"
"github.com/davidji99/terraform-provider-asana/version"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"log"
)

var (
UserAgent = fmt.Sprintf("terraform-provider-herokux/v%s", version.ProviderVersion)
)

type Config struct {
API *api.Client
Headers map[string]string
acessToken string
baseURL string
}

func NewConfig() *Config {
return &Config{}
}

func (c *Config) initializeAPI() error {
// Initialize the custom API client for non Heroku Platform APIs
api, clientInitErr := api.New(
api.AccessToken(c.acessToken), api.CustomHTTPHeaders(c.Headers),
api.UserAgent(UserAgent), api.BaseURL(c.baseURL))
if clientInitErr != nil {
return clientInitErr
}
c.API = api

log.Printf("[INFO] Asana Client configured")

return nil
}

func (c *Config) applySchema(d *schema.ResourceData) (err error) {
if v, ok := d.GetOk("headers"); ok {
headersRaw := v.(map[string]interface{})
h := make(map[string]string)

for k, v := range headersRaw {
h[k] = fmt.Sprintf("%v", v)
}

c.Headers = h
}

if v, ok := d.GetOk("url"); ok {
c.baseURL = v.(string)
}

return nil
}
Loading

0 comments on commit 4a3ebc2

Please sign in to comment.