redmine

package module
v0.0.0-...-db474ff Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2024 License: LGPL-3.0 Imports: 11 Imported by: 2

README

Redmine Go Library

This Go library provides an interface to interact with the Redmine project management tool. It supports creating and updating issues, retrieving issue notes, and handling retries for API calls. Its main focus is on using redmine issues as a ticketing system for customer support.

Features

  • Dynamic Configuration: Easily configure the library to connect to your Redmine instance using functional options, and change the configuration at runtime.
  • Issue Management: Create, update, and check the status of issues in Redmine.
  • Notes Retrieval: Retrieve notes from issues.
  • Retry Mechanism: Built-in retry mechanism for API calls to handle transient errors.
  • Logging: (Optional) Integration with zerolog for structured logging.
  • Graceful Shutdown: Wait for all goroutines to finish before shutting down.

Installation

To install the library, use go get:

go get gitlab.com/etke.cc/go/redmine

Usage

Configuration

You need to configure the library to connect to your Redmine instance, here is an example with all available options set:

import (
    "gitlab.com/etke.cc/go/redmine"
    "github.com/rs/zerolog"
    "os"
)

func main() {
    logger := zerolog.New(os.Stdout).With().Timestamp().Logger() // completely optional, by default discard logs
    
    client, err := redmine.New(
        redmine.WithLog(&logger), // optional, by default discard logs
        redmine.WithHost("https://redmine.example.com"), // required
        redmine.WithAPIKey("your_api_key"), // required
        redmine.WithProjectIdentifier("my-project"), // required, the identifier of the project. You may use redmine.WithProjectID() instead
        redmine.WithTrackerID(1), // required
        redmine.WithWaitingForOperatorStatusID(1), // required, the status ID for issues waiting for operator. Usually "In Progress" status
        redmine.WithWaitingForCustomerStatusID(2), // required, the status ID for issues waiting for customer. Usually "New" status
        redmine.WithDoneStatusID(3), // required, the status ID for closed issues. Usually "Closed" status
    )
    
    if err != nil {
        logger.Fatal().Err(err).Msg("Failed to create Redmine client")
    }
    
    // Use the client...
}
Creating an Issue

To create a new issue, use the NewIssue method:

issueID, err := client.NewIssue("Issue Subject", "email", "user@example.com", "Issue description text")
if err != nil {
    logger.Error().Err(err).Msg("Failed to create issue")
} else {
    logger.Info().Int64("issue_id", issueID).Msg("Issue created")
}
Updating an Issue

To update the status of an existing issue, use the UpdateIssue method:

err := client.UpdateIssue(issueID, redmine.WaitingForCustomer, "Your notes")
if err != nil {
    logger.Error().Err(err).Msg("Failed to update issue")
}
Retrieving Issue Notes

To retrieve the notes of an issue, use the GetNotes method:

notes, err := client.GetNotes(issueID)
if err != nil {
    logger.Error().Err(err).Msg("Failed to get issue notes")
} else {
    for _, note := range notes {
        fmt.Println(note.Notes)
    }
}
Checking if an Issue is Closed

To check if an issue is closed, use the IsClosed method:

isClosed, err := client.IsClosed(issueID)
if err != nil {
    logger.Error().Err(err).Msg("Failed to check if issue is closed")
} else if isClosed {
    logger.Info().Msg("Issue is closed")
} else {
    logger.Info().Msg("Issue is open")
}
Shutdown

To gracefully shutdown the client and wait for all goroutines to finish, use the Shutdown method:

client.Shutdown()

Documentation

Index

Constants

View Source
const (
	// MaxRetries is the maximum number of retries
	MaxRetries = 5
	// RetryDelay is the delay step between retries
	RetryDelay = 5 * time.Second
)

Variables

This section is empty.

Functions

func Retry

func Retry(log *zerolog.Logger, fn func() (redmine.StatusCode, error)) error

Retry retries the given function until it succeeds or the maximum number of retries is reached it should be used everywhere you call the underlying API object (inside the library: r.cfg.api, outside: r.GetAPI())

func RetryResult

func RetryResult[V any](log *zerolog.Logger, fn func() (V, redmine.StatusCode, error)) (V, error)

RetryResult retries the given function until it succeeds or the maximum number of retries is reached it should be used everywhere you call the underlying API object (inside the library: r.cfg.api, outside: r.GetAPI())

Types

type API

