summaryrefslogtreecommitdiffstats
path: root/modules/structs/issue.go
blob: a67bdcf50eb6590ab83f30699970876dbfcf1d16 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
// Copyright 2016 The Gogs Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package structs

import (
	"fmt"
	"path"
	"slices"
	"strings"
	"time"

	"gopkg.in/yaml.v3"
)

// StateType issue state type
type StateType string

const (
	// StateOpen pr is opend
	StateOpen StateType = "open"
	// StateClosed pr is closed
	StateClosed StateType = "closed"
	// StateAll is all
	StateAll StateType = "all"
)

// PullRequestMeta PR info if an issue is a PR
type PullRequestMeta struct {
	HasMerged        bool       `json:"merged"`
	Merged           *time.Time `json:"merged_at"`
	IsWorkInProgress bool       `json:"draft"`
	HTMLURL          string     `json:"html_url"`
}

// RepositoryMeta basic repository information
type RepositoryMeta struct {
	ID       int64  `json:"id"`
	Name     string `json:"name"`
	Owner    string `json:"owner"`
	FullName string `json:"full_name"`
}

// Issue represents an issue in a repository
// swagger:model
type Issue struct {
	ID               int64         `json:"id"`
	URL              string        `json:"url"`
	HTMLURL          string        `json:"html_url"`
	Index            int64         `json:"number"`
	Poster           *User         `json:"user"`
	OriginalAuthor   string        `json:"original_author"`
	OriginalAuthorID int64         `json:"original_author_id"`
	Title            string        `json:"title"`
	Body             string        `json:"body"`
	Ref              string        `json:"ref"`
	Attachments      []*Attachment `json:"assets"`
	Labels           []*Label      `json:"labels"`
	Milestone        *Milestone    `json:"milestone"`
	// deprecated
	Assignee  *User   `json:"assignee"`
	Assignees []*User `json:"assignees"`
	// Whether the issue is open or closed
	//
	// type: string
	// enum: ["open", "closed"]
	State    StateType `json:"state"`
	IsLocked bool      `json:"is_locked"`
	Comments int       `json:"comments"`
	// swagger:strfmt date-time
	Created time.Time `json:"created_at"`
	// swagger:strfmt date-time
	Updated time.Time `json:"updated_at"`
	// swagger:strfmt date-time
	Closed *time.Time `json:"closed_at"`
	// swagger:strfmt date-time
	Deadline *time.Time `json:"due_date"`

	PullRequest *PullRequestMeta `json:"pull_request"`
	Repo        *RepositoryMeta  `json:"repository"`

	PinOrder int `json:"pin_order"`
}

// CreateIssueOption options to create one issue
type CreateIssueOption struct {
	// required:true
	Title string `json:"title" binding:"Required"`
	Body  string `json:"body"`
	Ref   string `json:"ref"`
	// deprecated
	Assignee  string   `json:"assignee"`
	Assignees []string `json:"assignees"`
	// swagger:strfmt date-time
	Deadline *time.Time `json:"due_date"`
	// milestone id
	Milestone int64 `json:"milestone"`
	// list of label ids
	Labels []int64 `json:"labels"`
	Closed bool    `json:"closed"`
}

// EditIssueOption options for editing an issue
type EditIssueOption struct {
	Title string  `json:"title"`
	Body  *string `json:"body"`
	Ref   *string `json:"ref"`
	// deprecated
	Assignee  *string  `json:"assignee"`
	Assignees []string `json:"assignees"`
	Milestone *int64   `json:"milestone"`
	State     *string  `json:"state"`
	// swagger:strfmt date-time
	Deadline       *time.Time `json:"due_date"`
	RemoveDeadline *bool      `json:"unset_due_date"`
	// swagger:strfmt date-time
	Updated *time.Time `json:"updated_at"`
}

// EditDeadlineOption options for creating a deadline
type EditDeadlineOption struct {
	// required:true
	// swagger:strfmt date-time
	Deadline *time.Time `json:"due_date"`
}

// IssueDeadline represents an issue deadline
// swagger:model
type IssueDeadline struct {
	// swagger:strfmt date-time
	Deadline *time.Time `json:"due_date"`
}

// IssueFormFieldType defines issue form field type, can be "markdown", "textarea", "input", "dropdown" or "checkboxes"
type IssueFormFieldType string

