Skip to content

Commit c0b9ce1

Browse files
committed
Working named template registration/retrieval
Still can't do anything with the templates though. And they're not validated
1 parent 89bb0fa commit c0b9ce1

7 files changed

Lines changed: 176 additions & 34 deletions

.sqlx/query-f3b05af5405d08d1577c12fbc483d19c75504c917b65e59ae7f396508251079b.json renamed to .sqlx/query-15bdb26777072892c7feb712ae373379f45ec83a36351fc19353f8f48262c79e.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.sqlx/query-74ddd4d18b2b3529839b609905af032ed741abbd5f26fb9dd0c3079a313ef958.json

Lines changed: 0 additions & 12 deletions
This file was deleted.

.sqlx/query-b09ef7bc16a56728acaf3b4a9e899bf4d9e2893f77b02765f9c9712f74b0e56b.json

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
DROP TABLE templates;
1+
DROP VIEW beamline_template;
2+
DROP TABLE template;
Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
11
-- Add new table for additional templates
2-
CREATE TABLE templates (
2+
CREATE TABLE template (
33
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
4-
name TEXT NOT NULL CHECK (length(name) > 0),
5-
template TEXT NOT NULL CHECK (length(name) > 0),
6-
beamline INTEGER NOT NULL REFERENCES beamline(id) ON DELETE CASCADE ON UPDATE CASCADE
7-
)
4+
name TEXT NOT NULL,
5+
template TEXT NOT NULL,
6+
beamline INTEGER NOT NULL REFERENCES beamline(id) ON DELETE CASCADE ON UPDATE CASCADE,
7+
8+
CONSTRAINT duplicate_names UNIQUE (name, beamline) ON CONFLICT REPLACE,
9+
10+
CONSTRAINT empty_template CHECK (length(template) > 0),
11+
CONSTRAINT empty_name CHECK (length(name) > 0)
12+
);
13+
14+
CREATE VIEW beamline_template (beamline, name, template) AS
15+
SELECT
16+
beamline.name, template.name, template.template
17+
FROM beamline
18+
JOIN template
19+
ON beamline.id = template.beamline;

src/db_service.rs

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ use std::fmt;
1616
use std::marker::PhantomData;
1717
use std::path::Path;
1818

19-
pub use error::ConfigurationError;
2019
use error::NewConfigurationError;
20+
pub use error::{ConfigurationError, NamedTemplateError};
2121
use sqlx::sqlite::{SqliteConnectOptions, SqliteRow};
22-
use sqlx::{query, query_as, FromRow, QueryBuilder, Row, Sqlite, SqlitePool};
22+
use sqlx::{query_as, FromRow, QueryBuilder, Row, Sqlite, SqlitePool};
2323
use tracing::{info, instrument, trace};
2424

2525
use crate::paths::{
@@ -41,6 +41,15 @@ pub struct NamedTemplate {
4141
pub template: String,
4242
}
4343

44+
impl<'r> FromRow<'r, SqliteRow> for NamedTemplate {
45+
fn from_row(row: &'r SqliteRow) -> Result<Self, sqlx::Error> {
46+
Ok(Self {
47+
name: row.try_get("name")?,
48+
template: row.try_get("template")?,
49+
})
50+
}
51+
}
52+
4453
#[derive(Debug, PartialEq, Eq)]
4554
struct RawPathTemplate<F>(String, PhantomData<F>);
4655

@@ -347,37 +356,52 @@ impl SqliteScanPathService {
347356
.ok_or(ConfigurationError::MissingInstrument(instrument.into()))
348357
}
349358

350-
pub async fn additional_templates(
359+
pub async fn all_additional_templates(
351360
&self,
352361
beamline: &str,
353362
) -> Result<Vec<NamedTemplate>, ConfigurationError> {
354363
Ok(query_as!(
355364
NamedTemplate,
356-
"SELECT templates.name, templates.template
357-
FROM beamline JOIN templates ON beamline.id = templates.beamline
358-
WHERE beamline.name = ?",
365+
"SELECT name, template FROM beamline_template WHERE beamline = ?",
359366
beamline
360367
)
361368
.fetch_all(&self.pool)
362369
.await?)
363370
}
371+
pub async fn additional_templates(
372+
&self,
373+
beamline: &str,
374+
names: Vec<String>,
375+
) -> Result<Vec<NamedTemplate>, ConfigurationError> {
376+
let mut q =
377+
QueryBuilder::new("SELECT name, template FROM beamline_template WHERE beamline = ");
378+
q.push_bind(beamline);
379+
q.push(" AND name IN (");
380+
let mut name_query = q.separated(", ");
381+
for name in names {
382+
name_query.push_bind(name);
383+
}
384+
q.push(")");
385+
let query = q.build_query_as();
386+
Ok(query.fetch_all(&self.pool).await?)
387+
}
364388

365389
pub async fn register_template(
366390
&self,
367391
beamline: &str,
368392
name: String,
369393
template: String,
370-
) -> Result<(), ConfigurationError> {
371-
query!(
372-
"INSERT INTO templates (name, template, beamline)
373-
VALUES (?, ?, (SELECT id FROM beamline WHERE name = ?));",
394+
) -> Result<NamedTemplate, NamedTemplateError> {
395+
Ok(query_as!(
396+
NamedTemplate,
397+
"INSERT INTO template (name, template, beamline)
398+
VALUES (?, ?, (SELECT id FROM beamline WHERE name = ?)) RETURNING name, template;",
374399
name,
375400
template,
376401
beamline
377402
)
378-
.execute(&self.pool)
379-
.await?;
380-
Ok(())
403+
.fetch_one(&self.pool)
404+
.await?)
381405
}
382406

383407
/// Create a db service from a new empty/schema-less DB
@@ -407,7 +431,9 @@ impl fmt::Debug for SqliteScanPathService {
407431
}
408432

409433
mod error {
434+
410435
use derive_more::{Display, Error, From};
436+
use sqlx::error::ErrorKind;
411437

412438
#[derive(Debug, Display, Error, From)]
413439
pub enum ConfigurationError {
@@ -431,6 +457,46 @@ mod error {
431457
Self::MissingField(value.into())
432458
}
433459
}
460+
461+
#[derive(Debug, Display, Error)]
462+
pub enum NamedTemplateError {
463+
#[display("No configuration for beamline")]
464+
MissingBeamline,
465+
#[display("Template name was empty")]
466+
EmptyName,
467+
#[display("Template was empty")]
468+
EmptyTemplate,
469+
#[display("Error accessing named template: {_0}")]
470+
DbError(sqlx::Error),
471+
}
472+
473+
impl From<sqlx::Error> for NamedTemplateError {
474+
fn from(value: sqlx::Error) -> Self {
475+
match value {
476+
sqlx::Error::Database(err) => match (err.kind(), err.message().split_once(": ")) {
477+
(ErrorKind::NotNullViolation, Some((_, "template.beamline"))) => {
478+
NamedTemplateError::MissingBeamline
479+
}
480+
// pretty sure these two are not possible as strings can't be null
481+
(ErrorKind::NotNullViolation, Some((_, "template.name"))) => {
482+
NamedTemplateError::EmptyName
483+
}
484+
(ErrorKind::NotNullViolation, Some((_, "template.template"))) => {
485+
NamedTemplateError::EmptyTemplate
486+
}
487+
// Values are empty - these rely on the named checks in the schema
488+
(ErrorKind::CheckViolation, Some((_, "empty_name"))) => {
489+
NamedTemplateError::EmptyName
490+
}
491+
(ErrorKind::CheckViolation, Some((_, "empty_template"))) => {
492+
NamedTemplateError::EmptyTemplate
493+
}
494+
(_, _) => NamedTemplateError::DbError(sqlx::Error::Database(err)),
495+
},
496+
err => err.into(),
497+
}
498+
}
499+
}
434500
}
435501

436502
#[cfg(test)]

src/graphql/mod.rs

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use tracing::{debug, info, instrument, trace, warn};
4141
use crate::build_info::ServerStatus;
4242
use crate::cli::ServeOptions;
4343
use crate::db_service::{
44-
InstrumentConfiguration, InstrumentConfigurationUpdate, SqliteScanPathService,
44+
InstrumentConfiguration, InstrumentConfigurationUpdate, NamedTemplate, SqliteScanPathService,
4545
};
4646
use crate::numtracker::NumTracker;
4747
use crate::paths::{
@@ -302,6 +302,23 @@ impl CurrentConfiguration {
302302
}
303303
}
304304

305+
#[derive(Debug, InputObject)]
306+
struct TemplateInput {
307+
name: String,
308+
template: String,
309+
}
310+
311+
#[Object]
312+
impl NamedTemplate {
313+
async fn name(&self) -> &str {
314+
&self.name
315+
}
316+
317+
async fn template(&self) -> &str {
318+
&self.template
319+
}
320+
}
321+
305322
impl FieldSource<ScanField> for ScanPaths {
306323
fn resolve(&self, field: &ScanField) -> Cow<'_, str> {
307324
match field {
@@ -384,6 +401,24 @@ impl Query {
384401
.into_iter()
385402
.collect()
386403
}
404+
405+
#[instrument(skip(self, ctx))]
406+
async fn named_templates<'ctx>(
407+
&self,
408+
ctx: &Context<'ctx>,
409+
beamline: String,
410+
names: Option<Vec<String>>,
411+
) -> async_graphql::Result<Vec<NamedTemplate>> {
412+
check_auth(ctx, |policy, token| {
413+
policy.check_beamline_admin(token, &beamline)
414+
})
415+
.await?;
416+
let db = ctx.data::<SqliteScanPathService>()?;
417+
match names {
418+
Some(names) => Ok(db.additional_templates(&beamline, names).await?),
419+
None => Ok(db.all_additional_templates(&beamline).await?),
420+
}
421+
}
387422
}
388423

389424
#[Object]
@@ -451,6 +486,20 @@ impl Mutation {
451486
};
452487
CurrentConfiguration::for_config(db_config, nt).await
453488
}
489+
490+
#[instrument(skip(self, ctx))]
491+
async fn register_template<'ctx>(
492+
&self,
493+
ctx: &Context<'ctx>,
494+
beamline: String,
495+
template: TemplateInput,
496+
) -> async_graphql::Result<NamedTemplate> {
497+
check_auth(ctx, |pc, token| pc.check_beamline_admin(token, &beamline)).await?;
498+
let db = ctx.data::<SqliteScanPathService>()?;
499+
Ok(db
500+
.register_template(&beamline, template.name, template.template)
501+
.await?)
502+
}
454503
}
455504

456505
async fn check_auth<'ctx, Check, R>(ctx: &Context<'ctx>, check: Check) -> async_graphql::Result<()>

0 commit comments

Comments
 (0)