Skip to content

Commit

Permalink
Implement /count API
Browse files Browse the repository at this point in the history
  • Loading branch information
pklimai committed Jan 4, 2025
1 parent b1524f3 commit 855f9da
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 13 deletions.
30 changes: 21 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,27 @@
## About

This software is part of the Event Metadata System (EMS), providing REST API and Web User Interface (UI) for the
Event Catalogue of an experiment on particle collisions. The PostgreSQL database is currently used as the event metadata storage. EMS supports integration with the [Unified Condition Database (UniConDa)](https://git.jinr.ru/nica_db/unidb_platform) containing run metadata of an experiment for fast pre-selection and KeyCloak identity provider ensuring cental authentification and authorization for users.
Event Catalogue of an experiment on particle collisions. The PostgreSQL database is currently used as the event metadata
storage. EMS supports integration with the [Unified Condition Database (UniConDa)](https://git.jinr.ru/nica_db/unidb_platform)
containing run metadata of an experiment for fast pre-selection and KeyCloak identity provider ensuring central
authentication and authorization for users.

## Deployment

### Setting up the configuration file

The system is configurable via YAML file for a particular experiment on particle collisions, including a set of specific event metadata. The configuration should be prepared in a file named `ems.config.yaml`. An example of the EMS configuration for the BM@N experiment can be seen in the `ems.bmn-config.yaml` file.
The system is configurable via YAML file for a particular experiment on particle collisions, including a set of specific
event metadata. The configuration should be prepared in a file named `ems.config.yaml`. An example of the EMS configuration
for the BM@N experiment can be seen in the `ems.bmn-config.yaml` file.

In the configuration file, you must provide credentials for the event database (Event Catalogue) and optionally for the Unified Condition database (if it is employed), optionally set KeyCloak server parameters, and specify URLs and event parameters (metadata) stored in the Event Catalogue.
In the configuration file, you must provide credentials for the event database (Event Catalogue) and optionally for the
Unified Condition database (if it is employed), optionally set KeyCloak server parameters, and specify URLs and event
parameters (metadata) stored in the Event Catalogue.

Supported event parameter types are currently: `int`, `float`, `string`, `bool`. Acceptable ranges for `int` and `float` values can be defined using `|` separator for both Web interface and API (for instance, `track-number=10|15`). The ranges are inclusive, that is start and end of the intervals are included. The intervals unbound from one side are also supported, for example, `track-number=10|` or `track-number=|15`.
Supported event parameter types are currently: `int`, `float`, `string`, `bool`. Acceptable ranges for `int` and `float`
values can be defined using `|` separator for both Web interface and API (for instance, `track-number=10|15`). The ranges
are inclusive, that is start and end of the intervals are included. The intervals unbound from one side are also
supported, for example, `track-number=10|` or `track-number=|15`.

### Run installation of the EMS interfaces on a RedHat-based Operating System (AlmaLinux, CentOS, RedHat)

Expand Down Expand Up @@ -107,6 +117,12 @@ Message body must contain the JSON list of events (only `reference:` part is req
optional and ignored, if present).


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

Returns number of records matching the request, in a form `{"count": number}`.


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

Expand All @@ -127,10 +143,6 @@ Message body example `{"software_id": 100, "software_version": "22.11"}`
Message body example `{"storage_id": 100, "storage_name": "data1"}`


#### 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[...]]]`

Expand Down Expand Up @@ -187,4 +199,4 @@ curl -X POST -u USER:PASS -H "Content-Type: application/json" http://127.0.0.1/e
```

Note: `software_version` and `storage_name` must exist in the corresponding EMS database tables.
The `file_path` will be created in the `file_` table, if not there yet.
The `file_path` will be automatically created in the `file_` table, if not there yet.
1 change: 1 addition & 0 deletions src/commonMain/kotlin/URLs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ const val SOFTWARE_URL = "/event_api/v1/software"
const val STORAGE_URL = "/event_api/v1/storage"
const val STATISTICS_URL = "/event_api/v1/statistics"
const val EVENT_ENTITY_API_NAME = "event"
const val EVENT_COUNT_API_NAME = "count"
33 changes: 31 additions & 2 deletions src/jvmMain/kotlin/Application.kt
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,12 @@ fun Application.main() {
// no role checking here - any user allowed
val parameterBundle = ParameterBundle.buildFromCall(call, page)
if (parameterBundle.hasInvalidParameters()) {
call.respond(HttpStatusCode.BadRequest)
call.respond(HttpStatusCode.BadRequest, "Invalid parameters detected in request")
return@get
}
var connEMD: Connection? = null
try {
connEMD = newEMDConnection(config, this.context)!!
// val softwareMap = getSoftwareMap(connEMD) // not used for now
val res = queryEMD(parameterBundle, page, connCondition, connEMD!!, null)
val lstEvents = ArrayList<EventRepr>()
while (res?.next() == true) {
Expand Down Expand Up @@ -328,6 +327,36 @@ fun Application.main() {
}
}

get("/${EVENT_COUNT_API_NAME}") {
val parameterBundle = ParameterBundle.buildFromCall(call, page)
if (parameterBundle.hasInvalidParameters()) {
call.respond(HttpStatusCode.BadRequest, "Invalid parameters detected in request")
return@get
}
var connEMD: Connection? = null
try {
connEMD = newEMDConnection(config, this.context)!!
val res = queryEMD(parameterBundle, page, connCondition, connEMD!!, null, countOnly = true)
if (res?.next() == true) {
call.respond(HttpStatusCode.OK, mapOf("count" to res.getInt(1)))
} else {
call.respond(HttpStatusCode.ServiceUnavailable, "Could not obtain event count from database")
}
} 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 obtaining event data: $err")
} finally {
connEMD?.close()
}
}

post("/${EVENT_ENTITY_API_NAME}") {
val roles = call.principal<WithRoles>()?.roles!!
// println("Roles in EVENT_ENTITY_API_NAME: $roles")
Expand Down
5 changes: 3 additions & 2 deletions src/jvmMain/kotlin/EventDBUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,14 @@ fun queryEMD(
connCondition: Connection?,
connEMD: Connection,
body: BODY?,
defaultLimit: Int? = null
defaultLimit: Int? = null,
countOnly: Boolean = false
): ResultSet? {
with(parameterBundle) {
val et = page.db_table_name
// TODO: Check how joins affect the performance. Consider doing DIY joins?
var query =
"""SELECT * FROM $et
"""SELECT ${if (countOnly) "count(1)" else "*"} FROM $et
INNER JOIN software_ ON $et.software_id = software_.software_id
INNER JOIN file_ ON $et.file_guid = file_.file_guid
INNER JOIN storage_ ON file_.storage_id = storage_.storage_id
Expand Down
4 changes: 4 additions & 0 deletions src/jvmMain/kotlin/models.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ fun EventReprForDelete.str(): String =
data class EventListRepr(
val events: Array<EventRepr>
)

data class EventCountRepr(
val count: Long
)
17 changes: 17 additions & 0 deletions src/jvmTest/kotlin/TestREST.kt
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,14 @@ class RestApiTest {
println(response.bodyAsText())
}

println("************************************************************")
println("Counting events")
response = authenticatedClient().get("$BASE_URL/count?period_number=$PERIOD&run_number=$RUN") {
contentType(ContentType.Application.Json)
}
val eventsCount1 = gson.fromJson(response.bodyAsText(), EventCountRepr::class.java)
println("Initially we had ${eventsCount1.count} events")

println("************************************************************")
println("Creating events 1, 2")
response = authenticatedClient().post("$BASE_URL/event") {
Expand All @@ -196,6 +204,15 @@ class RestApiTest {
println(response.status)
println(response.bodyAsText())

println("************************************************************")
println("Counting events")
response = authenticatedClient().get("$BASE_URL/count?period_number=$PERIOD&run_number=$RUN") {
contentType(ContentType.Application.Json)
}
val eventsCount2 = gson.fromJson(response.bodyAsText(), EventCountRepr::class.java)
println("After creation we have ${eventsCount2.count} events")
assertEquals(eventsCount1.count + 2, eventsCount2.count)

println("************************************************************")
println("Checking that events 1,2 are in catalogue and event 3 is not")
response = authenticatedClient().get("$BASE_URL/event?period_number=$PERIOD&run_number=$RUN") {
Expand Down

0 comments on commit 855f9da

Please sign in to comment.