From 86315e5a6cc49346f3ddbbb1ebc345e10bcec5c5 Mon Sep 17 00:00:00 2001 From: Yousef Moazzam Date: Thu, 23 Jul 2026 11:46:20 +0100 Subject: [PATCH 1/4] Add env vars for graphql endpoints --- README.md | 2 ++ frontend/.env.production | 2 ++ frontend/src/RelayEnvironment.ts | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 22737ad..29bcc21 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ VITE_KEYCLOAK_URL = "https://identity-test.diamond.ac.uk" VITE_KEYCLOAK_REALM = "dls" VITE_KEYCLOAK_CLIENT_ID = "ImagingHubDev" VITE_KEYCLOAK_SCOPE = "openid profile posix-uid email" +VITE_GRAPHQL_HTTP_ENDPOINT = "https://workflows.diamond.ac.uk/graphql" +VITE_GRAPHQL_WS_ENDPOINT = "wss://workflows.diamond.ac.uk/graphql/ws" ``` #### Install dependencies and generate code from GraphQL schema diff --git a/frontend/.env.production b/frontend/.env.production index d5b7add..8ac598f 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -3,3 +3,5 @@ VITE_KEYCLOAK_URL = "https://identity.diamond.ac.uk" VITE_KEYCLOAK_REALM = "dls" VITE_KEYCLOAK_CLIENT_ID = "ImagingHub" VITE_KEYCLOAK_SCOPE = "openid profile posix-uid email" +VITE_GRAPHQL_HTTP_ENDPOINT = "https://workflows.diamond.ac.uk/graphql" +VITE_GRAPHQL_WS_ENDPOINT = "wss://workflows.diamond.ac.uk/graphql/ws" diff --git a/frontend/src/RelayEnvironment.ts b/frontend/src/RelayEnvironment.ts index 7a2c57a..f13b6e7 100644 --- a/frontend/src/RelayEnvironment.ts +++ b/frontend/src/RelayEnvironment.ts @@ -11,8 +11,8 @@ import { import keycloak from "./keycloak"; import { createClient } from "graphql-ws"; -const HTTP_ENDPOINT = "https://workflows.diamond.ac.uk/graphql"; -const WS_ENDPOINT = "wss://workflows.diamond.ac.uk/graphql/ws"; +const HTTP_ENDPOINT = import.meta.env.VITE_GRAPHQL_HTTP_ENDPOINT; +const WS_ENDPOINT = import.meta.env.VITE_GRAPHQL_WS_ENDPOINT; const fetchFn: FetchFunction = async (request, variables) => { // Refresh token if needed (minValidity in seconds) From 2411c853cec63d0c21ccfea795d94dfb467f1982 Mon Sep 17 00:00:00 2001 From: Yousef Moazzam Date: Thu, 23 Jul 2026 12:00:47 +0100 Subject: [PATCH 2/4] Move from workflows subgraph to supergraph --- README.md | 4 +- frontend/.env.production | 4 +- frontend/relay.config.json | 2 +- frontend/src/supergraph.graphql | 1073 +++++++++++++++++++++++++++++++ frontend/src/workflows.graphql | 859 ------------------------- 5 files changed, 1078 insertions(+), 864 deletions(-) create mode 100644 frontend/src/supergraph.graphql delete mode 100644 frontend/src/workflows.graphql diff --git a/README.md b/README.md index 29bcc21..4e352d7 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ VITE_KEYCLOAK_URL = "https://identity-test.diamond.ac.uk" VITE_KEYCLOAK_REALM = "dls" VITE_KEYCLOAK_CLIENT_ID = "ImagingHubDev" VITE_KEYCLOAK_SCOPE = "openid profile posix-uid email" -VITE_GRAPHQL_HTTP_ENDPOINT = "https://workflows.diamond.ac.uk/graphql" -VITE_GRAPHQL_WS_ENDPOINT = "wss://workflows.diamond.ac.uk/graphql/ws" +VITE_GRAPHQL_HTTP_ENDPOINT = "https://graph-nightly.diamond.ac.uk/graphql" +VITE_GRAPHQL_WS_ENDPOINT = "wss://graph-nightly.diamond.ac.uk/graphql/ws" ``` #### Install dependencies and generate code from GraphQL schema diff --git a/frontend/.env.production b/frontend/.env.production index 8ac598f..347fe8f 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -3,5 +3,5 @@ VITE_KEYCLOAK_URL = "https://identity.diamond.ac.uk" VITE_KEYCLOAK_REALM = "dls" VITE_KEYCLOAK_CLIENT_ID = "ImagingHub" VITE_KEYCLOAK_SCOPE = "openid profile posix-uid email" -VITE_GRAPHQL_HTTP_ENDPOINT = "https://workflows.diamond.ac.uk/graphql" -VITE_GRAPHQL_WS_ENDPOINT = "wss://workflows.diamond.ac.uk/graphql/ws" +VITE_GRAPHQL_HTTP_ENDPOINT = "https://graph.diamond.ac.uk/graphql" +VITE_GRAPHQL_WS_ENDPOINT = "wss://graph.diamond.ac.uk/graphql/ws" diff --git a/frontend/relay.config.json b/frontend/relay.config.json index d84850e..bc961ce 100644 --- a/frontend/relay.config.json +++ b/frontend/relay.config.json @@ -3,7 +3,7 @@ "projects": { "tomography": { "language": "typescript", - "schema": "src/workflows.graphql", + "schema": "src/supergraph.graphql", "eagerEsModules": true } }, diff --git a/frontend/src/supergraph.graphql b/frontend/src/supergraph.graphql new file mode 100644 index 0000000..68a7249 --- /dev/null +++ b/frontend/src/supergraph.graphql @@ -0,0 +1,1073 @@ +type Artifact { + """The file name of the artifact""" + name: String! + + """The download URL for the artifact""" + url: Url! + + """The MIME type of the artifact data""" + mimeType: String! +} + +scalar Creator + +""" +Implement the DateTime scalar + +The input/output is a string in RFC3339 format. +""" +scalar DateTime + +""" +The `JSON` scalar type represents JSON values as specified by [ECMA-404](https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf). +""" +scalar JSON + +"""A scalar that can represent any JSON Object value.""" +scalar JSONObject + +"""A single log line streamed from a pod""" +type LogEntry { + """The log line content""" + content: String! + + """The name of the pod producing the log""" + podName: String! +} + +"""The root mutation of the service""" +type Mutation { + """submit specific workflow template""" + submitWorkflowTemplate(name: String!, visit: VisitInput!, parameters: JSON!): Workflow! + createExperimentDefinition(input: CreateExperimentDefinitionInput!): ExperimentDefinition + experimentDefinition(id: UUID!): ExperimentDefinitionMutations + createInstrumentSession(input: CreateInstrumentSessionInput!): InstrumentSession! + instrumentSession(proposalNumber: Int!, instrumentSessionNumber: Int!): InstrumentSessionMutations + createOrValidateSamples(input: CreateOrValidateSampleInput!): CreateSamplesResponse! + createSamples(input: CreateSampleInput!): [Sample!]! @deprecated(reason: "Will be replaced by createOrValidateSamples") + sample(sampleId: UUID!): SampleMutations +} + +"""Represents Relay Node types""" +union NodeValue = Workflow + +"""Information about pagination in a connection""" +type PageInfo { + """When paginating backwards, are there more items?""" + hasPreviousPage: Boolean! + + """When paginating forwards, are there more items?""" + hasNextPage: Boolean! + + """When paginating backwards, the cursor to continue.""" + startCursor: String + + """When paginating forwards, the cursor to continue.""" + endCursor: String +} + +"""The root query of the service""" +type Query { + node(id: ID!): NodeValue + + """Get a single [`Workflow`] by proposal, visit, and name""" + workflow(visit: VisitInput!, name: String!): Workflow! + + """Find all workflows available for a given visit""" + workflows(visit: VisitInput!, cursor: String, limit: Int, filter: WorkflowFilter): WorkflowConnection! + + """Retrieves a single cluster workflow template by name""" + workflowTemplate(name: String!): WorkflowTemplate! + + """ + Retrieves all cluster workflow templates with respective pagination data + """ + workflowTemplates(cursor: String, limit: Int, filter: WorkflowTemplatesFilter): WorkflowTemplateConnection! + jsonSchema(url: String!): JSONSchema + jsonSchemas(type: String = null, instrument: String = null): [JSONSchema!]! + experimentDefinition(id: UUID!): ExperimentDefinition + experimentDefinitions(instrumentSessions: [InstrumentSessionInput!]!, first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentDefinitionConnection + experiment(id: UUID!): Experiment + experiments(instrumentSessions: [InstrumentSessionInput!]!, first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentConnection + + """Get a proposal by its number""" + proposal(proposalNumber: Int!): Proposal + + """Get a list of proposals""" + proposals(first: Int = null, last: Int = null, after: String = null, before: String = null, sortBy: [ProposalSortInput!] = null, filterBy: ProposalFilterInput = null): ProposalConnection! + + """ + Get a proposal by its reference string e.g. 'MX12345'. The lookup is case-insensitive. + """ + proposalByReference(reference: String!): Proposal + + """Get a instrument session""" + instrumentSession(proposalNumber: Int!, instrumentSessionNumber: Int!): InstrumentSession + + """ + Get an instrument session by its reference string e.g. 'MX12345-1'. The lookup is case-insensitive. + """ + instrumentSessionByReference(reference: String!): InstrumentSession + + """Get a list of instrument sessions""" + instrumentSessions(proposalNumber: Int = null, proposalCategory: String = null, first: Int = null, last: Int = null, after: String = null, before: String = null, sortBy: [InstrumentSessionSortInput!] = null, filterBy: InstrumentSessionFilterInput = null): InstrumentSessionConnection! + + """Get an instrument by name""" + instrumentByName(name: String!): Instrument + + """Get an instrument by key""" + instrumentByKey(key: String!): Instrument + + """Get a list of instruments""" + instruments(scienceGroup: String = null): [Instrument!]! + + """Get an account""" + account(username: String!): Account + + """Get a sample by its id""" + sample(sampleId: UUID!): Sample + + """Get a list of samples associated with a given instrument session""" + samples(first: Int!, instrumentSessions: [InstrumentSessionInput!] = null, filter: SampleFilterInput! = {schemaUrl: null, createdTime: null, updatedTime: null, name: null, data: null}, before: String = null, after: String = null, last: Int = null, orderBy: SampleOrder! = {name: null, createdTime: null, updatedTime: null}): SampleConnection! +} + +"""Supported DLS science groups""" +enum ScienceGroup { + """Macromolecular Crystallography""" + MX + + """Workflows Examples""" + EXAMPLES + + """Magnetic Materials""" + MAGNETIC_MATERIALS + + """Soft Condensed Matter""" + CONDENSED_MATTER + + """Imaging and Microscopy""" + IMAGING + + """Biological Cryo-Imaging""" + BIO_CRYO_IMAGING + + """Structures and Surfaces""" + SURFACES + + """Crystallography""" + CRYSTALLOGRAPHY + + """Spectroscopy""" + SPECTROSCOPY +} + +"""The root mutation of the service""" +type Subscription { + """Processing to subscribe to logs for a single pod of a workflow""" + logs(visit: VisitInput!, workflowName: String!, taskId: String!): LogEntry! + + """Processing to subscribe to data for all workflows in a session""" + workflow(visit: VisitInput!, name: String!): Workflow! +} + +type Task { + """Unique name of the task""" + id: String! + + """Display name of the task""" + name: String! + + """Current status of a task""" + status: TaskStatus! + + """Parent of a task""" + depends: [String!]! + + """Children of a task""" + dependencies: [String!]! + + """Artifacts produced by a task""" + artifacts: [Artifact!]! + + """Node type - Pod, DAG, etc""" + stepType: String! + + """Start time for a task on a workflow""" + startTime: DateTime + + """End time for a task on a workflow""" + endTime: DateTime + + """ + A human readable message indicating details about why this step is in this condition + """ + message: String +} + +enum TaskStatus { + PENDING + RUNNING + SUCCEEDED + SKIPPED + FAILED + ERROR + OMITTED +} + +scalar Template + +"""Information about where the template is stored""" +type TemplateSource { + """The URL of the GitHub repository""" + repositoryUrl: String! + + """The path to the template within the repository""" + path: String! + + """The current tracked branch of the repository""" + targetRevision: String! +} + +""" +URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/) +""" +scalar Url + +"""A visit to an instrument as part of a session""" +type Visit { + """Project Proposal Code""" + proposalCode: String! + + """Project Proposal Number""" + proposalNumber: Int! + + """Session visit Number""" + number: Int! +} + +"""A visit to an instrument as part of a session""" +input VisitInput { + """Project Proposal Code""" + proposalCode: String! + + """Project Proposal Number""" + proposalNumber: Int! + + """Session visit Number""" + number: Int! +} + +type Workflow { + """The unique ID derived from the visit and name""" + id: ID! + + """The name given to the workflow, unique within a given visit""" + name: String! + + """The visit the Workflow was run against""" + visit: Visit! + + """The current status of the workflow""" + status: WorkflowStatus + + """The top-level workflow parameters""" + parameters: JSONObject + + """The name of the template used to run the workflow""" + templateRef: String + + """The workflow creator""" + creator: WorkflowCreator! +} + +type WorkflowConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [WorkflowEdge!]! + + """A list of nodes.""" + nodes: [Workflow!]! +} + +"""Information about the creator of a workflow.""" +type WorkflowCreator { + """ + An identifier unique to the creator of the workflow. + Typically this is the creator's Fed-ID. + """ + creatorId: String! +} + +"""An edge in a connection.""" +type WorkflowEdge { + """The item at the end of the edge""" + node: Workflow! + + """A cursor for use in pagination""" + cursor: String! +} + +"""All tasks in the workflow have errored""" +type WorkflowErroredStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""All tasks in the workflow have failed""" +type WorkflowFailedStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""All the supported Workflows filters""" +input WorkflowFilter { + """The status field for a workflow""" + workflowStatusFilter: WorkflowStatusFilter + + """The fedid of the user who created the workflow""" + creator: Creator + + """The name of the workflow template""" + template: Template +} + +type WorkflowPendingStatus { + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String +} + +type WorkflowRunningStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +"""The status of a workflow""" +union WorkflowStatus = WorkflowPendingStatus | WorkflowRunningStatus | WorkflowSucceededStatus | WorkflowFailedStatus | WorkflowErroredStatus + +"""Represents workflow status filters""" +input WorkflowStatusFilter { + pending: Boolean! = false + running: Boolean! = false + succeeded: Boolean! = false + failed: Boolean! = false + error: Boolean! = false +} + +"""All tasks in the workflow have succeded""" +type WorkflowSucceededStatus { + """Time at which this workflow started""" + startTime: DateTime! + + """Time at which this workflow completed""" + endTime: DateTime! + + """ + A human readable message indicating details about why the workflow is in this condition + """ + message: String + + """Tasks created by the workflow""" + tasks: [Task!]! +} + +type WorkflowTemplate { + """The name given to the workflow template, globally unique""" + name: String! + + """The group who maintains the workflow template""" + maintainer: String! + + """A human readable title for the workflow template""" + title: String + + """A human readable description of the workflow which is created""" + description: String + + """The repository storing the code associated with this template.""" + repository: String + + """A JSON Schema describing the arguments of a Workflow Template""" + arguments: JSON! + + """ + A JSON Forms UI Schema describing how to render the arguments of the Workflow Template + """ + uiSchema: JSON + + """Information about where the template is obtained from""" + templateSource: TemplateSource +} + +type WorkflowTemplateConnection { + """Information to aid in pagination.""" + pageInfo: PageInfo! + + """A list of edges.""" + edges: [WorkflowTemplateEdge!]! + + """A list of nodes.""" + nodes: [WorkflowTemplate!]! +} + +"""An edge in a connection.""" +type WorkflowTemplateEdge { + """The item at the end of the edge""" + node: WorkflowTemplate! + + """A cursor for use in pagination""" + cursor: String! +} + +"""Supported label filters for ClusterWorkflowTemplates""" +input WorkflowTemplatesFilter { + """The science group owning the template eg imaging""" + scienceGroup: [ScienceGroup!] +} + +"""A JSON schema""" +type JSONSchema { + """The identifier of the schema""" + id: String! + + """A URL from which the schema can be accessed""" + url: String! + + """The type of object the shema describes (if known)""" + type: String + + """The title of the schema""" + title: String + + """The version of the schema""" + version: String + + """The instrument the schema was created for""" + instrument: String + + """The description og the schema""" + description: String +} + +"""Values required to create an experiment definition""" +input CreateExperimentDefinitionInput { + name: String! + data: JSON! + dataSchemaUrl: String! + proposalNumber: Int! + instrumentSessionNumber: Int! +} + +"""Values required for the createExperiments mutation""" +input CreateExperimentsInput { + experiments: [ExperimentInput!]! +} + +type Experiment { + id: UUID! + name: String! + createdTime: DateTime! + updatedTime: DateTime! + experimentDefinition: ExperimentDefinition! + + """The sample that this experiment is associated with""" + sample: Sample! +} + +type ExperimentConnection { + edges: [ExperimentEdge!]! + pageInfo: PageInfo! +} + +type ExperimentDefinition { + id: UUID! + name: String! + createdTime: DateTime! + updatedTime: DateTime! + data: JSON! + dataSchemaUrl: String! + proposalNumber: Int! + instrumentSessionNumber: Int! + + """ + The instrument session that this experiment definition is associated with + """ + instrumentSession: InstrumentSession! + + """Experiments associated with this experiment definition""" + experiments: [Experiment!]! +} + +type ExperimentDefinitionConnection { + edges: [ExperimentDefinitionEdge!]! + pageInfo: PageInfo! +} + +type ExperimentDefinitionEdge { + cursor: String! + node: ExperimentDefinition! +} + +"""Mutations for a given experiment defintion""" +type ExperimentDefinitionMutations { + createExperiments(input: CreateExperimentsInput!): [Experiment!]! +} + +type ExperimentEdge { + cursor: String! + node: Experiment! +} + +"""Values required to create an experiment""" +input ExperimentInput { + name: String! + sampleId: UUID! +} + +type InstrumentSession { + instrumentSessionNumber: Int! + proposal: Proposal + + """Experiment Definitions""" + experimentDefinitions(first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentDefinitionConnection! + + """Experiments associated with this session""" + experiments(first: Int = null, last: Int = null, after: String = null, before: String = null): ExperimentConnection! + instrumentSessionId: Int! @deprecated(reason: "instrument_session_id is deprecated and will be removed in a future version.") + startTime: DateTime + endTime: DateTime + type: String + state: String + riskRating: String + + """ + A human-readable reference for this session in the form '-' e.g. 'MX12345-1'. + """ + instrumentSessionReference: String + instrument: Instrument! + roles: [InstrumentSessionRole!]! + + """Samples associated with a given instrument session""" + samples(first: Int!, filter: SampleFilterInput! = {schemaUrl: null, createdTime: null, updatedTime: null, name: null, data: null}, before: String = null, after: String = null, last: Int = null, orderBy: SampleOrder! = {name: null, createdTime: null, updatedTime: null}): SampleConnection! +} + +"""Values required to uniquely identify an instrument session""" +input InstrumentSessionInput { + proposalNumber: Int! + instrumentSessionNumber: Int! +} + +type Proposal { + proposalNumber: Int! + proposalCategory: String + title: String + summary: String + state: ProposalState! + + """ + A human-readable reference for this proposal in the form '' e.g. 'MX12345'. + """ + proposalReference: String + instrumentSessions: [InstrumentSession!]! + instruments: [Instrument!]! + roles: [ProposalAccount!]! +} + +type Sample { + id: UUID! + experiments: [Experiment!]! + name: String! + data: JSON! + createdTime: DateTime! + updatedTime: DateTime! + dataSchemaUrl: String! + + """Samples from which this sample is derived""" + parents(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! + + """Samples derived from this sample""" + children(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! + + """Events linked to this sample""" + events(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleEventConnection! + + """The JSON schema that the sample's `data` conforms to""" + dataSchema: JSON! + + """The instrument sessions that this sample is associated with""" + instrumentSessions: [InstrumentSession!]! + images: [SampleImage!]! +} + +scalar UUID + +type Account { + accountId: Int! @deprecated(reason: "account_id is deprecated and will be removed in a future version.") + username: String! + emailAddress: String + title: String + givenName: String + familyName: String + type: AccountType! + state: AccountState! + proposalRoles: [ProposalAccount!]! + instrumentSessionRoles: [InstrumentSessionRole!]! +} + +enum AccountState { + enabled + disabled +} + +enum AccountType { + user + staff + functional +} + +input CreateInstrumentSessionInput { + """Number of the proposal the session is for""" + proposalNumber: Int! + + """Name of the instrument the session is for""" + instrumentName: String! + + """ + Instrument Session information that isn't needed by the Session Service but should be passed through the UAS + """ + sessionInfo: String! +} + +input DateTimeFilterInput { + eq: DateTime = null + neq: DateTime = null + gt: DateTime = null + lt: DateTime = null + gte: DateTime = null + lte: DateTime = null +} + +type Instrument { + name: String! + key: String! + scienceGroup: String + description: String + proposals: [Proposal!]! + instrumentSessions: [InstrumentSession!]! +} + +type InstrumentSessionConnection { + edges: [InstrumentSessionEdge!]! + pageInfo: PageInfo! +} + +type InstrumentSessionEdge { + cursor: String! + node: InstrumentSession! +} + +input InstrumentSessionFilterInput { + instrumentSessionNumber: IntFilterInput = null + startTime: DateTimeFilterInput = null + endTime: DateTimeFilterInput = null + type: StringFilterInput = null + state: InstrumentSessionStateFilterInput = null + riskRating: StringFilterInput = null + proposalNumber: IntFilterInput = null +} + +type InstrumentSessionMutations { + instrumentSessionNumber: Int! + proposalNumber: Int! + + """Create or validate samples associated with this instrument session""" + createOrValidateSamples(input: CreateOrValidateSampleInputBase!): CreateSamplesResponse! +} + +type InstrumentSessionRole { + instrumentSession: InstrumentSession! + account: Account! + role: String! + onSite: Boolean! +} + +enum InstrumentSessionSortField { + instrumentSessionNumber + startTime + endTime + type + state + riskRating + proposalNumber +} + +input InstrumentSessionSortInput { + field: InstrumentSessionSortField! + orderBy: SortOrder! +} + +enum InstrumentSessionState { + CANCELLED + COMPLETED + FUTURE + IN_PROGRESS +} + +input InstrumentSessionStateFilterInput { + eq: InstrumentSessionState = null + neq: InstrumentSessionState = null +} + +input IntFilterInput { + eq: Int = null + neq: Int = null + gt: Int = null + lt: Int = null + gte: Int = null + lte: Int = null +} + +type ProposalAccount { + proposal: Proposal! + account: Account! + role: String! +} + +type ProposalConnection { + edges: [ProposalEdge!]! + pageInfo: PageInfo! +} + +type ProposalEdge { + cursor: String! + node: Proposal! +} + +input ProposalFilterInput { + proposalNumber: IntFilterInput = null + proposalCategory: StringFilterInput = null + title: StringFilterInput = null + state: ProposalStateFilterInput = null +} + +enum ProposalSortField { + proposalNumber +} + +input ProposalSortInput { + field: ProposalSortField! + orderBy: SortOrder! +} + +enum ProposalState { + OPEN + CLOSED + CANCELLED +} + +input ProposalStateFilterInput { + eq: ProposalState = null + neq: ProposalState = null +} + +enum SortOrder { + ASC + DESC +} + +input StringFilterInput { + eq: String = null + neq: String = null + contains: String = null + notContains: String = null + startsWith: String = null + endsWith: String = null +} + +input AddSampleEventInput { + description: String! +} + +input CreateOrValidateSampleInput { + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String! + + """Samples to be created""" + samples: [SampleIn!]! + + """ + Whether or not the provided samples should only be validated and not created + """ + validateOnly: Boolean! = false + + """Number of the proposal the samples should be associated with""" + proposalNumber: Int! + + """Number of the instrument session the samples should be associated with""" + instrumentSessionNumber: Int! +} + +input CreateOrValidateSampleInputBase { + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String! + + """Samples to be created""" + samples: [SampleIn!]! + + """ + Whether or not the provided samples should only be validated and not created + """ + validateOnly: Boolean! = false +} + +input CreateSampleInput { + proposalNumber: Int! + instrumentSessionNumber: Int! + samples: [SampleInLegacy!]! + validateOnly: Boolean! = false +} + +"""Return type when creating or validating samples""" +type CreateSamplesResponse { + """Whether the operation has succeeded without validation errors""" + success: Boolean! + + """Samples that have been created""" + samples: [Sample!]! + + """Errors that occurred during sample validation""" + errors: [SampleValidationError!]! +} + +input DatetimeOperatorInput { + """ + Will filter to items where the `DateTime` field is greater than (i.e. after) the provided value + """ + gt: DateTime = null + + """ + Will filter to items where the `DateTime` field is less than (i.e. before) the provided value + """ + lt: DateTime = null +} + +"""The details of sample validation error""" +type ErrorDetails { + """The type of error that occurred""" + type: String! + + """ + Tuple of strings identifying where in the sample schema the error occurred. + """ + location: [String!]! + + """A human readable error message.""" + message: String! +} + +input JSONOperator @oneOf { + stringOperator: StringOperatorInput = null + datetimeOperator: DatetimeOperatorInput = null + numericOperator: NumericOperatorInput = null +} + +input JSONOperatorInput { + """A JSON path specifying the value to filter. Must start with '$.'""" + path: String! + + """The operator to apply to the JSON field""" + operator: JSONOperator! +} + +input NumericOperatorInput { + """ + Will filter to items where the numeric field is greater than the provided value + """ + gt: Float = null + + """ + Will filter to items where the numeric field is less than the provided value + """ + lt: Float = null +} + +type SampleConnection { + edges: [SampleEdge!]! + pageInfo: PageInfo! +} + +type SampleEdge { + cursor: String! + node: Sample! +} + +type SampleEvent { + id: UUID! + timestamp: DateTime! + description: String! +} + +type SampleEventConnection { + edges: [SampleEventEdge!]! + pageInfo: PageInfo! +} + +type SampleEventEdge { + cursor: String! + node: SampleEvent! +} + +input SampleFilterInput { + """Filter on the `schemaUrl` field of `Sample`""" + schemaUrl: StringOperatorInput = null + + """Filter on the `createdTime` field of `Sample`""" + createdTime: DatetimeOperatorInput = null + + """Filter on the `createdTime` field of `Sample`""" + updatedTime: DatetimeOperatorInput = null + + """Filter on the `name` field of `Sample`""" + name: StringOperatorInput = null + + """Filter on the `data` field of `Sample`""" + data: [JSONOperatorInput!] = null +} + +type SampleImage { + url: String! + filename: String! +} + +input SampleIn { + """Name of the sample""" + name: String! + + """Data of the sample""" + data: JSON! +} + +input SampleInLegacy { + name: String! + data: JSON! + dataSchemaUrl: String! + parentIds: [Int!] = null + children: [SampleInLegacy!] = null +} + +type SampleMutations { + sampleId: UUID! + updateSample(input: UpdateSampleInput!): UpdateSampleResponse! + linkInstrumentSessionToSample(proposalNumber: Int!, instrumentSessionNumber: Int!): Void + addSampleEvent(sampleEvent: AddSampleEventInput!): SampleEvent! + createSampleImageUploadUrl(filename: String!, contentType: String!, contentLength: Int!): String! +} + +input SampleOrder { + name: SortingOrder = null + createdTime: SortingOrder = null + updatedTime: SortingOrder = null +} + +"""The details of errors occurred when validating a sample""" +type SampleValidationError { + """ + The index of the sample in CreateSampleInput.samples for which the error occurred + """ + index: Int! + + """Errors that occurred when validating the sample""" + errors: [ErrorDetails!]! +} + +enum SortingOrder { + ASC + DESC +} + +"""Conditions used to filter results based on the value of a String field""" +input StringOperatorInput { + """ + Will filter to items where the `String` field is equal to the provided value + """ + eq: String = null + + """ + Will filter to items where the `String` field is not equal to the provided value + """ + ne: String = null + + """ + Will filter to items where the `String` field is a member of the provided value + """ + in: [String!] = null + + """ + Will filter to items where the `String` field is not a member of the provided value + """ + nin: [String!] = null + + """ + Will filter to items where the `String` field is contains the provided value + """ + contains: String = null +} + +input UpdateSampleInput { + """Name of the sample""" + name: String + + """Data of the sample""" + data: JSON + + """URL of the JSON schema the samples' `data` should be validated against""" + dataSchemaUrl: String +} + +"""Return type when creating or validating samples""" +type UpdateSampleResponse { + """Whether the operation has succeeded without validation errors""" + success: Boolean! + + """Sample that has been updated""" + sample: Sample + + """Errors that occurred during sample validation""" + errors: [SampleValidationError!]! +} + +"""Represents NULL values""" +scalar Void \ No newline at end of file diff --git a/frontend/src/workflows.graphql b/frontend/src/workflows.graphql deleted file mode 100644 index be4cbf8..0000000 --- a/frontend/src/workflows.graphql +++ /dev/null @@ -1,859 +0,0 @@ -schema - @link(url: "https://specs.apollo.dev/link/v1.0") - @link(url: "https://specs.apollo.dev/join/v0.3", for: EXECUTION) -{ - query: Query - mutation: Mutation - subscription: Subscription -} - -directive @join__enumValue(graph: join__Graph!) repeatable on ENUM_VALUE - -directive @join__field(graph: join__Graph, requires: join__FieldSet, provides: join__FieldSet, type: String, external: Boolean, override: String, usedOverridden: Boolean) repeatable on FIELD_DEFINITION | INPUT_FIELD_DEFINITION - -directive @join__graph(name: String!, url: String!) on ENUM_VALUE - -directive @join__implements(graph: join__Graph!, interface: String!) repeatable on OBJECT | INTERFACE - -directive @join__type(graph: join__Graph!, key: join__FieldSet, extension: Boolean! = false, resolvable: Boolean! = true, isInterfaceObject: Boolean! = false) repeatable on OBJECT | INTERFACE | UNION | ENUM | INPUT_OBJECT | SCALAR - -directive @join__unionMember(graph: join__Graph!, member: String!) repeatable on UNION - -directive @link(url: String, as: String, for: link__Purpose, import: [link__Import]) repeatable on SCHEMA - -type Account - @join__type(graph: INSTRUMENT_SESSIONS) -{ - accountId: Int! - username: String! - emailAddress: String - title: String - givenName: String - familyName: String - type: AccountType! - state: AccountState! - proposalRoles: [ProposalAccount!]! - instrumentSessionRoles: [InstrumentSessionRole!]! -} - -enum AccountState - @join__type(graph: INSTRUMENT_SESSIONS) -{ - enabled @join__enumValue(graph: INSTRUMENT_SESSIONS) - disabled @join__enumValue(graph: INSTRUMENT_SESSIONS) -} - -enum AccountType - @join__type(graph: INSTRUMENT_SESSIONS) -{ - user @join__enumValue(graph: INSTRUMENT_SESSIONS) - staff @join__enumValue(graph: INSTRUMENT_SESSIONS) - functional @join__enumValue(graph: INSTRUMENT_SESSIONS) -} - -input AddSampleEventInput - @join__type(graph: SAMPLES) -{ - description: String! -} - -type Artifact - @join__type(graph: WORKFLOWS) -{ - """The file name of the artifact""" - name: String! - - """The download URL for the artifact""" - url: Url! - - """The MIME type of the artifact data""" - mimeType: String! -} - -input CreateOrValidateSampleInput - @join__type(graph: SAMPLES) -{ - """URL of the JSON schema the samples' `data` should be validated against""" - dataSchemaUrl: String! - - """Number of the proposal the samples should be associated with""" - proposalNumber: Int! - - """Number of the instrument session the samples should be associated with""" - instrumentSessionNumber: Int! - - """Samples to be created""" - samples: [SampleIn!]! - - """ - Whether or not the provided samples should only be validated and not created - """ - validateOnly: Boolean! = false -} - -input CreateSampleInput - @join__type(graph: SAMPLES) -{ - proposalNumber: Int! - instrumentSessionNumber: Int! - samples: [SampleInLegacy!]! - validateOnly: Boolean! = false -} - -"""Return type when creating or validating samples""" -type CreateSamplesResponse - @join__type(graph: SAMPLES) -{ - """Whether the operation has succeeded without validation errors""" - success: Boolean! - - """Samples that have been created""" - samples: [Sample!]! - - """Errors that occurred during sample validation""" - errors: [SampleValidationError!]! -} - -scalar Creator - @join__type(graph: WORKFLOWS) - -"""Date with time (isoformat)""" -scalar DateTime - @join__type(graph: INSTRUMENT_SESSIONS) - @join__type(graph: SAMPLES) - @join__type(graph: WORKFLOWS) - -input DatetimeOperatorInput - @join__type(graph: SAMPLES) -{ - """ - Will filter to items where the `DateTime` field is greater than (i.e. after) the provided value - """ - gt: DateTime = null - - """ - Will filter to items where the `DateTime` field is less than (i.e. before) the provided value - """ - lt: DateTime = null -} - -"""The details of sample validation error""" -type ErrorDetails - @join__type(graph: SAMPLES) -{ - """The type of error that occurred""" - type: String! - - """ - Tuple of strings identifying where in the sample schema the error occurred. - """ - location: [String!]! - - """A human readable error message.""" - message: String! -} - -type Instrument - @join__type(graph: INSTRUMENT_SESSIONS) -{ - name: String! - scienceGroup: String - description: String - proposals: [Proposal!]! - instrumentSessions: [InstrumentSession!]! -} - -type InstrumentSession - @join__type(graph: INSTRUMENT_SESSIONS) -{ - instrumentSessionId: Int! - instrumentSessionNumber: Int! - startTime: DateTime - endTime: DateTime - type: String - state: String - riskRating: String - proposal: Proposal - instrument: Instrument! - roles: [InstrumentSessionRole!]! -} - -type InstrumentSessionRole - @join__type(graph: INSTRUMENT_SESSIONS) -{ - instrumentSession: InstrumentSession! - account: Account! - role: String! - onSite: Boolean! -} - -scalar join__FieldSet - -enum join__Graph { - INSTRUMENT_SESSIONS @join__graph(name: "instrument_sessions", url: "https://instrument-sessions.diamond.ac.uk/api/graphql") - SAMPLES @join__graph(name: "samples", url: "https://sample-information.diamond.ac.uk/api/graphql") - WORKFLOWS @join__graph(name: "workflows", url: "https://workflows.diamond.ac.uk/graphql") -} - -""" -The `JSON` scalar type represents JSON values as specified by [ECMA-404](https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf). -""" -scalar JSON - @join__type(graph: SAMPLES) - @join__type(graph: WORKFLOWS) - @specifiedBy(url: "https://ecma-international.org/wp-content/uploads/ECMA-404_2nd_edition_december_2017.pdf") - -"""A scalar that can represent any JSON Object value.""" -scalar JSONObject - @join__type(graph: WORKFLOWS) - -scalar link__Import - -enum link__Purpose { - """ - `SECURITY` features provide metadata necessary to securely resolve fields. - """ - SECURITY - - """ - `EXECUTION` features provide metadata necessary for operation execution. - """ - EXECUTION -} - -"""A single log line streamed from a pod""" -type LogEntry - @join__type(graph: WORKFLOWS) -{ - """The log line content""" - content: String! - - """The name of the pod producing the log""" - podName: String! -} - -"""The root mutation of the service""" -type Mutation - @join__type(graph: SAMPLES) - @join__type(graph: WORKFLOWS) -{ - createSamples(input: CreateSampleInput!): [Sample!]! @join__field(graph: SAMPLES) @deprecated(reason: "Will be replaced by createOrValidateSamples") - createOrValidateSamples(input: CreateOrValidateSampleInput!): CreateSamplesResponse! @join__field(graph: SAMPLES) - sample(sampleId: UUID!): SampleMutations @join__field(graph: SAMPLES) - submitWorkflowTemplate(name: String!, visit: VisitInput!, parameters: JSON!): Workflow! @join__field(graph: WORKFLOWS) -} - -"""Information about pagination in a connection""" -type PageInfo - @join__type(graph: SAMPLES) - @join__type(graph: WORKFLOWS) -{ - """When paginating backwards, the cursor to continue.""" - startCursor: String - - """When paginating forwards, the cursor to continue.""" - endCursor: String - - """When paginating backwards, are there more items?""" - hasPreviousPage: Boolean! - - """When paginating forwards, are there more items?""" - hasNextPage: Boolean! -} - -type Proposal - @join__type(graph: INSTRUMENT_SESSIONS) -{ - proposalNumber: Int! - proposalCategory: String - title: String - summary: String - state: ProposalState! - instrumentSessions: [InstrumentSession!]! - instruments: [Instrument!]! - roles: [ProposalAccount!]! -} - -type ProposalAccount - @join__type(graph: INSTRUMENT_SESSIONS) -{ - proposal: Proposal! - account: Account! - role: String! -} - -enum ProposalState - @join__type(graph: INSTRUMENT_SESSIONS) -{ - Open @join__enumValue(graph: INSTRUMENT_SESSIONS) - Closed @join__enumValue(graph: INSTRUMENT_SESSIONS) - Cancelled @join__enumValue(graph: INSTRUMENT_SESSIONS) -} - -"""The root query of the service""" -type Query - @join__type(graph: INSTRUMENT_SESSIONS) - @join__type(graph: SAMPLES) - @join__type(graph: WORKFLOWS) -{ - """Get a proposal by its number""" - proposal(proposalNumber: Int!): Proposal @join__field(graph: INSTRUMENT_SESSIONS) - - """Get a list of proposals""" - proposals(proposalCategory: String = null): [Proposal!]! @join__field(graph: INSTRUMENT_SESSIONS) - - """Get a instrument session""" - instrumentSession(proposalNumber: Int!, instrumentSessionNumber: Int!): InstrumentSession @join__field(graph: INSTRUMENT_SESSIONS) - - """Get a instrument session""" - instrumentSessions(proposalNumber: Int = null, proposalCategory: String = null): [InstrumentSession!] @join__field(graph: INSTRUMENT_SESSIONS) - - """Get an instrument""" - instrument(instrumentName: String!): Instrument @join__field(graph: INSTRUMENT_SESSIONS) - - """Get a list of instruments""" - instruments(scienceGroup: String = null): [Instrument!]! @join__field(graph: INSTRUMENT_SESSIONS) - - """Get an account""" - account(username: String!): Account @join__field(graph: INSTRUMENT_SESSIONS) - - """Get a sample by its id""" - sample(sampleId: UUID!): Sample @join__field(graph: SAMPLES) - - """Get a list of samples associated with a given instrument session""" - samples(proposalNumber: Int!, instrumentSessionNumber: Int!, first: Int!, filter: SampleFilterInput! = {}, before: String = null, after: String = null, last: Int = null, orderBy: SampleOrder! = {}): SampleConnection! @join__field(graph: SAMPLES) - - """Get a single [`Workflow`] by proposal, visit, and name""" - workflow(visit: VisitInput!, name: String!): Workflow! @join__field(graph: WORKFLOWS) - workflows(visit: VisitInput!, cursor: String, limit: Int, filter: WorkflowFilter): WorkflowConnection! @join__field(graph: WORKFLOWS) - workflowTemplate(name: String!): WorkflowTemplate! @join__field(graph: WORKFLOWS) - workflowTemplates(cursor: String, limit: Int, filter: WorkflowTemplatesFilter): WorkflowTemplateConnection! @join__field(graph: WORKFLOWS) -} - -type Sample - @join__type(graph: SAMPLES) -{ - id: UUID! - name: String! - data: JSON! - createdTime: DateTime! - updatedTime: DateTime! - dataSchemaUrl: String! - - """Samples from which this sample is derived""" - parents(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! - - """Samples derived from this sample""" - children(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleConnection! - - """Events linked to this sample""" - events(first: Int = null, before: String = null, after: String = null, last: Int = null): SampleEventConnection! - - """The JSON schema that the sample's `data` conforms to""" - dataSchema: JSON! -} - -type SampleConnection - @join__type(graph: SAMPLES) -{ - edges: [SampleEdge!]! - pageInfo: PageInfo! -} - -type SampleEdge - @join__type(graph: SAMPLES) -{ - cursor: String! - node: Sample! -} - -type SampleEvent - @join__type(graph: SAMPLES) -{ - id: UUID! - timestamp: DateTime! - description: String! -} - -type SampleEventConnection - @join__type(graph: SAMPLES) -{ - edges: [SampleEventEdge!]! - pageInfo: PageInfo! -} - -type SampleEventEdge - @join__type(graph: SAMPLES) -{ - cursor: String! - node: SampleEvent! -} - -input SampleFilterInput - @join__type(graph: SAMPLES) -{ - """Filter on the `schemaUrl` field of `Sample`""" - schemaUrl: StringOperatorInput = null - - """Filter on the `createdTime` field of `Sample`""" - createdTime: DatetimeOperatorInput = null - - """Filter on the `createdTime` field of `Sample`""" - updatedTime: DatetimeOperatorInput = null - - """Filter on the `name` field of `Sample`""" - name: StringOperatorInput = null -} - -input SampleIn - @join__type(graph: SAMPLES) -{ - """Name of the sample""" - name: String! - - """Data of the sample""" - data: JSON! -} - -input SampleInLegacy - @join__type(graph: SAMPLES) -{ - name: String! - data: JSON! - dataSchemaUrl: String! - parentIds: [Int!] = null - children: [SampleInLegacy!] = null -} - -type SampleMutations - @join__type(graph: SAMPLES) -{ - sampleId: UUID! - linkInstrumentSessionToSample(proposalNumber: Int!, instrumentSessionNumber: Int!): Void - addSampleEvent(sampleEvent: AddSampleEventInput!): SampleEvent! -} - -input SampleOrder - @join__type(graph: SAMPLES) -{ - name: SortingOrder = null - createdTime: SortingOrder = null - updatedTime: SortingOrder = null -} - -"""The details of errors occurred when validating a sample""" -type SampleValidationError - @join__type(graph: SAMPLES) -{ - """ - The index of the sample in CreateSampleInput.samples for which the error occurred - """ - index: Int! - - """Errors that occurred when validating the sample""" - errors: [ErrorDetails!]! -} - -"""Supported DLS science groups""" -enum ScienceGroup - @join__type(graph: WORKFLOWS) -{ - """Macromolecular Crystallography""" - MX @join__enumValue(graph: WORKFLOWS) - - """Workflows Examples""" - EXAMPLES @join__enumValue(graph: WORKFLOWS) - - """Magnetic Materials""" - MAGNETIC_MATERIALS @join__enumValue(graph: WORKFLOWS) - - """Soft Condensed Matter""" - CONDENSED_MATTER @join__enumValue(graph: WORKFLOWS) - - """Imaging and Microscopy""" - IMAGING @join__enumValue(graph: WORKFLOWS) - - """Biological Cryo-Imaging""" - BIO_CRYO_IMAGING @join__enumValue(graph: WORKFLOWS) - - """Structures and Surfaces""" - SURFACES @join__enumValue(graph: WORKFLOWS) - - """Crystallography""" - CRYSTALLOGRAPHY @join__enumValue(graph: WORKFLOWS) - - """Spectroscopy""" - SPECTROSCOPY @join__enumValue(graph: WORKFLOWS) -} - -enum SortingOrder - @join__type(graph: SAMPLES) -{ - ASC @join__enumValue(graph: SAMPLES) - DESC @join__enumValue(graph: SAMPLES) -} - -"""Conditions used to filter results based on the value of a String field""" -input StringOperatorInput - @join__type(graph: SAMPLES) -{ - """ - Will filter to items where the `String` field is equal to the provided value - """ - eq: String = null - - """ - Will filter to items where the `String` field is not equal to the provided value - """ - ne: String = null - - """ - Will filter to items where the `String` field is a member of the provided value - """ - in: [String!] = null - - """ - Will filter to items where the `String` field is not a member of the provided value - """ - nin: [String!] = null - - """ - Will filter to items where the `String` field is contains the provided value - """ - contains: String = null -} - -"""The root mutation of the service""" -type Subscription - @join__type(graph: WORKFLOWS) -{ - """Processing to subscribe to logs for a single pod of a workflow""" - logs(visit: VisitInput!, workflowName: String!, taskId: String!): LogEntry! - - """Processing to subscribe to data for all workflows in a session""" - workflow(visit: VisitInput!, name: String!): Workflow! -} - -type Task - @join__type(graph: WORKFLOWS) -{ - """Unique name of the task""" - id: String! - - """Display name of the task""" - name: String! - - """Current status of a task""" - status: TaskStatus! - - """Parent of a task""" - depends: [String!]! - - """Children of a task""" - dependencies: [String!]! - - """Artifacts produced by a task""" - artifacts: [Artifact!]! - - """Node type - Pod, DAG, etc""" - stepType: String! - - """Start time for a task on a workflow""" - startTime: DateTime - - """End time for a task on a workflow""" - endTime: DateTime - - """ - A human readable message indicating details about why this step is in this condition - """ - message: String -} - -enum TaskStatus - @join__type(graph: WORKFLOWS) -{ - PENDING @join__enumValue(graph: WORKFLOWS) - RUNNING @join__enumValue(graph: WORKFLOWS) - SUCCEEDED @join__enumValue(graph: WORKFLOWS) - SKIPPED @join__enumValue(graph: WORKFLOWS) - FAILED @join__enumValue(graph: WORKFLOWS) - ERROR @join__enumValue(graph: WORKFLOWS) - OMITTED @join__enumValue(graph: WORKFLOWS) -} - -scalar Template - @join__type(graph: WORKFLOWS) - -""" -URL is a String implementing the [URL Standard](http://url.spec.whatwg.org/) -""" -scalar Url - @join__type(graph: WORKFLOWS) - -scalar UUID - @join__type(graph: SAMPLES) - -"""A visit to an instrument as part of a session""" -type Visit - @join__type(graph: WORKFLOWS) -{ - """Project Proposal Code""" - proposalCode: String! - - """Project Proposal Number""" - proposalNumber: Int! - - """Session visit Number""" - number: Int! -} - -"""A visit to an instrument as part of a session""" -input VisitInput - @join__type(graph: WORKFLOWS) -{ - """Project Proposal Code""" - proposalCode: String! - - """Project Proposal Number""" - proposalNumber: Int! - - """Session visit Number""" - number: Int! -} - -"""Represents NULL values""" -scalar Void - @join__type(graph: SAMPLES) - -type Workflow - @join__type(graph: WORKFLOWS) -{ - """The name given to the workflow, unique within a given visit""" - name: String! - - """The visit the Workflow was run against""" - visit: Visit! - - """The current status of the workflow""" - status: WorkflowStatus - - """The top-level workflow parameters""" - parameters: JSONObject - - """The name of the template used to run the workflow""" - templateRef: String - - """The workflow creator""" - creator: WorkflowCreator! -} - -type WorkflowConnection - @join__type(graph: WORKFLOWS) -{ - """Information to aid in pagination.""" - pageInfo: PageInfo! - - """A list of edges.""" - edges: [WorkflowEdge!]! - - """A list of nodes.""" - nodes: [Workflow!]! -} - -"""Information about the creator of a workflow.""" -type WorkflowCreator - @join__type(graph: WORKFLOWS) -{ - """ - An identifier unique to the creator of the workflow. - Typically this is the creator's Fed-ID. - """ - creatorId: String! -} - -"""An edge in a connection.""" -type WorkflowEdge - @join__type(graph: WORKFLOWS) -{ - """The item at the end of the edge""" - node: Workflow! - - """A cursor for use in pagination""" - cursor: String! -} - -"""All tasks in the workflow have errored""" -type WorkflowErroredStatus - @join__type(graph: WORKFLOWS) -{ - """Time at which this workflow started""" - startTime: DateTime! - - """Time at which this workflow completed""" - endTime: DateTime! - - """ - A human readable message indicating details about why the workflow is in this condition - """ - message: String - - """Tasks created by the workflow""" - tasks: [Task!]! -} - -"""All tasks in the workflow have failed""" -type WorkflowFailedStatus - @join__type(graph: WORKFLOWS) -{ - """Time at which this workflow started""" - startTime: DateTime! - - """Time at which this workflow completed""" - endTime: DateTime! - - """ - A human readable message indicating details about why the workflow is in this condition - """ - message: String - - """Tasks created by the workflow""" - tasks: [Task!]! -} - -"""All the supported Workflows filters""" -input WorkflowFilter - @join__type(graph: WORKFLOWS) -{ - """The status field for a workflow""" - workflowStatusFilter: WorkflowStatusFilter - - """The fedid of the user who created the workflow""" - creator: Creator - - """The name of the workflow template""" - template: Template -} - -type WorkflowPendingStatus - @join__type(graph: WORKFLOWS) -{ - """ - A human readable message indicating details about why the workflow is in this condition - """ - message: String -} - -type WorkflowRunningStatus - @join__type(graph: WORKFLOWS) -{ - """Time at which this workflow started""" - startTime: DateTime! - - """ - A human readable message indicating details about why the workflow is in this condition - """ - message: String - - """Tasks created by the workflow""" - tasks: [Task!]! -} - -"""The status of a workflow""" -union WorkflowStatus - @join__type(graph: WORKFLOWS) - @join__unionMember(graph: WORKFLOWS, member: "WorkflowPendingStatus") - @join__unionMember(graph: WORKFLOWS, member: "WorkflowRunningStatus") - @join__unionMember(graph: WORKFLOWS, member: "WorkflowSucceededStatus") - @join__unionMember(graph: WORKFLOWS, member: "WorkflowFailedStatus") - @join__unionMember(graph: WORKFLOWS, member: "WorkflowErroredStatus") - = WorkflowPendingStatus | WorkflowRunningStatus | WorkflowSucceededStatus | WorkflowFailedStatus | WorkflowErroredStatus - -"""Represents workflow status filters""" -input WorkflowStatusFilter - @join__type(graph: WORKFLOWS) -{ - pending: Boolean! = false - running: Boolean! = false - succeeded: Boolean! = false - failed: Boolean! = false - error: Boolean! = false -} - -"""All tasks in the workflow have succeded""" -type WorkflowSucceededStatus - @join__type(graph: WORKFLOWS) -{ - """Time at which this workflow started""" - startTime: DateTime! - - """Time at which this workflow completed""" - endTime: DateTime! - - """ - A human readable message indicating details about why the workflow is in this condition - """ - message: String - - """Tasks created by the workflow""" - tasks: [Task!]! -} - -type WorkflowTemplate - @join__type(graph: WORKFLOWS) -{ - """The name given to the workflow template, globally unique""" - name: String! - - """The group who maintains the workflow template""" - maintainer: String! - - """A human readable title for the workflow template""" - title: String - - """A human readable description of the workflow which is created""" - description: String - - """The repository storing the code associated with this template.""" - repository: String - - """A JSON Schema describing the arguments of a Workflow Template""" - arguments: JSON! - - """ - A JSON Forms UI Schema describing how to render the arguments of the Workflow Template - """ - uiSchema: JSON -} - -type WorkflowTemplateConnection - @join__type(graph: WORKFLOWS) -{ - """Information to aid in pagination.""" - pageInfo: PageInfo! - - """A list of edges.""" - edges: [WorkflowTemplateEdge!]! - - """A list of nodes.""" - nodes: [WorkflowTemplate!]! -} - -"""An edge in a connection.""" -type WorkflowTemplateEdge - @join__type(graph: WORKFLOWS) -{ - """The item at the end of the edge""" - node: WorkflowTemplate! - - """A cursor for use in pagination""" - cursor: String! -} - -"""Supported label filters for ClusterWorkflowTemplates""" -input WorkflowTemplatesFilter - @join__type(graph: WORKFLOWS) -{ - """The science group owning the template eg imaging""" - scienceGroup: [ScienceGroup!] -} \ No newline at end of file From 3653c8b54c1086d33c6aff7be3e5ae3afbb2f6a1 Mon Sep 17 00:00:00 2001 From: Yousef Moazzam Date: Fri, 24 Jul 2026 16:44:35 +0100 Subject: [PATCH 3/4] Add instrument session service query through supergraph Primarily using this as a means to check that querying the instrument session service through the supergraph works. --- frontend/relay.config.json | 8 +++++- .../src/components/SessionSelector.tsx | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/frontend/relay.config.json b/frontend/relay.config.json index bc961ce..8e76b35 100644 --- a/frontend/relay.config.json +++ b/frontend/relay.config.json @@ -5,10 +5,16 @@ "language": "typescript", "schema": "src/supergraph.graphql", "eagerEsModules": true + }, + "unified": { + "language": "typescript", + "schema": "src/supergraph.graphql", + "eagerEsModules": true } }, "sources": { - "tomography/src": "tomography" + "tomography/src": "tomography", + "unified/src": "unified" }, "excludes": ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"] } diff --git a/frontend/unified/src/components/SessionSelector.tsx b/frontend/unified/src/components/SessionSelector.tsx index 04404e8..4f6b4b0 100644 --- a/frontend/unified/src/components/SessionSelector.tsx +++ b/frontend/unified/src/components/SessionSelector.tsx @@ -6,6 +6,28 @@ import { Stack, } from "@mui/material"; import { useState } from "react"; +import { useLazyLoadQuery } from "react-relay"; +import { graphql } from "relay-runtime"; +import { SessionSelectorTestQuery as SessionSelectorTestQueryType } from "./__generated__/SessionSelectorTestQuery.graphql"; + +const testInstrumentSessionQuery = graphql` + query SessionSelectorTestQuery { + account(username: "twi18192") { + proposalRoles { + proposal { + proposalNumber + proposalCategory + title + instrumentSessions { + startTime + endTime + instrumentSessionNumber + } + } + } + } + } +`; enum SessionSelectionMode { Latest = "Latest", @@ -20,6 +42,11 @@ export const SessionSelector: React.FC = () => { const [sessionSelectionMode, setSessionSelectionMode] = useState(SessionSelectionMode.Latest); const [textInputValue, setTextInputValue] = useState(""); + const sessions = useLazyLoadQuery( + testInstrumentSessionQuery, + {} + ); + console.log("sessions: ", sessions); return ( From e4e0f7fda24dca8a9fb07114a2ea9acd57fb2d9f Mon Sep 17 00:00:00 2001 From: Yousef Moazzam Date: Fri, 24 Jul 2026 16:47:25 +0100 Subject: [PATCH 4/4] Bump version --- frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/package.json b/frontend/package.json index e74e293..e956d5c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "imaginghub", "private": true, - "version": "0.8.0", + "version": "0.8.1", "type": "module", "scripts": { "dev": "vite",