Skip to content

Commit 638e0c4

Browse files
feat: added status on venue
1 parent c6518a7 commit 638e0c4

File tree

10 files changed

+276
-72
lines changed

10 files changed

+276
-72
lines changed

cmd/api/api.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ func (app *application) mount() http.Handler {
141141
// Routes that require venue ownership
142142
r.Route("/{venueID}", func(r chi.Router) {
143143
r.Use(app.IsOwnerMiddleware)
144+
r.Delete("/", app.deleteVenueHandler)
144145
r.Get("/pending-bookings", app.getPendingBookingsHandler)
145146
r.Get("/scheduled-bookings", app.getScheduledBookingsHandler)
146147
r.Post("/pending-bookings/{bookingID}/accept", app.acceptBookingHandler)

cmd/api/images.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ func (app *application) parseVenueForm(w http.ResponseWriter, r *http.Request, d
2323
return nil, fmt.Errorf("missing venue data in form")
2424
}
2525

26+
fmt.Printf("📚 venueData %s\n", venueData)
27+
2628
// Decode JSON payload
2729
if err := json.Unmarshal([]byte(venueData), data); err != nil {
2830
return nil, fmt.Errorf("failed to decode JSON venue data: %w", err)

cmd/api/venues.go

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ type VenueListResponse struct {
241241
// @Param limit query int false "Items per page" default(7)
242242
// @Success 200 {array} VenueListResponse
243243
//
244-
// @Security ApiKeyAuth
244+
// @Security ApiKeyAuth
245245
//
246246
// @Router /venues/list-venues [get]
247247
func (app *application) listVenuesHandler(w http.ResponseWriter, r *http.Request) {
@@ -336,8 +336,6 @@ func nullString(s string) *string {
336336
// 3. Update the venue record with the returned image URLs.
337337
// If an error occurs at any external step, the venue is deleted to ensure consistency.
338338
func (app *application) CreateAndUploadVenue(ctx context.Context, venue *store.Venue, files []*multipart.FileHeader, w http.ResponseWriter, r *http.Request) error {
339-
// Step 1: Create the venue in the database (without images).
340-
fmt.Print("calling database for Create")
341339
if err := app.store.Venues.Create(ctx, venue); err != nil {
342340
return fmt.Errorf("error creating venue: %w", err)
343341
}
@@ -352,7 +350,6 @@ func (app *application) CreateAndUploadVenue(ctx context.Context, venue *store.V
352350
return fmt.Errorf("error uploading images: %w", err)
353351
}
354352

355-
fmt.Print("calling database to updateImageURLs")
356353
// Step 3: Update the venue with the uploaded image URLs.
357354
if err := app.store.Venues.UpdateImageURLs(ctx, venue.ID, imageUrls); err != nil {
358355
// Compensate: Delete the venue if updating image URLs fails.
@@ -390,6 +387,7 @@ func (app *application) CreateAndUploadVenue(ctx context.Context, venue *store.V
390387
// @Security ApiKeyAuth
391388
// @Router /venues [post]
392389
func (app *application) createVenueHandler(w http.ResponseWriter, r *http.Request) {
390+
fmt.Println("🚀 Entering createVenueHandler")
393391
var payload CreateVenuePayload
394392

395393
// Parse form and JSON payload from multipart form.
@@ -399,19 +397,15 @@ func (app *application) createVenueHandler(w http.ResponseWriter, r *http.Reques
399397
return
400398
}
401399

400+
fmt.Printf("🎾 Parsed payload: %+v, %d files\n", payload, len(files))
401+
//for debug only
402+
for _, fh := range files {
403+
fmt.Printf("📷 got file: %q (%d bytes)\n", fh.Filename, fh.Size)
404+
}
405+
402406
// Retrieve the current user (owner) from the request context.
403407
user := getUserFromContext(r)
404-
405-
// Check for an existing venue with the same name for this owner.
406-
exists, err := app.store.Venues.CheckIfVenueExists(r.Context(), payload.Name, user.ID)
407-
if err != nil {
408-
app.internalServerError(w, r, err)
409-
return
410-
}
411-
if exists {
412-
app.alreadyExistVenue(w, r, fmt.Errorf("a venue with this name already exists for this owner"))
413-
return
414-
}
408+
fmt.Println("👤 current user:", user.ID)
415409

416410
// Prepare the venue model without image URLs initially.
417411
venue := &store.Venue{
@@ -427,12 +421,16 @@ func (app *application) createVenueHandler(w http.ResponseWriter, r *http.Reques
427421
// ImageURLs field remains empty until updated.
428422
}
429423

424+
fmt.Println("🏗️ About to Create venue in DB:", venue)
425+
430426
// Use the SAGA pattern to create the venue record and upload images externally.
431427
if err := app.CreateAndUploadVenue(r.Context(), venue, files, w, r); err != nil {
432428
app.internalServerError(w, r, err)
433429
return
434430
}
435431

432+
fmt.Println("✅ Venue created successfully:", venue.ID)
433+
436434
// Return a JSON response with the created venue details.
437435
if err := app.jsonResponse(w, http.StatusCreated, venue); err != nil {
438436
app.internalServerError(w, r, err)
@@ -515,3 +513,56 @@ func (app *application) getVenueDetailHandler(w http.ResponseWriter, r *http.Req
515513
return
516514
}
517515
}
516+
517+
// -----------------------------------------------
518+
// HTTP Handler for Venue Deletion
519+
// -----------------------------------------------
520+
521+
// deleteVenueHandler handles deletion of a venue and its associated images from Cloudinary.
522+
523+
// DeleteVenue godoc
524+
//
525+
// @Summary Delete a venue from the system
526+
// @Description Deletes a venue by ID and removes all associated images from Cloudinary.
527+
// @Tags Venue-Owner
528+
// @Produce json
529+
// @Param venueID path int true "Venue ID"
530+
// @Success 200 {object} map[string]string "Venue deleted successfully"
531+
// @Failure 400 {object} error "Invalid venue ID"
532+
// @Failure 401 {object} error "Unauthorized"
533+
// @Failure 404 {object} error "Venue not found"
534+
// @Failure 500 {object} error "Internal server error"
535+
// @Security ApiKeyAuth
536+
// @Router /venues/{venueID} [delete]
537+
func (app *application) deleteVenueHandler(w http.ResponseWriter, r *http.Request) {
538+
venueIDStr := chi.URLParam(r, "venueID")
539+
540+
// Convert venueID to int64
541+
venueID, err := strconv.ParseInt(venueIDStr, 10, 64)
542+
if err != nil {
543+
app.badRequestResponse(w, r, fmt.Errorf("invalid venueID: %v", err))
544+
return
545+
}
546+
urls, err := app.store.Venues.GetImageURLs(r.Context(), venueID)
547+
if err != nil {
548+
app.internalServerError(w, r, err)
549+
return
550+
}
551+
552+
// 2) Delete each from Cloudinary
553+
for _, url := range urls {
554+
if err := app.deletePhotoFromCloudinary(url); err != nil {
555+
app.logger.Warnw("failed to delete image from Cloudinary", "url", url, "err", err)
556+
app.internalServerError(w, r, err)
557+
return
558+
559+
}
560+
}
561+
562+
if err := app.store.Venues.Delete(r.Context(), venueID); err != nil {
563+
app.internalServerError(w, r, err)
564+
return
565+
}
566+
567+
app.jsonResponse(w, http.StatusOK, map[string]string{"message": "Venue deleted successfully"})
568+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- 1. Remove the column
2+
ALTER TABLE venues DROP COLUMN status;
3+
4+
-- 2. Drop the enum type
5+
DROP TYPE venue_status;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- 1. Create the enum type
2+
CREATE TYPE venue_status AS ENUM ('requested', 'active', 'rejected', 'hold');
3+
4+
-- 2. Add the column using the new enum type
5+
ALTER TABLE venues ADD COLUMN status venue_status;
6+
7+
-- 3. Set default to 'requested' for future inserts (optional)
8+
ALTER TABLE venues ALTER COLUMN status SET DEFAULT 'requested';
9+
10+
-- 4. Set status to 'active' for all existing records
11+
UPDATE venues SET status = 'active' WHERE status IS NULL;
12+
13+
-- (Optional) 5. Add NOT NULL constraint if status should always be present
14+
ALTER TABLE venues ALTER COLUMN status SET NOT NULL;

docs/docs.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,6 +2248,57 @@ const docTemplate = `{
22482248
}
22492249
},
22502250
"/venues/{venueID}": {
2251+
"delete": {
2252+
"security": [
2253+
{
2254+
"ApiKeyAuth": []
2255+
}
2256+
],
2257+
"description": "Deletes a venue by ID and removes all associated images from Cloudinary.",
2258+
"produces": [
2259+
"application/json"
2260+
],
2261+
"tags": [
2262+
"Venue-Owner"
2263+
],
2264+
"summary": "Delete a venue from the system",
2265+
"parameters": [
2266+
{
2267+
"type": "integer",
2268+
"description": "Venue ID",
2269+
"name": "venueID",
2270+
"in": "path",
2271+
"required": true
2272+
}
2273+
],
2274+
"responses": {
2275+
"200": {
2276+
"description": "Venue deleted successfully",
2277+
"schema": {
2278+
"type": "object",
2279+
"additionalProperties": {
2280+
"type": "string"
2281+
}
2282+
}
2283+
},
2284+
"400": {
2285+
"description": "Invalid venue ID",
2286+
"schema": {}
2287+
},
2288+
"401": {
2289+
"description": "Unauthorized",
2290+
"schema": {}
2291+
},
2292+
"404": {
2293+
"description": "Venue not found",
2294+
"schema": {}
2295+
},
2296+
"500": {
2297+
"description": "Internal server error",
2298+
"schema": {}
2299+
}
2300+
}
2301+
},
22512302
"patch": {
22522303
"security": [
22532304
{

docs/swagger.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2240,6 +2240,57 @@
22402240
}
22412241
},
22422242
"/venues/{venueID}": {
2243+
"delete": {
2244+
"security": [
2245+
{
2246+
"ApiKeyAuth": []
2247+
}
2248+
],
2249+
"description": "Deletes a venue by ID and removes all associated images from Cloudinary.",
2250+
"produces": [
2251+
"application/json"
2252+
],
2253+
"tags": [
2254+
"Venue-Owner"
2255+
],
2256+
"summary": "Delete a venue from the system",
2257+
"parameters": [
2258+
{
2259+
"type": "integer",
2260+
"description": "Venue ID",
2261+
"name": "venueID",
2262+
"in": "path",
2263+
"required": true
2264+
}
2265+
],
2266+
"responses": {
2267+
"200": {
2268+
"description": "Venue deleted successfully",
2269+
"schema": {
2270+
"type": "object",
2271+
"additionalProperties": {
2272+
"type": "string"
2273+
}
2274+
}
2275+
},
2276+
"400": {
2277+
"description": "Invalid venue ID",
2278+
"schema": {}
2279+
},
2280+
"401": {
2281+
"description": "Unauthorized",
2282+
"schema": {}
2283+
},
2284+
"404": {
2285+
"description": "Venue not found",
2286+
"schema": {}
2287+
},
2288+
"500": {
2289+
"description": "Internal server error",
2290+
"schema": {}
2291+
}
2292+
}
2293+
},
22432294
"patch": {
22442295
"security": [
22452296
{

docs/swagger.yaml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,6 +2217,40 @@ paths:
22172217
tags:
22182218
- Venue-Owner
22192219
/venues/{venueID}:
2220+
delete:
2221+
description: Deletes a venue by ID and removes all associated images from Cloudinary.
2222+
parameters:
2223+
- description: Venue ID
2224+
in: path
2225+
name: venueID
2226+
required: true
2227+
type: integer
2228+
produces:
2229+
- application/json
2230+
responses:
2231+
"200":
2232+
description: Venue deleted successfully
2233+
schema:
2234+
additionalProperties:
2235+
type: string
2236+
type: object
2237+
"400":
2238+
description: Invalid venue ID
2239+
schema: {}
2240+
"401":
2241+
description: Unauthorized
2242+
schema: {}
2243+
"404":
2244+
description: Venue not found
2245+
schema: {}
2246+
"500":
2247+
description: Internal server error
2248+
schema: {}
2249+
security:
2250+
- ApiKeyAuth: []
2251+
summary: Delete a venue from the system
2252+
tags:
2253+
- Venue-Owner
22202254
patch:
22212255
consumes:
22222256
- application/json

internal/store/storage.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type Storage struct {
4747
GetOwnedVenueIDs(ctx context.Context, userID int64) ([]int64, error)
4848
List(ctx context.Context, filter VenueFilter) ([]VenueListing, error)
4949
GetVenueDetail(ctx context.Context, venueID int64) (*VenueDetail, error)
50+
GetImageURLs(ctx context.Context, venueID int64) ([]string, error)
5051
}
5152
Reviews interface {
5253
CreateReview(context.Context, *Review) error

0 commit comments

Comments
 (0)