Skip to content

Commit

Permalink
PUT method support for updating metadata
Browse files Browse the repository at this point in the history
pklimai committed Jan 2, 2025

Unverified

No user is associated with the committer email.
1 parent 3bbd31a commit 890d46f
Showing 2 changed files with 103 additions and 16 deletions.
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ sudo docker run -d --rm --name nica-ems -p 80:8080 -v ./ems.config.yaml:/app/bin

### Methods supported

#### Get event metadata:
#### Read event records (get event metadata):
`GET /event_api/v1/event[?parameter1=value1[&parameter2=value2[...]]]`

Here and below, for parameter values we have
@@ -80,7 +80,7 @@ Here and below, for parameter values we have
- `limit` (int)
- `offset` (int)

* Any custom parameters specified in YAML file
* Any custom parameters specified in YAML configuration file

Example:
`http://127.0.0.1:8080/event_api/v1/event?limit=5&software_version=~19._&beam_particle=~A_&track_number=11|`
@@ -89,39 +89,54 @@ Example:
#### Create event records in the metadata catalog (Writer or Admin role required):
`POST /event_api/v1/event`

Message body must contain the JSON list of events using format as given below.
Message body must contain the JSON list of events to be written, using format as given below. In case of any events
already present, an error is returned and whole transaction is cancelled.


#### Update event records in the metadata catalog (Writer or Admin role required):
`PUT /event_api/v1/event`

Message body must contain the JSON list of events to be created or updated. The request succeeds even if some records
are already present (if any parameters are different, they are updated).


#### Delete event records from the metadata catalog (Admin role required)
`DELETE /event_api/v1/event`

Message body must contain the JSON list of events (only `reference:` part is required, other fields are
optional and ignored, if present).


#### Read software records from dictionary
`GET /event_api/v1/software`


#### Create software record in dictionary
`POST /event_api/v1/software`

Message body example `{"software_id": 100, "software_version": "22.11"}`


#### Read storage records from dictionary
`GET /event_api/v1/storage`


#### Create storage record in dictionary
`POST /event_api/v1/storage`

Message body example `{"storage_id": 100, "storage_name": "data1"}`


#### Delete event records from the metadata catalog (Admin role required)
`DELETE /event_api/v1/event`

Message body must contain the JSON list of events (only `reference:` part is required, other fields are
optional and ignored, if present).

#### TODO: Count number of entries in EMS and return just this value
`GET /count[?parameter1=value1[&parameter2=value2[...]]]`


#### TODO: Get event records as a ROOT file (synchronous)
`GET /event_api/v1/eventFile[?parameter1=value1[&parameter2=value2[...]]]`

File is built and downloaded immediately (same HTTP session)


#### TODO: Get event records as a ROOT file reference (asynchronous)
`GET /event_api/v1/eventFileRef[?parameter1=value1[&parameter2=value2[...]]]`

86 changes: 79 additions & 7 deletions src/jvmMain/kotlin/Application.kt
Original file line number Diff line number Diff line change
@@ -329,14 +329,12 @@ fun Application.main() {
}

