Skip to content

Architecture

Note, should the following diagram code not render, copy it into the mermaid editor.

Overview

---
config:
    class:
        hideEmptyMembersBox: true
    theme: default
---
classDiagram
direction LR
    namespace s2gos_client {
        class api.AsyncClient
        class api.Client
        class gui.Client
        class cli
    }
    namespace s2gos_common {
        class models
        class service.Service
    }
    namespace s2gos_server {
        class server
        class routes
        class services.local.LocalService
        class services.airflow.AirflowService
    }

    cli ..> api.Client : use
    gui.Client ..|> api.Client : inherits
    api.Client ..> service.Service : uses
    api.AsyncClient ..> service.Service : uses
    service.Service ..> models : uses
    services.local.LocalService --|> service.Service : implements
    services.airflow.AirflowService --|> service.Service : implements
    routes ..> service.Service : uses
    server ..> routes : uses
    server ..> services.local.LocalService : can run with
    server ..> services.airflow.AirflowService : can run with

    note for gui.Client "will later inherit from AsyncClient"

S2GOS Client - GUI

Given here is the design used in package s2gos_client.gui.component. The package contains the code to generate widgets and panels from the JSON schema s2gos_common.models.InputDescription instances contained in a s2gos_common.models.ProcessDescription instance.

The ComponentContainer maps every InputDescription to a visual Component that is created for a given JSON schema.

---
config:
  class:
    hideEmptyMembersBox: true
  layout: elk
---
classDiagram
direction LR
    class models.InputDescription {
      title
      description
      schema
    }
    class ComponentContainer {
        \_\_init\_\_(input_descriptions)
      get_components()
      get_viewables()
    }
    ComponentContainer ..> ComponentFactoryRegistry : use
    ComponentContainer o--> models.InputDescription : 1..n by name
    ComponentContainer o--> Component : 1..n
    ComponentFactory ..> Component : create
    ComponentFactoryRegistry *--> ComponentFactory

A suitable ComponentFactory is selected for a given JSON schema and will create the Component when it is needed. The possible ComponentFactory instances are registered in a ComponentFactoryRegistry singleton.

---
config:
  class:
    hideEmptyMembersBox: true
  layout: dagre
---
classDiagram
direction TB
    class panel.viewable.Viewable {
        \_\_panel\_\_()
    }
    class Component {
        viewable
        json_codec
        _get_value_()
        _set_value_(val)
        _watch_value_(cb)
    }
    class WidgetComponent {
    }
    class ComponentFactory {
        _accept_(schema)
        _create_component_(schema)
    }
    class ComponentFactoryRegistry {
      register_factory(factory, type, format)
      find_factory(schema)
    }
    class ComponentFactoryBase {
        type
        format
        accept(schema)
    }
    class BooleanCF {
    }
    class IntegerCF {
    }
    class NumberCF {
    }
    class StringCF {
    }
    class DateCF {
    }
    class BboxCF {
    }
    Component <|-- WidgetComponent
    Component --> panel.viewable.Viewable : 1 
    ComponentFactory ..> Component : create
    ComponentFactoryRegistry *--> ComponentFactory : 0..N
    ComponentFactory <|-- ComponentFactoryBase
    ComponentFactoryBase <|-- BooleanCF
    ComponentFactoryBase <|-- IntegerCF
    ComponentFactoryBase <|-- NumberCF
    ComponentFactoryBase <|-- StringCF
    ComponentFactoryBase <|-- DateCF
    ComponentFactoryBase <|-- BboxCF

S2GOS Common

Given here is the design used in package s2gos_common.service.

classDiagram
direction TB
    class Service {
        get_conformance()
        get_capabilities()
        get_processes()
        get_process(process_id)
        execute_process(process_id, process_request)
        get_jobs()
        get_job(job_id)
        get_job_result(job_id)
    }
    class ProcessList {
    }
    class ProcessSummary {
        process_id
    }
    class ProcessDescription {
    }
    class ProcessRequest {
        inputs
        outputs
        response
        subscriber
    }
    class JobList {
    }
    class JobInfo {
        process_id
        job_id
        status
        progress
    }
    class JobResult {
    }
    class InputDescription {
        schema
    }
    class Description {
        title
        description
    }
    ProcessList *--> ProcessSummary : 0 .. N 
    ProcessSummary --|> Description
    ProcessDescription --|> ProcessSummary
    ProcessDescription *--> InputDescription : 0 .. N by name
    ProcessDescription *--> OutputDescription : 0 .. N by name
    InputDescription --|> Description
    OutputDescription --|> Description
    JobList *--> JobInfo : 0 .. N 
    Service ..> ProcessList : obtain
    Service ..> ProcessDescription : obtain
    Service ..> JobList : obtain
    Service ..> JobInfo : obtain
    Service ..> JobResult : obtain   
    Service ..> ProcessRequest : use      

Code generation

---
config:
  theme: default
---
flowchart LR
    openapi@{ shape: lean-r, label: "OpenAPI.yaml" }
    sync_client@{ shape: stadium, label: "s2gos_client.api.Client" }
    async_client@{ shape: stadium, label: "s2gos_client.api.AsyncClient" }
    models@{ shape: stadium, label: "s2gos_common.models.*" }
    service@{ shape: stadium, label: "s2gos_common.service.Service" }
    routes@{ shape: stadium, label: "s2gos_server.routes" }
    openapi --> generate
    generate --> gen-client
    generate --> gen-common
    generate --> gen-server
    gen-client --> sync_client
    gen-client --> async_client
    gen-common --> models
    gen-common --> service
    gen-server --> routes

Generating Airflow DAGs:

---
config:
  theme: default
---
flowchart LR
    local_service@{ shape: stadium, label: "s2gos_server.services.local.testing:service" }
    dags@{ shape: stadium, label: "s2gos_airflow/dags" }
    local_service --> gen-dags
    gen-dags --> dags