✈️ FlightPlan Developer Guide

← Back to Guide

Creating an OpsO App

This guide walks through creating a new OpsO Suite application from scratch using the DOM-based architecture.

Step 1: Create the Project

In Visual Studio, create a new "Class Library" project:

Step 2: Update the .csproj file

Click on the .csproj file, and edit it to match:


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
      <TargetFramework>net8.0-windows</TargetFramework>
      <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
      <BaseOutputPath>..\FlightPlan</BaseOutputPath>
  </PropertyGroup>

    <ItemGroup>
        <ProjectReference Include="..\FlightPlan.Core\FlightPlan.Core.csproj" />
        <ProjectReference Include="..\FlightPlan.OpsO\FlightPlan.OpsO.csproj" />
        <ProjectReference Include="..\PixieDust.SqlServer\PixieDust.SqlServer.csproj" />
    </ItemGroup>

</Project>

Step 3: Create App Structure

Create the following files:

3a. App Class

Extends OpsOWebAppBase<TBasePage>. Initialize, Shutdown, GetRoutes, RouteToPage, CreateNotFoundResponse, RedirectToLogin, and RedirectToAccessDenied are all inherited. Only define the four identity properties and the auth check in HandleRequest:

using FlightPlan.Core;
using FlightPlan.OpsO;

namespace FlightPlan.YourApp
{
    public class YourAppApp : OpsOWebAppBase<YourAppBasePage>
    {
        protected override string PagesNamespace => "FlightPlan.YourApp.Pages";
        public override string RoutePath => "/YourApp";
        public override string AppName => "Your Application Name";
        public override string Version => "1.0";

        public override HttpResponse HandleRequest(HttpRequest request)
        {
            var userSession = AuthService.GetCurrentUser(request, "YourApp");

            if (!userSession.IsAuthenticated)
                return RedirectToLogin(request, "YourApp");
            if (!userSession.Roles.Contains("read"))
                return RedirectToAccessDenied("YourApp");

            return RouteToPage(request);
        }
    }
}

3b. Base Page Class

Extends OpsOBasePage. Defines the navigation menu, optional footer contact, and any app-wide CSS. Individual pages set PageTitle in their own BuildContent().

using FlightPlan.Core.Dom.Styles;
using FlightPlan.OpsO;

namespace FlightPlan.YourApp
{
    public abstract class YourAppBasePage : OpsOBasePage
    {
        protected override void ConfigureMasterPage()
        {
            FooterText = "For assistance contact:";
            FooterLinkText = "Your Name";
            FooterLinkUrl = "mailto:your.email@gsk.com";

            MenuItems.Add(new MenuItem("Home", "/YourApp"));

            if (HasRole("admin"))
            {
                var adminMenu = new MenuItem("Admin", "#");
                adminMenu.SubItems.Add(new MenuItem("Manage Users", "/YourApp/ManageUsers"));
                MenuItems.Add(adminMenu);
            }
        }

        protected override void ConfigureAppStyles(StyleLibrary styles)
        {
        }
    }
}

3c. Landing Page

Every page extends the app's BasePage and implements BuildContent(). RequiredRole gates the page; use MudID, Roles, and HasRole() for user-aware content:

using FlightPlan.Core.Dom;
using FlightPlan.Core.Dom.Elements;

namespace FlightPlan.YourApp.Pages
{
    public class LandingPage : YourAppBasePage
    {
        protected override string RequiredRole => "read";

        protected override void BuildContent(ContainerElement content)
        {
            PageTitle = "Your Application";
            content.Add(new H1("Welcome to Your Application"));
            content.Add(new P($"Logged in as: {MudID}"));
        }
    }
}

3d. Adapter Class (optional)

Static class for database queries. Add only if the app uses a database:

using PixieDust.SqlServer;
using System.Text;

namespace FlightPlan.YourApp
{
    public static class YourAppAdapter
    {
        public static DbResults GetSomeData(string filter)
        {
            StringBuilder s = new StringBuilder();
            s.AppendLine("SELECT * FROM YourTable");
            s.AppendLine("WHERE category = @filter");
            DbQuery q = new DbQuery(s.ToString());
            q.AddParameter("@filter", filter);
            return q.ExecuteReader();
        }
    }
}

Step 4: Build the DLL

Build the project in Release (or Debug) configuration and note the output path of the compiled DLL.

Step 5: Register the App

In the FlightPlan Server, open Manage Apps and add a new entry. The server stores registrations in apps.json. Each entry looks like:

{
  "AssemblyPath": "C:\\path\\to\\FlightPlan.YourApp.dll",
  "ClassName": "FlightPlan.YourApp.YourAppApp",
  "RoutePath": "/YourApp",
  "Enabled": true,
  "DisplayOrder": 10
}

RoutePath must match the RoutePath property in your App class. ClassName must be the fully qualified class name.

Step 6: Database Security Setup

Add user permissions in the webuser_security table using: https://opso.gsk.com/Team/Access

Step 7: Test

Start (or restart) the server and navigate to /YourApp. You should see the landing page with the OpsO header, nav, and footer.

Architecture Notes

App Class Hierarchy

OpsO suite apps sit in a two-level inheritance chain:

Apps that have no security (e.g. public reference tools) extend WebAppBase directly and skip the OpsO layer.

Page Rendering Flow

Key DOM Element Classes

← Back to Guide

Please wait...