const (
	IssueFormFieldTypeMarkdown   IssueFormFieldType = "markdown"
	IssueFormFieldTypeTextarea   IssueFormFieldType = "textarea"
	IssueFormFieldTypeInput      IssueFormFieldType = "input"
	IssueFormFieldTypeDropdown   IssueFormFieldType = "dropdown"
	IssueFormFieldTypeCheckboxes IssueFormFieldType = "checkboxes"
)

// IssueFormField represents a form field
// swagger:model
type IssueFormField struct {
	Type        IssueFormFieldType      `json:"type" yaml:"type"`
	ID          string                  `json:"id" yaml:"id"`
	Attributes  map[string]any          `json:"attributes" yaml:"attributes"`
	Validations map[string]any          `json:"validations" yaml:"validations"`
	Visible     []IssueFormFieldVisible `json:"visible,omitempty"`
}

func (iff IssueFormField) VisibleOnForm() bool {
	if len(iff.Visible) == 0 {
		return true
	}
	return slices.Contains(iff.Visible, IssueFormFieldVisibleForm)
}

func (iff IssueFormField) VisibleInContent() bool {
	if len(iff.Visible) == 0 {
		// we have our markdown exception
		return iff.Type != IssueFormFieldTypeMarkdown
	}
	return slices.Contains(iff.Visible, IssueFormFieldVisibleContent)
}

// IssueFormFieldVisible defines issue form field visible
// swagger:model
type IssueFormFieldVisible string

const (
	IssueFormFieldVisibleForm    IssueFormFieldVisible = "form"
	IssueFormFieldVisibleContent IssueFormFieldVisible = "content"
)

// IssueTemplate represents an issue template for a repository
// swagger:model
type IssueTemplate struct {
	Name     string              `json:"name" yaml:"name"`
	Title    string              `json:"title" yaml:"title"`
	About    string              `json:"about" yaml:"about"` // Using "description" in a template file is compatible
	Labels   IssueTemplateLabels `json:"labels" yaml:"labels"`
	Ref      string              `json:"ref" yaml:"ref"`
	Content  string              `json:"content" yaml:"-"`
	Fields   []*IssueFormField   `json:"body" yaml:"body"`
	FileName string              `json:"file_name" yaml:"-"`
}

type IssueTemplateLabels []string

func (l *IssueTemplateLabels) UnmarshalYAML(value *yaml.Node) error {
	var labels []string
	if value.IsZero() {
		*l = labels
		return nil
	}
	switch value.Kind {
	case yaml.ScalarNode:
		str := ""
		err := value.Decode(&str)
		if err != nil {
			return err
		}
		for _, v := range strings.Split(str, ",") {
			if v = strings.TrimSpace(v); v == "" {
				continue
			}
			labels = append(labels, v)
		}
		*l = labels
		return nil
	case yaml.SequenceNode:
		if err := value.Decode(&labels); err != nil {
			return err
		}
		*l = labels
		return nil
	}
	return fmt.Errorf("line %d: cannot unmarshal %s into IssueTemplateLabels", value.Line, value.ShortTag())
}

type IssueConfigContactLink struct {
	Name  string `json:"name" yaml:"name"`
	URL   string `json:"url" yaml:"url"`
	About string `json:"about" yaml:"about"`
}

type IssueConfig struct {
	BlankIssuesEnabled bool                     `json:"blank_issues_enabled" yaml:"blank_issues_enabled"`
	ContactLinks       []IssueConfigContactLink `json:"contact_links" yaml:"contact_links"`
}

type IssueConfigValidation struct {
	Valid   bool   `json:"valid"`
	Message string `json:"message"`
}

// IssueTemplateType defines issue template type
type IssueTemplateType string

const (
	IssueTemplateTypeMarkdown IssueTemplateType = "md"
	IssueTemplateTypeYaml     IssueTemplateType = "yaml"
)

// Type returns the type of IssueTemplate, can be "md", "yaml" or empty for known
func (it IssueTemplate) Type() IssueTemplateType {
	if base := path.Base(it.FileName); base == "config.yaml" || base == "config.yml" {
		// ignore config.yaml which is a special configuration file
		return ""
	}
	if ext := path.Ext(it.FileName); ext == ".md" {
		return IssueTemplateTypeMarkdown
	} else if ext == ".yaml" || ext == ".yml" {
		return IssueTemplateTypeYaml
	}
	return ""
}

// IssueMeta basic issue information
// swagger:model
type IssueMeta struct {
	Index int64  `json:"index"`
	Owner string `json:"owner"`
	Name  string `json:"repo"`
}