新增: 批量更改文件统一接口以及结构体定义

This commit is contained in:
yystopf 2022-07-12 11:43:02 +08:00
parent f2d50f4cb8
commit 93de827180
6 changed files with 204 additions and 241 deletions

View File

@ -25,6 +25,24 @@ import (
"golang.org/x/text/transform"
)
type FileActionType int
const (
ActionTypeCreate FileActionType = iota + 1
ActionTypeUpdate
ActionTypeDelete
)
var fileActionTypes = map[string]FileActionType{
"create": ActionTypeCreate,
"update": ActionTypeUpdate,
"delete": ActionTypeDelete,
}
func ToFileActionType(name string) FileActionType {
return fileActionTypes[name]
}
// IdentityOptions for a person's identity like an author or committer
type IdentityOptions struct {
Name string
@ -54,6 +72,25 @@ type UpdateRepoFileOptions struct {
Signoff bool
}
type BatchSingleFileOption struct {
Content string
TreePath string
FromTreePath string
ActionType FileActionType
}
type BatchUpdateFileOptions struct {
Files []BatchSingleFileOption
LastCommitID string
OldBranch string
NewBranch string
Message string
SHA string
Author *IdentityOptions
Commiter *IdentityOptions
Dates *CommitDateOptions
Signoff bool
}
func detectEncodingAndBOM(entry *git.TreeEntry, repo *models.Repository) (string, bool) {
reader, err := entry.Blob().DataAsync()
if err != nil {
@ -466,3 +503,9 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
}
return file, nil
}
func CreateOrUpdateOrDeleteRepofiles(repo *models.Repository, doer *models.User, opts *BatchUpdateFileOptions) ([]*structs.FileResponse, error) {
var responses []*structs.FileResponse
return responses, nil
}

View File

@ -31,8 +31,16 @@ type CreateFileOptions struct {
}
// BatchCreateFileOptions options for creating more files
type BatchCreateFileOptions struct {
BatchFiles []CreateFileOptions `json:"batch_files"`
type BatchChangeFileOptions struct {
Header FileOptions `json:"header"`
Files []struct {
// enum: text,base64
Encoding string `json:"encoding"`
FilePath string `json:"file_path"`
Content string `json:"content"`
// enum: create,update,delete
ActionType string `json:"action_type"`
} `json:"files"`
}
// DeleteFileOptions options for deleting files (used for other File structs below)
@ -44,11 +52,6 @@ type DeleteFileOptions struct {
SHA string `json:"sha" binding:"Required"`
}
// BatchDeleteFileOptions options for deleting files
type BatchDeleteFileOptions struct {
BatchFiles []DeleteFileOptions `json:"batch_files"`
}
// UpdateFileOptions options for updating files
// 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)
type UpdateFileOptions struct {
@ -60,11 +63,6 @@ type UpdateFileOptions struct {
FromPath string `json:"from_path" binding:"MaxSize(500)"`
}
// BatchUpdateFileOptions options for updating more files.
type BatchUpdateFileOptions struct {
BatchFiles []UpdateFileOptions `json:"batch_files"`
}
// FileLinksResponse contains the links for a repo's file
type FileLinksResponse struct {
Self *string `json:"self"`

View File

@ -1032,10 +1032,8 @@ func Routes() *web.Route {
m.Put("", bind(api.UpdateFileOptions{}), repo.UpdateFile)
m.Delete("", bind(api.DeleteFileOptions{}), repo.DeleteFile)
}, reqRepoWriter(models.UnitTypeCode), reqToken())
m.Group("/batch/*", func() {
m.Post("", bind(api.BatchCreateFileOptions{}), repo.BatchCreateFile)
m.Put("", bind(api.BatchUpdateFileOptions{}), repo.BatchUpdateFile)
m.Delete("", bind(api.BatchDeleteFileOptions{}), repo.BatchDeleteFile)
m.Group("/batch", func() {
m.Post("", bind(api.BatchChangeFileOptions{}), repo.BatchChangeFile)
}, reqRepoWriter(models.UnitTypeCode), reqToken())
}, reqRepoReader(models.UnitTypeCode))
m.Get("/signing-key.gpg", misc.SigningKey)

View File

@ -285,11 +285,11 @@ func CreateFile(ctx *context.APIContext) {
}
}
// BatchCreateFile handles API call for creating a file***
func BatchCreateFile(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/contents/batch/{filepath} repository repoBatchCreateFile
// BatchChangeFile handles API call for change some files***
func BatchChangeFile(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/contents/batch repository repoBatchChangeFile
// ---
// summary: Create some files in a repository***
// summary: Change some files in a repository***
// consumes:
// - application/json
// produces:
@ -305,16 +305,11 @@ func BatchCreateFile(ctx *context.APIContext) {
// description: name of the repo
// type: string
// required: true
// - name: filepath
// in: path
// description: path of the file to create
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/BatchCreateFileOptions"
// "$ref": "#/definitions/BatchChangeFileOptions"
// responses:
// "201":
// "$ref": "#/responses/FileResponse"
@ -324,7 +319,32 @@ func BatchCreateFile(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/error"
apiBatchOpts := web.GetForm(ctx).(*api.BatchChangeFileOptions)
fmt.Println(apiBatchOpts)
if ctx.Repo.Repository.IsEmpty {
ctx.Error(http.StatusUnprocessableEntity, "RepoIsEmpty", fmt.Errorf("repo is empty"))
}
if apiBatchOpts.Header.BranchName == "" {
apiBatchOpts.Header.BranchName = ctx.Repo.Repository.DefaultBranch
}
if apiBatchOpts.Header.Message == "" {
apiBatchOpts.Header.Message = time.Now().Format("RFC3339")
}
if apiBatchOpts.Header.Dates.Author.IsZero() {
apiBatchOpts.Header.Dates.Author = time.Now()
}
if apiBatchOpts.Header.Dates.Committer.IsZero() {
apiBatchOpts.Header.Dates.Committer = time.Now()
}
if _, err := createOrUpdateOrDeleteFiles(ctx, apiBatchOpts); err != nil {
handleCreateOrUpdateFileError(ctx, err)
}
ctx.JSON(http.StatusOK, map[string]string{})
}
@ -435,47 +455,51 @@ func handleCreateOrUpdateFileError(ctx *context.APIContext, err error) {
ctx.Error(http.StatusInternalServerError, "UpdateFile", err)
}
// BatchUpdateFile handles API call for updating a file
func BatchUpdateFile(ctx *context.APIContext) {
// swagger:operation PUT /repos/{owner}/{repo}/contents/batch/{filepath} repository repoBatchUpdateFile
// ---
// summary: Update some files in a repository***
// consumes:
// - application/json
// produces:
// - application/jsoncontent
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: filepath
// in: path
// description: path of the file to update
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/BatchUpdateFileOptions"
// responses:
// "200":
// "$ref": "#/responses/FileResponse"
// "403":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/error"
func createOrUpdateOrDeleteFiles(ctx *context.APIContext, apiBatchOpts *api.BatchChangeFileOptions) ([]*api.FileResponse, error) {
if !canWriteFiles(ctx.Repo) {
return nil, models.ErrUserDoesNotHaveAccessToRepo{
UserID: ctx.User.ID,
RepoName: ctx.Repo.Repository.LowerName,
}
}
ctx.JSON(http.StatusOK, map[string]string{})
var files []repofiles.BatchSingleFileOption
for _, f := range apiBatchOpts.Files {
if f.Encoding == "base64" {
content, err := base64.StdEncoding.DecodeString(f.Content)
if err != nil {
return nil, err
}
f.Content = string(content)
}
files = append(files, repofiles.BatchSingleFileOption{
Content: f.Content,
TreePath: f.FilePath,
ActionType: repofiles.ToFileActionType(f.ActionType),
})
}
opts := &repofiles.BatchUpdateFileOptions{
Files: files,
Message: apiBatchOpts.Header.Message,
OldBranch: apiBatchOpts.Header.BranchName,
NewBranch: apiBatchOpts.Header.NewBranchName,
Commiter: &repofiles.IdentityOptions{
Name: apiBatchOpts.Header.Committer.Name,
Email: apiBatchOpts.Header.Committer.Email,
},
Author: &repofiles.IdentityOptions{
Name: apiBatchOpts.Header.Author.Name,
Email: apiBatchOpts.Header.Author.Email,
},
Dates: &repofiles.CommitDateOptions{
Author: apiBatchOpts.Header.Dates.Author,
Committer: apiBatchOpts.Header.Dates.Committer,
},
Signoff: apiBatchOpts.Header.Signoff,
}
return repofiles.CreateOrUpdateOrDeleteRepofiles(ctx.Repo.Repository, ctx.User, opts)
}
// Called from both CreateFile or UpdateFile to handle both
@ -601,49 +625,6 @@ func DeleteFile(ctx *context.APIContext) {
}
}
// BatchDeleteFile Delete some files in a repository
func BatchDeleteFile(ctx *context.APIContext) {
// swagger:operation DELETE /repos/{owner}/{repo}/contents/batch/{filepath} repository repoBatchDeleteFile
// ---
// summary: Delete some files in a repository***
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: owner
// in: path
// description: owner of the repo
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo
// type: string
// required: true
// - name: filepath
// in: path
// description: path of the file to delete
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/BatchDeleteFileOptions"
// responses:
// "200":
// "$ref": "#/responses/FileDeleteResponse"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/error"
ctx.JSON(http.StatusOK, map[string]string{})
}
// GetContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
func GetContents(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/contents/{filepath} repository repoGetContents

View File

@ -120,7 +120,7 @@ type swaggerParameterBodies struct {
CreateFileOptions api.CreateFileOptions
// in:body
BatchCreateFileOptions api.BatchCreateFileOptions
BatchChangeFileOptions api.BatchChangeFileOptions
// in:body
UpdateFileOptions api.UpdateFileOptions

View File

@ -3469,65 +3469,7 @@
}
}
},
"/repos/{owner}/{repo}/contents/batch/{filepath}": {
"put": {
"consumes": [
"application/json"
],
"produces": [
"application/jsoncontent"
],
"tags": [
"repository"
],
"summary": "Update some files in a repository***",
"operationId": "repoBatchUpdateFile",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "path of the file to update",
"name": "filepath",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/BatchUpdateFileOptions"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/FileResponse"
},
"403": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
},
"422": {
"$ref": "#/responses/error"
}
}
},
"/repos/{owner}/{repo}/contents/batch": {
"post": {
"consumes": [
"application/json"
@ -3538,8 +3480,8 @@
"tags": [
"repository"
],
"summary": "Create some files in a repository***",
"operationId": "repoBatchCreateFile",
"summary": "Change some files in a repository***",
"operationId": "repoBatchChangeFile",
"parameters": [
{
"type": "string",
@ -3555,19 +3497,12 @@
"in": "path",
"required": true
},
{
"type": "string",
"description": "path of the file to create",
"name": "filepath",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/BatchCreateFileOptions"
"$ref": "#/definitions/BatchChangeFileOptions"
}
}
],
@ -3585,64 +3520,6 @@
"$ref": "#/responses/error"
}
}
},
"delete": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Delete some files in a repository***",
"operationId": "repoBatchDeleteFile",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "path of the file to delete",
"name": "filepath",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/BatchDeleteFileOptions"
}
}
],
"responses": {
"200": {
"$ref": "#/responses/FileDeleteResponse"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/error"
}
}
}
},
"/repos/{owner}/{repo}/contents/{filepath}": {
@ -13640,16 +13517,46 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"BatchCreateFileOptions": {
"BatchChangeFileOptions": {
"description": "BatchCreateFileOptions options for creating more files",
"type": "object",
"properties": {
"batch_files": {
"files": {
"type": "array",
"items": {
"$ref": "#/definitions/CreateFileOptions"
"type": "object",
"properties": {
"action_type": {
"type": "string",
"enum": [
"create",
"update",
"delete"
],
"x-go-name": "ActionType"
},
"content": {
"type": "string",
"x-go-name": "Content"
},
"encoding": {
"type": "string",
"enum": [
"text",
"base64"
],
"x-go-name": "Encoding"
},
"file_path": {
"type": "string",
"x-go-name": "FilePath"
}
}
},
"x-go-name": "BatchFiles"
"x-go-name": "Files"
},
"header": {
"$ref": "#/definitions/FileOptions"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
@ -16304,6 +16211,42 @@
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"FileOptions": {
"description": "FileOptions options for all file APIs",
"type": "object",
"properties": {
"author": {
"$ref": "#/definitions/Identity"
},
"branch": {
"description": "branch (optional) to base this file from. if not given, the default branch is used",
"type": "string",
"x-go-name": "BranchName"
},
"committer": {
"$ref": "#/definitions/Identity"
},
"dates": {
"$ref": "#/definitions/CommitDateOptions"
},
"message": {
"description": "message (optional) for the commit of this file. if not supplied, a default message will be used",
"type": "string",
"x-go-name": "Message"
},
"new_branch": {
"description": "new_branch (optional) will make a new branch from `branch` before creating the file",
"type": "string",
"x-go-name": "NewBranchName"
},
"signoff": {
"description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.",
"type": "boolean",
"x-go-name": "Signoff"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"FileResponse": {
"description": "FileResponse contains information about a repo's file",
"type": "object",