type API interface {
	ProjectSingleGet(identifier string, req redmine.ProjectSingleGetRequest) (redmine.ProjectObject, redmine.StatusCode, error)
	UserCurrentGet(req redmine.UserCurrentGetRequest) (redmine.UserObject, redmine.StatusCode, error)
	IssueCreate(req redmine.IssueCreate) (redmine.IssueObject, redmine.StatusCode, error)
	IssueUpdate(id int64, req redmine.IssueUpdate) (redmine.StatusCode, error)
	IssueSingleGet(id int64, req redmine.IssueSingleGetRequest) (redmine.IssueObject, redmine.StatusCode, error)
	IssueDelete(id int64) (redmine.StatusCode, error)
	AttachmentUpload(filePath string) (redmine.AttachmentUploadObject, redmine.StatusCode, error)
	AttachmentUploadStream(f io.Reader, fileName string) (redmine.AttachmentUploadObject, redmine.StatusCode, error)
	Del(in, out any, uri url.URL, statusExpected redmine.StatusCode) (redmine.StatusCode, error)
	Post(in, out any, uri url.URL, statusExpected redmine.StatusCode) (redmine.StatusCode, error)
}

API is an interface for Redmine API

type Config

type Config struct {
	Log                        *zerolog.Logger // Logger, defaults to discard
	Host                       string          // Redmine host, e.g. "https://redmine.example.com"
	APIKey                     string          // Redmine REST API Key
	ProjectIdentifier          string          // Redmine project identifier, e.g. "my-project"
	ProjectID                  int64           // Redmine project ID, e.g. 123. This is set automatically if you use ProjectIdentifier
	UserID                     int64           // Current redmine user ID, e.g. 123. This is set automatically
	TrackerID                  int64           // Task Tracker ID, e.g. 1
	WaitingForOperatorStatusID int64           // Status ID for "Waiting for operator", e.g. 1
	WaitingForCustomerStatusID int64           // Status ID for "Waiting for customer", e.g. 2
	DoneStatusID               int64           // Status ID for "Done", e.g. 3
	// contains filtered or unexported fields
}

Config is a configuration for Redmine

func NewConfig

func NewConfig(opts ...Option) *Config

NewConfig creates a new Config

func (*Config) Enabled

func (cfg *Config) Enabled() bool

type IssueRelation

type IssueRelation struct {
	IssueToID    int64  `json:"issue_to_id"`
	RelationType string `json:"relation_type"`
}

IssueRelation is a minimal struct for issue relations

type IssueRelationRequest

type IssueRelationRequest struct {
	Relation IssueRelation `json:"relation"`
}

IssueRelationRequest is a request to create a new issue relation

type Option

type Option func(*Config)

Option is a functional option for Config

func WithAPIKey

func WithAPIKey(apikey string) Option

WithAPIKey sets the API key

func WithDoneStatusID

func WithDoneStatusID(doneStatusID int) Option

WithDoneStatusID sets the done status ID

func WithHost

func WithHost(host string) Option

WithHost sets the host

func WithLog

func WithLog(log *zerolog.Logger) Option

WithLog sets the logger

func WithProjectID

func WithProjectID(projectID int) Option

WithProjectID sets the project ID. This is set automatically if you use ProjectIdentifier

func WithProjectIdentifier

func WithProjectIdentifier(projectIdentifier string) Option

WithProjectIdentifier sets the project identifier

func WithTrackerID

func WithTrackerID(trackerID int) Option

WithTrackerID sets the tracker ID

func WithUserID

func WithUserID(userID int) Option

WithUserID sets the user ID

func WithWaitingForCustomerStatusID

func WithWaitingForCustomerStatusID(waitingForCustomerStatusID int) Option

WithWaitingForCustomerStatusID sets the waiting for customer status ID

func WithWaitingForOperatorStatusID

func WithWaitingForOperatorStatusID(waitingForOperatorStatusID int) Option

WithWaitingForOperatorStatusID sets the waiting for operator status ID

type Redmine

type Redmine struct {
	// contains filtered or unexported fields
}

Redmine is a Redmine client

func New

func New(options ...Option) (*Redmine, error)

New creates a new Redmine client

func (*Redmine) Configure

func (r *Redmine) Configure(options ...Option) *Redmine

Configure applies the new configuration options in runtime It is advisable to call UpdateUser() (if API key and/or host was changed), and UpdatePorject() (if project identifier was changed) after this method

func (*Redmine) DeleteAttachment

