Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class StoreController(
) {
@StoreDoc.CreateStore
@PostMapping("/create")
fun addStore(
@RequestBody request: CreateStoreRequest,
fun createStore(
@ModelAttribute request: CreateStoreRequest,
@AuthenticationPrincipal ownerId: UUID
): ResponseEntity<StoreCreationResponse> {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotEmpty
import org.springframework.web.multipart.MultipartFile
import java.util.*

data class CreateStoreRequest(
@field:NotBlank(message = "Title is required")
val title: String,
@field:NotBlank(message = "Description can not be blank")
@field:NotBlank(message = "Description can not be null")
val description: String,
@field:NotBlank(message = "Phone number can not be blank")
val phoneNumber: String?,
@field:NotBlank(message = "City can not be blank")
@field:NotBlank(message = "City can not be null")
val city: String,
@field:NotBlank(message = "Government can not be blank")
val government: String,
@field:NotBlank(message = "Country can not be blank")
@field:NotBlank(message = "Country can not be null")
val country: String,
@field:NotEmpty(message = "At least one category is required")
val categoryIds: List<UUID>,
@field:NotBlank(message = "avatarImage can not be null")
val avatarImage: MultipartFile?,
@field:NotBlank(message = "coverImage can not be null")
val coverImage: MultipartFile?
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import java.util.*
data class StoreCreationResponse(
val id: UUID,
val title: String,
val ownerId: UUID,
val avatarUrl: String,
val coverUrl: String,
val createdAt: Instant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import java.util.*

data class StoreInfoResponse(
val id: UUID,
val ownerId: UUID,
val title: String,
val description: String,
val avatarImageURL: String?,
val coverImageURL: String?,
val storeContacts: List<StoreContactResponse>,
val city: String,
val government: String,
val country: String,
val featuredProducts: List<ProductCardResponse>,
val avgRating: Double,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ data class StoreResponse (
val id: UUID?,
val title: String,
val city: String,
val government: String,
val country: String,
val avatarImageURL: String?,
val coverImageURL: String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,12 @@ fun Store.toStoreDetailsResponse(
subCategories: List<SubCategoryResponse>
) = StoreInfoResponse(
id = this.id ?: throw IllegalStateException("Store ID was null for Store ${this.title}"),
ownerId = this.owner.id ?: throw IllegalStateException("Store owner ID was null for Store ${this.title}"),
title = this.title,
description = this.description,
avatarImageURL = this.avatarImageURL,
coverImageURL = this.coverImageURL,
featuredProducts = featuredProducts,
city = this.city,
government = this.government,
country = this.country,
avgRating = averageRating,
activeStoreDiscounts = discounts,
Expand All @@ -35,7 +33,6 @@ fun Store.toStoreResponse(): StoreResponse {
id = id,
title = title,
city = city,
government = government,
country = country,
avatarImageURL = avatarImageURL,
coverImageURL = coverImageURL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ object ErrorResponseExample {
"timestamp": "2025-11-05T21:50:12.995Z",
"status": 403,
"error": "Forbidden",
"message": "this owner ID already has a store",
"message": "You already has a store",
"path": "/v1/stores",
"code": "STORE_006"
"validationErrors": null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.ExampleObject
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.parameters.RequestBody
import io.swagger.v3.oas.annotations.responses.ApiResponse
import org.shangahi.sellio_backend.api.dto.request.CreateStoreRequest
import org.shangahi.sellio_backend.api.dto.response.ErrorResponse
import org.shangahi.sellio_backend.api.dto.response.StoreCreationResponse
import org.shangahi.sellio_backend.api.dto.response.StoreInfoResponse
Expand Down Expand Up @@ -100,30 +101,31 @@ annotation class StoreDoc {
)
annotation class GetStoreInfo


@Operation(
summary = "Insert new store",
description = "Insert new store info and make suer to use unique phone number and owner Id ",
summary = "Create a new store",
description = "Creates a new store for the authenticated user. The request must be sent as multipart/form-data and include the store images.",
requestBody = RequestBody(
required = true,
description = "Insert required fields to add new store",
description = "Store information and images",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = StoreCreationResponse::class),
mediaType = "multipart/form-data",
schema = Schema(implementation = CreateStoreRequest::class),
examples = [
ExampleObject(
name = "AddStoreRequestExample",
name = "CreateStoreRequestExample",
value = """
{
"ownerId": "c0c0c0c0-c0c0-c0c0-c0c0-c0c0c0c0c0c0",
"title": "laptop-store",
"description": "store for selling laptops",
"phoneNumber": "01212121213",
"city": "cairo",
"government": "cairo",
"country": "egypt"
}
{
"title": "Laptop Store",
"description": "Store for selling laptops",
"city": "Cairo",
"country": "Egypt",
"categoryIds": [
"be136218-f697-4289-85f7-1aaf904f9035"
],
"avatarImage": "<image file>",
"coverImage": "<image file>"
}
"""
)
]
Expand All @@ -133,65 +135,71 @@ annotation class StoreDoc {
responses = [
ApiResponse(
responseCode = "201",
description = "Store Inserted successfully",

description = "Store created successfully",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = StoreCreationResponse::class),
examples = [
ExampleObject(
name = "Store info",
name = "StoreCreatedResponse",
value = """
{
"id": "be136218-f697-4289-85f7-1aaf904f9035",
"title": "laptop-store",
"ownerId": "c0c0c0c0-c0c0-c0c0-c0c0-c0c0c0c0c0c0",
"avatarUrl": "",
"coverUrl": "",
"createdAt": "2025-11-10T17:13:24.908618400Z"
"id": "be136218-f697-4289-85f7-1aaf904f9035",
"title": "Laptop Store",
"avatarUrl": "https://cdn.example.com/stores/avatars/avatar.jpg",
"coverUrl": "https://cdn.example.com/stores/covers/cover.jpg",
"createdAt": "2025-11-10T17:13:24.908618400Z"
}
"""
"""
)
],
]
)
]
),
ApiResponse(
responseCode = "409",
description = "Conflict errors",
responseCode = "400",
description = "Bad request",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ErrorResponse::class),
examples = [
ExampleObject(
name = "PhoneNumberAlreadyExistErrorExample",
value = ErrorResponseExample.STORE_PHONE_NUMBER_ALREADY_EXISTS
),
ExampleObject(
name = "EmailAlreadyExistErrorExample",
value = ErrorResponseExample.STORE_EMAIL_ALREADY_EXISTS
),
name = "RequestBodyError",
value = ErrorResponseExample.REQUEST_BODY_ERROR
)
]
)
]
),
ApiResponse(
responseCode = "409",
description = "Conflict",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ErrorResponse::class),
examples = [
ExampleObject(
name = "StoreTitleAlreadyExistErrorExample",
name = "StoreTitleAlreadyExists",
value = ErrorResponseExample.STORE_TITLE_ALREADY_EXISTS
),
)
]
)
]
),
ApiResponse(
responseCode = "400",
description = "Bad request",
responseCode = "404",
description = "Referenced category not found",
content = [
Content(
mediaType = "application/json",
schema = Schema(implementation = ErrorResponse::class),
examples = [
ExampleObject(
name = "MissedFieldErrorExample",
value = ErrorResponseExample.REQUEST_BODY_ERROR
name = "CategoryNotFound",
value = ErrorResponseExample.CATEG_NOT_FOUND
)
]
)
Expand All @@ -206,18 +214,17 @@ annotation class StoreDoc {
schema = Schema(implementation = ErrorResponse::class),
examples = [
ExampleObject(
name = "InternalServerErrorExample",
name = "InternalServerError",
value = ErrorResponseExample.INTERNAL_SERVER_ERROR
)
]
)
]
),
)
]
)
annotation class CreateStore


@Operation(
summary = "Top Stores",
description = "Get page of top rated stores",
Expand Down
7 changes: 5 additions & 2 deletions src/main/kotlin/org/shangahi/sellio_backend/entity/Store.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,18 @@ data class Store(
@Column(name = "city", nullable = false)
val city: String,

@Column(name = "government", nullable = false)
val government: String,
@Column(name = "government", nullable = true)
val government: String? = null,

@Column(name = "country", nullable = false)
val country: String,

@OneToMany(mappedBy = "store", fetch = FetchType.LAZY)
val favoriteByUsers: Set<FavoriteStore> = emptySet(),

@OneToMany(mappedBy = "store", fetch = FetchType.LAZY, cascade = [CascadeType.ALL], orphanRemoval = true)
val storeCategories: Set<StoreCategory> = emptySet(),

@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
val createdAt: Instant? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.shangahi.sellio_backend.entity

import com.fasterxml.jackson.annotation.JsonBackReference
import jakarta.persistence.*
import org.hibernate.annotations.CreationTimestamp
import java.time.Instant
import java.util.*

@Entity
@Table(name = "store_category")
data class StoreCategory(
@Id
@GeneratedValue(strategy = GenerationType.UUID)
val id: UUID? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "store_id", nullable = false)
@JsonBackReference
val store: Store? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "category_id", nullable = false)
@JsonBackReference
val category: Category? = null,

@CreationTimestamp
@Column(name = "created_at", nullable = false, updatable = false)
val createdAt: Instant? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.shangahi.sellio_backend.repository

import org.shangahi.sellio_backend.entity.StoreCategory
import org.springframework.data.jpa.repository.JpaRepository
import java.util.*

interface StoreCategoryRepository : JpaRepository<StoreCategory, UUID>
Loading
Loading