Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
37b2629
refactor: Moved the getScope eventbus functions to their own file
rensPols Oct 7, 2025
2345060
feat(core): Add error response helper
rensPols Oct 7, 2025
0ccccd1
feat(scope): Add scope creation endpoints
rensPols Oct 7, 2025
dfed455
refactor(database): Add TODOs for editScope
rensPols Oct 7, 2025
2b6317b
refactor(scope): Use constant for cache address
rensPols Oct 7, 2025
086c692
Refactor(scope): Refactor scope tests
rensPols Oct 7, 2025
64ca1b3
feat(core): Add ScopeLevel enum
rensPols Oct 8, 2025
f6a035a
Convert "scopeKey" to "_id"
rensPols Oct 10, 2025
f15547f
fix(core): Use Scope::class for delivery options
rensPols Oct 10, 2025
7b7dfd7
feat(core): Add Attributes abstract class
rensPols Oct 10, 2025
9a3756c
Merge branch 'refs/heads/main' into attributes-system
rensPols Oct 13, 2025
900e0ae
refactor(scope): Simplify imports in ScopeJdbcVerticle
rensPols Oct 13, 2025
486c303
refactor(scope): Refactor scope handling
rensPols Oct 13, 2025
f4f42ce
fix(core): Correctly handle error messages
rensPols Oct 13, 2025
b5b1dc3
refactor(scope): Simplify ScopeJdbcVerticle
rensPols Oct 13, 2025
63dd983
feat(core): Implement in-memory scope caching
rensPols Oct 13, 2025
d17cf16
feat(attributes): Create getScopedDataSingle() function for use in th…
rensPols Oct 13, 2025
cf89938
Feat(attributes): Added getScopedData for use in the getAttribute fun…
rensPols Oct 13, 2025
95155bc
refactor(attributes): Make getScopedData functions internal
rensPols Oct 13, 2025
3c77c9d
refactor(attributes): Change client visibility to internal
rensPols Oct 13, 2025
2a932a2
refactor(attributes): Replace cliend.find(One)() with getScopedData(S…
rensPols Oct 13, 2025
a892ec4
refactor(core): Make getCollectionKey internal
rensPols Oct 13, 2025
63a640f
fix(core): Make clearAllAttributesValue non-abstract
rensPols Oct 13, 2025
57b48ab
feat(core): Add attribute key validation
rensPols Oct 20, 2025
c93a213
feat(core): Add Future.onFailure(promise) extension function
rensPols Oct 20, 2025
05a2b61
refactor(core): Refactor Attributes class to use an async attributes …
rensPols Oct 20, 2025
d020bb4
feat(futures): Add Future.onSuccess(promise) extension function
rensPols Oct 20, 2025
e1befc7
feat(core): Add onSuccess extension function for Promise<Unit>
rensPols Oct 20, 2025
7990ca9
Fix(core): Add abstract val allowedTypes to Attributes.kt
rensPols Oct 20, 2025
7828c94
refactor(core): Improve attribute type checking
rensPols Oct 20, 2025
ed3f814
refactor(attributes): Refactor attribute value checks
rensPols Oct 20, 2025
e700a53
Refactor(attributes): Improve attribute management
rensPols Oct 20, 2025
c78f562
feat(attributes): Add ProductAttributes class
rensPols Oct 20, 2025
2b91bdd
Grammar correction
rensPols Oct 20, 2025
1f3b6ee
feat(attributes): Add clearAttributeAllValues() to Attributes.kt
rensPols Oct 20, 2025
351ac5d
feat(futures): Add Future.onComplete extension functions
rensPols Oct 20, 2025
3dce3e6
feat(attributes): implement deleteAttribute in Attributes.kt
rensPols Oct 20, 2025
eabd912
Improved naming for the attributes config keys
rensPols Oct 20, 2025
f8dffef
feat(attributes): Add AttributeConfiguration data class
rensPols Oct 20, 2025
9ab4c74
Refactor(attributes): Implement system attributes
rensPols Oct 20, 2025
028bcb3
feat(attributes): Add initialiseSystemAttributes function
rensPols Oct 20, 2025
b863dfc
Resolve JVM name conflict for future parent-promise handlers
rensPols Nov 3, 2025
31f4769
Make sure that "process.scope.create.website" will always return the id
rensPols Nov 3, 2025
5c679f7
Cleaned up "process.scope.deleteScope"
rensPols Nov 3, 2025
760ead4
Fix ScopeJdbcVerticleTest.kt
rensPols Nov 3, 2025
b10d9b7
Use the ObjectId class for creating a scope
rensPols Nov 3, 2025
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 @@ -2,6 +2,7 @@ package com.ex_dock.ex_dock.database.scope

import io.vertx.core.json.JsonObject

@Deprecated("Scope() is deprecated, use JsonObject instead")
data class Scope(
var scopeId: String?,
var websiteName: String,
Expand Down
133 changes: 16 additions & 117 deletions src/main/kotlin/com/ex_dock/ex_dock/database/scope/ScopeJdbcVerticle.kt
Original file line number Diff line number Diff line change
@@ -1,152 +1,51 @@
package com.ex_dock.ex_dock.database.scope

import com.ex_dock.ex_dock.MainVerticle
import com.ex_dock.ex_dock.database.connection.getConnection
import com.ex_dock.ex_dock.frontend.cache.setCacheFlag
import com.ex_dock.ex_dock.helper.replyListMessage
import com.ex_dock.ex_dock.helper.replySingleMessage
import com.ex_dock.ex_dock.global.cachedScopes
import com.ex_dock.ex_dock.helper.messages.errorResponse
import io.vertx.core.Future
import io.vertx.core.VerticleBase
import io.vertx.core.eventbus.DeliveryOptions
import io.vertx.core.eventbus.EventBus
import io.vertx.core.json.JsonObject
import io.vertx.ext.mongo.MongoClient

class ScopeJdbcVerticle: VerticleBase() {
private lateinit var client: MongoClient
private lateinit var eventBus: EventBus
private val fullScopeDeliveryOptions: DeliveryOptions = DeliveryOptions().setCodecName("ScopeCodec")

companion object {
private const val CACHE_ADDRESS = "scopes"
const val CACHE_ADDRESS = "scopes"
}

override fun start(): Future<*>? {
client = vertx.getConnection()
eventBus = vertx.eventBus()

// Initialize all eventbus connections for basic scopes
getAllScopes()
getScopeById()
getScopesByWebsiteName()
getScopesByStoreViewName()
createScope()
editScope()
eventBus.getAllScopes(client)
eventBus.getScopeById(client)
eventBus.getScopesByWebsiteId(client)
eventBus.createWebsite(client)
eventBus.createStoreView(client)
deleteScope()

return Future.succeededFuture<Unit>()
}

private fun getAllScopes() {
val getAllScopesConsumer = eventBus.consumer<String>("process.scope.getAllScopes")
getAllScopesConsumer.handler { message ->
val query = JsonObject()

client.find("scopes", query).replyListMessage(message)
}
}

private fun getScopeById() {
val getScopeByWebsiteIdConsumer = eventBus.consumer<String>("process.scope.getScopeByWebsiteId")
getScopeByWebsiteIdConsumer.handler { message ->
val websiteId = message.body()
val query = JsonObject()
.put("_id", websiteId)

client.find("scopes", query).replySingleMessage(message)
}
}

private fun getScopesByWebsiteName() {
val getScopesByWebsiteNameConsumer = eventBus.consumer<String>("process.scope.getScopesByWebsiteName")
getScopesByWebsiteNameConsumer.handler { message ->
val websiteName = message.body()
val query = JsonObject()
.put("website_name", websiteName)

client.find("scopes", query).replyListMessage(message)
}
}

private fun getScopesByStoreViewName() {
val getScopesByStoreViewNameConsumer = eventBus.consumer<String>("process.scope.getScopesByStoreViewName")
getScopesByStoreViewNameConsumer.handler { message ->
val storeViewName = message.body()
val query = JsonObject()
.put("store_view_name", storeViewName)

client.find("scopes", query).replyListMessage(message)
}
}

private fun createScope() {
val createScopeConsumer = eventBus.consumer<Scope>("process.scope.createScope")
createScopeConsumer.handler { message ->
println("Received createScope message in ScopeJdbcVerticle")
val scope = message.body()
val document = scope.toDocument()

val rowsFuture = client.save("scopes", document)

rowsFuture.onFailure { res ->
println("Failed to execute query: $res")
message.fail(500, "Failed to execute query: $res")
}

rowsFuture.onSuccess { res ->
val lastInsertID: String? = res
if (lastInsertID != null) {
scope.scopeId = lastInsertID
}

setCacheFlag(eventBus, CACHE_ADDRESS)
message.reply(scope, fullScopeDeliveryOptions)
}
}
}

private fun editScope() {
val editScopeConsumer = eventBus.consumer<Scope>("process.scope.editScope")
editScopeConsumer.handler { message ->
val body = message.body()
if (body.scopeId == null) {
message.fail(400, "No scope ID provided")
return@handler
}
val document = body.toDocument()
val rowsFuture = client.save("scopes", document)

rowsFuture.onFailure { res ->
println("Failed to execute query: $res")
message.fail(500, "Failed to execute query: $res")
}

rowsFuture.onSuccess { res ->
val lastInsertID: String? = res
if (lastInsertID != null) {
body.scopeId = lastInsertID
}

setCacheFlag(eventBus, CACHE_ADDRESS)
message.reply(body, fullScopeDeliveryOptions)
}
}
}
// TODO: create editScope functions

private fun deleteScope() {
val deleteScopeConsumer = eventBus.consumer<String>("process.scope.deleteScope")
deleteScopeConsumer.handler { message ->
// TODO: remove all data associated with the scope
eventBus.consumer<String>("process.scope.deleteScope").handler { message ->
val scopeId = message.body()
val query = JsonObject()
.put("_id", scopeId)

val rowsFuture = client.removeDocument("scopes", query)

rowsFuture.onFailure { res ->
println("Failed to execute query: $res")
message.fail(500, "Failed to execute query: $res")
}

rowsFuture.onSuccess { res ->
client.removeDocument("scopes", query).onFailure { err ->
message.errorResponse(500, "Failed to execute query: $err")
}.onSuccess { _ ->
cachedScopes.remove(scopeId)
message.reply("Scope deleted successfully")
}
}
Expand Down
98 changes: 98 additions & 0 deletions src/main/kotlin/com/ex_dock/ex_dock/database/scope/createScopes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.ex_dock.ex_dock.database.scope

import com.ex_dock.ex_dock.global.cachedScopes
import com.ex_dock.ex_dock.helper.messages.errorResponse
import io.vertx.core.eventbus.EventBus
import io.vertx.core.json.JsonObject
import io.vertx.ext.mongo.MongoClient
import org.bson.types.ObjectId

internal fun EventBus.createWebsite(client: MongoClient) {
this.localConsumer<JsonObject>("process.scope.create.website").handler { message ->
val data = message.body()

val keyString = data.getString("scopeKey") ?: return@handler message.fail(400, "The key of the website (scope) is required.")
val key = ObjectId(keyString)
val name =
data.getString("scopeName") ?: return@handler message.fail(400, "The name of the website (scope) is required.")

client.findOne(
ScopeJdbcVerticle.CACHE_ADDRESS,
JsonObject().put("_id", key),
JsonObject().put("_id", 1),
).onFailure { err ->
message.errorResponse(400, err)
}.onSuccess { res ->
if (res != null) return@onSuccess message.fail(
409,
"This function is for creating websites (scope), not editing them."
)

val document = JsonObject().put("_id", key).put("scopeName", name).put("scopeType", "website")

client.insert(ScopeJdbcVerticle.CACHE_ADDRESS, document).onFailure { err ->
message.errorResponse(err)
}.onSuccess { res ->
cachedScopes.put(keyString, document)
message.reply(res ?: key)
}
}
}
}

internal fun EventBus.createStoreView(client: MongoClient) {
this.localConsumer<JsonObject>("process.scope.create.store-view").handler { message ->
val data = message.body()

val name =
data.getString("name") ?: return@handler message.fail(400, "The name of the store-view (scope) is required.")
val keyString = data.getString("key") ?: return@handler message.fail(400, "The key of the store-view (scope) is required.")
val key = ObjectId(keyString)
val websiteId = data.getString("websiteId") ?: return@handler message.fail(
400,
"The websiteId of the parent website (scope) is required."
)

client.findOne(
ScopeJdbcVerticle.CACHE_ADDRESS,
JsonObject().put("_id", key),
JsonObject().put("_id", 1),
).onFailure { err ->
message.errorResponse(400, err)
}.onSuccess { res ->
if (res != null) return@onSuccess message.fail(
409,
"This function is for creating store-views (scope), not editing them."
)

val searchWebsiteQuery = JsonObject().put("scopeType", "website").put("_id", websiteId)
client.find(ScopeJdbcVerticle.CACHE_ADDRESS, searchWebsiteQuery).onFailure { err ->
message.errorResponse(err)
}.onSuccess { res ->
if (res.isEmpty()) return@onSuccess message.fail(
400,
"The websiteId of the parent website (scope) does not exist"
)

client.find(ScopeJdbcVerticle.CACHE_ADDRESS, JsonObject().put("_id", key)).onFailure { err ->
message.errorResponse(err)
}.onSuccess { res ->
if (res.isNotEmpty()) return@onSuccess message.fail(
400,
"The key of the store-view (scope) already exists for a scope"
)

val document = JsonObject().put("_id", key).put("scopeName", name).put("scopeType", "store-view")
.put("websiteId", websiteId)

client.insert(ScopeJdbcVerticle.CACHE_ADDRESS, document).onFailure { err ->
message.errorResponse(err)
}.onSuccess { res ->
cachedScopes.put(keyString, document)
message.reply(res ?: key)
}
}
}
}
}
}
45 changes: 45 additions & 0 deletions src/main/kotlin/com/ex_dock/ex_dock/database/scope/getScopes.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.ex_dock.ex_dock.database.scope

import com.ex_dock.ex_dock.global.cachedScopes
import com.ex_dock.ex_dock.helper.replyListMessage
import com.ex_dock.ex_dock.helper.replySingleMessage
import io.vertx.core.eventbus.EventBus
import io.vertx.core.json.JsonObject
import io.vertx.ext.mongo.MongoClient


internal fun EventBus.getAllScopes(client: MongoClient) {
this.consumer<String>("process.scope.getAllScopes").handler { message ->
val query = JsonObject()

client.find("scopes", query).onSuccess { res ->
val newCachedScopes = JsonObject()
for (scope in res) newCachedScopes.put(scope.getString("_id"), scope)
cachedScopes = newCachedScopes
}.replyListMessage(message)
}
}

internal fun EventBus.getScopeById(client: MongoClient) {
this.consumer<String>("process.scope.getScopeById").handler { message ->
val websiteId = message.body()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This key is supposed to be an ObjectID instead of a String. So in needs to be initialized by ObjectId(data.getString(...))

val query = JsonObject()
.put("_id", websiteId)

client.find("scopes", query).onSuccess { res ->
cachedScopes.put(websiteId, res.first())
}.replySingleMessage(message)
}
}

internal fun EventBus.getScopesByWebsiteId(client: MongoClient) {
this.consumer<String>("process.scope.getScopesByWebsiteId").handler { message ->
val websiteName = message.body()
val query = JsonObject()
.put("websiteId", websiteName)

client.find("scopes", query).onSuccess { res ->
for (scope in res) cachedScopes.put(scope.getString("_id"), scope)
}.replyListMessage(message)
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/com/ex_dock/ex_dock/global/scopes.kt
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also put the scopes in the ExdockCache but we can also do that later. The cache is a bit better optimized for data fetching

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.ex_dock.ex_dock.global

import io.vertx.core.json.JsonObject

var cachedScopes: JsonObject = JsonObject()
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.ex_dock.ex_dock.helper.attributes

import com.ex_dock.ex_dock.helper.scopes.ScopeLevel
import io.vertx.core.json.JsonObject

data class AttributeConfiguration(
val attributeKey: String,
val attributeName: String,
val attributeDataType: String,
val scopeLevel: ScopeLevel,
) {
fun toDocument(): JsonObject {
return JsonObject()
.put("_id", attributeKey)
.put("attributeName", attributeName)
.put("attributeType", attributeDataType)
.put("attributeScopeLevel", scopeLevel.name)
}
}
Loading
Loading