diff --git a/models/attachment.go b/models/attachment.go index acb1f0716..ffe2eea80 100644 --- a/models/attachment.go +++ b/models/attachment.go @@ -11,10 +11,12 @@ import ( "os" "path" - gouuid "github.com/satori/go.uuid" - "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + api "code.gitea.io/sdk/gitea" + + "github.com/go-xorm/xorm" + gouuid "github.com/satori/go.uuid" ) // Attachment represent a attachment of issue/comment/release. @@ -39,6 +41,20 @@ func (a *Attachment) IncreaseDownloadCount() error { return nil } +// APIFormat converts models.Attachment to api.Attachment +func (a *Attachment) APIFormat() *api.Attachment { + size, _ := a.Size() + return &api.Attachment{ + ID: a.ID, + Name: a.Name, + Created: a.CreatedUnix.AsTime(), + DownloadCount: a.DownloadCount, + Size: size, + UUID: a.UUID, + DownloadURL: a.DownloadURL(), + } +} + // AttachmentLocalPath returns where attachment is stored in local file // system based on given UUID. func AttachmentLocalPath(uuid string) string { @@ -50,6 +66,20 @@ func (a *Attachment) LocalPath() string { return AttachmentLocalPath(a.UUID) } +// Size returns the file's size of the attachment +func (a *Attachment) Size() (int64, error) { + fi, err := os.Stat(a.LocalPath()) + if err != nil { + return 0, err + } + return fi.Size(), nil +} + +// DownloadURL returns the download url of the attached file +func (a *Attachment) DownloadURL() string { + return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID) +} + // NewAttachment creates a new attachment object. func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, err error) { attach := &Attachment{ @@ -81,6 +111,22 @@ func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment, return attach, nil } +// GetAttachmentByID returns attachment by given id +func GetAttachmentByID(id int64) (*Attachment, error) { + return getAttachmentByID(x, id) +} + +func getAttachmentByID(e Engine, id int64) (*Attachment, error) { + attach := &Attachment{ID: id} + + if has, err := e.Get(attach); err != nil { + return nil, err + } else if !has { + return nil, ErrAttachmentNotExist{ID: id, UUID: ""} + } + return attach, nil +} + func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) { attach := &Attachment{UUID: uuid} has, err := e.Get(attach) @@ -180,3 +226,20 @@ func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) { return DeleteAttachments(attachments, remove) } + +// UpdateAttachment updates the given attachment in database +func UpdateAttachment(atta *Attachment) error { + return updateAttachment(x, atta) +} + +func updateAttachment(e Engine, atta *Attachment) error { + var sess *xorm.Session + if atta.ID != 0 && atta.UUID == "" { + sess = e.ID(atta.ID) + } else { + // Use uuid only if id is not set and uuid is set + sess = e.Where("uuid = ?", atta.UUID) + } + _, err := sess.Cols("name", "issue_id", "release_id", "comment_id", "download_count").Update(atta) + return err +} diff --git a/models/attachment_test.go b/models/attachment_test.go index d568e3943..be4baf305 100644 --- a/models/attachment_test.go +++ b/models/attachment_test.go @@ -58,3 +58,32 @@ func TestDeleteAttachments(t *testing.T) { assert.True(t, IsErrAttachmentNotExist(err)) assert.Nil(t, attachment) } + +func TestGetAttachmentByID(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + attach, err := GetAttachmentByID(1) + assert.NoError(t, err) + assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID) +} + +func TestAttachment_DownloadURL(t *testing.T) { + attach := &Attachment{ + UUID: "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", + ID: 1, + } + assert.Equal(t, "https://try.gitea.io/attachments/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.DownloadURL()) +} + +func TestUpdateAttachment(t *testing.T) { + assert.NoError(t, PrepareTestDatabase()) + + attach, err := GetAttachmentByID(1) + assert.NoError(t, err) + assert.Equal(t, "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", attach.UUID) + + attach.Name = "new_name" + assert.NoError(t, UpdateAttachment(attach)) + + AssertExistsAndLoadBean(t, &Attachment{Name: "new_name"}) +} diff --git a/models/release.go b/models/release.go index 66202615d..586f494e7 100644 --- a/models/release.go +++ b/models/release.go @@ -53,7 +53,7 @@ func (r *Release) loadAttributes(e Engine) error { return err } } - return nil + return GetReleaseAttachments(r) } // LoadAttributes load repo and publisher attributes for a release @@ -79,6 +79,10 @@ func (r *Release) TarURL() string { // APIFormat convert a Release to api.Release func (r *Release) APIFormat() *api.Release { + assets := make([]*api.Attachment, 0) + for _, att := range r.Attachments { + assets = append(assets, att.APIFormat()) + } return &api.Release{ ID: r.ID, TagName: r.TagName, @@ -92,6 +96,7 @@ func (r *Release) APIFormat() *api.Release { CreatedAt: r.CreatedUnix.AsTime(), PublishedAt: r.CreatedUnix.AsTime(), Publisher: r.Publisher.APIFormat(), + Attachments: assets, } } diff --git a/package.json b/package.json index 8251e5026..3410bb1f3 100644 --- a/package.json +++ b/package.json @@ -4,4 +4,4 @@ "less": "^2.7.2", "less-plugin-clean-css": "^1.5.1" } -} \ No newline at end of file +} diff --git a/public/swagger.v1.json b/public/swagger.v1.json index cc918e015..ad1b91ad8 100644 --- a/public/swagger.v1.json +++ b/public/swagger.v1.json @@ -3225,6 +3225,37 @@ }, "/repos/{owner}/{repo}/releases": { "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "List a repo's releases", + "operationId": "repoListReleases", + "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 + } + ], + "responses": { + "200": { + "$ref": "#/responses/ReleaseList" + } + } + }, + "post": { "consumes": [ "application/json" ], @@ -3267,6 +3298,44 @@ } }, "/repos/{owner}/{repo}/releases/{id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get a release", + "operationId": "repoGetRelease", + "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": "integer", + "description": "id of the release to get", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Release" + } + } + }, "delete": { "tags": [ "repository" @@ -3351,6 +3420,247 @@ } } }, + "/repos/{owner}/{repo}/releases/{id}/assets": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "List release's attachments", + "operationId": "repoListReleaseAttachments", + "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": "integer", + "description": "id of the release", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/AttachmentList" + } + } + }, + "post": { + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Create a release attachment", + "operationId": "repoCreateReleaseAttachment", + "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": "integer", + "description": "id of the release", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the attachment", + "name": "name", + "in": "query" + }, + { + "type": "file", + "description": "attachment to upload", + "name": "attachment", + "in": "formData", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/Attachment" + } + } + } + }, + "/repos/{owner}/{repo}/releases/{id}/assets/{attachment_id}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get a release attachment", + "operationId": "repoGetReleaseAttachment", + "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": "integer", + "description": "id of the release", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the attachment to get", + "name": "attachment_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Attachment" + } + } + }, + "delete": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Delete a release attachment", + "operationId": "repoDeleteReleaseAttachment", + "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": "integer", + "description": "id of the release", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the attachment to delete", + "name": "attachment_id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/empty" + } + } + }, + "patch": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Edit a release attachment", + "operationId": "repoEditReleaseAttachment", + "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": "integer", + "description": "id of the release", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "id of the attachment to edit", + "name": "attachment_id", + "in": "path", + "required": true + }, + { + "name": "body", + "in": "body", + "schema": { + "$ref": "#/definitions/EditAttachmentOptions" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/Attachment" + } + } + } + }, "/repos/{owner}/{repo}/stargazers": { "get": { "produces": [ @@ -4994,6 +5304,45 @@ }, "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "Attachment": { + "description": "Attachment a generic attachment", + "type": "object", + "properties": { + "browser_download_url": { + "type": "string", + "x-go-name": "DownloadURL" + }, + "created_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Created" + }, + "download_count": { + "type": "integer", + "format": "int64", + "x-go-name": "DownloadCount" + }, + "id": { + "type": "integer", + "format": "int64", + "x-go-name": "ID" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "size": { + "type": "integer", + "format": "int64", + "x-go-name": "Size" + }, + "uuid": { + "type": "string", + "x-go-name": "UUID" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "Branch": { "description": "Branch represents a repository branch", "type": "object", @@ -5202,6 +5551,11 @@ "uniqueItems": true, "x-go-name": "Key" }, + "read_only": { + "description": "Describe if the key has only read access or read/write", + "type": "boolean", + "x-go-name": "ReadOnly" + }, "title": { "description": "Title of the key to add", "type": "string", @@ -5540,6 +5894,17 @@ }, "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" }, + "EditAttachmentOptions": { + "description": "EditAttachmentOptions options for editing attachments", + "type": "object", + "properties": { + "name": { + "type": "string", + "x-go-name": "Name" + } + }, + "x-go-package": "code.gitea.io/gitea/vendor/code.gitea.io/sdk/gitea" + }, "EditHookOption": { "description": "EditHookOption options when modify one hook", "type": "object", @@ -6459,6 +6824,13 @@ "description": "Release represents a repository release", "type": "object", "properties": { + "assets": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + }, + "x-go-name": "Attachments" + }, "author": { "$ref": "#/definitions/User" }, @@ -6848,6 +7220,19 @@ "AccessTokenList": { "description": "AccessTokenList represents a list of API access token." }, + "Attachment": { + "schema": { + "$ref": "#/definitions/Attachment" + } + }, + "AttachmentList": { + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Attachment" + } + } + }, "Branch": { "schema": { "$ref": "#/definitions/Branch" @@ -7131,7 +7516,7 @@ }, "parameterBodies": { "schema": { - "$ref": "#/definitions/MigrateRepoForm" + "$ref": "#/definitions/EditAttachmentOptions" }, "headers": { "AddCollaboratorOption": {}, @@ -7152,6 +7537,7 @@ "CreateTeamOption": {}, "CreateUserOption": {}, "DeleteEmailOption": {}, + "EditAttachmentOptions": {}, "EditHookOption": {}, "EditIssueCommentOption": {}, "EditIssueOption": {}, diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 240bb2fc4..4c454cb6a 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -469,9 +469,18 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/releases", func() { m.Combo("").Get(repo.ListReleases). Post(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.CreateReleaseOption{}), repo.CreateRelease) - m.Combo("/:id").Get(repo.GetRelease). - Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). - Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease) + m.Group("/:id", func() { + m.Combo("").Get(repo.GetRelease). + Patch(reqToken(), reqRepoWriter(), context.ReferencesGitRepo(), bind(api.EditReleaseOption{}), repo.EditRelease). + Delete(reqToken(), reqRepoWriter(), repo.DeleteRelease) + m.Group("/assets", func() { + m.Combo("").Get(repo.ListReleaseAttachments). + Post(reqToken(), reqRepoWriter(), repo.CreateReleaseAttachment) + m.Combo("/:asset").Get(repo.GetReleaseAttachment). + Patch(reqToken(), reqRepoWriter(), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). + Delete(reqToken(), reqRepoWriter(), repo.DeleteReleaseAttachment) + }) + }) }) m.Post("/mirror-sync", reqToken(), reqRepoWriter(), repo.MirrorSync) m.Get("/editorconfig/:filename", context.RepoRef(), repo.GetEditorconfig) diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index 41efa482d..629fb2ea5 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -13,7 +13,7 @@ import ( // GetRelease get a single release of a repository func GetRelease(ctx *context.APIContext) { - // swagger:operation GET /repos/{owner}/{repo}/releases repository repoGetRelease + // swagger:operation GET /repos/{owner}/{repo}/releases/{id} repository repoGetRelease // --- // summary: Get a release // produces: @@ -29,7 +29,7 @@ func GetRelease(ctx *context.APIContext) { // description: name of the repo // type: string // required: true - // - name: repo + // - name: id // in: path // description: id of the release to get // type: integer diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go new file mode 100644 index 000000000..80b2064df --- /dev/null +++ b/routers/api/v1/repo/release_attachment.go @@ -0,0 +1,322 @@ +// Copyright 2018 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package repo + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/sdk/gitea" + "errors" + "net/http" + "strings" +) + +// GetReleaseAttachment gets a single attachment of the release +func GetReleaseAttachment(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment + // --- + // summary: Get a release attachment + // 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: id + // in: path + // description: id of the release + // type: integer + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to get + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/Attachment" + releaseID := ctx.ParamsInt64(":id") + attachID := ctx.ParamsInt64(":asset") + attach, err := models.GetAttachmentByID(attachID) + if err != nil { + ctx.Error(500, "GetAttachmentByID", err) + return + } + if attach.ReleaseID != releaseID { + ctx.Status(404) + return + } + // FIXME Should prove the existence of the given repo, but results in unnecessary database requests + ctx.JSON(200, attach.APIFormat()) +} + +// ListReleaseAttachments lists all attachments of the release +func ListReleaseAttachments(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets repository repoListReleaseAttachments + // --- + // summary: List release's attachments + // 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: id + // in: path + // description: id of the release + // type: integer + // required: true + // responses: + // "200": + // "$ref": "#/responses/AttachmentList" + releaseID := ctx.ParamsInt64(":id") + release, err := models.GetReleaseByID(releaseID) + if err != nil { + ctx.Error(500, "GetReleaseByID", err) + return + } + if release.RepoID != ctx.Repo.Repository.ID { + ctx.Status(404) + return + } + if err := release.LoadAttributes(); err != nil { + ctx.Error(500, "LoadAttributes", err) + return + } + ctx.JSON(200, release.APIFormat().Attachments) +} + +// CreateReleaseAttachment creates an attachment and saves the given file +func CreateReleaseAttachment(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/releases/{id}/assets repository repoCreateReleaseAttachment + // --- + // summary: Create a release attachment + // produces: + // - application/json + // consumes: + // - multipart/form-data + // 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: id + // in: path + // description: id of the release + // type: integer + // required: true + // - name: name + // in: query + // description: name of the attachment + // type: string + // required: false + // - name: attachment + // in: formData + // description: attachment to upload + // type: file + // required: true + // responses: + // "201": + // "$ref": "#/responses/Attachment" + + // Check if attachments are enabled + if !setting.AttachmentEnabled { + ctx.Error(404, "AttachmentEnabled", errors.New("attachment is not enabled")) + return + } + + // Check if release exists an load release + releaseID := ctx.ParamsInt64(":id") + release, err := models.GetReleaseByID(releaseID) + if err != nil { + ctx.Error(500, "GetReleaseByID", err) + return + } + + // Get uploaded file from request + file, header, err := ctx.GetFile("attachment") + if err != nil { + ctx.Error(500, "GetFile", err) + return + } + defer file.Close() + + buf := make([]byte, 1024) + n, _ := file.Read(buf) + if n > 0 { + buf = buf[:n] + } + + // Check if the filetype is allowed by the settings + fileType := http.DetectContentType(buf) + + allowedTypes := strings.Split(setting.AttachmentAllowedTypes, ",") + allowed := false + for _, t := range allowedTypes { + t := strings.Trim(t, " ") + if t == "*/*" || t == fileType { + allowed = true + break + } + } + + if !allowed { + ctx.Error(400, "DetectContentType", errors.New("File type is not allowed")) + return + } + + var filename = header.Filename + if query := ctx.Query("name"); query != "" { + filename = query + } + + // Create a new attachment and save the file + attach, err := models.NewAttachment(filename, buf, file) + if err != nil { + ctx.Error(500, "NewAttachment", err) + return + } + attach.ReleaseID = release.ID + if err := models.UpdateAttachment(attach); err != nil { + ctx.Error(500, "UpdateAttachment", err) + return + } + ctx.JSON(201, attach.APIFormat()) +} + +// EditReleaseAttachment updates the given attachment +func EditReleaseAttachment(ctx *context.APIContext, form api.EditAttachmentOptions) { + // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment + // --- + // summary: Edit a release attachment + // produces: + // - application/json + // consumes: + // - 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: id + // in: path + // description: id of the release + // type: integer + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to edit + // type: integer + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/EditAttachmentOptions" + // responses: + // "201": + // "$ref": "#/responses/Attachment" + + // Check if release exists an load release + releaseID := ctx.ParamsInt64(":id") + attachID := ctx.ParamsInt64(":attachment") + attach, err := models.GetAttachmentByID(attachID) + if err != nil { + ctx.Error(500, "GetAttachmentByID", err) + return + } + if attach.ReleaseID != releaseID { + ctx.Status(404) + return + } + // FIXME Should prove the existence of the given repo, but results in unnecessary database requests + if form.Name != "" { + attach.Name = form.Name + } + + if err := models.UpdateAttachment(attach); err != nil { + ctx.Error(500, "UpdateAttachment", attach) + } + ctx.JSON(201, attach.APIFormat()) +} + +// DeleteReleaseAttachment delete a given attachment +func DeleteReleaseAttachment(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoDeleteReleaseAttachment + // --- + // summary: Delete a release attachment + // 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: id + // in: path + // description: id of the release + // type: integer + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to delete + // type: integer + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + + // Check if release exists an load release + releaseID := ctx.ParamsInt64(":id") + attachID := ctx.ParamsInt64(":attachment") + attach, err := models.GetAttachmentByID(attachID) + if err != nil { + ctx.Error(500, "GetAttachmentByID", err) + return + } + if attach.ReleaseID != releaseID { + ctx.Status(404) + return + } + // FIXME Should prove the existence of the given repo, but results in unnecessary database requests + + if err := models.DeleteAttachment(attach, true); err != nil { + ctx.Error(500, "DeleteAttachment", err) + return + } + ctx.Status(204) +} diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 31251eb3e..3ea324186 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -63,4 +63,6 @@ type swaggerParameterBodies struct { EditUserOption api.EditUserOption MigrateRepoForm auth.MigrateRepoForm + + EditAttachmentOptions api.EditAttachmentOptions } diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 703f7d18d..1f2569110 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -90,3 +90,15 @@ type swaggerResponseWatchInfo struct { type swaggerResponseSearchResults struct { Body api.SearchResults `json:"body"` } + +// swagger:response AttachmentList +type swaggerResponseAttachmentList struct { + //in: body + Body []api.Attachment `json:"body"` +} + +// swagger:response Attachment +type swaggerResponseAttachment struct { + //in: body + Body api.Attachment `json:"body"` +} diff --git a/vendor/code.gitea.io/sdk/gitea/admin_user.go b/vendor/code.gitea.io/sdk/gitea/admin_user.go index 6d736d346..bc0f36994 100644 --- a/vendor/code.gitea.io/sdk/gitea/admin_user.go +++ b/vendor/code.gitea.io/sdk/gitea/admin_user.go @@ -12,7 +12,7 @@ import ( // CreateUserOption create user options type CreateUserOption struct { - SourceID int64 `json:"source_id"` + SourceID int64 `json:"source_id"` LoginName string `json:"login_name"` // required: true Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(35)"` @@ -21,8 +21,8 @@ type CreateUserOption struct { // swagger:strfmt email Email string `json:"email" binding:"Required;Email;MaxSize(254)"` // required: true - Password string `json:"password" binding:"Required;MaxSize(255)"` - SendNotify bool `json:"send_notify"` + Password string `json:"password" binding:"Required;MaxSize(255)"` + SendNotify bool `json:"send_notify"` } // AdminCreateUser create a user @@ -37,20 +37,20 @@ func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, error) { // EditUserOption edit user options type EditUserOption struct { - SourceID int64 `json:"source_id"` + SourceID int64 `json:"source_id"` LoginName string `json:"login_name"` - FullName string `json:"full_name" binding:"MaxSize(100)"` + FullName string `json:"full_name" binding:"MaxSize(100)"` // required: true // swagger:strfmt email - Email string `json:"email" binding:"Required;Email;MaxSize(254)"` - Password string `json:"password" binding:"MaxSize(255)"` - Website string `json:"website" binding:"MaxSize(50)"` - Location string `json:"location" binding:"MaxSize(50)"` - Active *bool `json:"active"` - Admin *bool `json:"admin"` - AllowGitHook *bool `json:"allow_git_hook"` - AllowImportLocal *bool `json:"allow_import_local"` - MaxRepoCreation *int `json:"max_repo_creation"` + Email string `json:"email" binding:"Required;Email;MaxSize(254)"` + Password string `json:"password" binding:"MaxSize(255)"` + Website string `json:"website" binding:"MaxSize(50)"` + Location string `json:"location" binding:"MaxSize(50)"` + Active *bool `json:"active"` + Admin *bool `json:"admin"` + AllowGitHook *bool `json:"allow_git_hook"` + AllowImportLocal *bool `json:"allow_import_local"` + MaxRepoCreation *int `json:"max_repo_creation"` } // AdminEditUser modify user informations diff --git a/vendor/code.gitea.io/sdk/gitea/attachment.go b/vendor/code.gitea.io/sdk/gitea/attachment.go index 10e7a1d56..e90a7114d 100644 --- a/vendor/code.gitea.io/sdk/gitea/attachment.go +++ b/vendor/code.gitea.io/sdk/gitea/attachment.go @@ -3,15 +3,90 @@ // license that can be found in the LICENSE file. package gitea // import "code.gitea.io/sdk/gitea" -import "time" +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "time" +) // Attachment a generic attachment +// swagger:model type Attachment struct { - ID int64 `json:"id"` - Name string `json:"name"` - Size int64 `json:"size"` - DownloadCount int64 `json:"download_count"` - Created time.Time `json:"created_at"` - UUID string `json:"uuid"` - DownloadURL string `json:"browser_download_url"` + ID int64 `json:"id"` + Name string `json:"name"` + Size int64 `json:"size"` + DownloadCount int64 `json:"download_count"` + // swagger:strfmt date-time + Created time.Time `json:"created_at"` + UUID string `json:"uuid"` + DownloadURL string `json:"browser_download_url"` +} + +// ListReleaseAttachments list release's attachments +func (c *Client) ListReleaseAttachments(user, repo string, release int64) ([]*Attachment, error) { + attachments := make([]*Attachment, 0, 10) + err := c.getParsedResponse("GET", + fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release), + nil, nil, &attachments) + return attachments, err +} + +// ListReleaseAttachments list release's attachments +func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, error) { + a := new(Attachment) + err := c.getParsedResponse("GET", + fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), + nil, nil, &a) + return a, err +} + +// CreateReleaseAttachment creates an attachment for the given release +func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, error) { + // Write file to body + body := new(bytes.Buffer) + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("attachment", filename) + if err != nil { + return nil, err + } + + if _, err = io.Copy(part, file); err != nil { + return nil, err + } + if err = writer.Close(); err != nil { + return nil, err + } + + // Send request + attachment := new(Attachment) + err = c.getParsedResponse("POST", + fmt.Sprintf("/repos/%s/%s/releases/%d/assets", user, repo, release), + http.Header{"Content-Type": {writer.FormDataContentType()}}, body, &attachment) + return attachment, err +} + +// EditReleaseAttachment updates the given attachment with the given options +func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, error) { + body, err := json.Marshal(&form) + if err != nil { + return nil, err + } + attach := new(Attachment) + return attach, c.getParsedResponse("PATCH", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, attachment), jsonHeader, bytes.NewReader(body), attach) +} + +// DeleteReleaseAttachment deletes the given attachment including the uploaded file +func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) error { + _, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil) + return err +} + +// EditAttachmentOptions options for editing attachments +// swagger:model +type EditAttachmentOptions struct { + Name string `json:"name"` } diff --git a/vendor/code.gitea.io/sdk/gitea/gitea.go b/vendor/code.gitea.io/sdk/gitea/gitea.go index 20cb44fee..374a3faa9 100644 --- a/vendor/code.gitea.io/sdk/gitea/gitea.go +++ b/vendor/code.gitea.io/sdk/gitea/gitea.go @@ -7,6 +7,7 @@ package gitea import ( "encoding/json" "errors" + "fmt" "io" "io/ioutil" "net/http" @@ -69,6 +70,8 @@ func (c *Client) getResponse(method, path string, header http.Header, body io.Re return nil, errors.New("403 Forbidden") case 404: return nil, errors.New("404 Not Found") + case 422: + return nil, fmt.Errorf("422 Unprocessable Entity: %s", string(data)) } if resp.StatusCode/100 != 2 { diff --git a/vendor/code.gitea.io/sdk/gitea/hook.go b/vendor/code.gitea.io/sdk/gitea/hook.go index b7109482f..a9b0bdbd0 100644 --- a/vendor/code.gitea.io/sdk/gitea/hook.go +++ b/vendor/code.gitea.io/sdk/gitea/hook.go @@ -21,16 +21,16 @@ var ( // Hook a hook is a web hook when one repository changed type Hook struct { - ID int64 `json:"id"` - Type string `json:"type"` - URL string `json:"-"` - Config map[string]string `json:"config"` - Events []string `json:"events"` - Active bool `json:"active"` + ID int64 `json:"id"` + Type string `json:"type"` + URL string `json:"-"` + Config map[string]string `json:"config"` + Events []string `json:"events"` + Active bool `json:"active"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` + Updated time.Time `json:"updated_at"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at"` } // HookList represents a list of API hook. @@ -67,7 +67,7 @@ type CreateHookOption struct { Type string `json:"type" binding:"Required"` // required: true Config map[string]string `json:"config" binding:"Required"` - Events []string `json:"events"` + Events []string `json:"events"` // default: false Active bool `json:"active"` } @@ -95,8 +95,8 @@ func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, // EditHookOption options when modify one hook type EditHookOption struct { Config map[string]string `json:"config"` - Events []string `json:"events"` - Active *bool `json:"active"` + Events []string `json:"events"` + Active *bool `json:"active"` } // EditOrgHook modify one hook of an organization, with hook id and options @@ -140,7 +140,7 @@ type Payloader interface { // PayloadUser represents the author or committer of a commit type PayloadUser struct { // Full name of the commit author - Name string `json:"name"` + Name string `json:"name"` // swagger:strfmt email Email string `json:"email"` UserName string `json:"username"` @@ -159,7 +159,7 @@ type PayloadCommit struct { Committer *PayloadUser `json:"committer"` Verification *PayloadCommitVerification `json:"verification"` // swagger:strfmt date-time - Timestamp time.Time `json:"timestamp"` + Timestamp time.Time `json:"timestamp"` } // PayloadCommitVerification represents the GPG verification of a commit diff --git a/vendor/code.gitea.io/sdk/gitea/issue.go b/vendor/code.gitea.io/sdk/gitea/issue.go index 720f54caa..206c52a59 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue.go +++ b/vendor/code.gitea.io/sdk/gitea/issue.go @@ -43,12 +43,12 @@ type Issue struct { // // type: string // enum: open,closed - State StateType `json:"state"` - Comments int `json:"comments"` + State StateType `json:"state"` + Comments int `json:"comments"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` + Updated time.Time `json:"updated_at"` PullRequest *PullRequestMeta `json:"pull_request"` } @@ -86,15 +86,15 @@ func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, error) { // CreateIssueOption options to create one issue type CreateIssueOption struct { // required:true - Title string `json:"title" binding:"Required"` - Body string `json:"body"` + Title string `json:"title" binding:"Required"` + Body string `json:"body"` // username of assignee - Assignee string `json:"assignee"` + Assignee string `json:"assignee"` // milestone id - Milestone int64 `json:"milestone"` + Milestone int64 `json:"milestone"` // list of label ids - Labels []int64 `json:"labels"` - Closed bool `json:"closed"` + Labels []int64 `json:"labels"` + Closed bool `json:"closed"` } // CreateIssue create a new issue for a given repository diff --git a/vendor/code.gitea.io/sdk/gitea/issue_comment.go b/vendor/code.gitea.io/sdk/gitea/issue_comment.go index f146c1fe2..2c8127c60 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue_comment.go +++ b/vendor/code.gitea.io/sdk/gitea/issue_comment.go @@ -13,16 +13,16 @@ import ( // Comment represents a comment on a commit or issue type Comment struct { - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - PRURL string `json:"pull_request_url"` - IssueURL string `json:"issue_url"` - Poster *User `json:"user"` - Body string `json:"body"` + ID int64 `json:"id"` + HTMLURL string `json:"html_url"` + PRURL string `json:"pull_request_url"` + IssueURL string `json:"issue_url"` + Poster *User `json:"user"` + Body string `json:"body"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` + Updated time.Time `json:"updated_at"` } // ListIssueComments list comments on an issue. diff --git a/vendor/code.gitea.io/sdk/gitea/issue_label.go b/vendor/code.gitea.io/sdk/gitea/issue_label.go index 3acf1440b..47d1b8221 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue_label.go +++ b/vendor/code.gitea.io/sdk/gitea/issue_label.go @@ -13,8 +13,8 @@ import ( // Label a label to an issue or a pr // swagger:model type Label struct { - ID int64 `json:"id"` - Name string `json:"name"` + ID int64 `json:"id"` + Name string `json:"name"` // example: 00aabb Color string `json:"color"` URL string `json:"url"` @@ -36,7 +36,7 @@ func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, error) { // CreateLabelOption options for creating a label type CreateLabelOption struct { // required:true - Name string `json:"name" binding:"Required"` + Name string `json:"name" binding:"Required"` // required:true // example: #00aabb Color string `json:"color" binding:"Required;Size(7)"` diff --git a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go b/vendor/code.gitea.io/sdk/gitea/issue_milestone.go index d82006742..775a6a911 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue_milestone.go +++ b/vendor/code.gitea.io/sdk/gitea/issue_milestone.go @@ -13,16 +13,16 @@ import ( // Milestone milestone is a collection of issues on one repository type Milestone struct { - ID int64 `json:"id"` - Title string `json:"title"` - Description string `json:"description"` - State StateType `json:"state"` - OpenIssues int `json:"open_issues"` - ClosedIssues int `json:"closed_issues"` + ID int64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + State StateType `json:"state"` + OpenIssues int `json:"open_issues"` + ClosedIssues int `json:"closed_issues"` // swagger:strfmt date-time - Closed *time.Time `json:"closed_at"` + Closed *time.Time `json:"closed_at"` // swagger:strfmt date-time - Deadline *time.Time `json:"due_on"` + Deadline *time.Time `json:"due_on"` } // ListRepoMilestones list all the milestones of one repository @@ -39,10 +39,10 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, error) // CreateMilestoneOption options for creating a milestone type CreateMilestoneOption struct { - Title string `json:"title"` - Description string `json:"description"` + Title string `json:"title"` + Description string `json:"description"` // swagger:strfmt date-time - Deadline *time.Time `json:"due_on"` + Deadline *time.Time `json:"due_on"` } // CreateMilestone create one milestone with options diff --git a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go b/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go index dcdecbb2a..7f4b64cbb 100644 --- a/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go +++ b/vendor/code.gitea.io/sdk/gitea/issue_tracked_time.go @@ -13,7 +13,7 @@ import ( // TrackedTime worked time for an issue / pr type TrackedTime struct { - ID int64 `json:"id"` + ID int64 `json:"id"` // swagger:strfmt date-time Created time.Time `json:"created"` // Time in seconds diff --git a/vendor/code.gitea.io/sdk/gitea/org.go b/vendor/code.gitea.io/sdk/gitea/org.go index d67d707e7..19238d102 100644 --- a/vendor/code.gitea.io/sdk/gitea/org.go +++ b/vendor/code.gitea.io/sdk/gitea/org.go @@ -42,11 +42,11 @@ func (c *Client) GetOrg(orgname string) (*Organization, error) { // CreateOrgOption options for creating an organization type CreateOrgOption struct { // required: true - UserName string `json:"username" binding:"Required"` - FullName string `json:"full_name"` + UserName string `json:"username" binding:"Required"` + FullName string `json:"full_name"` Description string `json:"description"` - Website string `json:"website"` - Location string `json:"location"` + Website string `json:"website"` + Location string `json:"location"` } // EditOrgOption options for editing an organization diff --git a/vendor/code.gitea.io/sdk/gitea/org_team.go b/vendor/code.gitea.io/sdk/gitea/org_team.go index 0c9a74035..2fc6796d4 100644 --- a/vendor/code.gitea.io/sdk/gitea/org_team.go +++ b/vendor/code.gitea.io/sdk/gitea/org_team.go @@ -10,7 +10,7 @@ type Team struct { Name string `json:"name"` Description string `json:"description"` // enum: none,read,write,admin,owner - Permission string `json:"permission"` + Permission string `json:"permission"` } // CreateTeamOption options for creating a team @@ -19,7 +19,7 @@ type CreateTeamOption struct { Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` Description string `json:"description" binding:"MaxSize(255)"` // enum: read,write,admin - Permission string `json:"permission"` + Permission string `json:"permission"` } // EditTeamOption options for editing a team @@ -28,5 +28,5 @@ type EditTeamOption struct { Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"` Description string `json:"description" binding:"MaxSize(255)"` // enum: read,write,admin - Permission string `json:"permission"` + Permission string `json:"permission"` } diff --git a/vendor/code.gitea.io/sdk/gitea/pull.go b/vendor/code.gitea.io/sdk/gitea/pull.go index 0aa0ea8cc..ee3fe116d 100644 --- a/vendor/code.gitea.io/sdk/gitea/pull.go +++ b/vendor/code.gitea.io/sdk/gitea/pull.go @@ -29,8 +29,8 @@ type PullRequest struct { DiffURL string `json:"diff_url"` PatchURL string `json:"patch_url"` - Mergeable bool `json:"mergeable"` - HasMerged bool `json:"merged"` + Mergeable bool `json:"mergeable"` + HasMerged bool `json:"merged"` // swagger:strfmt date-time Merged *time.Time `json:"merged_at"` MergedCommitID *string `json:"merge_commit_sha"` diff --git a/vendor/code.gitea.io/sdk/gitea/release.go b/vendor/code.gitea.io/sdk/gitea/release.go index 01e41e35a..396251dca 100644 --- a/vendor/code.gitea.io/sdk/gitea/release.go +++ b/vendor/code.gitea.io/sdk/gitea/release.go @@ -13,21 +13,22 @@ import ( // Release represents a repository release type Release struct { - ID int64 `json:"id"` - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - URL string `json:"url"` - TarURL string `json:"tarball_url"` - ZipURL string `json:"zipball_url"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` + ID int64 `json:"id"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + URL string `json:"url"` + TarURL string `json:"tarball_url"` + ZipURL string `json:"zipball_url"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` // swagger:strfmt date-time - CreatedAt time.Time `json:"created_at"` + CreatedAt time.Time `json:"created_at"` // swagger:strfmt date-time - PublishedAt time.Time `json:"published_at"` - Publisher *User `json:"author"` + PublishedAt time.Time `json:"published_at"` + Publisher *User `json:"author"` + Attachments []*Attachment `json:"assets"` } // ListReleases list releases of a repository diff --git a/vendor/code.gitea.io/sdk/gitea/repo.go b/vendor/code.gitea.io/sdk/gitea/repo.go index 3def6e26c..339cbd33d 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo.go +++ b/vendor/code.gitea.io/sdk/gitea/repo.go @@ -41,10 +41,10 @@ type Repository struct { OpenIssues int `json:"open_issues_count"` DefaultBranch string `json:"default_branch"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` - Permissions *Permission `json:"permissions,omitempty"` + Updated time.Time `json:"updated_at"` + Permissions *Permission `json:"permissions,omitempty"` } // ListMyRepos lists all repositories for the authenticated user that has access to. @@ -122,15 +122,15 @@ func (c *Client) DeleteRepo(owner, repo string) error { // MigrateRepoOption options for migrating a repository from an external service type MigrateRepoOption struct { // required: true - CloneAddr string `json:"clone_addr" binding:"Required"` + CloneAddr string `json:"clone_addr" binding:"Required"` AuthUsername string `json:"auth_username"` AuthPassword string `json:"auth_password"` // required: true UID int `json:"uid" binding:"Required"` // required: true - RepoName string `json:"repo_name" binding:"Required"` - Mirror bool `json:"mirror"` - Private bool `json:"private"` + RepoName string `json:"repo_name" binding:"Required"` + Mirror bool `json:"mirror"` + Private bool `json:"private"` Description string `json:"description"` } diff --git a/vendor/code.gitea.io/sdk/gitea/repo_key.go b/vendor/code.gitea.io/sdk/gitea/repo_key.go index f2f1038aa..ec53311bd 100644 --- a/vendor/code.gitea.io/sdk/gitea/repo_key.go +++ b/vendor/code.gitea.io/sdk/gitea/repo_key.go @@ -13,10 +13,10 @@ import ( // DeployKey a deploy key type DeployKey struct { - ID int64 `json:"id"` - Key string `json:"key"` - URL string `json:"url"` - Title string `json:"title"` + ID int64 `json:"id"` + Key string `json:"key"` + URL string `json:"url"` + Title string `json:"title"` // swagger:strfmt date-time Created time.Time `json:"created_at"` ReadOnly bool `json:"read_only"` diff --git a/vendor/code.gitea.io/sdk/gitea/status.go b/vendor/code.gitea.io/sdk/gitea/status.go index 5ce25321a..3060ab1b2 100644 --- a/vendor/code.gitea.io/sdk/gitea/status.go +++ b/vendor/code.gitea.io/sdk/gitea/status.go @@ -38,9 +38,9 @@ type Status struct { Context string `json:"context"` Creator *User `json:"creator"` // swagger:strfmt date-time - Created time.Time `json:"created_at"` + Created time.Time `json:"created_at"` // swagger:strfmt date-time - Updated time.Time `json:"updated_at"` + Updated time.Time `json:"updated_at"` } // CombinedStatus holds the combined state of several statuses for a single commit diff --git a/vendor/code.gitea.io/sdk/gitea/user.go b/vendor/code.gitea.io/sdk/gitea/user.go index d104cc993..f6b687e97 100644 --- a/vendor/code.gitea.io/sdk/gitea/user.go +++ b/vendor/code.gitea.io/sdk/gitea/user.go @@ -13,13 +13,13 @@ import ( // swagger:model type User struct { // the user's id - ID int64 `json:"id"` + ID int64 `json:"id"` // the user's username - UserName string `json:"login"` + UserName string `json:"login"` // the user's full name - FullName string `json:"full_name"` + FullName string `json:"full_name"` // swagger:strfmt email - Email string `json:"email"` + Email string `json:"email"` // URL to the user's avatar AvatarURL string `json:"avatar_url"` } diff --git a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go b/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go index ae5dcdcb1..0817d8946 100644 --- a/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go +++ b/vendor/code.gitea.io/sdk/gitea/user_gpgkey.go @@ -24,9 +24,9 @@ type GPGKey struct { CanEncryptStorage bool `json:"can_encrypt_storage"` CanCertify bool `json:"can_certify"` // swagger:strfmt date-time - Created time.Time `json:"created_at,omitempty"` + Created time.Time `json:"created_at,omitempty"` // swagger:strfmt date-time - Expires time.Time `json:"expires_at,omitempty"` + Expires time.Time `json:"expires_at,omitempty"` } // GPGKeyEmail an email attached to a GPGKey diff --git a/vendor/code.gitea.io/sdk/gitea/user_search.go b/vendor/code.gitea.io/sdk/gitea/user_search.go new file mode 100644 index 000000000..65ab980d6 --- /dev/null +++ b/vendor/code.gitea.io/sdk/gitea/user_search.go @@ -0,0 +1,14 @@ +package gitea + +import "fmt" + +type searchUsersResponse struct { + Users []*User `json:"data"` +} + +// SearchUsers finds users by query +func (c *Client) SearchUsers(query string, limit int) ([]*User, error) { + resp := new(searchUsersResponse) + err := c.getParsedResponse("GET", fmt.Sprintf("/users/search?q=%s&limit=%d", query, limit), nil, nil, &resp) + return resp.Users, err +} diff --git a/vendor/vendor.json b/vendor/vendor.json index c6a10a585..967a297b2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -9,10 +9,10 @@ "revisionTime": "2018-02-10T03:05:43Z" }, { - "checksumSHA1": "Qtq0kW+BnpYMOriaoCjMa86WGG8=", + "checksumSHA1": "PWaIU7g1YSkETxka2DIS1EYsPK0=", "path": "code.gitea.io/sdk/gitea", - "revision": "79eee8f12c7fc1cc5b802c5cdc5b494ef3733866", - "revisionTime": "2017-12-20T06:57:50Z" + "revision": "cdbef997666132599cc92dc22aa94de3db04adeb", + "revisionTime": "2018-03-02T14:48:43Z" }, { "checksumSHA1": "bOODD4Gbw3GfcuQPU2dI40crxxk=",