func (r *Redmine) DeleteAttachment(attachmentID int64) error

DeleteAttachment deletes an attachment by its ID

func (*Redmine) DeleteIssue

func (r *Redmine) DeleteIssue(issueID int64) error

DeleteIssue deletes an issue by its ID

func (*Redmine) Enabled

func (r *Redmine) Enabled() bool

Enabled returns true if the Redmine client is enabled

func (*Redmine) GetAPI

func (r *Redmine) GetAPI() *redmine.Context

GetAPI returns the underlying API object from the github.com/nixys/nxs-go-redmine package (actual version may vary) it's useful for calling methods that are not exposed by this package WARNING: it CAN return nil (API interface is defined for tests only, but this method does type casting, so if the type is wrong, it will return nil)

func (*Redmine) GetAPIKey

func (r *Redmine) GetAPIKey() string

GetAPIKey returns the Redmine API key

func (*Redmine) GetDoneStatusID

func (r *Redmine) GetDoneStatusID() int64

GetDoneStatusID returns the Redmine done status ID

func (*Redmine) GetHost

func (r *Redmine) GetHost() string

GetHost returns the Redmine host

func (*Redmine) GetIssue

func (r *Redmine) GetIssue(issueID int64, includes ...redmine.IssueInclude) (redmine.IssueObject, error)

GetIssue returns an issue by its ID

func (*Redmine) GetNotes

func (r *Redmine) GetNotes(issueID int64) ([]*redmine.IssueJournalObject, error)

GetNotes returns the notes of an issue

func (*Redmine) GetProjectID

func (r *Redmine) GetProjectID() int64

GetProjectID returns the Redmine project ID

func (*Redmine) GetProjectIdentifier

func (r *Redmine) GetProjectIdentifier() string

GetProjectIdentifier returns the Redmine project identifier

func (*Redmine) GetStatus

func (r *Redmine) GetStatus(issueID int64) (redmine.IssueStatusObject, error)

GetStatus returns the status of an issue

func (*Redmine) GetTrackerID

func (r *Redmine) GetTrackerID() int64

GetTrackerID returns the Redmine tracker ID

func (*Redmine) GetUserID

func (r *Redmine) GetUserID() int64

GetUserID returns the Redmine user ID

func (*Redmine) GetWaitingForCustomerStatusID

func (r *Redmine) GetWaitingForCustomerStatusID() int64

GetWaitingForCustomerStatusID returns the Redmine waiting for customer status ID

func (*Redmine) GetWaitingForOperatorStatusID

func (r *Redmine) GetWaitingForOperatorStatusID() int64

GetNewStatusID returns the Redmine new status ID

func (*Redmine) IsClosed

func (r *Redmine) IsClosed(issueID int64) (bool, error)

IsClosed returns true if the issue is closed or has "Done" status

func (*Redmine) NewIssue

func (r *Redmine) NewIssue(subject, senderMedium, senderAddress, text string, files ...*UploadRequest) (int64, error)

NewIssue creates a new issue in Redmine

func (*Redmine) NewIssueRelation

func (r *Redmine) NewIssueRelation(issueID, relatedIssueID int64, relationType string) error

func (*Redmine) Shutdown

func (r *Redmine) Shutdown()

Shutdown waits for all goroutines to finish

func (*Redmine) StatusToID

func (r *Redmine) StatusToID(status Status) int64

StatusToID converts a Status-typed number to actual status ID

func (*Redmine) UpdateIssue

func (r *Redmine) UpdateIssue(issueID, statusID int64, text string, files ...*UploadRequest) error

UpdateIssue updates the status using one of the constants and notes of an issue

func (*Redmine) UpdateProject

func (r *Redmine) UpdateProject() error

UpdateProject updates the project ID, it should be called after changing the project identifier

func (*Redmine) UpdateUser

func (r *Redmine) UpdateUser() error

UpdateUser updates the current user ID, it should be called after changing the API key and/or host

type Status

type Status int

Status is a type for issue statuses it should be used instead of sending raw status IDs to allow dynamic (re-)configuration

const (
	WaitingForOperator Status = iota
	WaitingForCustomer
	Done
)

type UploadRequest

type UploadRequest struct {
	Path   string
	Stream io.Reader
}

UploadRequest is a request to upload a file you can use either only Path to specify the file from the filesystem OR Stream to specify the file from a stream. In this case, Path is used as a filename

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL