1// Copyright 2014 The Gogs Authors. All rights reserved.
2// Copyright 2019 The Gitea Authors. All rights reserved.
3// Use of this source code is governed by a MIT-style
4// license that can be found in the LICENSE file.
5
6package gitea
7
8import (
9	"bytes"
10	"encoding/json"
11	"fmt"
12)
13
14// FileOptions options for all file APIs
15type FileOptions struct {
16	// message (optional) for the commit of this file. if not supplied, a default message will be used
17	Message string `json:"message"`
18	// branch (optional) to base this file from. if not given, the default branch is used
19	BranchName string `json:"branch"`
20	// new_branch (optional) will make a new branch from `branch` before creating the file
21	NewBranchName string `json:"new_branch"`
22	// `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
23	Author    Identity          `json:"author"`
24	Committer Identity          `json:"committer"`
25	Dates     CommitDateOptions `json:"dates"`
26}
27
28// CreateFileOptions options for creating files
29// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
30type CreateFileOptions struct {
31	FileOptions
32	// content must be base64 encoded
33	// required: true
34	Content string `json:"content"`
35}
36
37// DeleteFileOptions options for deleting files (used for other File structs below)
38// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
39type DeleteFileOptions struct {
40	FileOptions
41	// sha is the SHA for the file that already exists
42	// required: true
43	SHA string `json:"sha"`
44}
45
46// UpdateFileOptions options for updating files
47// Note: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)
48type UpdateFileOptions struct {
49	FileOptions
50	// sha is the SHA for the file that already exists
51	// required: true
52	SHA string `json:"sha"`
53	// content must be base64 encoded
54	// required: true
55	Content string `json:"content"`
56	// from_path (optional) is the path of the original file which will be moved/renamed to the path in the URL
57	FromPath string `json:"from_path"`
58}
59
60// FileLinksResponse contains the links for a repo's file
61type FileLinksResponse struct {
62	Self    *string `json:"self"`
63	GitURL  *string `json:"git"`
64	HTMLURL *string `json:"html"`
65}
66
67// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
68type ContentsResponse struct {
69	Name string `json:"name"`
70	Path string `json:"path"`
71	SHA  string `json:"sha"`
72	// `type` will be `file`, `dir`, `symlink`, or `submodule`
73	Type string `json:"type"`
74	Size int64  `json:"size"`
75	// `encoding` is populated when `type` is `file`, otherwise null
76	Encoding *string `json:"encoding"`
77	// `content` is populated when `type` is `file`, otherwise null
78	Content *string `json:"content"`
79	// `target` is populated when `type` is `symlink`, otherwise null
80	Target      *string `json:"target"`
81	URL         *string `json:"url"`
82	HTMLURL     *string `json:"html_url"`
83	GitURL      *string `json:"git_url"`
84	DownloadURL *string `json:"download_url"`
85	// `submodule_git_url` is populated when `type` is `submodule`, otherwise null
86	SubmoduleGitURL *string            `json:"submodule_git_url"`
87	Links           *FileLinksResponse `json:"_links"`
88}
89
90// FileCommitResponse contains information generated from a Git commit for a repo's file.
91type FileCommitResponse struct {
92	CommitMeta
93	HTMLURL   string        `json:"html_url"`
94	Author    *CommitUser   `json:"author"`
95	Committer *CommitUser   `json:"committer"`
96	Parents   []*CommitMeta `json:"parents"`
97	Message   string        `json:"message"`
98	Tree      *CommitMeta   `json:"tree"`
99}
100
101// FileResponse contains information about a repo's file
102type FileResponse struct {
103	Content      *ContentsResponse          `json:"content"`
104	Commit       *FileCommitResponse        `json:"commit"`
105	Verification *PayloadCommitVerification `json:"verification"`
106}
107
108// FileDeleteResponse contains information about a repo's file that was deleted
109type FileDeleteResponse struct {
110	Content      interface{}                `json:"content"` // to be set to nil
111	Commit       *FileCommitResponse        `json:"commit"`
112	Verification *PayloadCommitVerification `json:"verification"`
113}
114
115// GetFile downloads a file of repository, ref can be branch/tag/commit.
116// e.g.: ref -> master, tree -> macaron.go(no leading slash)
117func (c *Client) GetFile(user, repo, ref, tree string) ([]byte, error) {
118	return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", user, repo, ref, tree), nil, nil)
119}
120
121// GetContents get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
122// ref is optional
123func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, error) {
124	cr := new(ContentsResponse)
125	return cr, c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, ref), jsonHeader, nil, cr)
126
127}
128
129// CreateFile create a file in a repository
130func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, error) {
131	body, err := json.Marshal(&opt)
132	if err != nil {
133		return nil, err
134	}
135	fr := new(FileResponse)
136	return fr, c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
137}
138
139// UpdateFile update a file in a repository
140func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions) (*FileResponse, error) {
141	body, err := json.Marshal(&opt)
142	if err != nil {
143		return nil, err
144	}
145	fr := new(FileResponse)
146	return fr, c.getParsedResponse("PUT", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body), fr)
147}
148
149// DeleteFile delete a file from repository
150func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions) error {
151	body, err := json.Marshal(&opt)
152	if err != nil {
153		return err
154	}
155	status, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/contents/%s", owner, repo, filepath), jsonHeader, bytes.NewReader(body))
156	if err != nil {
157		return err
158	}
159	if status != 200 && status != 204 {
160		return fmt.Errorf("unexpected Status: %d", status)
161	}
162	return nil
163}
164