diff --git a/backend/models/lists.go b/backend/models/stacks.go similarity index 77% rename from backend/models/lists.go rename to backend/models/stacks.go index 6d3861a..6d01ef7 100644 --- a/backend/models/lists.go +++ b/backend/models/stacks.go @@ -54,7 +54,7 @@ func (m StackModel) List(ctx context.Context, userId uuid.UUID) ([]StackWithItem return lists, err } -func (m StackModel) ListItems(ctx context.Context, listID uuid.UUID) ([]ImageWithSchema, error) { +func (m StackModel) ListItems(ctx context.Context, stackID uuid.UUID) ([]ImageWithSchema, error) { getListItems := SELECT( ImageStacks.AllColumns, ImageSchemaItems.AllColumns, @@ -63,7 +63,7 @@ func (m StackModel) ListItems(ctx context.Context, listID uuid.UUID) ([]ImageWit ImageStacks. INNER_JOIN(ImageSchemaItems, ImageSchemaItems.ImageID.EQ(ImageStacks.ImageID)), ). - WHERE(ImageStacks.StackID.EQ(UUID(listID))) + WHERE(ImageStacks.StackID.EQ(UUID(stackID))) listItems := make([]ImageWithSchema, 0) err := getListItems.QueryContext(ctx, m.dbPool, &listItems) @@ -71,8 +71,8 @@ func (m StackModel) ListItems(ctx context.Context, listID uuid.UUID) ([]ImageWit return listItems, err } -func (m StackModel) Get(ctx context.Context, listID uuid.UUID) (model.Stacks, error) { - getStackStmt := Stacks.SELECT(Stacks.AllColumns).WHERE(Stacks.ID.EQ(UUID(listID))) +func (m StackModel) Get(ctx context.Context, stackID uuid.UUID) (model.Stacks, error) { + getStackStmt := Stacks.SELECT(Stacks.AllColumns).WHERE(Stacks.ID.EQ(UUID(stackID))) stack := model.Stacks{} err := getStackStmt.QueryContext(ctx, m.dbPool, &stack) @@ -146,10 +146,25 @@ func (m StackModel) SaveSchemaItems(ctx context.Context, imageID uuid.UUID, item // DELETE methods // ======================================== -func (m StackModel) DeleteImage(ctx context.Context, listID uuid.UUID, imageID uuid.UUID) error { +func (m StackModel) DeleteSchemaItem(ctx context.Context, stackID uuid.UUID, schemaItemID uuid.UUID) error { + deleteImageListStmt := SchemaItems.DELETE(). + WHERE( + SchemaItems.ID.EQ(UUID(schemaItemID)). + // The StackID check is a sanity check. + // We don't technically need it, but it adds extra protection + // in case we make a mistake later on + AND(SchemaItems.StackID.EQ(UUID(stackID))), + ) + + _, err := deleteImageListStmt.ExecContext(ctx, m.dbPool) + + return err +} + +func (m StackModel) DeleteImage(ctx context.Context, stackID uuid.UUID, imageID uuid.UUID) error { deleteImageListStmt := ImageStacks.DELETE(). WHERE( - ImageStacks.StackID.EQ(UUID(listID)). + ImageStacks.StackID.EQ(UUID(stackID)). AND(ImageStacks.ImageID.EQ(UUID(imageID))), ) @@ -158,8 +173,8 @@ func (m StackModel) DeleteImage(ctx context.Context, listID uuid.UUID, imageID u return err } -func (m StackModel) Delete(ctx context.Context, listID uuid.UUID, userID uuid.UUID) error { - deleteStackStmt := Stacks.DELETE().WHERE(Stacks.ID.EQ(UUID(listID)).AND(Stacks.UserID.EQ(UUID(userID)))) +func (m StackModel) Delete(ctx context.Context, stackID uuid.UUID, userID uuid.UUID) error { + deleteStackStmt := Stacks.DELETE().WHERE(Stacks.ID.EQ(UUID(stackID)).AND(Stacks.UserID.EQ(UUID(userID)))) _, err := deleteStackStmt.ExecContext(ctx, m.dbPool) diff --git a/backend/stacks/handler.go b/backend/stacks/handler.go index 5465226..7748eeb 100644 --- a/backend/stacks/handler.go +++ b/backend/stacks/handler.go @@ -50,14 +50,14 @@ func (h *StackHandler) getStackItems(w http.ResponseWriter, r *http.Request) { return } - listID, err := middleware.GetPathParamID(h.logger, "listID", w, r) + stackID, err := middleware.GetPathParamID(h.logger, "listID", w, r) if err != nil { return } // TODO: must check for permission here. - lists, err := h.stackModel.ListItems(ctx, listID) + lists, err := h.stackModel.ListItems(ctx, stackID) if err != nil { h.logger.Warn("could not get list items", "err", err) w.WriteHeader(http.StatusBadRequest) @@ -83,12 +83,12 @@ func (h *StackHandler) deleteStack(w http.ResponseWriter, r *http.Request) { return } - listID, err := middleware.GetPathParamID(h.logger, "listID", w, r) + stackID, err := middleware.GetPathParamID(h.logger, "listID", w, r) if err != nil { return } - err = h.stackModel.Delete(ctx, listID, userID) + err = h.stackModel.Delete(ctx, stackID, userID) if err != nil { h.logger.Warn("could not delete stack", "err", err) w.WriteHeader(http.StatusBadRequest) @@ -101,8 +101,8 @@ func (h *StackHandler) deleteStack(w http.ResponseWriter, r *http.Request) { func (h *StackHandler) deleteImageFromStack(w http.ResponseWriter, r *http.Request) { ctx := r.Context() + stringListID := chi.URLParam(r, "stackID") stringImageID := chi.URLParam(r, "imageID") - stringListID := chi.URLParam(r, "listID") imageID, err := uuid.Parse(stringImageID) if err != nil { @@ -110,7 +110,7 @@ func (h *StackHandler) deleteImageFromStack(w http.ResponseWriter, r *http.Reque return } - listID, err := uuid.Parse(stringListID) + stackID, err := uuid.Parse(stringListID) if err != nil { w.WriteHeader(http.StatusBadRequest) return @@ -123,7 +123,7 @@ func (h *StackHandler) deleteImageFromStack(w http.ResponseWriter, r *http.Reque return } - stack, err := h.stackModel.Get(ctx, listID) + stack, err := h.stackModel.Get(ctx, stackID) if err != nil { w.WriteHeader(http.StatusInternalServerError) return @@ -134,7 +134,58 @@ func (h *StackHandler) deleteImageFromStack(w http.ResponseWriter, r *http.Reque return } - err = h.stackModel.DeleteImage(ctx, listID, imageID) + err = h.stackModel.DeleteImage(ctx, stackID, imageID) + if err != nil { + h.logger.Warn("failed to delete image from list", "error", err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) +} + +func (h *StackHandler) deleteImageStackSchemaItem(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + stringImageID := chi.URLParam(r, "stackID") + stringSchemaItemID := chi.URLParam(r, "schemaItemID") + + imageID, err := uuid.Parse(stringImageID) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + schemaItemID, err := uuid.Parse(stringSchemaItemID) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + // TODO: this should be extracted into a middleware of sorts + userID, err := middleware.GetUserID(ctx, h.logger, w) + if err != nil { + w.WriteHeader(http.StatusUnauthorized) + return + } + + stack, err := h.stackModel.Get(ctx, schemaItemID) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + if stack.UserID != userID { + w.WriteHeader(http.StatusUnauthorized) + return + } + + // TODO: + // The code above is repeated, because it contains stack & image + // manipulations. So we could create a middleware. + // If you repeat this 3 times, then organise it :) + + err = h.stackModel.DeleteSchemaItem(ctx, schemaItemID, imageID) if err != nil { h.logger.Warn("failed to delete image from list", "error", err) w.WriteHeader(http.StatusInternalServerError) @@ -175,12 +226,13 @@ func (h *StackHandler) CreateRoutes(r chi.Router) { r.Use(middleware.SetJson) r.Get("/", h.getAllStacks) - r.Get("/{listID}", h.getStackItems) + r.Get("/{stackID}", h.getStackItems) r.Post("/", middleware.WithLimit(h.logger, h.limitsManager.HasReachedStackLimit, middleware.WithValidatedPost(h.createStack))) - r.Patch("/{listID}", middleware.WithValidatedPost(h.editStack)) - r.Delete("/{listID}", h.deleteStack) - r.Delete("/{listID}/{imageID}", h.deleteImageFromStack) + r.Patch("/{stackID}", middleware.WithValidatedPost(h.editStack)) + r.Delete("/{stackID}", h.deleteStack) + r.Delete("/{stackID}/{imageID}", h.deleteImageFromStack) + r.Delete("/{stackID}/{schemaItemID}", h.deleteImageStackSchemaItem) }) }