post("/${EVENT_ENTITY_API_NAME}") {

val roles = call.principal<WithRoles>()?.roles!!
// println("Roles in EVENT_ENTITY_API_NAME: $roles")
if (!(roles.isWriter or roles.isAdmin)) {
call.respond(HttpStatusCode.Unauthorized)
return@post
}

var connEMD: Connection? = null
try {
val events = call.receive<Array<EventRepr>>()
@@ -347,10 +345,8 @@ fun Application.main() {
events.forEach { event ->
println("Create event: $event")
val software_id = softwareMap.str_to_id[event.software_version]
val storage_id = storageMap.str_to_id[event.reference.storage_name]
val file_path = event.reference.file_path
val storage_name = event.reference.storage_name
val storage_id = storageMap.str_to_id[storage_name]

val file_guid: Int
val res = connEMD!!.createStatement().executeQuery(
"""SELECT file_guid FROM file_ WHERE storage_id = $storage_id AND file_path = '$file_path'"""
@@ -367,7 +363,7 @@ fun Application.main() {
res2.next()
file_guid = res2.getInt("file_guid")
}
println("File GUID = $file_guid")
println("File GUID for $file_path = $file_guid")
val parameterValuesStr =
page.parameters.joinToString(", ") {
when (it.type.uppercase()) {
@@ -403,7 +399,83 @@ fun Application.main() {
}

put("/${EVENT_ENTITY_API_NAME}") {
TODO()
val roles = call.principal<WithRoles>()?.roles!!
if (!(roles.isWriter or roles.isAdmin)) {
call.respond(HttpStatusCode.Unauthorized)
return@put
}
var connEMD: Connection? = null
try {
val events = call.receive<Array<EventRepr>>()
connEMD = newEMDConnection(config, this.context)
connEMD!!.autoCommit = false
val softwareMap = getSoftwareMap(connEMD!!)
val storageMap = getStorageMap(connEMD!!)
events.forEach { event ->
println("Create or update event: $event")
val software_id = softwareMap.str_to_id[event.software_version]
val storage_id = storageMap.str_to_id[event.reference.storage_name]
val file_path = event.reference.file_path
val file_guid: Int
val res = connEMD!!.createStatement().executeQuery(
"""SELECT file_guid FROM file_ WHERE storage_id = $storage_id AND file_path = '$file_path'"""
)
if (res.next()) { // file record already exists
file_guid = res.getInt("file_guid")
} else { // create file record
val fileQuery = """INSERT INTO file_ (storage_id, file_path) VALUES ($storage_id, '$file_path')"""
println(fileQuery)
connEMD!!.createStatement().executeUpdate(fileQuery)
val res2 = connEMD!!.createStatement().executeQuery(
"""SELECT file_guid FROM file_ WHERE storage_id = $storage_id AND file_path = '$file_path'"""
)
res2.next()
file_guid = res2.getInt("file_guid")
}
println("File GUID for $file_path = $file_guid")
val parameterValuesStr =
page.parameters.joinToString(", ") {
when (it.type.uppercase()) {
"STRING" -> "'" + event.parameters[it.name].toString() + "'"
else -> event.parameters[it.name].toString()
}
}
val parameterNamesEqValuesStr =
page.parameters.joinToString(", ") {
when (it.type.uppercase()) {
"STRING" -> "${it.name}='${event.parameters[it.name].toString()}'"
else -> "${it.name}=${event.parameters[it.name].toString()}"
}
}
val query = """
INSERT INTO ${page.db_table_name}
(file_guid, event_number, software_id, period_number, run_number,
${page.parameters.joinToString(", ") { it.name }})
VALUES ($file_guid, ${event.reference.event_number}, $software_id, ${event.period_number},
${event.run_number}, $parameterValuesStr)
ON CONFLICT (file_guid, event_number) DO UPDATE SET
software_id=$software_id, period_number=${event.period_number}, run_number=${event.run_number},
$parameterNamesEqValuesStr
""".trimIndent()
println(query)
val res3 = connEMD!!.createStatement().executeUpdate(query)
println("res of update is $res3")
}
connEMD!!.commit()
call.respond(HttpStatusCode.OK, "Success: ${events.size} event(s) were put")
} catch (err: PSQLException) {
if (err.toString().contains("The connection attempt failed.")) {
call.respond(HttpStatusCode.ServiceUnavailable, "Database connection failed: $err")
} else {
call.respond(HttpStatusCode.Conflict, "Database error: $err")
}
} catch (err: BadRequestException) {
call.respond(HttpStatusCode.UnprocessableEntity, "Error processing content: $err")
} catch (err: Exception) {
call.respond(HttpStatusCode.InternalServerError, "Error writing event data: $err")
} finally {
connEMD?.close()
}
}

delete("/${EVENT_ENTITY_API_NAME}") {

0 comments on commit 890d46f

Please sign in to comment.