code.gitea.io/gitea@v1.22.3/modules/structs/issue.go (about)

     1  // Copyright 2016 The Gogs Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package structs
     5  
     6  import (
     7  	"fmt"
     8  	"path"
     9  	"slices"
    10  	"strings"
    11  	"time"
    12  
    13  	"gopkg.in/yaml.v3"
    14  )
    15  
    16  // StateType issue state type
    17  type StateType string
    18  
    19  const (
    20  	// StateOpen pr is opend
    21  	StateOpen StateType = "open"
    22  	// StateClosed pr is closed
    23  	StateClosed StateType = "closed"
    24  	// StateAll is all
    25  	StateAll StateType = "all"
    26  )
    27  
    28  // PullRequestMeta PR info if an issue is a PR
    29  type PullRequestMeta struct {
    30  	HasMerged        bool       `json:"merged"`
    31  	Merged           *time.Time `json:"merged_at"`
    32  	IsWorkInProgress bool       `json:"draft"`
    33  	HTMLURL          string     `json:"html_url"`
    34  }
    35  
    36  // RepositoryMeta basic repository information
    37  type RepositoryMeta struct {
    38  	ID       int64  `json:"id"`
    39  	Name     string `json:"name"`
    40  	Owner    string `json:"owner"`
    41  	FullName string `json:"full_name"`
    42  }
    43  
    44  // Issue represents an issue in a repository
    45  // swagger:model
    46  type Issue struct {
    47  	ID               int64         `json:"id"`
    48  	URL              string        `json:"url"`
    49  	HTMLURL          string        `json:"html_url"`
    50  	Index            int64         `json:"number"`
    51  	Poster           *User         `json:"user"`
    52  	OriginalAuthor   string        `json:"original_author"`
    53  	OriginalAuthorID int64         `json:"original_author_id"`
    54  	Title            string        `json:"title"`
    55  	Body             string        `json:"body"`
    56  	Ref              string        `json:"ref"`
    57  	Attachments      []*Attachment `json:"assets"`
    58  	Labels           []*Label      `json:"labels"`
    59  	Milestone        *Milestone    `json:"milestone"`
    60  	// deprecated
    61  	Assignee  *User   `json:"assignee"`
    62  	Assignees []*User `json:"assignees"`
    63  	// Whether the issue is open or closed
    64  	//
    65  	// type: string
    66  	// enum: open,closed
    67  	State    StateType `json:"state"`
    68  	IsLocked bool      `json:"is_locked"`
    69  	Comments int       `json:"comments"`
    70  	// swagger:strfmt date-time
    71  	Created time.Time `json:"created_at"`
    72  	// swagger:strfmt date-time
    73  	Updated time.Time `json:"updated_at"`
    74  	// swagger:strfmt date-time
    75  	Closed *time.Time `json:"closed_at"`
    76  	// swagger:strfmt date-time
    77  	Deadline *time.Time `json:"due_date"`
    78  
    79  	PullRequest *PullRequestMeta `json:"pull_request"`
    80  	Repo        *RepositoryMeta  `json:"repository"`
    81  
    82  	PinOrder int `json:"pin_order"`
    83  }
    84  
    85  // CreateIssueOption options to create one issue
    86  type CreateIssueOption struct {
    87  	// required:true
    88  	Title string `json:"title" binding:"Required"`
    89  	Body  string `json:"body"`
    90  	Ref   string `json:"ref"`
    91  	// deprecated
    92  	Assignee  string   `json:"assignee"`
    93  	Assignees []string `json:"assignees"`
    94  	// swagger:strfmt date-time
    95  	Deadline *time.Time `json:"due_date"`
    96  	// milestone id
    97  	Milestone int64 `json:"milestone"`
    98  	// list of label ids
    99  	Labels []int64 `json:"labels"`
   100  	Closed bool    `json:"closed"`
   101  }
   102  
   103  // EditIssueOption options for editing an issue
   104  type EditIssueOption struct {
   105  	Title string  `json:"title"`
   106  	Body  *string `json:"body"`
   107  	Ref   *string `json:"ref"`
   108  	// deprecated
   109  	Assignee  *string  `json:"assignee"`
   110  	Assignees []string `json:"assignees"`
   111  	Milestone *int64   `json:"milestone"`
   112  	State     *string  `json:"state"`
   113  	// swagger:strfmt date-time
   114  	Deadline       *time.Time `json:"due_date"`
   115  	RemoveDeadline *bool      `json:"unset_due_date"`
   116  }
   117  
   118  // EditDeadlineOption options for creating a deadline
   119  type EditDeadlineOption struct {
   120  	// required:true
   121  	// swagger:strfmt date-time
   122  	Deadline *time.Time `json:"due_date"`
   123  }
   124  
   125  // IssueDeadline represents an issue deadline
   126  // swagger:model
   127  type IssueDeadline struct {
   128  	// swagger:strfmt date-time
   129  	Deadline *time.Time `json:"due_date"`
   130  }
   131  
   132  // IssueFormFieldType defines issue form field type, can be "markdown", "textarea", "input", "dropdown" or "checkboxes"
   133  type IssueFormFieldType string
   134  
   135  const (
   136  	IssueFormFieldTypeMarkdown   IssueFormFieldType = "markdown"
   137  	IssueFormFieldTypeTextarea   IssueFormFieldType = "textarea"
   138  	IssueFormFieldTypeInput      IssueFormFieldType = "input"
   139  	IssueFormFieldTypeDropdown   IssueFormFieldType = "dropdown"
   140  	IssueFormFieldTypeCheckboxes IssueFormFieldType = "checkboxes"
   141  )
   142  
   143  // IssueFormField represents a form field
   144  // swagger:model
   145  type IssueFormField struct {
   146  	Type        IssueFormFieldType      `json:"type" yaml:"type"`
   147  	ID          string                  `json:"id" yaml:"id"`
   148  	Attributes  map[string]any          `json:"attributes" yaml:"attributes"`
   149  	Validations map[string]any          `json:"validations" yaml:"validations"`
   150  	Visible     []IssueFormFieldVisible `json:"visible,omitempty"`
   151  }
   152  
   153  func (iff IssueFormField) VisibleOnForm() bool {
   154  	if len(iff.Visible) == 0 {
   155  		return true
   156  	}
   157  	return slices.Contains(iff.Visible, IssueFormFieldVisibleForm)
   158  }
   159  
   160  func (iff IssueFormField) VisibleInContent() bool {
   161  	if len(iff.Visible) == 0 {
   162  		// we have our markdown exception
   163  		return iff.Type != IssueFormFieldTypeMarkdown
   164  	}
   165  	return slices.Contains(iff.Visible, IssueFormFieldVisibleContent)
   166  }
   167  
   168  // IssueFormFieldVisible defines issue form field visible
   169  // swagger:model
   170  type IssueFormFieldVisible string
   171  
   172  const (
   173  	IssueFormFieldVisibleForm    IssueFormFieldVisible = "form"
   174  	IssueFormFieldVisibleContent IssueFormFieldVisible = "content"
   175  )
   176  
   177  // IssueTemplate represents an issue template for a repository
   178  // swagger:model
   179  type IssueTemplate struct {
   180  	Name     string              `json:"name" yaml:"name"`
   181  	Title    string              `json:"title" yaml:"title"`
   182  	About    string              `json:"about" yaml:"about"` // Using "description" in a template file is compatible
   183  	Labels   IssueTemplateLabels `json:"labels" yaml:"labels"`
   184  	Ref      string              `json:"ref" yaml:"ref"`
   185  	Content  string              `json:"content" yaml:"-"`
   186  	Fields   []*IssueFormField   `json:"body" yaml:"body"`
   187  	FileName string              `json:"file_name" yaml:"-"`
   188  }
   189  
   190  type IssueTemplateLabels []string
   191  
   192  func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error {
   193  	var labels []string
   194  	if value.IsZero() {
   195  		*l = labels
   196  		return nil
   197  	}
   198  	switch value.Kind {
   199  	case yaml.ScalarNode:
   200  		str := ""
   201  		err := value.Decode(&str)
   202  		if err != nil {
   203  			return err
   204  		}
   205  		for _, v := range strings.Split(str, ",") {
   206  			if v = strings.TrimSpace(v); v == "" {
   207  				continue
   208  			}
   209  			labels = append(labels, v)
   210  		}
   211  		*l = labels
   212  		return nil
   213  	case yaml.SequenceNode:
   214  		if err := value.Decode(&labels); err != nil {
   215  			return err
   216  		}
   217  		*l = labels
   218  		return nil
   219  	}
   220  	return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateLabels", value.Line, value.ShortTag())
   221  }
   222  
   223  type IssueConfigContactLink struct {
   224  	Name  string `json:"name" yaml:"name"`
   225  	URL   string `json:"url" yaml:"url"`
   226  	About string `json:"about" yaml:"about"`
   227  }
   228  
   229  type IssueConfig struct {
   230  	BlankIssuesEnabled bool                     `json:"blank_issues_enabled" yaml:"blank_issues_enabled"`
   231  	ContactLinks       []IssueConfigContactLink `json:"contact_links" yaml:"contact_links"`
   232  }
   233  
   234  type IssueConfigValidation struct {
   235  	Valid   bool   `json:"valid"`
   236  	Message string `json:"message"`
   237  }
   238  
   239  // IssueTemplateType defines issue template type
   240  type IssueTemplateType string
   241  
   242  const (
   243  	IssueTemplateTypeMarkdown IssueTemplateType = "md"
   244  	IssueTemplateTypeYaml     IssueTemplateType = "yaml"
   245  )
   246  
   247  // Type returns the type of IssueTemplate, can be "md", "yaml" or empty for known
   248  func (it IssueTemplate) Type() IssueTemplateType {
   249  	if base := path.Base(it.FileName); base == "config.yaml" || base == "config.yml" {
   250  		// ignore config.yaml which is a special configuration file
   251  		return ""
   252  	}
   253  	if ext := path.Ext(it.FileName); ext == ".md" {
   254  		return IssueTemplateTypeMarkdown
   255  	} else if ext == ".yaml" || ext == ".yml" {
   256  		return IssueTemplateTypeYaml
   257  	}
   258  	return ""
   259  }
   260  
   261  // IssueMeta basic issue information
   262  // swagger:model
   263  type IssueMeta struct {
   264  	Index int64  `json:"index"`
   265  	Owner string `json:"owner"`
   266  	Name  string `json:"repo"`
   267  }