implement attachment post route (WIP)

This commit is contained in:
Hayden 2022-09-14 20:05:13 -08:00
parent 5ee9082301
commit 48036eabce
12 changed files with 225 additions and 6 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
# Project Specific
backend/homebox-data/*
config.yml
homebox.db
.idea

View file

@ -224,6 +224,60 @@ const docTemplate = `{
}
}
},
"/v1/items/{id}/attachment": {
"post": {
"security": [
{
"Bearer": []
}
],
"produces": [
"application/json"
],
"tags": [
"Items"
],
"summary": "imports items into the database",
"parameters": [
{
"type": "string",
"description": "Item ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "file",
"description": "File attachment",
"name": "file",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "Type of file",
"name": "type",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "name of the file including extension",
"name": "name",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.ItemOut"
}
}
}
}
},
"/v1/labels": {
"get": {
"security": [
@ -881,6 +935,9 @@ const docTemplate = `{
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedAt": {
"type": "string"
}

View file

@ -216,6 +216,60 @@
}
}
},
"/v1/items/{id}/attachment": {
"post": {
"security": [
{
"Bearer": []
}
],
"produces": [
"application/json"
],
"tags": [
"Items"
],
"summary": "imports items into the database",
"parameters": [
{
"type": "string",
"description": "Item ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "file",
"description": "File attachment",
"name": "file",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "Type of file",
"name": "type",
"in": "formData",
"required": true
},
{
"type": "string",
"description": "name of the file including extension",
"name": "name",
"in": "formData",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/types.ItemOut"
}
}
}
}
},
"/v1/labels": {
"get": {
"security": [
@ -873,6 +927,9 @@
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"updatedAt": {
"type": "string"
}

View file

@ -55,6 +55,8 @@ definitions:
$ref: '#/definitions/types.DocumentOut'
id:
type: string
type:
type: string
updatedAt:
type: string
type: object
@ -503,6 +505,41 @@ paths:
summary: updates a item
tags:
- Items
/v1/items/{id}/attachment:
post:
parameters:
- description: Item ID
in: path
name: id
required: true
type: string
- description: File attachment
in: formData
name: file
required: true
type: file
- description: Type of file
in: formData
name: type
required: true
type: string
- description: name of the file including extension
in: formData
name: name
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/types.ItemOut'
security:
- Bearer: []
summary: imports items into the database
tags:
- Items
/v1/items/import:
post:
parameters:

View file

@ -81,6 +81,8 @@ func (a *app) newRouter(repos *repo.AllRepos) *chi.Mux {
r.Get(v1Base("/items/{id}"), v1Ctrl.HandleItemGet())
r.Put(v1Base("/items/{id}"), v1Ctrl.HandleItemUpdate())
r.Delete(v1Base("/items/{id}"), v1Ctrl.HandleItemDelete())
r.Post(v1Base("/items/{id}/attachment"), v1Ctrl.HandleItemAttachmentCreate())
})
}

View file

@ -2,8 +2,10 @@ package v1
import (
"encoding/csv"
"errors"
"net/http"
"github.com/hay-kot/homebox/backend/ent/attachment"
"github.com/hay-kot/homebox/backend/internal/services"
"github.com/hay-kot/homebox/backend/internal/types"
"github.com/hay-kot/homebox/backend/pkgs/server"
@ -89,8 +91,8 @@ func (ctrl *V1Controller) HandleItemDelete() http.HandlerFunc {
// @Summary Gets a item and fields
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Success 200 {object} types.ItemOut
// @Param id path string true "Item ID"
// @Success 200 {object} types.ItemOut
// @Router /v1/items/{id} [GET]
// @Security Bearer
func (ctrl *V1Controller) HandleItemGet() http.HandlerFunc {
@ -189,3 +191,64 @@ func (ctrl *V1Controller) HandleItemsImport() http.HandlerFunc {
server.Respond(w, http.StatusNoContent, nil)
}
}
// HandleItemsImport godocs
// @Summary imports items into the database
// @Tags Items
// @Produce json
// @Param id path string true "Item ID"
// @Param file formData file true "File attachment"
// @Param type formData string true "Type of file"
// @Param name formData string true "name of the file including extension"
// @Success 200 {object} types.ItemOut
// @Router /v1/items/{id}/attachment [Post]
// @Security Bearer
func (ctrl *V1Controller) HandleItemAttachmentCreate() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Max upload size of 10 MB - TODO: Set via config
err := r.ParseMultipartForm(10 << 20)
if err != nil {
log.Err(err).Msg("failed to parse multipart form")
server.RespondServerError(w)
return
}
file, _, err := r.FormFile("file")
if err != nil {
log.Err(err).Msg("failed to get file from form")
server.RespondServerError(w)
return
}
attachmentName := r.FormValue("name")
if attachmentName == "" {
log.Err(err).Msg("failed to get name from form")
server.RespondError(w, http.StatusBadRequest, errors.New("name is required"))
}
attachmentType := r.FormValue("type")
if attachmentType == "" {
attachmentName = "attachment"
}
uid, user, err := ctrl.partialParseIdAndUser(w, r)
if err != nil {
return
}
item, err := ctrl.svc.Items.AddAttachment(
r.Context(),
user.GroupID,
uid,
attachmentName,
attachment.Type(attachmentType),
file,
)
if err != nil {
log.Err(err).Msg("failed to add attachment")
server.RespondServerError(w)
return
}
server.Respond(w, http.StatusOK, item)
}
}

View file

@ -10,6 +10,7 @@ func ToItemAttachment(attachment *ent.Attachment) *types.ItemAttachment {
ID: attachment.ID,
CreatedAt: attachment.CreatedAt,
UpdatedAt: attachment.UpdatedAt,
Type: attachment.Type.String(),
Document: types.DocumentOut{
ID: attachment.Edges.Document.ID,
Title: attachment.Edges.Document.Title,

View file

@ -101,7 +101,7 @@ func (svc *ItemService) attachmentPath(gid, itemId uuid.UUID, filename string) s
// AddAttachment adds an attachment to an item by creating an entry in the Documents table and linking it to the Attachment
// Table and Items table. The file provided via the reader is stored on the file system based on the provided
// relative path during construction of the service.
func (svc *ItemService) AddAttachment(ctx context.Context, gid, itemId uuid.UUID, filename string, file io.Reader) (*types.ItemOut, error) {
func (svc *ItemService) AddAttachment(ctx context.Context, gid, itemId uuid.UUID, filename string, attachmentType attachment.Type, file io.Reader) (*types.ItemOut, error) {
// Get the Item
item, err := svc.repo.Items.GetOne(ctx, itemId)
if err != nil {
@ -122,7 +122,7 @@ func (svc *ItemService) AddAttachment(ctx context.Context, gid, itemId uuid.UUID
}
// Create the attachment
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachment.TypeAttachment)
_, err = svc.repo.Attachments.Create(ctx, itemId, doc.ID, attachmentType)
if err != nil {
return nil, err
}

View file

@ -126,7 +126,7 @@ func TestItemService_AddAttachment(t *testing.T) {
reader := strings.NewReader(contents)
// Setup
afterAttachment, err := svc.AddAttachment(context.Background(), tGroup.ID, itm.ID, "testfile.txt", reader)
afterAttachment, err := svc.AddAttachment(context.Background(), tGroup.ID, itm.ID, "testfile.txt", "attachment", reader)
assert.NoError(t, err)
assert.NotNil(t, afterAttachment)

View file

@ -103,5 +103,6 @@ type ItemAttachment struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Type string `json:"type"`
Document DocumentOut `json:"document"`
}

View file

@ -21,7 +21,6 @@ type LabelUpdate struct {
type LabelSummary struct {
ID uuid.UUID `json:"id"`
GroupID uuid.UUID `json:"groupId"`
Name string `json:"name"`
Description string `json:"description"`
CreatedAt time.Time `json:"createdAt"`

View file

@ -45,6 +45,7 @@ export interface ItemAttachment {
createdAt: Date;
document: DocumentOut;
id: string;
type: string;
updatedAt: Date;
}