projects

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 8, 2025 License: MIT Imports: 9 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Comment added in v0.6.0

type Comment struct {
	// ID is the unique identifier of the comment.
	ID int64 `json:"id"`

	// Body is the body of the comment.
	Body string `json:"body"`

	// HTMLBody is the HTML representation of the comment body.
	HTMLBody string `json:"htmlBody"`

	// ContentType is the content type of the comment body. It can be "TEXT" or
	// "HTML".
	ContentType string `json:"contentType"`

	// Object is the relationship to the object (task, milestone, project) that
	// this comment is associated with.
	Object *twapi.Relationship `json:"object"`

	// Project is the relationship to the project that this comment belongs to.
	Project twapi.Relationship `json:"project"`

	// PostedBy is the ID of the user who posted the comment.
	PostedBy *int64 `json:"postedBy"`

	// PostedAt is the date and time when the comment was posted.
	PostedAt *time.Time `json:"postedDateTime"`

	// LastEditedBy is the ID of the user who last edited the comment, if it was
	// edited.
	LastEditedBy *int64 `json:"lastEditedBy"`

	// EditedAt is the date and time when the comment was last edited, if it was
	// edited.
	EditedAt *time.Time `json:"dateLastEdited"`

	// Deleted indicates whether the comment has been deleted.
	Deleted bool `json:"deleted"`

	// DeletedBy is the ID of the user who deleted the comment, if it was deleted.
	DeletedBy *int64 `json:"deletedBy"`

	// DeletedAt is the date and time when the comment was deleted, if it was
	// deleted.
	DeletedAt *time.Time `json:"dateDeleted"`
}

Comment is a way for users to communicate and collaborate directly within tasks, milestones, files, or other project items. Comments allow team members to provide updates, ask questions, give feedback, or share relevant information in a centralized and contextual manner. They support rich text formatting, file attachments, and @mentions to notify specific users or teams, helping keep discussions organized and easily accessible within the project. Comments are visible to all users with access to the item, promoting transparency and keeping everyone aligned.

More information can be found at: https://support.teamwork.com/projects/getting-started/comments-overview

type CommentCreateRequest added in v0.6.0

type CommentCreateRequest struct {
	// Path contains the path parameters for the request.
	Path CommentCreateRequestPath `json:"-"`

	// Body is the body of the comment. It can contain plain text or HTML.
	Body string `json:"body"`

	// ContentType is the content type of the comment body. It can be "TEXT" or
	// "HTML". If not provided, it defaults to "TEXT".
	ContentType *string `json:"contentType,omitempty"`
}

CommentCreateRequest represents the request body for creating a new comment.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/post-resource-resource-id-comments-json

func NewCommentCreateRequestInFileVersion added in v0.6.0

func NewCommentCreateRequestInFileVersion(fileVersionID int64, body string) CommentCreateRequest

NewCommentCreateRequestInFileVersion creates a new CommentCreateRequest with the provided file version ID.

func NewCommentCreateRequestInLink(linkID int64, body string) CommentCreateRequest

NewCommentCreateRequestInLink creates a new CommentCreateRequest with the provided link ID.

func NewCommentCreateRequestInMilestone added in v0.6.0

func NewCommentCreateRequestInMilestone(milestoneID int64, body string) CommentCreateRequest

NewCommentCreateRequestInMilestone creates a new CommentCreateRequest with the provided milestone ID.

func NewCommentCreateRequestInNotebook added in v0.6.0

func NewCommentCreateRequestInNotebook(notebookID int64, body string) CommentCreateRequest

NewCommentCreateRequestInNotebook creates a new CommentCreateRequest with the provided notebook ID.

func NewCommentCreateRequestInTask added in v0.6.0

func NewCommentCreateRequestInTask(taskID int64, body string) CommentCreateRequest

NewCommentCreateRequestInTask creates a new CommentCreateRequest with the provided task ID.

func (CommentCreateRequest) HTTPRequest added in v0.6.0

func (t CommentCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CommentCreateRequest.

type CommentCreateRequestPath added in v0.6.0

type CommentCreateRequestPath struct {
	// FileVersionID is the unique identifier of the file version where the
	// comment will be created. Each file can have multiple versions, and the
	// comments are associated with a specific version.
	FileVersionID int64

	// MilestoneID is the unique identifier of the milestone where the comment
	// will be created.
	MilestoneID int64

	// NotebookID is the unique identifier of the notebook where the comment will
	// be created.
	NotebookID int64

	// TaskID is the unique identifier of the task where the comment will be
	// created.
	TaskID int64

	// LinkID is the unique identifier of the link where the comment will be
	// created.
	LinkID int64
}

CommentUpdateRequestPath contains the path parameters for creating a comment.

type CommentCreateResponse added in v0.6.0

type CommentCreateResponse struct {
	// ID is the unique identifier of the created comment.
	ID LegacyNumber `json:"id"`
}

CommentCreateResponse represents the response body for creating a new comment.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/post-resource-resource-id-comments-json

func CommentCreate added in v0.6.0

func CommentCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req CommentCreateRequest,
) (*CommentCreateResponse, error)

CommentCreate creates a new comment using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCommentServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	commentRequest := projects.NewCommentCreateRequestInTask(777, "<h1>New Comment</h1>")
	commentRequest.ContentType = twapi.Ptr("HTML")

	commentResponse, err := projects.CommentCreate(ctx, engine, commentRequest)
	if err != nil {
		fmt.Printf("failed to create comment: %s", err)
	} else {
		fmt.Printf("created comment with identifier %d\n", commentResponse.ID)
	}

}

func startCommentServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /tasks/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created comment with identifier 12345

func (*CommentCreateResponse) HandleHTTPResponse added in v0.6.0

func (t *CommentCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CommentCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CommentDeleteRequest added in v0.6.0

type CommentDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path CommentDeleteRequestPath
}

CommentDeleteRequest represents the request body for deleting a comment.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/delete-comments-id-json

func NewCommentDeleteRequest added in v0.6.0

func NewCommentDeleteRequest(commentID int64) CommentDeleteRequest

NewCommentDeleteRequest creates a new CommentDeleteRequest with the provided comment ID.

func (CommentDeleteRequest) HTTPRequest added in v0.6.0

func (t CommentDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CommentDeleteRequest.

type CommentDeleteRequestPath added in v0.6.0

type CommentDeleteRequestPath struct {
	// ID is the unique identifier of the comment to be deleted.
	ID int64
}

CommentDeleteRequestPath contains the path parameters for deleting a comment.

type CommentDeleteResponse added in v0.6.0

type CommentDeleteResponse struct{}

CommentDeleteResponse represents the response body for deleting a comment.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/delete-comments-id-json

func CommentDelete added in v0.6.0

func CommentDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req CommentDeleteRequest,
) (*CommentDeleteResponse, error)

CommentDelete deletes a comment using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCommentServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.CommentDelete(ctx, engine, projects.NewCommentDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete comment: %s", err)
	} else {
		fmt.Println("comment deleted!")
	}

}

func startCommentServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /tasks/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

comment deleted!

func (*CommentDeleteResponse) HandleHTTPResponse added in v0.6.0

func (t *CommentDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CommentDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CommentGetRequest added in v0.6.0

type CommentGetRequest struct {
	// Path contains the path parameters for the request.
	Path CommentGetRequestPath
}

CommentGetRequest represents the request body for loading a single comment.

https://apidocs.teamwork.com/docs/teamwork/v3/comments/get-projects-api-v3-comments-id-json

func NewCommentGetRequest added in v0.6.0

func NewCommentGetRequest(commentID int64) CommentGetRequest

NewCommentGetRequest creates a new CommentGetRequest with the provided comment ID. The ID is required to load a comment.

func (CommentGetRequest) HTTPRequest added in v0.6.0

func (t CommentGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CommentGetRequest.

type CommentGetRequestPath added in v0.6.0

type CommentGetRequestPath struct {
	// ID is the unique identifier of the comment to be retrieved.
	ID int64 `json:"id"`
}

CommentGetRequestPath contains the path parameters for loading a single comment.

type CommentGetResponse added in v0.6.0

type CommentGetResponse struct {
	Comment Comment `json:"comments"`
}

CommentGetResponse contains all the information related to a comment.

https://apidocs.teamwork.com/docs/teamwork/v3/comments/get-projects-api-v3-comments-id-json

func CommentGet added in v0.6.0

func CommentGet(
	ctx context.Context,
	engine *twapi.Engine,
	req CommentGetRequest,
) (*CommentGetResponse, error)

CommentGet retrieves a single comment using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCommentServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	commentResponse, err := projects.CommentGet(ctx, engine, projects.NewCommentGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve comment: %s", err)
	} else {
		fmt.Printf("retrieved comment with identifier %d\n", commentResponse.Comment.ID)
	}

}

func startCommentServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /tasks/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved comment with identifier 12345

func (*CommentGetResponse) HandleHTTPResponse added in v0.6.0

func (t *CommentGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CommentGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CommentListRequest added in v0.6.0

type CommentListRequest struct {
	// Path contains the path parameters for the request.
	Path CommentListRequestPath

	// Filters contains the filters for loading multiple comments.
	Filters CommentListRequestFilters
}

CommentListRequest represents the request body for loading multiple comments.

https://apidocs.teamwork.com/docs/teamwork/v3/comments/get-projects-api-v3-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/file-version-comments/get-projects-api-v3-fileversions-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/milestone-comments/get-projects-api-v3-milestones-milestone-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/notebook-comments/get-projects-api-v3-notebooks-notebook-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/task-comments/get-projects-api-v3-tasks-task-id-comments-json

func NewCommentListRequest added in v0.6.0

func NewCommentListRequest() CommentListRequest

NewCommentListRequest creates a new CommentListRequest with default values.

func (CommentListRequest) HTTPRequest added in v0.6.0

func (t CommentListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CommentListRequest.

type CommentListRequestFilters added in v0.6.0

type CommentListRequestFilters struct {
	// SearchTerm is an optional search term to filter comments by name, description
	// or commentlist's name.
	SearchTerm string

	// UserIDs is an optional list of user IDs to filter comments by users.
	UserIDs []int64

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of comments to retrieve per page. Defaults to 50.
	PageSize int64
}

CommentListRequestFilters contains the filters for loading multiple comments.

type CommentListRequestPath added in v0.6.0

type CommentListRequestPath struct {
	// FileVersionID is the unique identifier of the file version whose comments
	// are to be retrieved. Each file can have multiple versions, and the comments
	// are associated with a specific version.
	FileVersionID int64

	// MilestoneID is the unique identifier of the milestone whose comments are to
	// be retrieved.
	MilestoneID int64

	// NotebookID is the unique identifier of the notebook whose comments are to
	// be retrieved.
	NotebookID int64

	// TaskID is the unique identifier of the task whose comments are to be
	// retrieved.
	TaskID int64
}

CommentListRequestPath contains the path parameters for loading multiple comments.

type CommentListResponse added in v0.6.0

type CommentListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Comments []Comment `json:"comments"`
	// contains filtered or unexported fields
}

CommentListResponse contains information by multiple comments matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/file-version-comments/get-projects-api-v3-fileversions-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/milestone-comments/get-projects-api-v3-milestones-milestone-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/notebook-comments/get-projects-api-v3-notebooks-notebook-id-comments-json https://apidocs.teamwork.com/docs/teamwork/v3/task-comments/get-projects-api-v3-tasks-task-id-comments-json

func CommentList added in v0.6.0

func CommentList(
	ctx context.Context,
	engine *twapi.Engine,
	req CommentListRequest,
) (*CommentListResponse, error)

CommentList retrieves multiple comments using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCommentServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	commentsRequest := projects.NewCommentListRequest()
	commentsRequest.Filters.SearchTerm = "Example"

	commentsResponse, err := projects.CommentList(ctx, engine, commentsRequest)
	if err != nil {
		fmt.Printf("failed to list comments: %s", err)
	} else {
		for _, comment := range commentsResponse.Comments {
			fmt.Printf("retrieved comment with identifier %d\n", comment.ID)
		}
	}

}

func startCommentServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /tasks/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved comment with identifier 12345
retrieved comment with identifier 12346

func (*CommentListResponse) HandleHTTPResponse added in v0.6.0

func (t *CommentListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CommentListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*CommentListResponse) Iterate added in v0.6.0

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*CommentListResponse) SetRequest added in v0.6.0

func (t *CommentListResponse) SetRequest(req CommentListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type CommentUpdateRequest added in v0.6.0

type CommentUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path CommentUpdateRequestPath `json:"-"`

	// Body is the body of the comment. It can contain plain text or HTML.
	Body string `json:"body"`

	// ContentType is the content type of the comment body. It can be "TEXT" or
	// "HTML". If not provided, it defaults to "TEXT".
	ContentType *string `json:"contentType,omitempty"`
}

CommentUpdateRequest represents the request body for updating a comment. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/put-comments-id-json

func NewCommentUpdateRequest added in v0.6.0

func NewCommentUpdateRequest(commentID int64) CommentUpdateRequest

NewCommentUpdateRequest creates a new CommentUpdateRequest with the provided comment ID. The ID is required to update a comment.

func (CommentUpdateRequest) HTTPRequest added in v0.6.0

func (t CommentUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CommentUpdateRequest.

type CommentUpdateRequestPath added in v0.6.0

type CommentUpdateRequestPath struct {
	// ID is the unique identifier of the comment to be updated.
	ID int64
}

CommentUpdateRequestPath contains the path parameters for updating a comment.

type CommentUpdateResponse added in v0.6.0

type CommentUpdateResponse struct{}

CommentUpdateResponse represents the response body for updating a comment.

https://apidocs.teamwork.com/docs/teamwork/v1/comments/put-comments-id-json

func CommentUpdate added in v0.6.0

func CommentUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req CommentUpdateRequest,
) (*CommentUpdateResponse, error)

CommentUpdate updates a comment using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCommentServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	commentRequest := projects.NewCommentUpdateRequest(12345)
	commentRequest.Body = "<h1>Updated Comment</h1>"

	_, err = projects.CommentUpdate(ctx, engine, commentRequest)
	if err != nil {
		fmt.Printf("failed to update comment: %s", err)
	} else {
		fmt.Println("comment updated!")
	}

}

func startCommentServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /tasks/{id}/comments", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/comments", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"comments":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

comment updated!

func (*CommentUpdateResponse) HandleHTTPResponse added in v0.6.0

func (t *CommentUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CommentUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Company added in v0.4.0

type Company struct {
	// ID is the unique identifier of the company.
	ID int64 `json:"id"`

	// AddressOne is the first line of the company's address.
	AddressOne string `json:"addressOne"`

	// AddressTwo is the second line of the company's address.
	AddressTwo string `json:"addressTwo"`

	// City is the city where the company is located.
	City string `json:"city"`

	// CountryCode is the ISO 3166-1 alpha-2 country code where the company is
	// located.
	CountryCode string `json:"countryCode"`

	// EmailOne is the primary email address of the company.
	EmailOne string `json:"emailOne"`

	// EmailTwo is the secondary email address of the company.
	EmailTwo string `json:"emailTwo"`

	// EmailThree is the tertiary email address of the company.
	EmailThree string `json:"emailThree"`

	// Fax is the fax number of the company.
	Fax string `json:"fax"`

	// Name is the name of the company.
	Name string `json:"name"`

	// Phone is the phone number of the company.
	Phone string `json:"phone"`

	// Profile is the profile text of the company.
	Profile *string `json:"profileText"`

	// State is the state or province where the company is located.
	State string `json:"state"`

	// Website is the website URL of the company.
	Website string `json:"website"`

	// Zip is the ZIP or postal code where the company is located.
	Zip string `json:"zip"`

	// ManagedBy is the user managing the company.
	ManagedBy *twapi.Relationship `json:"clientManagedBy"`

	// Industry is the industry the company belongs to.
	Industry *twapi.Relationship `json:"industry"`

	// Tags is a list of tags associated with the company.
	Tags []twapi.Relationship `json:"tags"`

	// CreatedAt is the date and time when the company was created.
	CreatedAt *time.Time `json:"createdAt"`

	// UpdatedAt is the date and time when the company was last updated.
	UpdatedAt *time.Time `json:"updatedAt"`

	// Status is the status of the company. Possible values are "active" or
	// "deleted".
	Status string `json:"status"`
}

Company represents an organization or business entity that can be associated with users, projects, and tasks within the platform, and it is often referred to as a “client.” It serves as a way to group related users and projects under a single organizational umbrella, making it easier to manage permissions, assign responsibilities, and organize work. Companies (or clients) are frequently used to distinguish between internal teams and external collaborators, enabling teams to work efficiently while maintaining clear boundaries around ownership, visibility, and access levels across different projects.

More information can be found at: https://support.teamwork.com/projects/getting-started/companies-owner-and-external

type CompanyCreateRequest added in v0.4.0

type CompanyCreateRequest struct {
	// AddressOne is the first line of the company's address.
	AddressOne *string `json:"addressOne,omitempty"`

	// AddressTwo is the second line of the company's address.
	AddressTwo *string `json:"addressTwo,omitempty"`

	// City is the city where the company is located.
	City *string `json:"city,omitempty"`

	// CountryCode is the ISO 3166-1 alpha-2 country code where the company is
	// located.
	CountryCode *string `json:"countrycode,omitempty"`

	// EmailOne is the primary email address of the company.
	EmailOne *string `json:"emailOne,omitempty"`

	// EmailTwo is the secondary email address of the company.
	EmailTwo *string `json:"emailTwo,omitempty"`

	// EmailThree is the tertiary email address of the company.
	EmailThree *string `json:"emailThree,omitempty"`

	// Fax is the fax number of the company.
	Fax *string `json:"fax,omitempty"`

	// Name is the name of the company. This field is required.
	Name string `json:"name"`

	// Phone is the phone number of the company.
	Phone *string `json:"phone,omitempty"`

	// Profile is the profile text of the company.
	Profile *string `json:"profile,omitempty"`

	// State is the state or province where the company is located.
	State *string `json:"state,omitempty"`

	// Website is the website URL of the company.
	Website *string `json:"website,omitempty"`

	// Zip is the ZIP or postal code where the company is located.
	Zip *string `json:"zip,omitempty"`

	// ManagerID is the user ID of the user managing the company.
	ManagerID *int64 `json:"clientManagedBy"`

	// IndustryID is the industry ID the company belongs to.
	IndustryID *int64 `json:"industryCatId,omitempty"`

	// TagIDs is a list of tag IDs to associate with the company.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

CompanyCreateRequest represents the request body for creating a new client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/post-projects-api-v3-companies-json

func NewCompanyCreateRequest added in v0.4.0

func NewCompanyCreateRequest(name string) CompanyCreateRequest

NewCompanyCreateRequest creates a new CompanyCreateRequest with the provided name in a specific project.

func (CompanyCreateRequest) HTTPRequest added in v0.4.0

func (c CompanyCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CompanyCreateRequest.

type CompanyCreateResponse added in v0.4.0

type CompanyCreateResponse struct {
	// Company is the created company.
	Company Company `json:"company"`
}

CompanyCreateResponse represents the response body for creating a new client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/post-projects-api-v3-companies-json

func CompanyCreate added in v0.4.0

func CompanyCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req CompanyCreateRequest,
) (*CompanyCreateResponse, error)

CompanyCreate creates a new client/company using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCompanyServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	companyRequest := projects.NewCompanyCreateRequest("Test Company")
	companyRequest.Profile = twapi.Ptr("A company created via the API.")

	companyResponse, err := projects.CompanyCreate(ctx, engine, companyRequest)
	if err != nil {
		fmt.Printf("failed to create company: %s", err)
	} else {
		fmt.Printf("created company with identifier %d\n", companyResponse.Company.ID)
	}

}

func startCompanyServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/companies", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/companies", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"companies":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created company with identifier 12345

func (*CompanyCreateResponse) HandleHTTPResponse added in v0.4.0

func (c *CompanyCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CompanyCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CompanyDeleteRequest added in v0.4.0

type CompanyDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path CompanyDeleteRequestPath
}

CompanyDeleteRequest represents the request body for deleting a client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/delete-projects-api-v3-companies-company-id-json

func NewCompanyDeleteRequest added in v0.4.0

func NewCompanyDeleteRequest(companyID int64) CompanyDeleteRequest

NewCompanyDeleteRequest creates a new CompanyDeleteRequest with the provided company ID.

func (CompanyDeleteRequest) HTTPRequest added in v0.4.0

func (c CompanyDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CompanyDeleteRequest.

type CompanyDeleteRequestPath added in v0.4.0

type CompanyDeleteRequestPath struct {
	// ID is the unique identifier of the company to be deleted.
	ID int64
}

CompanyDeleteRequestPath contains the path parameters for deleting a client/company.

type CompanyDeleteResponse added in v0.4.0

type CompanyDeleteResponse struct{}

CompanyDeleteResponse represents the response body for deleting a client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/delete-projects-api-v3-companies-company-id-json

func CompanyDelete added in v0.4.0

func CompanyDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req CompanyDeleteRequest,
) (*CompanyDeleteResponse, error)

CompanyDelete deletes a client/company using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCompanyServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.CompanyDelete(ctx, engine, projects.NewCompanyDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete company: %s", err)
	} else {
		fmt.Println("company deleted!")
	}

}

func startCompanyServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/companies", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/companies", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"companies":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

company deleted!

func (*CompanyDeleteResponse) HandleHTTPResponse added in v0.4.0

func (c *CompanyDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CompanyDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CompanyGetRequest added in v0.4.0

type CompanyGetRequest struct {
	// Path contains the path parameters for the request.
	Path CompanyGetRequestPath
}

CompanyGetRequest represents the request body for loading a single client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/get-projects-api-v3-companies-company-id-json

func NewCompanyGetRequest added in v0.4.0

func NewCompanyGetRequest(companyID int64) CompanyGetRequest

NewCompanyGetRequest creates a new CompanyGetRequest with the provided company ID. The ID is required to load a company.

func (CompanyGetRequest) HTTPRequest added in v0.4.0

func (c CompanyGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CompanyGetRequest.

type CompanyGetRequestPath added in v0.4.0

type CompanyGetRequestPath struct {
	// ID is the unique identifier of the company to be retrieved.
	ID int64 `json:"id"`
}

CompanyGetRequestPath contains the path parameters for loading a single company.

type CompanyGetResponse added in v0.4.0

type CompanyGetResponse struct {
	Company Company `json:"company"`
}

CompanyGetResponse contains all the information related to a client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/get-projects-api-v3-companies-company-id-json

func CompanyGet added in v0.4.0

func CompanyGet(
	ctx context.Context,
	engine *twapi.Engine,
	req CompanyGetRequest,
) (*CompanyGetResponse, error)

CompanyGet retrieves a single client/company using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCompanyServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	companyResponse, err := projects.CompanyGet(ctx, engine, projects.NewCompanyGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve company: %s", err)
	} else {
		fmt.Printf("retrieved company with identifier %d\n", companyResponse.Company.ID)
	}

}

func startCompanyServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/companies", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/companies", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"companies":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved company with identifier 12345

func (*CompanyGetResponse) HandleHTTPResponse added in v0.4.0

func (c *CompanyGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CompanyGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type CompanyListRequest added in v0.4.0

type CompanyListRequest struct {
	// Filters contains the filters for loading multiple companies.
	Filters CompanyListRequestFilters
}

CompanyListRequest represents the request body for loading multiple clients/companies.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/get-projects-api-v3-companies-json

func NewCompanyListRequest added in v0.4.0

func NewCompanyListRequest() CompanyListRequest

NewCompanyListRequest creates a new CompanyListRequest with default values.

func (CompanyListRequest) HTTPRequest added in v0.4.0

func (c CompanyListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CompanyListRequest.

type CompanyListRequestFilters added in v0.4.0

type CompanyListRequestFilters struct {
	// SearchTerm is an optional search term to filter clients/companies by name.
	SearchTerm string

	// TagIDs is an optional list of tag IDs to filter companies by tags.
	TagIDs []int64

	// MatchAllTags is an optional flag to indicate if all tags must match. If set
	// to true, only companies matching all specified tags will be returned.
	MatchAllTags *bool

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of companies to retrieve per page. Defaults to 50.
	PageSize int64
}

CompanyListRequestFilters contains the filters for loading multiple clients/companies.

type CompanyListResponse added in v0.4.0

type CompanyListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Companies []Company `json:"companies"`
	// contains filtered or unexported fields
}

CompanyListResponse contains information by multiple clients/companies matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/get-projects-api-v3-companies-json

func CompanyList added in v0.4.0

func CompanyList(
	ctx context.Context,
	engine *twapi.Engine,
	req CompanyListRequest,
) (*CompanyListResponse, error)

CompanyList retrieves multiple clients/companies using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCompanyServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	companiesRequest := projects.NewCompanyListRequest()
	companiesRequest.Filters.SearchTerm = "John"

	companiesResponse, err := projects.CompanyList(ctx, engine, companiesRequest)
	if err != nil {
		fmt.Printf("failed to list companies: %s", err)
	} else {
		for _, company := range companiesResponse.Companies {
			fmt.Printf("retrieved company with identifier %d\n", company.ID)
		}
	}

}

func startCompanyServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/companies", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/companies", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"companies":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved company with identifier 12345
retrieved company with identifier 12346

func (*CompanyListResponse) HandleHTTPResponse added in v0.4.0

func (c *CompanyListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CompanyListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*CompanyListResponse) Iterate added in v0.4.0

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*CompanyListResponse) SetRequest added in v0.4.0

func (c *CompanyListResponse) SetRequest(req CompanyListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type CompanyUpdateRequest added in v0.4.0

type CompanyUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path CompanyUpdateRequestPath `json:"-"`

	// AddressOne is the first line of the company's address.
	AddressOne *string `json:"addressOne,omitempty"`

	// AddressTwo is the second line of the company's address.
	AddressTwo *string `json:"addressTwo,omitempty"`

	// City is the city where the company is located.
	City *string `json:"city,omitempty"`

	// CountryCode is the ISO 3166-1 alpha-2 country code where the company is
	// located.
	CountryCode *string `json:"countrycode,omitempty"`

	// EmailOne is the primary email address of the company.
	EmailOne *string `json:"emailOne,omitempty"`

	// EmailTwo is the secondary email address of the company.
	EmailTwo *string `json:"emailTwo,omitempty"`

	// EmailThree is the tertiary email address of the company.
	EmailThree *string `json:"emailThree,omitempty"`

	// Fax is the fax number of the company.
	Fax *string `json:"fax,omitempty"`

	// Name is the name of the company.
	Name *string `json:"name"`

	// Phone is the phone number of the company.
	Phone *string `json:"phone,omitempty"`

	// Profile is the profile text of the company.
	Profile *string `json:"profile,omitempty"`

	// State is the state or province where the company is located.
	State *string `json:"state,omitempty"`

	// Website is the website URL of the company.
	Website *string `json:"website,omitempty"`

	// Zip is the ZIP or postal code where the company is located.
	Zip *string `json:"zip,omitempty"`

	// ManagerID is the user ID of the user managing the company.
	ManagerID *int64 `json:"clientManagedBy"`

	// IndustryID is the industry ID the company belongs to.
	IndustryID *int64 `json:"industryCatId,omitempty"`

	// TagIDs is a list of tag IDs to associate with the company.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

CompanyUpdateRequest represents the request body for updating a client/company. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/patch-projects-api-v3-companies-company-id-json

func NewCompanyUpdateRequest added in v0.4.0

func NewCompanyUpdateRequest(companyID int64) CompanyUpdateRequest

NewCompanyUpdateRequest creates a new CompanyUpdateRequest with the provided client/company ID. The ID is required to update a company.

func (CompanyUpdateRequest) HTTPRequest added in v0.4.0

func (c CompanyUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the CompanyUpdateRequest.

type CompanyUpdateRequestPath added in v0.4.0

type CompanyUpdateRequestPath struct {
	// ID is the unique identifier of the company to be updated.
	ID int64
}

CompanyUpdateRequestPath contains the path parameters for updating a client/company.

type CompanyUpdateResponse added in v0.4.0

type CompanyUpdateResponse struct {
	// Company is the updated company.
	Company Company `json:"company"`
}

CompanyUpdateResponse represents the response body for updating a client/company.

https://apidocs.teamwork.com/docs/teamwork/v3/companies/patch-projects-api-v3-companies-company-id-json

func CompanyUpdate added in v0.4.0

func CompanyUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req CompanyUpdateRequest,
) (*CompanyUpdateResponse, error)

CompanyUpdate updates a new client/company using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startCompanyServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	companyRequest := projects.NewCompanyUpdateRequest(12345)
	companyRequest.Profile = twapi.Ptr("Updated profile")

	_, err = projects.CompanyUpdate(ctx, engine, companyRequest)
	if err != nil {
		fmt.Printf("failed to update company: %s", err)
	} else {
		fmt.Println("company updated!")
	}

}

func startCompanyServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/companies", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/companies/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"company":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/companies", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"companies":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

company updated!

func (*CompanyUpdateResponse) HandleHTTPResponse added in v0.4.0

func (c *CompanyUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the CompanyUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type LegacyDate

type LegacyDate time.Time

LegacyDate is a type alias for time.Time, used to represent legacy date values in the API.

func NewLegacyDate added in v0.3.0

func NewLegacyDate(t time.Time) LegacyDate

NewLegacyDate creates a new LegacyDate from a time.Time value.

func (LegacyDate) MarshalJSON

func (d LegacyDate) MarshalJSON() ([]byte, error)

MarshalJSON encodes the LegacyDate as a string in the format "20060102".

func (*LegacyDate) UnmarshalJSON

func (d *LegacyDate) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON string into a LegacyDate type.

type LegacyNumber

type LegacyNumber int64

LegacyNumber is a type alias for int64, used to represent numeric values in the API.

func NewLegacyNumber added in v0.3.0

func NewLegacyNumber(n int64) LegacyNumber

NewLegacyNumber creates a new LegacyNumber from an int64 value.

func (LegacyNumber) MarshalJSON

func (n LegacyNumber) MarshalJSON() ([]byte, error)

MarshalJSON encodes the LegacyNumber as a string.

func (*LegacyNumber) UnmarshalJSON

func (n *LegacyNumber) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON string into a LegacyNumber type.

type LegacyNumericList added in v0.5.0

type LegacyNumericList []int64

LegacyNumericList is a type alias for a slice of int64, used to represent a list of numeric values in the API.

func (*LegacyNumericList) Add added in v0.5.0

func (l *LegacyNumericList) Add(n float64)

Add adds a numeric value to the LegacyNumericList.

func (LegacyNumericList) MarshalJSON added in v0.5.0

func (l LegacyNumericList) MarshalJSON() ([]byte, error)

MarshalJSON encodes the LegacyNumericList as a JSON array of strings.

type LegacyRelationship added in v0.5.0

type LegacyRelationship struct {
	ID   LegacyNumber `json:"id"`
	Type string       `json:"type"`
}

LegacyRelationship describes the relation between the main entity and a sideload type.

type LegacyUserGroups added in v0.3.0

type LegacyUserGroups struct {
	UserIDs    []int64
	CompanyIDs []int64
	TeamIDs    []int64
}

LegacyUserGroups represents a collection of users, companies, and teams in a legacy format, where IDs are represented as strings.

func (LegacyUserGroups) IsEmpty added in v0.3.0

func (m LegacyUserGroups) IsEmpty() bool

IsEmpty checks if the LegacyUserGroups contains no IDs.

func (LegacyUserGroups) MarshalJSON added in v0.3.0

func (m LegacyUserGroups) MarshalJSON() ([]byte, error)

MarshalJSON encodes the LegacyUserGroups as a JSON object.

func (*LegacyUserGroups) UnmarshalJSON added in v0.3.0

func (m *LegacyUserGroups) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes a JSON string into a LegacyUserGroups type.

type Milestone added in v0.3.0

type Milestone struct {
	// ID is the unique identifier of the milestone.
	ID int64 `json:"id"`

	// Name is the name of the milestone.
	Name string `json:"name"`

	// Description is the description of the milestone.
	Description string `json:"description"`

	// DueAt is the due date of the milestone.
	DueAt time.Time `json:"deadline"`

	// Project is the project associated with the milestone.
	Project twapi.Relationship `json:"project"`

	// Tasklists is the list of tasklists associated with the milestone.
	Tasklists []twapi.Relationship `json:"tasklists"`

	// Tags is the list of tags associated with the milestone.
	Tags []twapi.Relationship `json:"tags"`

	// ResponsibleParties is the list of assingees (users, teams and
	// clients/companies) responsible for the milestone.
	ResponsibleParties []twapi.Relationship `json:"responsibleParties"`

	// CreatedAt is the date and time when the milestone was created.
	CreatedAt *time.Time `json:"createdOn"`

	// UpdatedAt is the date and time when the milestone was last updated.
	UpdatedAt *time.Time `json:"lastChangedOn"`

	// DeletedAt is the date and time when the milestone was deleted, if it was
	// deleted.
	DeletedAt *time.Time `json:"deletedOn"`

	// CompletedAt is the date and time when the milestone was completed, if it
	// was completed.
	CompletedAt *time.Time `json:"completedOn"`

	// CompletedBy is the ID of the user who completed the milestone, if it was
	// completed.
	CompletedBy *int64 `json:"completedBy"`

	// Completed indicates whether the milestone is completed or not.
	Completed bool `json:"completed"`

	// Status is the status of the milestone. It can be "new", "reopened",
	// "completed" or "deleted".
	Status string `json:"status"`
}

Milestone represents a significant point or goal within a project that marks the completion of a major phase or a key deliverable. It acts as a high-level indicator of progress, helping teams track whether work is advancing according to plan. Milestones are typically used to coordinate efforts across different tasks and task lists, providing a clear deadline or objective that multiple team members or departments can align around. They don't contain individual tasks themselves but serve as checkpoints to ensure the project is moving in the right direction.

More information can be found at: https://support.teamwork.com/projects/getting-started/milestones-overview

type MilestoneCreateRequest added in v0.3.0

type MilestoneCreateRequest struct {
	// Path contains the path parameters for the request.
	Path MilestoneCreateRequestPath `json:"-"`

	// Name is the name of the milestone.
	Name string `json:"title"`

	// Description is an optional description of the milestone.
	Description *string `json:"description,omitempty"`

	// DueAt is the due date of the milestone.
	DueAt LegacyDate `json:"deadline"`

	// TasklistIDs is an optional list of tasklist IDs to associate with the
	// milestone.
	TasklistIDs []int64 `json:"tasklistIds,omitempty"`

	// TagIDs is an optional list of tag IDs to associate with the milestone.
	TagIDs []int64 `json:"tagIds,omitempty"`

	// Assignees is a list of users, companies and teams responsible for the
	// milestone.
	Assignees LegacyUserGroups `json:"responsible-party-ids"`
}

MilestoneCreateRequest represents the request body for creating a new milestone.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/post-projects-id-milestones-json

func NewMilestoneCreateRequest added in v0.3.0

func NewMilestoneCreateRequest(
	projectID int64,
	name string,
	dueAt LegacyDate,
	assignees LegacyUserGroups,
) MilestoneCreateRequest

NewMilestoneCreateRequest creates a new MilestoneCreateRequest with the provided required fields.

func (MilestoneCreateRequest) HTTPRequest added in v0.3.0

func (m MilestoneCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the MilestoneCreateRequest.

type MilestoneCreateRequestPath added in v0.3.0

type MilestoneCreateRequestPath struct {
	// ProjectID is the unique identifier of the project that will contain the
	// milestone.
	ProjectID int64
}

MilestoneUpdateRequestPath contains the path parameters for creating a milestone.

type MilestoneCreateResponse added in v0.3.0

type MilestoneCreateResponse struct {
	// ID is the unique identifier of the created milestone.
	ID LegacyNumber `json:"milestoneId"`
}

MilestoneCreateResponse represents the response body for creating a new milestone.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/post-projects-id-milestones-json

func MilestoneCreate added in v0.3.0

func MilestoneCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req MilestoneCreateRequest,
) (*MilestoneCreateResponse, error)

MilestoneCreate creates a new milestone using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startMilestoneServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	milestoneRequest := projects.NewMilestoneCreateRequest(777, "New Milestone", projects.NewLegacyDate(time.Now()),
		projects.LegacyUserGroups{
			UserIDs: []int64{456, 789},
		},
	)
	milestoneRequest.Description = twapi.Ptr("This is a new milestone created via the API.")

	milestoneResponse, err := projects.MilestoneCreate(ctx, engine, milestoneRequest)
	if err != nil {
		fmt.Printf("failed to create milestone: %s", err)
	} else {
		fmt.Printf("created milestone with identifier %d\n", milestoneResponse.ID)
	}

}

func startMilestoneServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/milestones", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","milestoneId":"12345"}`)
	})
	mux.HandleFunc("PUT /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestone":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestones":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created milestone with identifier 12345

func (*MilestoneCreateResponse) HandleHTTPResponse added in v0.3.0

func (m *MilestoneCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the MilestoneCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type MilestoneDeleteRequest added in v0.3.0

type MilestoneDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path MilestoneDeleteRequestPath
}

MilestoneDeleteRequest represents the request body for deleting a milestone.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/delete-milestones-id-json

func NewMilestoneDeleteRequest added in v0.3.0

func NewMilestoneDeleteRequest(milestoneID int64) MilestoneDeleteRequest

NewMilestoneDeleteRequest creates a new MilestoneDeleteRequest with the provided milestone ID.

func (MilestoneDeleteRequest) HTTPRequest added in v0.3.0

func (m MilestoneDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the MilestoneDeleteRequest.

type MilestoneDeleteRequestPath added in v0.3.0

type MilestoneDeleteRequestPath struct {
	// ID is the unique identifier of the milestone to be deleted.
	ID int64
}

MilestoneDeleteRequestPath contains the path parameters for deleting a milestone.

type MilestoneDeleteResponse added in v0.3.0

type MilestoneDeleteResponse struct{}

MilestoneDeleteResponse represents the response body for deleting a milestone.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/delete-milestones-id-json

func MilestoneDelete added in v0.3.0

func MilestoneDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req MilestoneDeleteRequest,
) (*MilestoneDeleteResponse, error)

MilestoneDelete deletes a milestone using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startMilestoneServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.MilestoneDelete(ctx, engine, projects.NewMilestoneDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete milestone: %s", err)
	} else {
		fmt.Println("milestone deleted!")
	}

}

func startMilestoneServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/milestones", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","milestoneId":"12345"}`)
	})
	mux.HandleFunc("PUT /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestone":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestones":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

milestone deleted!

func (*MilestoneDeleteResponse) HandleHTTPResponse added in v0.3.0

func (m *MilestoneDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the MilestoneDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type MilestoneGetRequest added in v0.3.0

type MilestoneGetRequest struct {
	// Path contains the path parameters for the request.
	Path MilestoneGetRequestPath
}

MilestoneGetRequest represents the request body for loading a single milestone.

https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-milestones-mileston-id-json

func NewMilestoneGetRequest added in v0.3.0

func NewMilestoneGetRequest(milestoneID int64) MilestoneGetRequest

NewMilestoneGetRequest creates a new MilestoneGetRequest with the provided milestone ID. The ID is required to load a milestone.

func (MilestoneGetRequest) HTTPRequest added in v0.3.0

func (m MilestoneGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the MilestoneGetRequest.

type MilestoneGetRequestPath added in v0.3.0

type MilestoneGetRequestPath struct {
	// ID is the unique identifier of the milestone to be retrieved.
	ID int64 `json:"id"`
}

MilestoneGetRequestPath contains the path parameters for loading a single milestone.

type MilestoneGetResponse added in v0.3.0

type MilestoneGetResponse struct {
	Milestone Milestone `json:"milestone"`
}

MilestoneGetResponse contains all the information related to a milestone.

https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-milestones-mileston-id-json

func MilestoneGet added in v0.3.0

func MilestoneGet(
	ctx context.Context,
	engine *twapi.Engine,
	req MilestoneGetRequest,
) (*MilestoneGetResponse, error)

MilestoneGet retrieves a single milestone using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startMilestoneServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	milestoneResponse, err := projects.MilestoneGet(ctx, engine, projects.NewMilestoneGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve milestone: %s", err)
	} else {
		fmt.Printf("retrieved milestone with identifier %d\n", milestoneResponse.Milestone.ID)
	}

}

func startMilestoneServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/milestones", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","milestoneId":"12345"}`)
	})
	mux.HandleFunc("PUT /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestone":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestones":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved milestone with identifier 12345

func (*MilestoneGetResponse) HandleHTTPResponse added in v0.3.0

func (m *MilestoneGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the MilestoneGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type MilestoneListRequest added in v0.3.0

type MilestoneListRequest struct {
	// Path contains the path parameters for the request.
	Path MilestoneListRequestPath

	// Filters contains the filters for loading multiple milestones.
	Filters MilestoneListRequestFilters
}

MilestoneListRequest represents the request body for loading multiple milestones.

https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-milestones-json https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-projects-project-id-milestones-json

func NewMilestoneListRequest added in v0.3.0

func NewMilestoneListRequest() MilestoneListRequest

NewMilestoneListRequest creates a new MilestoneListRequest with default values.

func (MilestoneListRequest) HTTPRequest added in v0.3.0

func (m MilestoneListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the MilestoneListRequest.

type MilestoneListRequestFilters added in v0.3.0

type MilestoneListRequestFilters struct {
	// SearchTerm is an optional search term to filter milestones by name.
	SearchTerm string

	// TagIDs is an optional list of tag IDs to filter milestones by tags.
	TagIDs []int64

	// MatchAllTags is an optional flag to indicate if all tags must match. If set
	// to true, only milestones matching all specified tags will be returned.
	MatchAllTags *bool

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of milestones to retrieve per page. Defaults to 50.
	PageSize int64
}

MilestoneListRequestFilters contains the filters for loading multiple milestones.

type MilestoneListRequestPath added in v0.3.0

type MilestoneListRequestPath struct {
	// ProjectID is the unique identifier of the project whose milestones are to
	// be retrieved.
	ProjectID int64
}

MilestoneListRequestPath contains the path parameters for loading multiple milestones.

type MilestoneListResponse added in v0.3.0

type MilestoneListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Milestones []Milestone `json:"milestones"`
	// contains filtered or unexported fields
}

MilestoneListResponse contains information by multiple milestones matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-milestones-json https://apidocs.teamwork.com/docs/teamwork/v3/milestones/get-projects-api-v3-projects-project-id-milestones-json

func MilestoneList added in v0.3.0

func MilestoneList(
	ctx context.Context,
	engine *twapi.Engine,
	req MilestoneListRequest,
) (*MilestoneListResponse, error)

MilestoneList retrieves multiple milestones using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startMilestoneServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	milestonesRequest := projects.NewMilestoneListRequest()
	milestonesRequest.Filters.SearchTerm = "Example"

	milestonesResponse, err := projects.MilestoneList(ctx, engine, milestonesRequest)
	if err != nil {
		fmt.Printf("failed to list milestones: %s", err)
	} else {
		for _, milestone := range milestonesResponse.Milestones {
			fmt.Printf("retrieved milestone with identifier %d\n", milestone.ID)
		}
	}

}

func startMilestoneServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/milestones", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","milestoneId":"12345"}`)
	})
	mux.HandleFunc("PUT /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestone":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestones":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved milestone with identifier 12345
retrieved milestone with identifier 12346

func (*MilestoneListResponse) HandleHTTPResponse added in v0.3.0

func (m *MilestoneListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the MilestoneListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*MilestoneListResponse) Iterate added in v0.3.0

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*MilestoneListResponse) SetRequest added in v0.3.0

func (m *MilestoneListResponse) SetRequest(req MilestoneListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type MilestoneUpdateRequest added in v0.3.0

type MilestoneUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path MilestoneUpdateRequestPath `json:"-"`

	// Name is the name of the milestone.
	Name *string `json:"title,omitempty"`

	// Description is an optional description of the milestone.
	Description *string `json:"description,omitempty"`

	// DueAt is the due date of the milestone.
	DueAt *LegacyDate `json:"deadline,omitempty"`

	// TasklistIDs is an optional list of tasklist IDs to associate with the
	// milestone.
	TasklistIDs []int64 `json:"tasklistIds,omitempty"`

	// TagIDs is an optional list of tag IDs to associate with the milestone.
	TagIDs []int64 `json:"tagIds,omitempty"`

	// Assignees is a list of users, companies and teams responsible for the
	// milestone.
	Assignees *LegacyUserGroups `json:"responsible-party-ids,omitempty"`
}

MilestoneUpdateRequest represents the request body for updating a milestone. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/put-milestones-id-json

func NewMilestoneUpdateRequest added in v0.3.0

func NewMilestoneUpdateRequest(milestoneID int64) MilestoneUpdateRequest

NewMilestoneUpdateRequest creates a new MilestoneUpdateRequest with the provided milestone ID. The ID is required to update a milestone.

func (MilestoneUpdateRequest) HTTPRequest added in v0.3.0

func (m MilestoneUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the MilestoneUpdateRequest.

type MilestoneUpdateRequestPath added in v0.3.0

type MilestoneUpdateRequestPath struct {
	// ID is the unique identifier of the milestone to be updated.
	ID int64
}

MilestoneUpdateRequestPath contains the path parameters for updating a milestone.

type MilestoneUpdateResponse added in v0.3.0

type MilestoneUpdateResponse struct{}

MilestoneUpdateResponse represents the response body for updating a milestone.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/put-milestones-id-json

func MilestoneUpdate added in v0.3.0

func MilestoneUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req MilestoneUpdateRequest,
) (*MilestoneUpdateResponse, error)

MilestoneUpdate updates a milestone using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startMilestoneServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	milestoneRequest := projects.NewMilestoneUpdateRequest(12345)
	milestoneRequest.Description = twapi.Ptr("This is an updated description.")

	_, err = projects.MilestoneUpdate(ctx, engine, milestoneRequest)
	if err != nil {
		fmt.Printf("failed to update milestone: %s", err)
	} else {
		fmt.Println("milestone updated!")
	}

}

func startMilestoneServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/milestones", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","milestoneId":"12345"}`)
	})
	mux.HandleFunc("PUT /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestone":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/milestones", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"milestones":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

milestone updated!

func (*MilestoneUpdateResponse) HandleHTTPResponse added in v0.3.0

func (m *MilestoneUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the MilestoneUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Project added in v0.0.3

type Project struct {
	// ID is the unique identifier of the project.
	ID int64 `json:"id"`

	// Description is an optional description of the project.
	Description *string `json:"description"`

	// Name is the name of the project.
	Name string `json:"name"`

	// StartAt is the start date of the project.
	StartAt *time.Time `json:"startAt"`

	// EndAt is the end date of the project.
	EndAt *time.Time `json:"endAt"`

	// Company is the company associated with the project.
	Company twapi.Relationship `json:"company"`

	// Owner is the user who owns the project.
	Owner *twapi.Relationship `json:"projectOwner"`

	// Tags is a list of tags associated with the project.
	Tags []twapi.Relationship `json:"tags"`

	// CreatedAt is the date and time when the project was created.
	CreatedAt *time.Time `json:"createdAt"`

	// CreatedBy is the ID of the user who created the project.
	CreatedBy *int64 `json:"createdBy"`

	// UpdatedAt is the date and time when the project was last updated.
	UpdatedAt *time.Time `json:"updatedAt"`

	// UpdatedBy is the ID of the user who last updated the project.
	UpdatedBy *int64 `json:"updatedBy"`

	// CompletedAt is the date and time when the project was completed.
	CompletedAt *time.Time `json:"completedAt"`

	// CompletedBy is the ID of the user who completed the project.
	CompletedBy *int64 `json:"completedBy"`

	// Status is the status of the project. It can be "active", "inactive"
	// (archived) or "deleted".
	Status string `json:"status"`

	// Type is the type of the project. It can be "normal", "tasklists-template",
	// "projects-template", "personal", "holder-project", "tentative" or
	// "global-messages".
	Type string `json:"type"`
}

Project serves as the central workspace for organizing and managing a specific piece of work or initiative. Each project provides a dedicated area where teams can plan tasks, assign responsibilities, set deadlines, and track progress toward shared goals. Projects include tools for communication, file sharing, milestones, and time tracking, allowing teams to stay aligned and informed throughout the entire lifecycle of the work. Whether it's a product launch, client engagement, or internal initiative, projects in Teamwork.com help teams structure their efforts, collaborate more effectively, and deliver results with greater visibility and accountability.

More information can be found at: https://support.teamwork.com/projects/getting-started/projects-overview

type ProjectCreateRequest

type ProjectCreateRequest struct {
	// Name is the name of the project.
	Name string `json:"name"`

	// Description is an optional description of the project.
	Description *string `json:"description,omitempty"`

	// StartAt is an optional start date for the project. By default it doesn't
	// have a start date.
	StartAt *LegacyDate `json:"start-date,omitempty"`

	// EndAt is an optional end date for the project. By default it doesn't have
	// an end date.
	EndAt *LegacyDate `json:"end-date,omitempty"`

	// CompanyID is an optional ID of the company/client associated with the
	// project. By default it is the ID of the company of the logged user
	// creating the project.
	CompanyID int64 `json:"companyId"`

	// OwnerID is an optional ID of the user who owns the project. By default it
	// is the ID of the logged user creating the project.
	OwnerID *int64 `json:"projectOwnerId,omitempty"`

	// TagIDs is an optional list of tag IDs associated with the project.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

ProjectCreateRequest represents the request body for creating a new project.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/post-projects-json

func NewProjectCreateRequest added in v0.0.6

func NewProjectCreateRequest(name string) ProjectCreateRequest

NewProjectCreateRequest creates a new ProjectCreateRequest with the provided name. The name is required to create a new project.

func (ProjectCreateRequest) HTTPRequest

func (p ProjectCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectCreateRequest.

type ProjectCreateResponse

type ProjectCreateResponse struct {
	// ID is the unique identifier of the created project.
	ID LegacyNumber `json:"id"`
}

ProjectCreateResponse represents the response body for creating a new project.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/post-projects-json

func ProjectCreate

func ProjectCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectCreateRequest,
) (*ProjectCreateResponse, error)

ProjectCreate creates a new project using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	projectRequest := projects.NewProjectCreateRequest("New Project")
	projectRequest.Description = twapi.Ptr("This is a new project created via the API.")
	projectRequest.StartAt = twapi.Ptr(projects.LegacyDate(time.Now().AddDate(0, 0, 1)))
	projectRequest.EndAt = twapi.Ptr(projects.LegacyDate(time.Now().AddDate(0, 0, 30)))
	projectRequest.CompanyID = 12345
	projectRequest.OwnerID = twapi.Ptr(int64(67890))
	projectRequest.TagIDs = []int64{11111, 22222}

	projectResponse, err := projects.ProjectCreate(ctx, engine, projectRequest)
	if err != nil {
		fmt.Printf("failed to create project: %s", err)
	} else {
		fmt.Printf("created project with identifier %d\n", projectResponse.ID)
	}

}

func startProjectServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"project":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"projects":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created project with identifier 12345

func (*ProjectCreateResponse) HandleHTTPResponse

func (p *ProjectCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type ProjectDeleteRequest added in v0.0.3

type ProjectDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path ProjectDeleteRequestPath
}

ProjectDeleteRequest represents the request body for deleting a project.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/delete-projects-id-json

func NewProjectDeleteRequest added in v0.0.6

func NewProjectDeleteRequest(projectID int64) ProjectDeleteRequest

NewProjectDeleteRequest creates a new ProjectDeleteRequest with the provided project ID.

func (ProjectDeleteRequest) HTTPRequest added in v0.0.3

func (p ProjectDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectDeleteRequest.

type ProjectDeleteRequestPath added in v0.0.6

type ProjectDeleteRequestPath struct {
	// ID is the unique identifier of the project to be deleted.
	ID int64
}

ProjectDeleteRequestPath contains the path parameters for deleting a project.

type ProjectDeleteResponse added in v0.0.3

type ProjectDeleteResponse struct{}

ProjectDeleteResponse represents the response body for deleting a project.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/delete-projects-id-json

func ProjectDelete added in v0.0.3

func ProjectDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectDeleteRequest,
) (*ProjectDeleteResponse, error)

ProjectDelete deletes a project using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.ProjectDelete(ctx, engine, projects.NewProjectDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete project: %s", err)
	} else {
		fmt.Println("project deleted!")
	}

}

func startProjectServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"project":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"projects":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

project deleted!

func (*ProjectDeleteResponse) HandleHTTPResponse added in v0.0.3

func (p *ProjectDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type ProjectGetRequest added in v0.0.6

type ProjectGetRequest struct {
	// Path contains the path parameters for the request.
	Path ProjectGetRequestPath
}

ProjectGetRequest represents the request body for loading a single project.

https://apidocs.teamwork.com/docs/teamwork/v3/projects/get-projects-api-v3-projects-project-id-json

func NewProjectGetRequest added in v0.0.6

func NewProjectGetRequest(projectID int64) ProjectGetRequest

NewProjectGetRequest creates a new ProjectGetRequest with the provided project ID. The ID is required to load a project.

func (ProjectGetRequest) HTTPRequest added in v0.0.6

func (p ProjectGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectGetRequest.

type ProjectGetRequestPath added in v0.0.6

type ProjectGetRequestPath struct {
	// ID is the unique identifier of the project to be retrieved.
	ID int64 `json:"id"`
}

ProjectGetRequestPath contains the path parameters for loading a single project.

type ProjectGetResponse added in v0.0.6

type ProjectGetResponse struct {
	Project Project `json:"project"`
}

ProjectGetResponse contains all the information related to a project.

https://apidocs.teamwork.com/docs/teamwork/v3/projects/get-projects-api-v3-projects-project-id-json

func ProjectGet added in v0.0.6

func ProjectGet(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectGetRequest,
) (*ProjectGetResponse, error)

ProjectGet retrieves a single project using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	projectResponse, err := projects.ProjectGet(ctx, engine, projects.NewProjectGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve project: %s", err)
	} else {
		fmt.Printf("retrieved project with identifier %d\n", projectResponse.Project.ID)
	}

}

func startProjectServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"project":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"projects":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved project with identifier 12345

func (*ProjectGetResponse) HandleHTTPResponse added in v0.0.6

func (p *ProjectGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type ProjectListRequest added in v0.0.6

type ProjectListRequest struct {
	// Filters contains the filters for loading multiple projects.
	Filters ProjectListRequestFilters
}

ProjectListRequest represents the request body for loading multiple projects.

https://apidocs.teamwork.com/docs/teamwork/v3/projects/get-projects-api-v3-projects-json

func NewProjectListRequest added in v0.0.6

func NewProjectListRequest() ProjectListRequest

NewProjectListRequest creates a new ProjectListRequest with default values.

func (ProjectListRequest) HTTPRequest added in v0.0.6

func (p ProjectListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectListRequest.

type ProjectListRequestFilters added in v0.0.6

type ProjectListRequestFilters struct {
	// SearchTerm is an optional search term to filter projects by name or
	// description.
	SearchTerm string

	// TagIDs is an optional list of tag IDs to filter projects by tags.
	TagIDs []int64

	// MatchAllTags is an optional flag to indicate if all tags must match. If
	// set to true, only projects matching all specified tags will be returned.
	MatchAllTags *bool

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of projects to retrieve per page. Defaults to 50.
	PageSize int64
}

ProjectListRequestFilters contains the filters for loading multiple projects.

type ProjectListResponse added in v0.0.6

type ProjectListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Projects []Project `json:"projects"`
	// contains filtered or unexported fields
}

ProjectListResponse contains information by multiple projects matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/projects/get-projects-api-v3-projects-json

func ProjectList added in v0.0.6

func ProjectList(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectListRequest,
) (*ProjectListResponse, error)

ProjectList retrieves multiple projects using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	projectsRequest := projects.NewProjectListRequest()
	projectsRequest.Filters.SearchTerm = "Example"

	projectsResponse, err := projects.ProjectList(ctx, engine, projectsRequest)
	if err != nil {
		fmt.Printf("failed to list projects: %s", err)
	} else {
		for _, project := range projectsResponse.Projects {
			fmt.Printf("retrieved project with identifier %d\n", project.ID)
		}
	}

}

func startProjectServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"project":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"projects":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved project with identifier 12345
retrieved project with identifier 12346

func (*ProjectListResponse) HandleHTTPResponse added in v0.0.6

func (p *ProjectListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*ProjectListResponse) Iterate added in v0.0.7

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*ProjectListResponse) SetRequest added in v0.0.7

func (p *ProjectListResponse) SetRequest(req ProjectListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type ProjectMemberAddRequest added in v0.3.0

type ProjectMemberAddRequest struct {
	// Path contains the path parameters for the request.
	Path ProjectMemberAddRequestPath `json:"-"`

	// UserIDs is a list of user IDs to add as project members.
	UserIDs []int64 `json:"userIds"`
}

ProjectMemberAddRequest represents the request body for adding users as project members.

https://apidocs.teamwork.com/docs/teamwork/v3/people/put-projects-api-v3-projects-project-id-people-json

func NewProjectMemberAddRequest added in v0.3.0

func NewProjectMemberAddRequest(projectID int64, userIDs ...int64) ProjectMemberAddRequest

NewProjectMemberAddRequest creates a new ProjectMemberAddRequest with the provided project and user IDs.

func (ProjectMemberAddRequest) HTTPRequest added in v0.3.0

func (u ProjectMemberAddRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectMemberAddRequest.

type ProjectMemberAddRequestPath added in v0.3.0

type ProjectMemberAddRequestPath struct {
	ProjectID int64 `json:"projectId"`
}

ProjectMemberAddRequestPath contains the path parameters for adding users as project members.

type ProjectMemberAddResponse added in v0.3.0

type ProjectMemberAddResponse struct{}

ProjectMemberAddResponse represents the response body for adding users as project members.

https://apidocs.teamwork.com/docs/teamwork/v3/people/put-projects-api-v3-projects-project-id-people-json

func ProjectMemberAdd added in v0.3.0

func ProjectMemberAdd(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectMemberAddRequest,
) (*ProjectMemberAddResponse, error)

ProjectMemberAdd adds users to a project.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectMemberServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.ProjectMemberAdd(ctx, engine, projects.NewProjectMemberAddRequest(123, 456, 789))
	if err != nil {
		fmt.Printf("failed to add project members: %s", err)
	} else {
		fmt.Println("added users as project members")
	}

}

func startProjectMemberServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("PUT /projects/api/v3/projects/{id}/people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "123" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"usersAdded":[456],"usersAlreadyInProject":[789],"usersNotAdded":[]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

added users as project members

func (*ProjectMemberAddResponse) HandleHTTPResponse added in v0.3.0

func (u *ProjectMemberAddResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectMemberAddResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type ProjectUpdateRequest added in v0.0.3

type ProjectUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path ProjectUpdateRequestPath

	// Name is the name of the project.
	Name *string `json:"name,omitempty"`

	// Description is the project description.
	Description *string `json:"description,omitempty"`

	// StartAt is the start date for the project.
	StartAt *LegacyDate `json:"start-date,omitempty"`

	// EndAt is the end date for the project.
	EndAt *LegacyDate `json:"end-date,omitempty"`

	// CompanyID is the company/client associated with the project.
	CompanyID *int64 `json:"companyId,omitempty"`

	// OwnerID is the ID of the user who owns the project.
	OwnerID *int64 `json:"projectOwnerId,omitempty"`

	// TagIDs is the list of tag IDs associated with the project.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

ProjectUpdateRequest represents the request body for updating a project. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/put-projects-id-json

func NewProjectUpdateRequest added in v0.0.6

func NewProjectUpdateRequest(projectID int64) ProjectUpdateRequest

NewProjectUpdateRequest creates a new ProjectUpdateRequest with the provided project ID. The ID is required to update a project.

func (ProjectUpdateRequest) HTTPRequest added in v0.0.3

func (p ProjectUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the ProjectUpdateRequest.

type ProjectUpdateRequestPath added in v0.0.6

type ProjectUpdateRequestPath struct {
	// ID is the unique identifier of the project to be updated.
	ID int64
}

ProjectUpdateRequestPath contains the path parameters for updating a project.

type ProjectUpdateResponse added in v0.0.3

type ProjectUpdateResponse struct{}

ProjectUpdateResponse represents the response body for updating a project.

https://apidocs.teamwork.com/docs/teamwork/v1/projects/put-projects-id-json

func ProjectUpdate added in v0.0.3

func ProjectUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req ProjectUpdateRequest,
) (*ProjectUpdateResponse, error)

ProjectUpdate updates a project using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startProjectServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	projectRequest := projects.NewProjectUpdateRequest(12345)
	projectRequest.Description = twapi.Ptr("This is an updated description.")

	_, err = projects.ProjectUpdate(ctx, engine, projectRequest)
	if err != nil {
		fmt.Printf("failed to update project: %s", err)
	} else {
		fmt.Println("project updated!")
	}

}

func startProjectServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"project":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/projects", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"projects":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

project updated!

func (*ProjectUpdateResponse) HandleHTTPResponse added in v0.0.3

func (p *ProjectUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the ProjectUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Tag added in v0.4.0

type Tag struct {
	// ID is the unique identifier of the tag.
	ID int64 `json:"id"`

	// Name is the name of the tag.
	Name string `json:"name"`

	// Project is the project the tag belongs to.
	Project *twapi.Relationship `json:"project"`
}

Tag is a customizable label that can be applied to various items such as tasks, projects, milestones, messages, and more, to help categorize and organize work efficiently. Tags provide a flexible way to filter, search, and group related items across the platform, making it easier for teams to manage complex workflows, highlight priorities, or track themes and statuses. Since tags are user-defined, they adapt to each team’s specific needs and can be color-coded for better visual clarity.

More information can be found at: https://support.teamwork.com/projects/glossary/tags-overview

type TagCreateRequest added in v0.4.0

type TagCreateRequest struct {
	// Name is the name of the tag. This field is required. It must be less than
	// 50 characters.
	Name string `json:"name"`

	// ProjectID is the unique identifier of the project the tag belongs to. This
	// is for project-scoped tags.
	ProjectID *int64 `json:"projectId,omitempty"`
}

TagCreateRequest represents the request body for creating a new tag.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/post-projects-api-v3-tags-json

func NewTagCreateRequest added in v0.4.0

func NewTagCreateRequest(name string) TagCreateRequest

NewTagCreateRequest creates a new TagCreateRequest with the provided name.

func (TagCreateRequest) HTTPRequest added in v0.4.0

func (t TagCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TagCreateRequest.

type TagCreateResponse added in v0.4.0

type TagCreateResponse struct {
	// Tag is the created tag.
	Tag Tag `json:"tag"`
}

TagCreateResponse represents the response body for creating a new tag.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/post-projects-api-v3-tags-json

func TagCreate added in v0.4.0

func TagCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req TagCreateRequest,
) (*TagCreateResponse, error)

TagCreate creates a new tag using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTagServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tagResponse, err := projects.TagCreate(ctx, engine, projects.NewTagCreateRequest("Test Tag"))
	if err != nil {
		fmt.Printf("failed to create tag: %s", err)
	} else {
		fmt.Printf("created tag with identifier %d\n", tagResponse.Tag.ID)
	}

}

func startTagServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tags", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tags", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tags":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created tag with identifier 12345

func (*TagCreateResponse) HandleHTTPResponse added in v0.4.0

func (t *TagCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TagCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TagDeleteRequest added in v0.4.0

type TagDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path TagDeleteRequestPath
}

TagDeleteRequest represents the request body for deleting a tag.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/delete-projects-api-v3-tags-tag-id-json

func NewTagDeleteRequest added in v0.4.0

func NewTagDeleteRequest(tagID int64) TagDeleteRequest

NewTagDeleteRequest creates a new TagDeleteRequest with the provided tag ID.

func (TagDeleteRequest) HTTPRequest added in v0.4.0

func (t TagDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TagDeleteRequest.

type TagDeleteRequestPath added in v0.4.0

type TagDeleteRequestPath struct {
	// ID is the unique identifier of the tag to be deleted.
	ID int64
}

TagDeleteRequestPath contains the path parameters for deleting a tag.

type TagDeleteResponse added in v0.4.0

type TagDeleteResponse struct{}

TagDeleteResponse represents the response body for deleting a tag.

https://apidocs.teamwork.com/docs/teamwork/v1/tags/delete-tags-id-json

func TagDelete added in v0.4.0

func TagDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req TagDeleteRequest,
) (*TagDeleteResponse, error)

TagDelete deletes a tag using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTagServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.TagDelete(ctx, engine, projects.NewTagDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete tag: %s", err)
	} else {
		fmt.Println("tag deleted!")
	}

}

func startTagServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tags", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tags", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tags":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

tag deleted!

func (*TagDeleteResponse) HandleHTTPResponse added in v0.4.0

func (t *TagDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TagDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TagGetRequest added in v0.4.0

type TagGetRequest struct {
	// Path contains the path parameters for the request.
	Path TagGetRequestPath
}

TagGetRequest represents the request body for loading a single tag.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/get-projects-api-v3-tags-tag-id-json

func NewTagGetRequest added in v0.4.0

func NewTagGetRequest(tagID int64) TagGetRequest

NewTagGetRequest creates a new TagGetRequest with the provided tag ID. The ID is required to load a tag.

func (TagGetRequest) HTTPRequest added in v0.4.0

func (t TagGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TagGetRequest.

type TagGetRequestPath added in v0.4.0

type TagGetRequestPath struct {
	// ID is the unique identifier of the tag to be retrieved.
	ID int64 `json:"id"`
}

TagGetRequestPath contains the path parameters for loading a single tag.

type TagGetResponse added in v0.4.0

type TagGetResponse struct {
	Tag Tag `json:"tag"`
}

TagGetResponse contains all the information related to a tag.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/get-projects-api-v3-tags-tag-id-json

func TagGet added in v0.4.0

func TagGet(
	ctx context.Context,
	engine *twapi.Engine,
	req TagGetRequest,
) (*TagGetResponse, error)

TagGet retrieves a single tag using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTagServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tagResponse, err := projects.TagGet(ctx, engine, projects.NewTagGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve tag: %s", err)
	} else {
		fmt.Printf("retrieved tag with identifier %d\n", tagResponse.Tag.ID)
	}

}

func startTagServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tags", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tags", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tags":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved tag with identifier 12345

func (*TagGetResponse) HandleHTTPResponse added in v0.4.0

func (t *TagGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TagGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TagListRequest added in v0.4.0

type TagListRequest struct {
	// Filters contains the filters for loading multiple tags.
	Filters TagListRequestFilters
}

TagListRequest represents the request body for loading multiple tags.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/get-projects-api-v3-tags-json

func NewTagListRequest added in v0.4.0

func NewTagListRequest() TagListRequest

NewTagListRequest creates a new TagListRequest with default values.

func (TagListRequest) HTTPRequest added in v0.4.0

func (t TagListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TagListRequest.

type TagListRequestFilters added in v0.4.0

type TagListRequestFilters struct {
	// SearchTerm is an optional search term to filter tags by name.
	SearchTerm string

	// ItemType is the type of item the tag is associated with. Valid values are
	// 'project', 'task', 'tasklist', 'milestone', 'message', 'timelog',
	// 'notebook', 'file', 'company' and 'link'.
	ItemType string

	// ProjectIDs is an optional list of project IDs to filter tags by
	// belonging to specific projects.
	ProjectIDs []int64

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of tags to retrieve per page. Defaults to 50.
	PageSize int64
}

TagListRequestFilters contains the filters for loading multiple tags.

type TagListResponse added in v0.4.0

type TagListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Tags []Tag `json:"tags"`
	// contains filtered or unexported fields
}

TagListResponse contains information by multiple tags matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/get-projects-api-v3-tags-json

func TagList added in v0.4.0

func TagList(
	ctx context.Context,
	engine *twapi.Engine,
	req TagListRequest,
) (*TagListResponse, error)

TagList retrieves multiple tags using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTagServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tagsRequest := projects.NewTagListRequest()
	tagsRequest.Filters.SearchTerm = "Q&A"

	tagsResponse, err := projects.TagList(ctx, engine, tagsRequest)
	if err != nil {
		fmt.Printf("failed to list tags: %s", err)
	} else {
		for _, tag := range tagsResponse.Tags {
			fmt.Printf("retrieved tag with identifier %d\n", tag.ID)
		}
	}

}

func startTagServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tags", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tags", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tags":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved tag with identifier 12345
retrieved tag with identifier 12346

func (*TagListResponse) HandleHTTPResponse added in v0.4.0

func (t *TagListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TagListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*TagListResponse) Iterate added in v0.4.0

func (t *TagListResponse) Iterate() *TagListRequest

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*TagListResponse) SetRequest added in v0.4.0

func (t *TagListResponse) SetRequest(req TagListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type TagUpdateRequest added in v0.4.0

type TagUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path TagUpdateRequestPath `json:"-"`

	// Name is the name of the tag. It must be less than 50 characters when
	// provided.
	Name *string `json:"name,omitempty"`

	// ProjectID is the unique identifier of the project the tag belongs to. This
	// is for project-scoped tags.
	ProjectID *int64 `json:"projectId,omitempty"`
}

TagUpdateRequest represents the request body for updating a tag. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v3/tags/patch-projects-api-v3-tags-tag-id-json

func NewTagUpdateRequest added in v0.4.0

func NewTagUpdateRequest(tagID int64) TagUpdateRequest

NewTagUpdateRequest creates a new TagUpdateRequest with the provided tag ID. The ID is required to update a tag.

func (TagUpdateRequest) HTTPRequest added in v0.4.0

func (t TagUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TagUpdateRequest.

type TagUpdateRequestPath added in v0.4.0

type TagUpdateRequestPath struct {
	// ID is the unique identifier of the tag to be updated.
	ID int64
}

TagUpdateRequestPath contains the path parameters for updating a tag.

type TagUpdateResponse added in v0.4.0

type TagUpdateResponse struct {
	// Tag is the updated tag.
	Tag Tag `json:"tag"`
}

TagUpdateResponse represents the response body for updating a tag.

https://apidocs.teamwork.com/docs/teamwork/v1/tags/put-tags-id-json

func TagUpdate added in v0.4.0

func TagUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req TagUpdateRequest,
) (*TagUpdateResponse, error)

TagUpdate updates a tag using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTagServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tagRequest := projects.NewTagUpdateRequest(12345)
	tagRequest.Name = twapi.Ptr("Updated tag")

	_, err = projects.TagUpdate(ctx, engine, tagRequest)
	if err != nil {
		fmt.Printf("failed to update tag: %s", err)
	} else {
		fmt.Println("tag updated!")
	}

}

func startTagServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tags", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/tags/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tag":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tags", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tags":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

tag updated!

func (*TagUpdateResponse) HandleHTTPResponse added in v0.4.0

func (t *TagUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TagUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Task added in v0.2.0

type Task struct {
	// ID is the unique identifier of the task.
	ID int64 `json:"id"`

	// Name is the name of the task.
	Name string `json:"name"`

	// Description is the description of the task.
	Description *string `json:"description"`

	// DescriptionContentType is the content type of the description. It can be
	// "TEXT" or "HTML".
	DescriptionContentType *string `json:"descriptionContentType"`

	// Priority is the priority of the task. It can be "none", "low", "medium" or
	// "high".
	Priority *string `json:"priority"`

	// Progress is the progress of the task, in percentage (0-100).
	Progress int64 `json:"progress"`

	// StartAt is the date and time when the task is scheduled to start.
	StartAt *time.Time `json:"startDate"`

	// DueAt is the date and time when the task is scheduled to be completed.
	DueAt *time.Time `json:"dueDate"`

	// EstimatedMinutes is the estimated time to complete the task, in minutes.
	EstimatedMinutes int64 `json:"estimateMinutes"`

	// Tasklist is the relationship to the tasklist containing this task.
	Tasklist twapi.Relationship `json:"tasklist"`

	// Assignees is the list of users, teams or clients/companies assigned to this
	// task.
	Assignees []twapi.Relationship `json:"assignees"`

	// Tags is the list of tags associated with this task.
	Tags []twapi.Relationship `json:"tags"`

	// CreatedBy is the ID of the user who created the task.
	CreatedBy *int64 `json:"createdBy"`

	// CreatedAt is the date and time when the task was created.
	CreatedAt *time.Time `json:"createdAt"`

	// UpdatedBy is the ID of the user who last updated the task.
	UpdatedBy *int64 `json:"updatedBy"`

	// UpdatedAt is the date and time when the task was last updated.
	UpdatedAt time.Time `json:"updatedAt"`

	// DeletedBy is the ID of the user who deleted the task, if it was deleted.
	DeletedBy *int64 `json:"deletedBy"`

	// DeletedAt is the date and time when the task was deleted, if it was
	// deleted.
	DeletedAt *time.Time `json:"deletedAt"`

	// CompletedBy is the ID of the user who completed the task, if it was
	// completed.
	CompletedBy *int64 `json:"completedBy,omitempty"`

	// CompletedAt is the date and time when the task was completed, if it was
	// completed.
	CompletedAt *time.Time `json:"completedAt,omitempty"`

	// Status is the status of the task. It can be "new", "reopened", "completed"
	// or "deleted".
	Status string `json:"status"`
}

Task represents an individual unit of work assigned to one or more team members within a project. Each task can include details such as a title, description, priority, estimated time, assignees, and due date, along with the ability to attach files, leave comments, track time, and set dependencies on other tasks. Tasks are organized within task lists, helping structure and sequence work logically. They serve as the building blocks of project management in Teamwork, allowing teams to collaborate, monitor progress, and ensure accountability throughout the project's lifecycle.

More information can be found at: https://support.teamwork.com/projects/getting-started/tasks-overview

type TaskCreateRequest added in v0.2.0

type TaskCreateRequest struct {
	// Path contains the path parameters for the request.
	Path TaskCreateRequestPath `json:"-"`

	// Name is the name of the task
	Name string `json:"name"`

	// Description is an optional description of the task.
	Description *string `json:"description,omitempty"`

	// Priority is the priority of the task. It can be "none", "low", "medium" or
	// "high".
	Priority *string `json:"priority,omitempty"`

	// Progress is the progress of the task, in percentage (0-100).
	Progress *int64 `json:"progress,omitempty"`

	// StartAt is the date and time when the task is scheduled to start.
	StartAt *twapi.Date `json:"startAt,omitempty"`

	// DueAt is the date and time when the task is scheduled to be completed.
	DueAt *twapi.Date `json:"dueAt,omitempty"`

	// EstimatedMinutes is the estimated time to complete the task, in minutes.
	EstimatedMinutes *int64 `json:"estimatedMinutes,omitempty"`

	// Assignees is the list of users, teams or clients/companies assigned to this
	// task.
	Assignees *UserGroups `json:"assignees,omitempty"`

	// TagIDs is the list of tag IDs associated with this task.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

TaskCreateRequest represents the request body for creating a new task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/post-projects-api-v3-tasklists-tasklist-id-tasks-json

func NewTaskCreateRequest added in v0.2.0

func NewTaskCreateRequest(tasklistID int64, name string) TaskCreateRequest

NewTaskCreateRequest creates a new TaskCreateRequest with the provided name in a specific tasklist.

func (TaskCreateRequest) HTTPRequest added in v0.2.0

func (t TaskCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TaskCreateRequest.

type TaskCreateRequestPath added in v0.2.0

type TaskCreateRequestPath struct {
	// TasklistID is the unique identifier of the tasklist that will contain the
	// task.
	TasklistID int64
}

TaskUpdateRequestPath contains the path parameters for creating a task.

type TaskCreateResponse added in v0.2.0

type TaskCreateResponse struct {
	// Task is the created task.
	Task Task `json:"task"`
}

TaskCreateResponse represents the response body for creating a new task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/post-projects-api-v3-tasklists-tasklist-id-tasks-json

func TaskCreate added in v0.2.0

func TaskCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req TaskCreateRequest,
) (*TaskCreateResponse, error)

TaskCreate creates a new task using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTaskServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	taskRequest := projects.NewTaskCreateRequest(777, "New Task")
	taskRequest.Description = twapi.Ptr("This is a new task created via the API.")

	taskResponse, err := projects.TaskCreate(ctx, engine, taskRequest)
	if err != nil {
		fmt.Printf("failed to create task: %s", err)
	} else {
		fmt.Printf("created task with identifier %d\n", taskResponse.Task.ID)
	}

}

func startTaskServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasklists/{id}/tasks", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("PUT /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"affected":{"taskIds":[12345]}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasks":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created task with identifier 12345

func (*TaskCreateResponse) HandleHTTPResponse added in v0.2.0

func (t *TaskCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TaskCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TaskDeleteRequest added in v0.2.0

type TaskDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path TaskDeleteRequestPath
}

TaskDeleteRequest represents the request body for deleting a task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/delete-projects-api-v3-tasks-task-id-json

func NewTaskDeleteRequest added in v0.2.0

func NewTaskDeleteRequest(taskID int64) TaskDeleteRequest

NewTaskDeleteRequest creates a new TaskDeleteRequest with the provided task ID.

func (TaskDeleteRequest) HTTPRequest added in v0.2.0

func (t TaskDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TaskDeleteRequest.

type TaskDeleteRequestPath added in v0.2.0

type TaskDeleteRequestPath struct {
	// ID is the unique identifier of the task to be deleted.
	ID int64
}

TaskDeleteRequestPath contains the path parameters for deleting a task.

type TaskDeleteResponse added in v0.2.0

type TaskDeleteResponse struct{}

TaskDeleteResponse represents the response body for deleting a task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/delete-projects-api-v3-tasks-task-id-json

func TaskDelete added in v0.2.0

func TaskDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req TaskDeleteRequest,
) (*TaskDeleteResponse, error)

TaskDelete deletes a task using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTaskServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.TaskDelete(ctx, engine, projects.NewTaskDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete task: %s", err)
	} else {
		fmt.Println("task deleted!")
	}

}

func startTaskServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasklists/{id}/tasks", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("PUT /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"affected":{"taskIds":[12345]}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasks":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

task deleted!

func (*TaskDeleteResponse) HandleHTTPResponse added in v0.2.0

func (t *TaskDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TaskDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TaskGetRequest added in v0.2.0

type TaskGetRequest struct {
	// Path contains the path parameters for the request.
	Path TaskGetRequestPath
}

TaskGetRequest represents the request body for loading a single task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasks-task-id-json

func NewTaskGetRequest added in v0.2.0

func NewTaskGetRequest(taskID int64) TaskGetRequest

NewTaskGetRequest creates a new TaskGetRequest with the provided task ID. The ID is required to load a task.

func (TaskGetRequest) HTTPRequest added in v0.2.0

func (t TaskGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TaskGetRequest.

type TaskGetRequestPath added in v0.2.0

type TaskGetRequestPath struct {
	// ID is the unique identifier of the task to be retrieved.
	ID int64 `json:"id"`
}

TaskGetRequestPath contains the path parameters for loading a single task.

type TaskGetResponse added in v0.2.0

type TaskGetResponse struct {
	Task Task `json:"task"`
}

TaskGetResponse contains all the information related to a task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasks-task-id-json

func TaskGet added in v0.2.0

func TaskGet(
	ctx context.Context,
	engine *twapi.Engine,
	req TaskGetRequest,
) (*TaskGetResponse, error)

TaskGet retrieves a single task using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTaskServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	taskResponse, err := projects.TaskGet(ctx, engine, projects.NewTaskGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve task: %s", err)
	} else {
		fmt.Printf("retrieved task with identifier %d\n", taskResponse.Task.ID)
	}

}

func startTaskServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasklists/{id}/tasks", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("PUT /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"affected":{"taskIds":[12345]}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasks":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved task with identifier 12345

func (*TaskGetResponse) HandleHTTPResponse added in v0.2.0

func (t *TaskGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TaskGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TaskListRequest added in v0.2.0

type TaskListRequest struct {
	// Path contains the path parameters for the request.
	Path TaskListRequestPath

	// Filters contains the filters for loading multiple tasks.
	Filters TaskListRequestFilters
}

TaskListRequest represents the request body for loading multiple tasks.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasks-json https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-projects-project-id-tasks-json https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasklists-tasklist-id-tasks-json

func NewTaskListRequest added in v0.2.0

func NewTaskListRequest() TaskListRequest

NewTaskListRequest creates a new TaskListRequest with default values.

func (TaskListRequest) HTTPRequest added in v0.2.0

func (t TaskListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TaskListRequest.

type TaskListRequestFilters added in v0.2.0

type TaskListRequestFilters struct {
	// SearchTerm is an optional search term to filter tasks by name, description
	// or tasklist's name.
	SearchTerm string

	// TagIDs is an optional list of tag IDs to filter tasks by tags.
	TagIDs []int64

	// MatchAllTags is an optional flag to indicate if all tags must match. If set
	// to true, only tasks matching all specified tags will be returned.
	MatchAllTags *bool

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of tasks to retrieve per page. Defaults to 50.
	PageSize int64
}

TaskListRequestFilters contains the filters for loading multiple tasks.

type TaskListRequestPath added in v0.2.0

type TaskListRequestPath struct {
	// ProjectID is the unique identifier of the project whose tasks are to be
	// retrieved.
	ProjectID int64
	// TasklistID is the unique identifier of the tasklist whose tasks are to be
	// retrieved. If provided, the ProjectID is ignored.
	TasklistID int64
}

TaskListRequestPath contains the path parameters for loading multiple tasks.

type TaskListResponse added in v0.2.0

type TaskListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Tasks []Task `json:"tasks"`
	// contains filtered or unexported fields
}

TaskListResponse contains information by multiple tasks matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasks-json https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-projects-project-id-tasks-json https://apidocs.teamwork.com/docs/teamwork/v3/tasks/get-projects-api-v3-tasklists-tasklist-id-tasks-json

func TaskList added in v0.2.0

func TaskList(
	ctx context.Context,
	engine *twapi.Engine,
	req TaskListRequest,
) (*TaskListResponse, error)

TaskList retrieves multiple tasks using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTaskServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tasksRequest := projects.NewTaskListRequest()
	tasksRequest.Filters.SearchTerm = "Example"

	tasksResponse, err := projects.TaskList(ctx, engine, tasksRequest)
	if err != nil {
		fmt.Printf("failed to list tasks: %s", err)
	} else {
		for _, task := range tasksResponse.Tasks {
			fmt.Printf("retrieved task with identifier %d\n", task.ID)
		}
	}

}

func startTaskServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasklists/{id}/tasks", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("PUT /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"affected":{"taskIds":[12345]}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasks":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved task with identifier 12345
retrieved task with identifier 12346

func (*TaskListResponse) HandleHTTPResponse added in v0.2.0

func (t *TaskListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TaskListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*TaskListResponse) Iterate added in v0.2.0

func (t *TaskListResponse) Iterate() *TaskListRequest

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*TaskListResponse) SetRequest added in v0.2.0

func (t *TaskListResponse) SetRequest(req TaskListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type TaskUpdateRequest added in v0.2.0

type TaskUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path TaskUpdateRequestPath `json:"-"`

	// Name is the name of the task
	Name *string `json:"name,omitempty"`

	// Description is an optional description of the task.
	Description *string `json:"description,omitempty"`

	// Priority is the priority of the task. It can be "none", "low", "medium" or
	// "high".
	Priority *string `json:"priority,omitempty"`

	// Progress is the progress of the task, in percentage (0-100).
	Progress *int64 `json:"progress,omitempty"`

	// StartAt is the date and time when the task is scheduled to start.
	StartAt *twapi.Date `json:"startAt,omitempty"`

	// DueAt is the date and time when the task is scheduled to be completed.
	DueAt *twapi.Date `json:"dueAt,omitempty"`

	// EstimatedMinutes is the estimated time to complete the task, in minutes.
	EstimatedMinutes *int64 `json:"estimatedMinutes,omitempty"`

	// TasklistID is the identifier of the tasklist that will contain the task. If
	// provided, the task will be moved to this tasklist.
	TasklistID *int64 `json:"tasklistId,omitempty"`

	// Assignees is the list of users, teams or clients/companies assigned to this
	// task.
	Assignees *UserGroups `json:"assignees,omitempty"`

	// TagIDs is the list of tag IDs associated with this task.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

TaskUpdateRequest represents the request body for updating a task. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/patch-projects-api-v3-tasks-task-id-json

func NewTaskUpdateRequest added in v0.2.0

func NewTaskUpdateRequest(taskID int64) TaskUpdateRequest

NewTaskUpdateRequest creates a new TaskUpdateRequest with the provided task ID. The ID is required to update a task.

func (TaskUpdateRequest) HTTPRequest added in v0.2.0

func (t TaskUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TaskUpdateRequest.

type TaskUpdateRequestPath added in v0.2.0

type TaskUpdateRequestPath struct {
	// ID is the unique identifier of the task to be updated.
	ID int64
}

TaskUpdateRequestPath contains the path parameters for updating a task.

type TaskUpdateResponse added in v0.2.0

type TaskUpdateResponse struct {
	// Task is the updated task.
	Task Task `json:"task"`
}

TaskUpdateResponse represents the response body for updating a task.

https://apidocs.teamwork.com/docs/teamwork/v3/tasks/patch-projects-api-v3-tasks-task-id-json

func TaskUpdate added in v0.2.0

func TaskUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req TaskUpdateRequest,
) (*TaskUpdateResponse, error)

TaskUpdate updates a task using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTaskServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	taskRequest := projects.NewTaskUpdateRequest(12345)
	taskRequest.Description = twapi.Ptr("This is an updated description.")

	_, err = projects.TaskUpdate(ctx, engine, taskRequest)
	if err != nil {
		fmt.Printf("failed to update task: %s", err)
	} else {
		fmt.Println("task updated!")
	}

}

func startTaskServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasklists/{id}/tasks", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("PUT /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"affected":{"taskIds":[12345]}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"task":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasks", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasks":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

task updated!

func (*TaskUpdateResponse) HandleHTTPResponse added in v0.2.0

func (t *TaskUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TaskUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Tasklist added in v0.1.0

type Tasklist struct {
	// ID is the unique identifier of the tasklist.
	ID int64 `json:"id"`

	// Name is the name of the tasklist.
	Name string `json:"name"`

	// Description is the description of the tasklist.
	Description string `json:"description"`

	// Project is the project associated with the tasklist.
	Project twapi.Relationship `json:"project"`

	// Milestone is the milestone associated with the tasklist.
	Milestone *twapi.Relationship `json:"milestone"`

	// CreatedAt is the date and time when the tasklist was created.
	CreatedAt *time.Time `json:"createdAt"`

	// UpdatedAt is the date and time when the tasklist was last updated.
	UpdatedAt *time.Time `json:"updatedAt"`

	// Status is the status of the tasklist. It can be "new", "reopened",
	// "completed" or "deleted".
	Status string `json:"status"`
}

Tasklist is a way to group related tasks within a project, helping teams organize their work into meaningful sections such as phases, categories, or deliverables. Each task list belongs to a specific project and can include multiple tasks that are typically aligned with a common goal. Task lists can be associated with milestones, and they support privacy settings that control who can view or interact with the tasks they contain. This structure helps teams manage progress, assign responsibilities, and maintain clarity across complex projects.

More information can be found at: https://support.teamwork.com/projects/getting-started/task-lists-overview

type TasklistCreateRequest added in v0.1.0

type TasklistCreateRequest struct {
	// Path contains the path parameters for the request.
	Path TasklistCreateRequestPath `json:"-"`

	// Name is the name of the tasklist
	Name string `json:"name"`

	// Description is an optional description of the tasklist.
	Description *string `json:"description,omitempty"`

	// MilestoneID is an optional ID of the milestone associated with the
	// tasklist.
	MilestoneID *int64 `json:"milestone-Id,omitempty"`
}

TasklistCreateRequest represents the request body for creating a new tasklist.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/post-projects-id-tasklists-json

func NewTasklistCreateRequest added in v0.1.0

func NewTasklistCreateRequest(projectID int64, name string) TasklistCreateRequest

NewTasklistCreateRequest creates a new TasklistCreateRequest with the provided name in a specific project.

func (TasklistCreateRequest) HTTPRequest added in v0.1.0

func (t TasklistCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TasklistCreateRequest.

type TasklistCreateRequestPath added in v0.1.0

type TasklistCreateRequestPath struct {
	// ProjectID is the unique identifier of the project that will contain the
	// tasklist.
	ProjectID int64
}

TasklistUpdateRequestPath contains the path parameters for creating a tasklist.

type TasklistCreateResponse added in v0.1.0

type TasklistCreateResponse struct {
	// ID is the unique identifier of the created tasklist.
	ID LegacyNumber `json:"tasklistId"`
}

TasklistCreateResponse represents the response body for creating a new tasklist.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/post-projects-id-tasklists-json

func TasklistCreate added in v0.1.0

func TasklistCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req TasklistCreateRequest,
) (*TasklistCreateResponse, error)

TasklistCreate creates a new tasklist using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTasklistServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tasklistRequest := projects.NewTasklistCreateRequest(777, "New Tasklist")
	tasklistRequest.Description = twapi.Ptr("This is a new tasklist created via the API.")

	tasklistResponse, err := projects.TasklistCreate(ctx, engine, tasklistRequest)
	if err != nil {
		fmt.Printf("failed to create tasklist: %s", err)
	} else {
		fmt.Printf("created tasklist with identifier %d\n", tasklistResponse.ID)
	}

}

func startTasklistServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/tasklists", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","tasklistId":"12345"}`)
	})
	mux.HandleFunc("PUT /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklist":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklists":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created tasklist with identifier 12345

func (*TasklistCreateResponse) HandleHTTPResponse added in v0.1.0

func (t *TasklistCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TasklistCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TasklistDeleteRequest added in v0.1.0

type TasklistDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path TasklistDeleteRequestPath
}

TasklistDeleteRequest represents the request body for deleting a tasklist.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/delete-tasklists-id-json

func NewTasklistDeleteRequest added in v0.1.0

func NewTasklistDeleteRequest(tasklistID int64) TasklistDeleteRequest

NewTasklistDeleteRequest creates a new TasklistDeleteRequest with the provided tasklist ID.

func (TasklistDeleteRequest) HTTPRequest added in v0.1.0

func (t TasklistDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TasklistDeleteRequest.

type TasklistDeleteRequestPath added in v0.1.0

type TasklistDeleteRequestPath struct {
	// ID is the unique identifier of the tasklist to be deleted.
	ID int64
}

TasklistDeleteRequestPath contains the path parameters for deleting a tasklist.

type TasklistDeleteResponse added in v0.1.0

type TasklistDeleteResponse struct{}

TasklistDeleteResponse represents the response body for deleting a tasklist.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/delete-tasklists-id-json

func TasklistDelete added in v0.1.0

func TasklistDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req TasklistDeleteRequest,
) (*TasklistDeleteResponse, error)

TasklistDelete deletes a tasklist using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTasklistServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.TasklistDelete(ctx, engine, projects.NewTasklistDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete tasklist: %s", err)
	} else {
		fmt.Println("tasklist deleted!")
	}

}

func startTasklistServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/tasklists", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","tasklistId":"12345"}`)
	})
	mux.HandleFunc("PUT /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklist":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklists":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

tasklist deleted!

func (*TasklistDeleteResponse) HandleHTTPResponse added in v0.1.0

func (t *TasklistDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TasklistDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TasklistGetRequest added in v0.1.0

type TasklistGetRequest struct {
	// Path contains the path parameters for the request.
	Path TasklistGetRequestPath
}

TasklistGetRequest represents the request body for loading a single tasklist.

https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-tasklists-tasklist-id

func NewTasklistGetRequest added in v0.1.0

func NewTasklistGetRequest(tasklistID int64) TasklistGetRequest

NewTasklistGetRequest creates a new TasklistGetRequest with the provided tasklist ID. The ID is required to load a tasklist.

func (TasklistGetRequest) HTTPRequest added in v0.1.0

func (t TasklistGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TasklistGetRequest.

type TasklistGetRequestPath added in v0.1.0

type TasklistGetRequestPath struct {
	// ID is the unique identifier of the tasklist to be retrieved.
	ID int64 `json:"id"`
}

TasklistGetRequestPath contains the path parameters for loading a single tasklist.

type TasklistGetResponse added in v0.1.0

type TasklistGetResponse struct {
	Tasklist Tasklist `json:"tasklist"`
}

TasklistGetResponse contains all the information related to a tasklist.

https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-tasklists-tasklist-id

func TasklistGet added in v0.1.0

func TasklistGet(
	ctx context.Context,
	engine *twapi.Engine,
	req TasklistGetRequest,
) (*TasklistGetResponse, error)

TasklistGet retrieves a single tasklist using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTasklistServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tasklistResponse, err := projects.TasklistGet(ctx, engine, projects.NewTasklistGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve tasklist: %s", err)
	} else {
		fmt.Printf("retrieved tasklist with identifier %d\n", tasklistResponse.Tasklist.ID)
	}

}

func startTasklistServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/tasklists", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","tasklistId":"12345"}`)
	})
	mux.HandleFunc("PUT /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklist":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklists":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved tasklist with identifier 12345

func (*TasklistGetResponse) HandleHTTPResponse added in v0.1.0

func (t *TasklistGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TasklistGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TasklistListRequest added in v0.1.0

type TasklistListRequest struct {
	// Path contains the path parameters for the request.
	Path TasklistListRequestPath

	// Filters contains the filters for loading multiple tasklists.
	Filters TasklistListRequestFilters
}

TasklistListRequest represents the request body for loading multiple tasklists.

https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-tasklists https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-projects-project-id-tasklists

func NewTasklistListRequest added in v0.1.0

func NewTasklistListRequest() TasklistListRequest

NewTasklistListRequest creates a new TasklistListRequest with default values.

func (TasklistListRequest) HTTPRequest added in v0.1.0

func (t TasklistListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TasklistListRequest.

type TasklistListRequestFilters added in v0.1.0

type TasklistListRequestFilters struct {
	// SearchTerm is an optional search term to filter tasklists by name.
	SearchTerm string

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of tasklists to retrieve per page. Defaults to 50.
	PageSize int64
}

TasklistListRequestFilters contains the filters for loading multiple tasklists.

type TasklistListRequestPath added in v0.1.0

type TasklistListRequestPath struct {
	// ProjectID is the unique identifier of the project whose tasklists are to be
	// retrieved.
	ProjectID int64
}

TasklistListRequestPath contains the path parameters for loading multiple tasklists.

type TasklistListResponse added in v0.1.0

type TasklistListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Tasklists []Tasklist `json:"tasklists"`
	// contains filtered or unexported fields
}

TasklistListResponse contains information by multiple tasklists matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-tasklists https://apidocs.teamwork.com/docs/teamwork/v3/task-lists/get-projects-api-v3-projects-project-id-tasklists

func TasklistList added in v0.1.0

func TasklistList(
	ctx context.Context,
	engine *twapi.Engine,
	req TasklistListRequest,
) (*TasklistListResponse, error)

TasklistList retrieves multiple tasklists using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTasklistServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tasklistsRequest := projects.NewTasklistListRequest()
	tasklistsRequest.Filters.SearchTerm = "Example"

	tasklistsResponse, err := projects.TasklistList(ctx, engine, tasklistsRequest)
	if err != nil {
		fmt.Printf("failed to list tasklists: %s", err)
	} else {
		for _, tasklist := range tasklistsResponse.Tasklists {
			fmt.Printf("retrieved tasklist with identifier %d\n", tasklist.ID)
		}
	}

}

func startTasklistServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/tasklists", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","tasklistId":"12345"}`)
	})
	mux.HandleFunc("PUT /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklist":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklists":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved tasklist with identifier 12345
retrieved tasklist with identifier 12346

func (*TasklistListResponse) HandleHTTPResponse added in v0.1.0

func (t *TasklistListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TasklistListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*TasklistListResponse) Iterate added in v0.1.0

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*TasklistListResponse) SetRequest added in v0.1.0

func (t *TasklistListResponse) SetRequest(req TasklistListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type TasklistUpdateRequest added in v0.1.0

type TasklistUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path TasklistUpdateRequestPath `json:"-"`

	// Name is the name of the tasklist.
	Name *string `json:"name,omitempty"`

	// Description is the tasklist description.
	Description *string `json:"description,omitempty"`

	// MilestoneID is the ID of the milestone associated with the tasklist.
	MilestoneID *int64 `json:"milestone-Id,omitempty"`
}

TasklistUpdateRequest represents the request body for updating a tasklist. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/put-tasklists-id-json

func NewTasklistUpdateRequest added in v0.1.0

func NewTasklistUpdateRequest(tasklistID int64) TasklistUpdateRequest

NewTasklistUpdateRequest creates a new TasklistUpdateRequest with the provided tasklist ID. The ID is required to update a tasklist.

func (TasklistUpdateRequest) HTTPRequest added in v0.1.0

func (t TasklistUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TasklistUpdateRequest.

type TasklistUpdateRequestPath added in v0.1.0

type TasklistUpdateRequestPath struct {
	// ID is the unique identifier of the tasklist to be updated.
	ID int64
}

TasklistUpdateRequestPath contains the path parameters for updating a tasklist.

type TasklistUpdateResponse added in v0.1.0

type TasklistUpdateResponse struct{}

TasklistUpdateResponse represents the response body for updating a tasklist.

https://apidocs.teamwork.com/docs/teamwork/v1/task-lists/put-tasklists-id-json

func TasklistUpdate added in v0.1.0

func TasklistUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req TasklistUpdateRequest,
) (*TasklistUpdateResponse, error)

TasklistUpdate updates a tasklist using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTasklistServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	tasklistRequest := projects.NewTasklistUpdateRequest(12345)
	tasklistRequest.Description = twapi.Ptr("This is an updated description.")

	_, err = projects.TasklistUpdate(ctx, engine, tasklistRequest)
	if err != nil {
		fmt.Printf("failed to update tasklist: %s", err)
	} else {
		fmt.Println("tasklist updated!")
	}

}

func startTasklistServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/{id}/tasklists", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","tasklistId":"12345"}`)
	})
	mux.HandleFunc("PUT /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklist":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/tasklists", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"tasklists":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

tasklist updated!

func (*TasklistUpdateResponse) HandleHTTPResponse added in v0.1.0

func (t *TasklistUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TasklistUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Team added in v0.5.0

type Team struct {
	// ID is the unique identifier of the team.
	ID LegacyNumber `json:"id"`

	// Name is the name of the team.
	Name string `json:"name"`

	// Description is an optional description of the team.
	Description *string `json:"description"`

	// Handle is the unique handle of the team, used in mentions.
	Handle string `json:"handle"`

	// LogoURL is the URL of the team's logo image.
	LogoURL *string `json:"logoUrl"`

	// LogoIcon is the icon of the team's logo, if available.
	LogoIcon *string `json:"logoIcon"`

	// LogoColor is the color of the team's logo, if available.
	LogoColor *string `json:"logoColor"`

	// ProjectID is the unique identifier of the project this team belongs to.
	// This is only set when the team is a project team.
	ProjectID LegacyNumber `json:"projectId"`

	// Company is the client/company the team belongs to.
	Company *struct {
		ID   LegacyNumber `json:"id"`
		Name string       `json:"name"`
	} `json:"company"`

	// ParentTeam is the parent team of this team, if available.
	ParentTeam *struct {
		ID     LegacyNumber `json:"id"`
		Name   string       `json:"name"`
		Handle string       `json:"handle"`
	} `json:"parentTeam"`

	// RootTeam is the root team of this team, if available.
	RootTeam *struct {
		ID     LegacyNumber `json:"id"`
		Name   string       `json:"name"`
		Handle string       `json:"handle"`
	} `json:"rootTeam"`

	// Members is the list of members in this team.
	Members []LegacyRelationship `json:"members"`

	// CreatedBy is the team who created this team.
	CreatedBy LegacyNumber `json:"createdByUserId"`

	// CreatedAt is the date and time when the team was created.
	CreatedAt time.Time `json:"dateCreated"`

	// UpdatedBy is the team who last updated this team.
	UpdatedBy LegacyNumber `json:"updatedByUserId"`

	// UpdatedAt is the date and time when the team was last updated.
	UpdatedAt time.Time `json:"dateUpdated"`

	// Deleted indicates whether the team has been deleted.
	Deleted bool `json:"deleted"`

	// DeletedAt is the date and time when the team was deleted, if applicable.
	DeletedAt *twapi.OptionalDateTime `json:"deletedDate"`
}

Team is a group of users who are organized together to collaborate more efficiently on projects and tasks. Teams help structure work by grouping individuals with similar roles, responsibilities, or departmental functions, making it easier to assign work, track progress, and manage communication. By using teams, organizations can streamline project planning and ensure the right people are involved in the right parts of a project, enhancing clarity and accountability across the platform.

type TeamCreateRequest added in v0.5.0

type TeamCreateRequest struct {
	// Name is the name of the team.
	Name string `json:"name"`

	// Handle is the unique handle of the team, used in mentions. It must not have
	// spaces or special characters.
	Handle *string `json:"handle,omitempty"`

	// Description is an optional description of the team.
	Description *string `json:"description,omitempty"`

	// ParentTeamID is the unique identifier of the parent team. If not provided,
	// the team will be created as a root team.
	ParentTeamID *int64 `json:"parentTeamId,omitempty"`

	// Company is the client/company the team belongs to. By default is the same
	// from the logged team creating the new team.
	CompanyID *int64 `json:"companyId,omitempty"`

	// ProjectID is the unique identifier of the project this team belongs to.
	ProjectID *int64 `json:"projectId,omitempty"`

	// UserIDs is the list of user IDs to be added as members of the team.
	UserIDs LegacyNumericList `json:"userIds,omitempty"`
}

TeamCreateRequest represents the request body for creating a new team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/post-teams-json

func NewTeamCreateRequest added in v0.5.0

func NewTeamCreateRequest(name string) TeamCreateRequest

NewTeamCreateRequest creates a new TeamCreateRequest with the provided name.

func (TeamCreateRequest) HTTPRequest added in v0.5.0

func (u TeamCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TeamCreateRequest.

type TeamCreateResponse added in v0.5.0

type TeamCreateResponse struct {
	// ID is the unique identifier of the created team.
	ID LegacyNumber `json:"id"`
}

TeamCreateResponse represents the response body for creating a new team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/post-teams-json

func TeamCreate added in v0.5.0

func TeamCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req TeamCreateRequest,
) (*TeamCreateResponse, error)

TeamCreate creates a new team using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTeamServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	teamRequest := projects.NewTeamCreateRequest("My Team")

	teamResponse, err := projects.TeamCreate(ctx, engine, teamRequest)
	if err != nil {
		fmt.Printf("failed to create team: %s", err)
	} else {
		fmt.Printf("created team with identifier %d\n", teamResponse.ID)
	}

}

func startTeamServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /teams", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"team":{"id":"12345"}}`)
	})
	mux.HandleFunc("GET /teams", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"teams":[{"id":"12345"},{"id":"12346"}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created team with identifier 12345

func (*TeamCreateResponse) HandleHTTPResponse added in v0.5.0

func (u *TeamCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TeamCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TeamDeleteRequest added in v0.5.0

type TeamDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path TeamDeleteRequestPath
}

TeamDeleteRequest represents the request body for deleting a team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/delete-teams-id-json

func NewTeamDeleteRequest added in v0.5.0

func NewTeamDeleteRequest(teamID int64) TeamDeleteRequest

NewTeamDeleteRequest creates a new TeamDeleteRequest with the provided team ID.

func (TeamDeleteRequest) HTTPRequest added in v0.5.0

func (u TeamDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TeamDeleteRequest.

type TeamDeleteRequestPath added in v0.5.0

type TeamDeleteRequestPath struct {
	// ID is the unique identifier of the team to be deleted.
	ID int64
}

TeamDeleteRequestPath contains the path parameters for deleting a team.

type TeamDeleteResponse added in v0.5.0

type TeamDeleteResponse struct{}

TeamDeleteResponse represents the response body for deleting a team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/delete-teams-id-json

func TeamDelete added in v0.5.0

func TeamDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req TeamDeleteRequest,
) (*TeamDeleteResponse, error)

TeamDelete deletes a team using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTeamServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.TeamDelete(ctx, engine, projects.NewTeamDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete team: %s", err)
	} else {
		fmt.Println("team deleted!")
	}

}

func startTeamServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /teams", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"team":{"id":"12345"}}`)
	})
	mux.HandleFunc("GET /teams", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"teams":[{"id":"12345"},{"id":"12346"}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

team deleted!

func (*TeamDeleteResponse) HandleHTTPResponse added in v0.5.0

func (u *TeamDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TeamDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TeamGetRequest added in v0.5.0

type TeamGetRequest struct {
	// Path contains the path parameters for the request.
	Path TeamGetRequestPath
}

TeamGetRequest represents the request body for loading a single team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-teams-id-json

func NewTeamGetRequest added in v0.5.0

func NewTeamGetRequest(teamID int64) TeamGetRequest

NewTeamGetRequest creates a new TeamGetRequest with the provided team ID. The ID is required to load a team.

func (TeamGetRequest) HTTPRequest added in v0.5.0

func (u TeamGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TeamGetRequest.

type TeamGetRequestPath added in v0.5.0

type TeamGetRequestPath struct {
	// ID is the unique identifier of the team to be retrieved.
	ID int64 `json:"id"`
}

TeamGetRequestPath contains the path parameters for loading a single team.

type TeamGetResponse added in v0.5.0

type TeamGetResponse struct {
	Team Team `json:"team"`
}

TeamGetResponse contains all the information related to a team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-teams-id-json

func TeamGet added in v0.5.0

func TeamGet(
	ctx context.Context,
	engine *twapi.Engine,
	req TeamGetRequest,
) (*TeamGetResponse, error)

TeamGet retrieves a single team using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTeamServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	teamResponse, err := projects.TeamGet(ctx, engine, projects.NewTeamGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve team: %s", err)
	} else {
		fmt.Printf("retrieved team with identifier %d\n", teamResponse.Team.ID)
	}

}

func startTeamServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /teams", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"team":{"id":"12345"}}`)
	})
	mux.HandleFunc("GET /teams", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"teams":[{"id":"12345"},{"id":"12346"}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved team with identifier 12345

func (*TeamGetResponse) HandleHTTPResponse added in v0.5.0

func (u *TeamGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TeamGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TeamListRequest added in v0.5.0

type TeamListRequest struct {
	// Path contains the path parameters for the request.
	Path TeamListRequestPath

	// Filters contains the filters for loading multiple teams.
	Filters TeamListRequestFilters
}

TeamListRequest represents the request body for loading multiple teams.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-teams-json https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-projects-id-teams-json https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-companies-id-teams-json

func NewTeamListRequest added in v0.5.0

func NewTeamListRequest() TeamListRequest

NewTeamListRequest creates a new TeamListRequest with default values.

func (TeamListRequest) HTTPRequest added in v0.5.0

func (u TeamListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TeamListRequest.

type TeamListRequestFilters added in v0.5.0

type TeamListRequestFilters struct {
	// SearchTerm is an optional search term to filter teams by name or e-mail.
	SearchTerm string

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of teams to retrieve per page. Defaults to 50.
	PageSize int64
}

TeamListRequestFilters contains the filters for loading multiple teams.

type TeamListRequestPath added in v0.5.0

type TeamListRequestPath struct {
	// ProjectID is the unique identifier of the project to load teams for.
	ProjectID int64

	// CompanyID is the unique identifier of the company to load teams for.
	CompanyID int64
}

TeamListRequestPath contains the path parameters for loading multiple teams.

type TeamListResponse added in v0.5.0

type TeamListResponse struct {
	Teams []Team `json:"teams"`
	// contains filtered or unexported fields
}

TeamListResponse contains information by multiple teams matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-teams-json https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-projects-id-teams-json https://apidocs.teamwork.com/docs/teamwork/v1/teams/get-companies-id-teams-json

func TeamList added in v0.5.0

func TeamList(
	ctx context.Context,
	engine *twapi.Engine,
	req TeamListRequest,
) (*TeamListResponse, error)

TeamList retrieves multiple teams using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTeamServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	teamsRequest := projects.NewTeamListRequest()
	teamsRequest.Filters.SearchTerm = "Team A"

	teamsResponse, err := projects.TeamList(ctx, engine, teamsRequest)
	if err != nil {
		fmt.Printf("failed to list teams: %s", err)
	} else {
		for _, team := range teamsResponse.Teams {
			fmt.Printf("retrieved team with identifier %d\n", team.ID)
		}
	}

}

func startTeamServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /teams", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"team":{"id":"12345"}}`)
	})
	mux.HandleFunc("GET /teams", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"teams":[{"id":"12345"},{"id":"12346"}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved team with identifier 12345
retrieved team with identifier 12346

func (*TeamListResponse) HandleHTTPResponse added in v0.5.0

func (u *TeamListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TeamListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*TeamListResponse) Iterate added in v0.5.0

func (u *TeamListResponse) Iterate() *TeamListRequest

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*TeamListResponse) SetRequest added in v0.5.0

func (u *TeamListResponse) SetRequest(req TeamListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type TeamUpdateRequest added in v0.5.0

type TeamUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path TeamUpdateRequestPath `json:"-"`

	// Name is the name of the team.
	Name *string `json:"name,omitempty"`

	// Handle is the unique handle of the team, used in mentions. It must not have
	// spaces or special characters.
	Handle *string `json:"handle,omitempty"`

	// Description is an optional description of the team.
	Description *string `json:"description,omitempty"`

	// CompanyID is the unique identifier of the company the team belongs to.
	CompanyID *int64 `json:"companyId,omitempty"`

	// ProjectID is the unique identifier of the project this team belongs to.
	ProjectID *int64 `json:"projectId,omitempty"`

	// UserIDs is the list of user IDs to be added as members of the team.
	UserIDs LegacyNumericList `json:"userIds,omitempty"`
}

TeamUpdateRequest represents the request body for updating a team. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/put-teams-id-json

func NewTeamUpdateRequest added in v0.5.0

func NewTeamUpdateRequest(teamID int64) TeamUpdateRequest

NewTeamUpdateRequest creates a new TeamUpdateRequest with the provided team ID. The ID is required to update a team.

func (TeamUpdateRequest) HTTPRequest added in v0.5.0

func (u TeamUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TeamUpdateRequest.

type TeamUpdateRequestPath added in v0.5.0

type TeamUpdateRequestPath struct {
	// ID is the unique identifier of the team to be updated.
	ID int64
}

TeamUpdateRequestPath contains the path parameters for updating a team.

type TeamUpdateResponse added in v0.5.0

type TeamUpdateResponse struct{}

TeamUpdateResponse represents the response body for updating a team.

https://apidocs.teamwork.com/docs/teamwork/v1/teams/put-teams-id-json

func TeamUpdate added in v0.5.0

func TeamUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req TeamUpdateRequest,
) (*TeamUpdateResponse, error)

TeamUpdate updates a team using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTeamServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	teamRequest := projects.NewTeamUpdateRequest(12345)
	teamRequest.Description = twapi.Ptr("This is an updated team description.")

	_, err = projects.TeamUpdate(ctx, engine, teamRequest)
	if err != nil {
		fmt.Printf("failed to update team: %s", err)
	} else {
		fmt.Println("team updated!")
	}

}

func startTeamServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /teams", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /teams/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"team":{"id":"12345"}}`)
	})
	mux.HandleFunc("GET /teams", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"teams":[{"id":"12345"},{"id":"12346"}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

team updated!

func (*TeamUpdateResponse) HandleHTTPResponse added in v0.5.0

func (u *TeamUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TeamUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type Timelog added in v0.7.0

type Timelog struct {
	// ID is the unique identifier of the timelog.
	ID int64 `json:"id"`

	// Description is the description of the timelog.
	Description string `json:"description"`

	// Billable indicates whether the timelog is billable or not.
	Billable bool `json:"billable"`

	// Minutes is the number of minutes logged in the timelog.
	Minutes int64 `json:"minutes"`

	// LoggedAt is the date and time when the timelog was logged.
	LoggedAt time.Time `json:"timeLogged"`

	// User is the user that this timelog belongs to.
	User twapi.Relationship `json:"user"`

	// Task is the task associated with the timelog. It can be nil if the timelog
	// is not associated with a task.
	Task *twapi.Relationship `json:"task"`

	// Project is the project associated with the timelog.
	Project twapi.Relationship `json:"project"`

	// Tags are the tags associated with the timelog. They can be used to
	// categorize or label the timelog for easier filtering and searching.
	Tags []twapi.Relationship `json:"tags,omitempty"`

	// CreatedAt is the date and time when the timelog was created.
	CreatedAt time.Time `json:"createdAt"`

	// LoggedBy is the unique identifier of the user who logged the timelog.
	LoggedBy int64 `json:"loggedBy"`

	// UpdatedAt is the date and time when the timelog was last updated.
	UpdatedAt *time.Time `json:"updatedAt"`

	// UpdatedBy is the unique identifier of the user who last updated the
	// timelog.
	UpdatedBy *int64 `json:"updatedBy"`

	// DeletedAt is the date and time when the timelog was deleted, if it has been
	// deleted.
	DeletedAt *time.Time `json:"deletedAt"`

	// DeletedBy is the unique identifier of the user who deleted the timelog, if
	// it has been deleted.
	DeletedBy *int64 `json:"deletedBy"`

	// Deleted indicates whether the timelog has been deleted or not.
	Deleted bool `json:"deleted"`
}

Timelog refers to a recorded entry that tracks the amount of time a person has spent working on a specific task, project, or piece of work. These entries typically include details such as the duration of time worked, the date and time it was logged, who logged it, and any optional notes describing what was done during that period. Timelogs are essential for understanding how time is being allocated across projects, enabling teams to manage resources more effectively, invoice clients accurately, and assess productivity. They can be created manually or with timers, and are often used for reporting and billing purposes.

type TimelogCreateRequest added in v0.7.0

type TimelogCreateRequest struct {
	// Path contains the path parameters for the request.
	Path TimelogCreateRequestPath `json:"-"`

	// Description is an optional description of the timelog.
	Description *string `json:"description"`

	// Date is the date when the timelog was logged. Only the date part is used,
	// the time part is ignored.
	Date twapi.Date `json:"date"`

	// Time is the time when the timelog was logged. It can be in local time or
	// UTC.
	Time twapi.Time `json:"time"`

	// IsUTC indicates whether the time is in UTC. When false, it will consider
	// the timezone configured for the logged user.
	IsUTC bool `json:"isUTC"`

	// Hours is the number of hours logged in the timelog. This is optional and
	// can be used instead of Minutes. If both Hours and Minutes are provided,
	// they will be summed up to calculate the total time logged.
	Hours int64 `json:"hours"`

	// Minutes is the number of minutes logged in the timelog. This is optional
	// and can be used instead of Hours. If both Hours and Minutes are provided,
	// they will be summed up to calculate the total time logged.
	Minutes int64 `json:"minutes"`

	// Billable indicates whether the timelog is billable or not.
	Billable bool `json:"isBillable"`

	// UserID is an optional ID of the user who logged the timelog. If not
	// provided, the timelog will be logged by the user making the request.
	UserID *int64 `json:"userId"`

	// TagIDs is an optional list of tag IDs to associate with the timelog.
	TagIDs []int64 `json:"tagIds"`
}

TimelogCreateRequest represents the request body for creating a new timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/post-projects-api-v3-tasks-task-id-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/post-projects-api-v3-projects-project-id-time-json

func NewTimelogCreateRequestInProject added in v0.7.0

func NewTimelogCreateRequestInProject(
	projectID int64,
	datetime time.Time,
	duration time.Duration,
) TimelogCreateRequest

NewTimelogCreateRequestInProject creates a new TimelogCreateRequest with the provided datetime and duration in a specific project.

func NewTimelogCreateRequestInTask added in v0.7.0

func NewTimelogCreateRequestInTask(taskID int64, datetime time.Time, duration time.Duration) TimelogCreateRequest

NewTimelogCreateRequestInTask creates a new TimelogCreateRequest with the provided datetime and duration, associating it with a specific task.

func (TimelogCreateRequest) HTTPRequest added in v0.7.0

func (t TimelogCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TimelogCreateRequest.

type TimelogCreateRequestPath added in v0.7.0

type TimelogCreateRequestPath struct {
	// TaskID is an optional ID of the task associated with the timelog. If
	// provided, the timelog will be associated with this task. At least one of
	// TaskID or ProjectID must be provided. If both are provided, the timelog
	// will be associated with the task.
	TaskID int64
	// ProjectID is the unique identifier of the project where the timelog will be
	// created. At least one of TaskID or ProjectID must be provided. If both are
	// provided, the timelog will be associated with the task.
	ProjectID int64
}

TimelogUpdateRequestPath contains the path parameters for creating a timelog.

type TimelogCreateResponse added in v0.7.0

type TimelogCreateResponse struct {
	// Timelog contains the created timelog information.
	Timelog Timelog `json:"timelog"`
}

TimelogCreateResponse represents the response body for creating a new timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/post-projects-api-v3-tasks-task-id-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/post-projects-api-v3-projects-project-id-time-json

func TimelogCreate added in v0.7.0

func TimelogCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req TimelogCreateRequest,
) (*TimelogCreateResponse, error)

TimelogCreate creates a new timelog using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"
	"time"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTimelogServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	timelogRequest := projects.NewTimelogCreateRequestInTask(777, time.Now(), 30*time.Minute)
	timelogRequest.Description = twapi.Ptr("This is a new timelog created via the API.")

	timelogResponse, err := projects.TimelogCreate(ctx, engine, timelogRequest)
	if err != nil {
		fmt.Printf("failed to create timelog: %s", err)
	} else {
		fmt.Printf("created timelog with identifier %d\n", timelogResponse.Timelog.ID)
	}

}

func startTimelogServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasks/{id}/time", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/time", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelogs":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created timelog with identifier 12345

func (*TimelogCreateResponse) HandleHTTPResponse added in v0.7.0

func (t *TimelogCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TimelogCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TimelogDeleteRequest added in v0.7.0

type TimelogDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path TimelogDeleteRequestPath
}

TimelogDeleteRequest represents the request body for deleting a timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/delete-projects-api-v3-time-timelog-id-json

func NewTimelogDeleteRequest added in v0.7.0

func NewTimelogDeleteRequest(timelogID int64) TimelogDeleteRequest

NewTimelogDeleteRequest creates a new TimelogDeleteRequest with the provided timelog ID.

func (TimelogDeleteRequest) HTTPRequest added in v0.7.0

func (t TimelogDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TimelogDeleteRequest.

type TimelogDeleteRequestPath added in v0.7.0

type TimelogDeleteRequestPath struct {
	// ID is the unique identifier of the timelog to be deleted.
	ID int64
}

TimelogDeleteRequestPath contains the path parameters for deleting a timelog.

type TimelogDeleteResponse added in v0.7.0

type TimelogDeleteResponse struct{}

TimelogDeleteResponse represents the response body for deleting a timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/delete-projects-api-v3-time-timelog-id-json

func TimelogDelete added in v0.7.0

func TimelogDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req TimelogDeleteRequest,
) (*TimelogDeleteResponse, error)

TimelogDelete deletes a timelog using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTimelogServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.TimelogDelete(ctx, engine, projects.NewTimelogDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete timelog: %s", err)
	} else {
		fmt.Println("timelog deleted!")
	}

}

func startTimelogServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasks/{id}/time", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/time", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelogs":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

timelog deleted!

func (*TimelogDeleteResponse) HandleHTTPResponse added in v0.7.0

func (t *TimelogDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TimelogDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TimelogGetRequest added in v0.7.0

type TimelogGetRequest struct {
	// Path contains the path parameters for the request.
	Path TimelogGetRequestPath
}

TimelogGetRequest represents the request body for loading a single timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-time-timelog-id-json

func NewTimelogGetRequest added in v0.7.0

func NewTimelogGetRequest(timelogID int64) TimelogGetRequest

NewTimelogGetRequest creates a new TimelogGetRequest with the provided timelog ID. The ID is required to load a timelog.

func (TimelogGetRequest) HTTPRequest added in v0.7.0

func (t TimelogGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TimelogGetRequest.

type TimelogGetRequestPath added in v0.7.0

type TimelogGetRequestPath struct {
	// ID is the unique identifier of the timelog to be retrieved.
	ID int64 `json:"id"`
}

TimelogGetRequestPath contains the path parameters for loading a single timelog.

type TimelogGetResponse added in v0.7.0

type TimelogGetResponse struct {
	Timelog Timelog `json:"timelog"`
}

TimelogGetResponse contains all the information related to a timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-time-timelog-id-json

func TimelogGet added in v0.7.0

func TimelogGet(
	ctx context.Context,
	engine *twapi.Engine,
	req TimelogGetRequest,
) (*TimelogGetResponse, error)

TimelogGet retrieves a single timelog using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTimelogServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	timelogResponse, err := projects.TimelogGet(ctx, engine, projects.NewTimelogGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve timelog: %s", err)
	} else {
		fmt.Printf("retrieved timelog with identifier %d\n", timelogResponse.Timelog.ID)
	}

}

func startTimelogServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasks/{id}/time", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/time", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelogs":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved timelog with identifier 12345

func (*TimelogGetResponse) HandleHTTPResponse added in v0.7.0

func (t *TimelogGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TimelogGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type TimelogListRequest added in v0.7.0

type TimelogListRequest struct {
	// Path contains the path parameters for the request.
	Path TimelogListRequestPath

	// Filters contains the filters for loading multiple timelogs.
	Filters TimelogListRequestFilters
}

TimelogListRequest represents the request body for loading multiple timelogs.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-tasks-task-id-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-projects-project-id-time-json

func NewTimelogListRequest added in v0.7.0

func NewTimelogListRequest() TimelogListRequest

NewTimelogListRequest creates a new TimelogListRequest with default values.

func (TimelogListRequest) HTTPRequest added in v0.7.0

func (t TimelogListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TimelogListRequest.

type TimelogListRequestFilters added in v0.7.0

type TimelogListRequestFilters struct {
	// TagIDs is an optional list of tag IDs to filter the timelogs by. If provided,
	// only timelogs associated with these tags will be returned.
	TagIDs []int64

	// MatchAllTags indicates whether to match all tags or any tag. If true, only
	// timelogs that have all the specified tags will be returned. If false,
	// timelogs that have at least one of the specified tags will be returned.
	MatchAllTags *bool

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of timelogs to retrieve per page. Defaults to 50.
	PageSize int64
}

TimelogListRequestFilters contains the filters for loading multiple timelogs.

type TimelogListRequestPath added in v0.7.0

type TimelogListRequestPath struct {
	// TaskID is an optional ID of the task whose timelogs are to be retrieved. If
	// provided, the timelogs will be filtered by this task. When both TaskID and
	// ProjectID are provided, the timelogs will be filtered by the task.
	TaskID int64
	// ProjectID is the unique identifier of the project whose timelogs are to be
	// retrieved. If provided, the timelogs will be filtered by this project. When
	// both TaskID and ProjectID are provided, the timelogs will be filtered by
	// the task.
	ProjectID int64
}

TimelogListRequestPath contains the path parameters for loading multiple timelogs.

type TimelogListResponse added in v0.7.0

type TimelogListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Timelogs []Timelog `json:"timelogs"`
	// contains filtered or unexported fields
}

TimelogListResponse contains information by multiple timelogs matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-tasks-task-id-time-json https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/get-projects-api-v3-projects-project-id-time-json

func TimelogList added in v0.7.0

func TimelogList(
	ctx context.Context,
	engine *twapi.Engine,
	req TimelogListRequest,
) (*TimelogListResponse, error)

TimelogList retrieves multiple timelogs using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTimelogServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	timelogsRequest := projects.NewTimelogListRequest()
	timelogsResponse, err := projects.TimelogList(ctx, engine, timelogsRequest)
	if err != nil {
		fmt.Printf("failed to list timelogs: %s", err)
	} else {
		for _, timelog := range timelogsResponse.Timelogs {
			fmt.Printf("retrieved timelog with identifier %d\n", timelog.ID)
		}
	}

}

func startTimelogServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasks/{id}/time", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/time", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelogs":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved timelog with identifier 12345
retrieved timelog with identifier 12346

func (*TimelogListResponse) HandleHTTPResponse added in v0.7.0

func (t *TimelogListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TimelogListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*TimelogListResponse) Iterate added in v0.7.0

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*TimelogListResponse) SetRequest added in v0.7.0

func (t *TimelogListResponse) SetRequest(req TimelogListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type TimelogUpdateRequest added in v0.7.0

type TimelogUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path TimelogUpdateRequestPath `json:"-"`

	// Description is an optional description of the timelog.
	Description *string `json:"description,omitempty"`

	// Date is the date when the timelog was logged. Only the date part is used,
	// the time part is ignored.
	Date *twapi.Date `json:"date,omitempty"`

	// Time is the time when the timelog was logged. It can be in local time or
	// UTC.
	Time *twapi.Time `json:"time,omitempty"`

	// IsUTC indicates whether the time is in UTC. When false, it will consider
	// the timezone configured for the logged user.
	IsUTC *bool `json:"isUTC,omitempty"`

	// Hours is the number of hours logged in the timelog. This is optional and
	// can be used instead of Minutes. If both Hours and Minutes are provided,
	// they will be summed up to calculate the total time logged.
	Hours *int64 `json:"hours,omitempty"`

	// Minutes is the number of minutes logged in the timelog. This is optional
	// and can be used instead of Hours. If both Hours and Minutes are provided,
	// they will be summed up to calculate the total time logged.
	Minutes *int64 `json:"minutes,omitempty"`

	// Billable indicates whether the timelog is billable or not.
	Billable *bool `json:"isBillable,omitempty"`

	// UserID is an optional ID of the user who logged the timelog. If not
	// provided, the timelog will be logged by the user making the request.
	UserID *int64 `json:"userId,omitempty"`

	// TagIDs is an optional list of tag IDs to associate with the timelog.
	TagIDs []int64 `json:"tagIds,omitempty"`
}

TimelogUpdateRequest represents the request body for updating a timelog. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/patch-projects-api-v3-time-timelog-id-json

func NewTimelogUpdateRequest added in v0.7.0

func NewTimelogUpdateRequest(timelogID int64) TimelogUpdateRequest

NewTimelogUpdateRequest creates a new TimelogUpdateRequest with the provided timelog ID. The ID is required to update a timelog.

func (TimelogUpdateRequest) HTTPRequest added in v0.7.0

func (t TimelogUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the TimelogUpdateRequest.

type TimelogUpdateRequestPath added in v0.7.0

type TimelogUpdateRequestPath struct {
	// ID is the unique identifier of the timelog to be updated.
	ID int64
}

TimelogUpdateRequestPath contains the path parameters for updating a timelog.

type TimelogUpdateResponse added in v0.7.0

type TimelogUpdateResponse struct{}

TimelogUpdateResponse represents the response body for updating a timelog.

https://apidocs.teamwork.com/docs/teamwork/v3/time-tracking/patch-projects-api-v3-time-timelog-id-json

func TimelogUpdate added in v0.7.0

func TimelogUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req TimelogUpdateRequest,
) (*TimelogUpdateResponse, error)

TimelogUpdate updates a timelog using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startTimelogServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	timelogRequest := projects.NewTimelogUpdateRequest(12345)
	timelogRequest.Description = twapi.Ptr("This is an updated description.")

	_, err = projects.TimelogUpdate(ctx, engine, timelogRequest)
	if err != nil {
		fmt.Printf("failed to update timelog: %s", err)
	} else {
		fmt.Println("timelog updated!")
	}

}

func startTimelogServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /projects/api/v3/tasks/{id}/time", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "777" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("PATCH /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{}`)
	})
	mux.HandleFunc("DELETE /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})
	mux.HandleFunc("GET /projects/api/v3/time/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelog":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/time", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"timelogs":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

timelog updated!

func (*TimelogUpdateResponse) HandleHTTPResponse added in v0.7.0

func (t *TimelogUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the TimelogUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type User added in v0.3.0

type User struct {
	// ID is the unique identifier of the user.
	ID int64 `json:"id"`

	// FirstName is the first name of the user.
	FirstName string `json:"firstName"`

	// LastName is the last name of the user.
	LastName string `json:"lastName"`

	// Title is the title of the user (e.g. "Senior Developer").
	Title *string `json:"title"`

	// Email is the email address of the user.
	Email string `json:"email"`

	// Admin indicates whether the user is an administrator.
	Admin bool `json:"isAdmin"`

	// Type is the type of user. Possible values are "account", "collaborator" or "contact".
	Type string `json:"type"`

	// Cost is the hourly cost, to your company, to employ this user.
	Cost *twapi.Money `json:"userCost"`

	// Rate is the individual's hourly rate. This is what you charge for someone's
	// time on a project.
	Rate *twapi.Money `json:"userRate"`

	// Company is the client/company the user belongs to.
	Company twapi.Relationship `json:"company"`

	// JobRoles are the job roles assigned to the user.
	JobRoles []twapi.Relationship `json:"jobRoles,omitempty"`

	// Skills are the skills assigned to the user.
	Skills []twapi.Relationship `json:"skills,omitempty"`

	// Deleted indicates whether the user has been deleted.
	Deleted bool `json:"deleted"`

	// CreatedBy is the user who created this user.
	CreatedBy *twapi.Relationship `json:"createdBy"`

	// CreatedAt is the date and time when the user was created.
	CreatedAt time.Time `json:"createdAt"`

	// UpdatedBy is the user who last updated this user.
	UpdatedBy *twapi.Relationship `json:"updatedBy"`

	// UpdatedAt is the date and time when the user was last updated.
	UpdatedAt *time.Time `json:"updatedAt"`
}

User is an individual who has access to one or more projects within a Teamwork site, typically as a team member, collaborator, or administrator. Users can be assigned tasks, participate in discussions, log time, share files, and interact with other members depending on their permission levels. Each user has a unique profile that defines their role, visibility, and access to features and project data. Users can belong to clients/companies or teams within the system, and their permissions can be customized to control what actions they can perform or what information they can see.

More information can be found at: https://support.teamwork.com/projects/getting-started/people-overview

type UserCreateRequest added in v0.3.0

type UserCreateRequest struct {
	// FirstName is the first name of the user.
	FirstName string `json:"first-name"`

	// LastName is the last name of the user.
	LastName string `json:"last-name"`

	// Title is the title of the user (e.g. "Senior Developer").
	Title *string `json:"title,omitempty"`

	// Email is the email address of the user.
	Email string `json:"email-address"`

	// Admin indicates whether the user is an administrator. By default it is
	// false.
	Admin *bool `json:"administrator,omitempty"`

	// Type is the type of user. Possible values are "account", "collaborator" or
	// "contact". By default it is "account".
	Type *string `json:"user-type,omitempty"`

	// Company is the client/company the user belongs to. By default is the same
	// from the logged user creating the new user.
	CompanyID *int64 `json:"company-id,omitempty"`
}

UserCreateRequest represents the request body for creating a new user.

https://apidocs.teamwork.com/docs/teamwork/v1/people/post-people-json

func NewUserCreateRequest added in v0.3.0

func NewUserCreateRequest(firstName, lastName, email string) UserCreateRequest

NewUserCreateRequest creates a new UserCreateRequest with the provided name in a specific project.

func (UserCreateRequest) HTTPRequest added in v0.3.0

func (u UserCreateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserCreateRequest.

type UserCreateResponse added in v0.3.0

type UserCreateResponse struct {
	// ID is the unique identifier of the created user.
	ID LegacyNumber `json:"id"`
}

UserCreateResponse represents the response body for creating a new user.

https://apidocs.teamwork.com/docs/teamwork/v1/people/post-people-json

func UserCreate added in v0.3.0

func UserCreate(
	ctx context.Context,
	engine *twapi.Engine,
	req UserCreateRequest,
) (*UserCreateResponse, error)

UserCreate creates a new user using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	userRequest := projects.NewUserCreateRequest("John", "Doe", "johndoe@example.com")

	userResponse, err := projects.UserCreate(ctx, engine, userRequest)
	if err != nil {
		fmt.Printf("failed to create user: %s", err)
	} else {
		fmt.Printf("created user with identifier %d\n", userResponse.ID)
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

created user with identifier 12345

func (*UserCreateResponse) HandleHTTPResponse added in v0.3.0

func (u *UserCreateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserCreateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type UserDeleteRequest added in v0.3.0

type UserDeleteRequest struct {
	// Path contains the path parameters for the request.
	Path UserDeleteRequestPath
}

UserDeleteRequest represents the request body for deleting a user.

https://apidocs.teamwork.com/docs/teamwork/v1/people/delete-people-id-json

func NewUserDeleteRequest added in v0.3.0

func NewUserDeleteRequest(userID int64) UserDeleteRequest

NewUserDeleteRequest creates a new UserDeleteRequest with the provided user ID.

func (UserDeleteRequest) HTTPRequest added in v0.3.0

func (u UserDeleteRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserDeleteRequest.

type UserDeleteRequestPath added in v0.3.0

type UserDeleteRequestPath struct {
	// ID is the unique identifier of the user to be deleted.
	ID int64
}

UserDeleteRequestPath contains the path parameters for deleting a user.

type UserDeleteResponse added in v0.3.0

type UserDeleteResponse struct{}

UserDeleteResponse represents the response body for deleting a user.

https://apidocs.teamwork.com/docs/teamwork/v1/people/delete-people-id-json

func UserDelete added in v0.3.0

func UserDelete(
	ctx context.Context,
	engine *twapi.Engine,
	req UserDeleteRequest,
) (*UserDeleteResponse, error)

UserDelete deletes a user using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	_, err = projects.UserDelete(ctx, engine, projects.NewUserDeleteRequest(12345))
	if err != nil {
		fmt.Printf("failed to delete user: %s", err)
	} else {
		fmt.Println("user deleted!")
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

user deleted!

func (*UserDeleteResponse) HandleHTTPResponse added in v0.3.0

func (u *UserDeleteResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserDeleteResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type UserGetMeRequest added in v0.3.0

type UserGetMeRequest struct {
}

UserGetMeRequest represents the request body for loading the logged user.

https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-me-json

func NewUserGetMeRequest added in v0.3.0

func NewUserGetMeRequest() UserGetMeRequest

NewUserGetMeRequest creates a new UserGetMeRequest.

func (UserGetMeRequest) HTTPRequest added in v0.3.0

func (u UserGetMeRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserGetMeRequest.

type UserGetMeResponse added in v0.3.0

type UserGetMeResponse struct {
	User User `json:"person"`
}

UserGetMeResponse contains all the information related to the logged user.

https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-me-json

func UserGetMe added in v0.3.0

func UserGetMe(
	ctx context.Context,
	engine *twapi.Engine,
	req UserGetMeRequest,
) (*UserGetMeResponse, error)

UserGetMe retrieves the logged user using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	userResponse, err := projects.UserGetMe(ctx, engine, projects.NewUserGetMeRequest())
	if err != nil {
		fmt.Printf("failed to retrieve logged user: %s", err)
	} else {
		fmt.Printf("retrieved logged user with identifier %d\n", userResponse.User.ID)
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved logged user with identifier 12345

func (*UserGetMeResponse) HandleHTTPResponse added in v0.3.0

func (u *UserGetMeResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserGetMeResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type UserGetRequest added in v0.3.0

type UserGetRequest struct {
	// Path contains the path parameters for the request.
	Path UserGetRequestPath
}

UserGetRequest represents the request body for loading a single user.

https://apidocs.teamwork.com/docs/teamwork/v3/person/get-projects-api-v3-people-person-id-json

func NewUserGetRequest added in v0.3.0

func NewUserGetRequest(userID int64) UserGetRequest

NewUserGetRequest creates a new UserGetRequest with the provided user ID. The ID is required to load a user.

func (UserGetRequest) HTTPRequest added in v0.3.0

func (u UserGetRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserGetRequest.

type UserGetRequestPath added in v0.3.0

type UserGetRequestPath struct {
	// ID is the unique identifier of the user to be retrieved.
	ID int64 `json:"id"`
}

UserGetRequestPath contains the path parameters for loading a single user.

type UserGetResponse added in v0.3.0

type UserGetResponse struct {
	User User `json:"person"`
}

UserGetResponse contains all the information related to a user.

https://apidocs.teamwork.com/docs/teamwork/v3/person/get-projects-api-v3-people-person-id-json

func UserGet added in v0.3.0

func UserGet(
	ctx context.Context,
	engine *twapi.Engine,
	req UserGetRequest,
) (*UserGetResponse, error)

UserGet retrieves a single user using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	userResponse, err := projects.UserGet(ctx, engine, projects.NewUserGetRequest(12345))
	if err != nil {
		fmt.Printf("failed to retrieve user: %s", err)
	} else {
		fmt.Printf("retrieved user with identifier %d\n", userResponse.User.ID)
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved user with identifier 12345

func (*UserGetResponse) HandleHTTPResponse added in v0.3.0

func (u *UserGetResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserGetResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

type UserGroups added in v0.2.0

type UserGroups struct {
	UserIDs    []int64 `json:"userIds"`
	CompanyIDs []int64 `json:"companyIds"`
	TeamIDs    []int64 `json:"teamIds"`
}

UserGroups represents a collection of users, companies, and teams.

type UserListRequest added in v0.3.0

type UserListRequest struct {
	// Path contains the path parameters for the request.
	Path UserListRequestPath

	// Filters contains the filters for loading multiple users.
	Filters UserListRequestFilters
}

UserListRequest represents the request body for loading multiple users.

https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-people-json https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-projects-project-id-people-json

func NewUserListRequest added in v0.3.0

func NewUserListRequest() UserListRequest

NewUserListRequest creates a new UserListRequest with default values.

func (UserListRequest) HTTPRequest added in v0.3.0

func (u UserListRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserListRequest.

type UserListRequestFilters added in v0.3.0

type UserListRequestFilters struct {
	// SearchTerm is an optional search term to filter users by name or e-mail.
	SearchTerm string

	// Type is an optional filter to load only users of a specific type. Possible
	// values are "account", "collaborator" or "contact".
	Type string

	// Page is the page number to retrieve. Defaults to 1.
	Page int64

	// PageSize is the number of users to retrieve per page. Defaults to 50.
	PageSize int64
}

UserListRequestFilters contains the filters for loading multiple users.

type UserListRequestPath added in v0.3.0

type UserListRequestPath struct {
	// ProjectID is the unique identifier of the project whose members are to be
	// retrieved.
	ProjectID int64
}

UserListRequestPath contains the path parameters for loading multiple users.

type UserListResponse added in v0.3.0

type UserListResponse struct {
	Meta struct {
		Page struct {
			HasMore bool `json:"hasMore"`
		} `json:"page"`
	} `json:"meta"`
	Users []User `json:"people"`
	// contains filtered or unexported fields
}

UserListResponse contains information by multiple users matching the request filters.

https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-people-json https://apidocs.teamwork.com/docs/teamwork/v3/people/get-projects-api-v3-projects-project-id-people-json

func UserList added in v0.3.0

func UserList(
	ctx context.Context,
	engine *twapi.Engine,
	req UserListRequest,
) (*UserListResponse, error)

UserList retrieves multiple users using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	usersRequest := projects.NewUserListRequest()
	usersRequest.Filters.SearchTerm = "John"

	usersResponse, err := projects.UserList(ctx, engine, usersRequest)
	if err != nil {
		fmt.Printf("failed to list users: %s", err)
	} else {
		for _, user := range usersResponse.Users {
			fmt.Printf("retrieved user with identifier %d\n", user.ID)
		}
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

retrieved user with identifier 12345
retrieved user with identifier 12346

func (*UserListResponse) HandleHTTPResponse added in v0.3.0

func (u *UserListResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserListResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

func (*UserListResponse) Iterate added in v0.3.0

func (u *UserListResponse) Iterate() *UserListRequest

Iterate returns the request set to the next page, if available. If there are no more pages, a nil request is returned.

func (*UserListResponse) SetRequest added in v0.3.0

func (u *UserListResponse) SetRequest(req UserListRequest)

SetRequest sets the request used to load this response. This is used for pagination purposes, so the Iterate method can return the next page.

type UserUpdateRequest added in v0.3.0

type UserUpdateRequest struct {
	// Path contains the path parameters for the request.
	Path UserUpdateRequestPath `json:"-"`

	// FirstName is the first name of the user.
	FirstName *string `json:"first-name,omitempty"`

	// LastName is the last name of the user.
	LastName *string `json:"last-name,omitempty"`

	// Title is the title of the user (e.g. "Senior Developer").
	Title *string `json:"title,omitempty"`

	// Email is the email address of the user.
	Email *string `json:"email-address,omitempty"`

	// Admin indicates whether the user is an administrator. By default it is
	// false.
	Admin *bool `json:"administrator,omitempty"`

	// Type is the type of user. Possible values are "account", "collaborator" or
	// "contact". By default it is "account".
	Type *string `json:"user-type,omitempty"`

	// Company is the client/company the user belongs to. By default is the same
	// from the logged user creating the new user.
	CompanyID *int64 `json:"company-id,omitempty"`
}

UserUpdateRequest represents the request body for updating a user. Besides the identifier, all other fields are optional. When a field is not provided, it will not be modified.

https://apidocs.teamwork.com/docs/teamwork/v1/people/put-people-id-json

func NewUserUpdateRequest added in v0.3.0

func NewUserUpdateRequest(userID int64) UserUpdateRequest

NewUserUpdateRequest creates a new UserUpdateRequest with the provided user ID. The ID is required to update a user.

func (UserUpdateRequest) HTTPRequest added in v0.3.0

func (u UserUpdateRequest) HTTPRequest(ctx context.Context, server string) (*http.Request, error)

HTTPRequest creates an HTTP request for the UserUpdateRequest.

type UserUpdateRequestPath added in v0.3.0

type UserUpdateRequestPath struct {
	// ID is the unique identifier of the user to be updated.
	ID int64
}

UserUpdateRequestPath contains the path parameters for updating a user.

type UserUpdateResponse added in v0.3.0

type UserUpdateResponse struct{}

UserUpdateResponse represents the response body for updating a user.

https://apidocs.teamwork.com/docs/teamwork/v1/people/put-people-id-json

func UserUpdate added in v0.3.0

func UserUpdate(
	ctx context.Context,
	engine *twapi.Engine,
	req UserUpdateRequest,
) (*UserUpdateResponse, error)

UserUpdate updates a user using the provided request and returns the response.

Example
package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"strings"

	twapi "github.com/teamwork/twapi-go-sdk"
	"github.com/teamwork/twapi-go-sdk/projects"
	"github.com/teamwork/twapi-go-sdk/session"
)

func main() {
	address, stop, err := startUserServer() // mock server for demonstration purposes
	if err != nil {
		fmt.Printf("failed to start server: %s", err)
		return
	}
	defer stop()

	ctx := context.Background()
	engine := twapi.NewEngine(session.NewBearerToken("your_token", fmt.Sprintf("http://%s", address)))

	userRequest := projects.NewUserUpdateRequest(12345)
	userRequest.Title = twapi.Ptr("Software Engineer")

	_, err = projects.UserUpdate(ctx, engine, userRequest)
	if err != nil {
		fmt.Printf("failed to update user: %s", err)
	} else {
		fmt.Println("user updated!")
	}

}

func startUserServer() (string, func(), error) {
	ln, err := net.Listen("tcp", "localhost:0")
	if err != nil {
		return "", nil, fmt.Errorf("failed to start server: %w", err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("POST /people", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		w.WriteHeader(http.StatusCreated)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK","id":"12345"}`)
	})
	mux.HandleFunc("PUT /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.Header.Get("Content-Type") != "application/json" {
			http.Error(w, "Unsupported Media Type", http.StatusUnsupportedMediaType)
			return
		}
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("DELETE /people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"STATUS":"OK"}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people/{id}", func(w http.ResponseWriter, r *http.Request) {
		if r.PathValue("id") != "12345" {
			http.Error(w, "Not Found", http.StatusNotFound)
			return
		}
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/me", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"person":{"id":12345}}`)
	})
	mux.HandleFunc("GET /projects/api/v3/people", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Header().Set("Content-Type", "application/json")
		_, _ = fmt.Fprintln(w, `{"people":[{"id":12345},{"id":12346}]}`)
	})

	server := &http.Server{
		Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			if r.Header.Get("Authorization") != "Bearer your_token" {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}
			r.URL.Path = strings.TrimSuffix(r.URL.Path, ".json")
			mux.ServeHTTP(w, r)
		}),
	}

	stop := make(chan struct{})
	go func() {
		_ = server.Serve(ln)
	}()
	go func() {
		<-stop
		_ = server.Shutdown(context.Background())
	}()

	return ln.Addr().String(), func() {
		close(stop)
	}, nil
}
Output:

user updated!

func (*UserUpdateResponse) HandleHTTPResponse added in v0.3.0

func (u *UserUpdateResponse) HandleHTTPResponse(resp *http.Response) error

HandleHTTPResponse handles the HTTP response for the UserUpdateResponse. If some unexpected HTTP status code is returned by the API, a twapi.HTTPError is returned.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL