Skip to content

Commit 6fa7e14

Browse files
feat(graph-proxy): implement Node on Workflow for refetchable fragments
1 parent 3776db1 commit 6fa7e14

File tree

2 files changed

+68
-11
lines changed

2 files changed

+68
-11
lines changed

backend/graph-proxy/src/graphql/mod.rs

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ pub mod subscription_integration;
1717
use crate::RouterState;
1818

1919
use self::{
20-
subscription::WorkflowsSubscription, workflow_templates::WorkflowTemplatesQuery,
21-
workflows::WorkflowsQuery,
20+
subscription::WorkflowsSubscription,
21+
workflow_templates::WorkflowTemplatesQuery,
22+
workflows::{Workflow, WorkflowsQuery},
2223
};
2324
use async_graphql::{
24-
parser::parse_query, InputObject, MergedObject, MergedSubscription, Schema, SchemaBuilder,
25-
SimpleObject,
25+
parser::parse_query, Context, InputObject, MergedObject, MergedSubscription, Object, Schema,
26+
SchemaBuilder, SimpleObject, Union, ID,
2627
};
2728
use async_graphql_axum::{GraphQLRequest, GraphQLResponse};
2829
use axum::extract::State;
@@ -50,12 +51,61 @@ pub fn root_schema_builder() -> SchemaBuilder<Query, Mutation, Subscription> {
5051

5152
/// The root query of the service
5253
#[derive(Debug, Clone, Default, MergedObject)]
53-
pub struct Query(WorkflowsQuery, WorkflowTemplatesQuery);
54+
pub struct Query(NodeQuery, WorkflowsQuery, WorkflowTemplatesQuery);
55+
56+
/// Provides Relay node resolver for fetching any Node by ID.
57+
#[derive(Debug, Clone, Default)]
58+
pub struct NodeQuery;
59+
60+
#[Object]
61+
impl NodeQuery {
62+
async fn node(&self, ctx: &Context<'_>, id: ID) -> Option<NodeValue> {
63+
let id_str = id.to_string();
64+
let parts: Vec<&str> = id_str.split(':').collect();
65+
if parts.len() != 2 {
66+
return None;
67+
}
68+
let visit_display = parts[0];
69+
let workflow_name = parts[1];
70+
71+
let visit_input = match parse_visit_display(visit_display) {
72+
Some(v) => v,
73+
None => return None,
74+
};
75+
76+
let workflows_query = WorkflowsQuery;
77+
match workflows_query
78+
.workflow(ctx, visit_input, workflow_name.to_string())
79+
.await
80+
{
81+
Ok(workflow) => Some(NodeValue::Workflow(workflow)),
82+
Err(_) => None,
83+
}
84+
}
85+
}
86+
87+
/// Helper to parse VisitInput Display back into VisitInput
88+
fn parse_visit_display(display: &str) -> Option<VisitInput> {
89+
let re = regex::Regex::new(r"^([A-Za-z]+)(\d+)-(\d+)$").ok()?;
90+
let caps = re.captures(display)?;
91+
Some(VisitInput {
92+
proposal_code: caps[1].to_string(),
93+
proposal_number: caps[2].parse().ok()?,
94+
number: caps[3].parse().ok()?,
95+
})
96+
}
5497

5598
/// The root mutation of the service
5699
#[derive(Debug, Clone, Default, MergedObject)]
57100
pub struct Mutation(WorkflowTemplatesMutation);
58101

102+
/// Represents Relay Node types
103+
#[derive(Union)]
104+
enum NodeValue {
105+
/// A workflow node.
106+
Workflow(Workflow),
107+
}
108+
59109
/// The root mutation of the service
60110
#[derive(Debug, Clone, Default, MergedSubscription)]
61111
pub struct Subscription(WorkflowsSubscription);
@@ -131,13 +181,13 @@ impl Display for Visit {
131181

132182
/// A visit to an instrument as part of a session
133183
#[derive(Debug, Clone, InputObject)]
134-
struct VisitInput {
184+
pub struct VisitInput {
135185
/// Project Proposal Code
136-
proposal_code: String,
186+
pub proposal_code: String,
137187
/// Project Proposal Number
138-
proposal_number: u32,
188+
pub proposal_number: u32,
139189
/// Session visit Number
140-
number: u32,
190+
pub number: u32,
141191
}
142192

143193
impl From<VisitInput> for Visit {

backend/graph-proxy/src/graphql/workflows.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use argo_workflows_openapi::{
66
};
77
use async_graphql::{
88
connection::{Connection, CursorType, Edge, EmptyFields, OpaqueCursor},
9-
Context, Enum, Object, SimpleObject, Union,
9+
Context, Enum, Object, SimpleObject, Union, ID,
1010
};
1111
use aws_sdk_s3::presigning::PresigningConfig;
1212
use axum_extra::headers::{authorization::Bearer, Authorization};
@@ -69,6 +69,13 @@ impl Workflow {
6969

7070
#[Object]
7171
impl Workflow {
72+
/// The unique ID derived from the visit and name
73+
async fn id(&self) -> ID {
74+
let visit_display = self.metadata.visit.to_string();
75+
let unique_id = format!("{}:{}", visit_display, self.metadata.name);
76+
ID::from(unique_id)
77+
}
78+
7279
/// The name given to the workflow, unique within a given visit
7380
async fn name(&self) -> &str {
7481
&self.metadata.name
@@ -540,7 +547,7 @@ pub struct WorkflowsQuery;
540547
impl WorkflowsQuery {
541548
/// Get a single [`Workflow`] by proposal, visit, and name
542549
#[instrument(name = "graph_proxy_workflow", skip(self, ctx))]
543-
async fn workflow(
550+
pub async fn workflow(
544551
&self,
545552
ctx: &Context<'_>,
546553
visit: VisitInput,

0 commit comments

Comments
 (0)