Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
4989cf6
chore: old diagram deprication
Lucki2g Oct 13, 2025
6f03fb2
feat: simple canvas movement. zoom, pan and scroll
Lucki2g Oct 13, 2025
efcac09
chore: disabled gutter for layout
Lucki2g Oct 13, 2025
1616db1
feat: add entity functionality & darktheme pane
Lucki2g Oct 13, 2025
eef0c8c
feat: initial attempt at managed identity setup for save/load of diag…
Lucki2g Oct 13, 2025
c742207
feat: inital attempt at managed identity authentication for digram sa…
Lucki2g Oct 14, 2025
423bd9f
fix: build error in deploy step caused by node_modules folder zipping…
Lucki2g Oct 14, 2025
70612d3
chore: lint failures
Lucki2g Oct 14, 2025
299e93d
feat: Diagram header toolbar with actions. Initial attempt at creatin…
Lucki2g Oct 14, 2025
fdd689a
chore: eslint...
Lucki2g Oct 14, 2025
03a5ebc
fix: removed SCM_DO_BUILD_DURING_DEPLOYMENT flag from bicep webservice
Lucki2g Oct 14, 2025
9e3e5dc
feat: load and save functionality, also download locally to device in…
Lucki2g Oct 15, 2025
ed749ea
feat: lock cloud buttons if not configured
Lucki2g Oct 15, 2025
9bb197b
chore: diagram information in the sidebar
Lucki2g Oct 15, 2025
a1db5c4
chore: implementation of list and rename of some functions
Lucki2g Oct 15, 2025
d08407b
chore: update badges and frontpage news
Lucki2g Oct 15, 2025
cc74108
fix: build error fix
Lucki2g Oct 15, 2025
55f555e
fix: replaced some queryparams for the load
Lucki2g Oct 15, 2025
743964d
fix: inconsistent envvariable naming.
Lucki2g Oct 15, 2025
8db109d
feat: local development using PAT and readme on it
Lucki2g Oct 15, 2025
bfea9e1
feat: diagramname textfield & graph state loading (zoom, pan etc)
Lucki2g Oct 16, 2025
bcb0c4f
chore: adjustments to the load modal (azure call moved to seperated s…
Lucki2g Oct 16, 2025
1ac3881
chore: refactored the group/entity accordion to seperate element and …
Lucki2g Oct 16, 2025
8d11897
feat: peak older version of loaded diagram
Lucki2g Oct 16, 2025
cb1c72e
feat: insert new entities in the center of the canvas
Lucki2g Oct 18, 2025
c1e19a4
feat: rightclick entity event to open contextmenu.
Lucki2g Oct 18, 2025
8d98e7a
chore: about update
Lucki2g Oct 18, 2025
96b2d55
feat: selection and multi selection of entities
Lucki2g Oct 20, 2025
7f99864
feat: selection via paper embeddings and custom class for it
Lucki2g Oct 21, 2025
b5e5946
fix: minor bug where old selections would be interactive in second se…
Lucki2g Oct 21, 2025
f289d7a
feat: Multiple entity selection and entity properties pane
Lucki2g Oct 22, 2025
88322ec
fix: disable entities already in the diagram when inserting from panes
Lucki2g Oct 22, 2025
c7a14b7
feat: remove entity in properties pane
Lucki2g Oct 22, 2025
4ab492a
feat: Selection properties and first simple gridlayout for entity sel…
Lucki2g Oct 22, 2025
5b811a2
chore: es lint error fixes
Lucki2g Oct 22, 2025
3c8e3ae
feat: initial libavoid router reimplementation with external worker t…
Lucki2g Oct 24, 2025
28c2ead
feat: 1-M M-1 indications on relationships. Click to see relationship…
Lucki2g Oct 25, 2025
4fcbef6
feat: selfreferencing relationship (minor bug where libavoid likes to…
Lucki2g Oct 25, 2025
bcf1f8a
fix: selection transoformation fixed to be relative to the paper tran…
Lucki2g Oct 25, 2025
55b7713
feat: show multiple relationships between entities in relationship link
Lucki2g Oct 26, 2025
bee1324
chore: refactor relationship logic to seperate file
Lucki2g Oct 26, 2025
0a44fb2
feat: control the exclude/include of relationships between entities. …
Lucki2g Oct 26, 2025
feb4efb
fix: load diagram fixes to looks and data states.
Lucki2g Oct 26, 2025
eb1e6cb
chore: small disclaimer warning
Lucki2g Oct 26, 2025
a52a957
feat: export diagram to png.
Lucki2g Oct 26, 2025
fc2c6d3
feat: complete removal of links and restoration of them.
Lucki2g Oct 27, 2025
1bc5583
fix: close sidepane if object is null.
Lucki2g Oct 27, 2025
b2486cb
feat: customizable labels for links.
Lucki2g Oct 27, 2025
cd12214
fix: removed text to show file metadata from load modal. (not availab…
Lucki2g Oct 27, 2025
40055ff
chore: ESLint & README
Lucki2g Oct 27, 2025
1347997
chore: removed branch selection in external pipeline
Lucki2g Oct 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion Infrastructure/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ param websitePassword string
@secure()
param sessionSecret string

param adoOrganizationUrl string = ''
param adoProjectName string = ''
param adoRepositoryName string = ''

var location = resourceGroup().location

@description('Create an App Service Plan')
Expand All @@ -23,6 +27,9 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2021-02-01' = {
resource webApp 'Microsoft.Web/sites@2021-02-01' = {
name: 'wa-${solutionId}'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
Expand All @@ -42,10 +49,24 @@ resource webApp 'Microsoft.Web/sites@2021-02-01' = {
name: 'WEBSITE_NODE_DEFAULT_VERSION'
value: '~20'
}
{
name: 'ADO_ORGANIZATION_URL'
value: adoOrganizationUrl
}
{
name: 'ADO_PROJECT_NAME'
value: adoProjectName
}
{
name: 'ADO_REPOSITORY_NAME'
value: adoRepositoryName
}
]
}
}
}

@description('Output the web app name')
@description('Output the web app name and managed identity info')
output webAppName string = webApp.name
output managedIdentityPrincipalId string = webApp.identity.principalId
output managedIdentityClientId string = webApp.identity.tenantId
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img src="https://raw.githubusercontent.com/delegateas/DataModelViewer/main/Website/public/DMVLOGOHORZ.svg" alt="logo" width="240" />
<img src="https://raw.githubusercontent.com/delegateas/DataModelViewer/main/Website/public/DMVLOGO.svg" alt="logo" width="120" />
</p>

<p align="center">
Expand Down Expand Up @@ -27,6 +27,8 @@

[Pipeline Setup](#settings-in-pipeline)

[ADO integration Setup](#setup-azuredevops-optional)

## Grouping
To create a group for a subset of tables, you must simply add a "#" at the start of your table description. See below:

Expand Down Expand Up @@ -71,6 +73,18 @@ Add `Website/.env` file to run this locally.
openssl rand -base64 32
```

## Setup AzureDevOps (optional)
In 2.2.0 you can integrate with ado to save/load diagrams. The 2.2.0 pipelines automatically creates a Managed Identity (MI) to the web service, so configuration is minimal to enable this integration.

1. Go to your Azure Dev Ops `Organization Settings > Users`.
2. Click `Add users` and search for `wa-<<WebsiteName>>` where `<<WebsiteName>>` is the environment variable you use for the website name.
3. Select the principle-user/MI from the search result, give `Basic` access level, add to correct ADO project, and UNTICK the `send email invites`.
4. Navigate to your ADO project.
5. Inside `Project Settings > Permissions > Users (at the top) > (Click on the MI) > Member of > Add > (e.g. Constributor)` (The MI should have at least read/write to your DataModelViewer repo)

> Note: Constributor gives additional unnecessary permissions. We suggest a least priviledges role.


## Running it
Generate data by running the Generator project from Visual Studio.
Afterwards go into the "Website"-folder from VS Code and open the terminal (of the "Command Prompt" type). If this the first time running it, type `npm install` (you need to have installed node.js first: https://nodejs.org/en/download/). Start the website on localhost by running `npm run dev`. Click the link in the terminal to view the website.
Expand All @@ -93,6 +107,7 @@ The pipeline expects a variable group called `DataModel`. It must have the follo
* (Optional) AdoWikiName: Name of your wiki found under "Overview -> Wiki" in ADO. (will be encoded so dont worry about space)
* (Optional) AdoWikiPagePath: Path to the introduction page you wish to show in DMV. (will also be encoded so dont worry about spaces)
* (Optional) WebResourceNameFunc: Function to fetch the entity logicalname from a webresource. The function must be a valid C# LINQ expression that works on the `name` input parameter. Default: `np(name.Split('/').LastOrDefault()).Split('.').Reverse().Skip(1).FirstOrDefault()`
* (Optional) AdoRepositoryName: Name of the existing repo to store diagrams. A folder will be created called `diagrams` first time being used.

## After deployment
* Go to portal.azure.com
Expand Down
3 changes: 3 additions & 0 deletions Website/.github/instructions/design/design.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ const ComponentName = ({ }: ComponentNameProps) => {
export default ComponentName;
```

# Index files
You must not create index.ts files for component folders.

208 changes: 208 additions & 0 deletions Website/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Data Model Viewer is a Next.js 15 application for visualizing Dataverse data models. It features an interactive diagram editor built on JointJS with custom routing using libavoid-js, Azure DevOps integration for persistence, and comprehensive metadata visualization.

## Development Commands

### Setup
```bash
npm i
```

Required environment variables in `.env.local`:
- `WebsitePassword` - Basic auth password
- `WebsiteSessionSecret` - Session encryption secret
- `ADO_PROJECT_NAME` - Azure DevOps project name
- `ADO_ORGANIZATION_URL` - Azure DevOps organization URL
- `ADO_REPOSITORY_NAME` - Repository name for diagram storage
- `AZURE_CLI_AUTHENTICATION_ENABLED` - Set to `true` for local dev
- `ADO_PAT` - Personal Access Token for Azure DevOps (generate at DevOps settings)

### Development
```bash
npm run dev # Start development server
npm run build # Build production bundle
npm run start # Start production server
npm run lint # Run ESLint
npm run prepipeline # Copy stub files (runs before pipeline build)
```

Note: The build process includes a `postbuild` script that creates required standalone directories for Next.js 15 deployment compatibility.

## Architecture

### Core Technology Stack
- **Next.js 15** with App Router and React 19
- **JointJS (@joint/core)** for diagram rendering and manipulation
- **libavoid-js** for intelligent relationship routing (runs in Web Worker)
- **MUI (Material-UI v7)** for UI components
- **Tailwind CSS 4** for styling
- **Azure DevOps REST API** for diagram persistence

### Context Providers (app/layout.tsx)
Application uses nested context providers in this order:
1. `AuthProvider` - Session management and Azure DevOps authentication
2. `SettingsProvider` - User preferences and UI settings
3. `DatamodelDataProvider` - Dataverse metadata (entities, relationships, attributes)
4. `SidebarProvider` - UI sidebar state
5. `SnackbarProvider` - Toast notifications

### Diagram Architecture

**Key Pattern**: Diagram uses JointJS for rendering with a Web Worker for routing calculations.

#### DiagramViewContext (`contexts/DiagramViewContext.tsx`)
- Central state management for the diagram canvas
- Maintains JointJS `dia.Graph` and `dia.Paper` instances
- Manages zoom, pan, entity selection, and entity lifecycle
- Provides actions: `addEntity`, `removeEntity`, `selectEntity`, `applySmartLayout`, etc.
- Tracks `entitiesInDiagram` Map for quick lookups

#### Custom JointJS Elements
**EntityElement** (`components/diagramview/diagram-elements/EntityElement.ts`):
- Custom JointJS element representing Dataverse entities
- Renders entity name, icon, and connection ports
- Stores `entityData` (EntityType) in attributes
- Uses custom `EntityElementView` for DOM interactions

**RelationshipLink** (`components/diagramview/diagram-elements/RelationshipLink.ts`):
- Custom JointJS link for entity relationships
- Stores `relationshipInformation` in attributes
- Supports both directed and undirected relationships
- Integrates with libavoid router for auto-routing

**Selection** (`components/diagramview/diagram-elements/Selection.ts`):
- Multi-entity selection boundary element
- Handles group transformations (move, scale, rotate)
- Calculates bounding boxes and applies transformations relative to paper matrix

#### Avoid Router (Orthogonal Routing)
**Location**: `components/diagramview/avoid-router/`

The diagram uses libavoid-js (C++ library compiled to WebAssembly) for intelligent orthogonal routing:

- **Web Worker** (`avoid-router/worker-thread/worker.ts`): Runs routing calculations off the main thread
- **AvoidRouter** (`avoid-router/shared/avoidrouter.ts`): Manages router state and communicates with worker
- **Initialization** (`avoid-router/shared/initialization.ts`): Sets up router with diagram graph

**Key Concept**: Main thread sends graph changes to worker, worker calculates routes using libavoid, results sent back to update link vertices.

#### Diagram Event Communication

**DiagramEventBridge** (`lib/diagram/DiagramEventBridge.ts`):
- Singleton pattern for cross-component communication
- Bridges JointJS (non-React) and React components
- Uses browser CustomEvents for type-safe messaging
- Event types: `selectObject`, `entityContextMenu`
- React components use `onSelectionEvent()` and `onContextMenuEvent()` convenience methods

**Pattern**: JointJS event listeners dispatch through DiagramEventBridge → React components listen via useEffect hooks.

### Serialization & Persistence

**DiagramSerializationService** (`lib/diagram/services/diagram-serialization.ts`):
- Converts JointJS graph to `SerializedDiagram` format
- Stores entity positions, sizes, zoom, pan state

**DiagramDeserializationService** (`lib/diagram/services/diagram-deserialization.ts`):
- Reconstructs JointJS graph from `SerializedDiagram`
- Recreates EntityElements and RelationshipLinks with proper routing

**AzureDevOpsService** (`app/api/services/AzureDevOpsService.ts`):
- Handles all Git operations for diagram storage
- Methods: `createFile`, `loadFile`, `listFiles`, `getVersions`
- Uses managed identity or PAT authentication

### Type System

**Core Types** (`lib/Types.ts`):
- `EntityType`: Dataverse entity metadata (attributes, relationships, security roles, keys)
- `RelationshipType`: N:1, 1:N, N:N relationship definitions
- `AttributeType`: Polymorphic attribute types (String, Lookup, Boolean, etc.)
- `SolutionType`, `SolutionComponentType`: Solution component tracking

**Diagram Types**:
- `SerializedDiagram` (`lib/diagram/models/serialized-diagram.ts`): Persistence format
- `SerializedEntity` (`lib/diagram/models/serialized-entity.ts`): Entity position/size/label
- `RelationshipInformation` (`lib/diagram/models/relationship-information.ts`): Relationship display data

### Component Organization

```
components/
diagramview/ # Diagram canvas and interactions
diagram-elements/ # Custom JointJS elements (EntityElement, RelationshipLink, Selection)
avoid-router/ # libavoid routing with worker thread
layout/ # SmartLayout for auto-arranging entities
panes/ # Side panels (entity list, properties)
modals/ # Dialogs (save, load, version history)
datamodelview/ # Metadata viewer for entities/attributes
entity/ # Entity detail components
attributes/ # Attribute type-specific renderers
insightsview/ # Analytics and reporting
shared/ # Reusable UI components (Layout, Sidebar)
```

### API Routes

All API routes are in `app/api/`:

**Authentication**:
- `POST /api/auth/login` - Password authentication
- `GET /api/auth/logout` - Session termination
- `GET /api/auth/session` - Session validation

**Diagram Operations**:
- `GET /api/diagram/list` - List saved diagrams from ADO
- `POST /api/diagram/load` - Load diagram JSON from ADO
- `POST /api/diagram/save` - Persist diagram to ADO Git repo
- `GET /api/diagram/versions` - Get version history for a diagram
- `POST /api/diagram/version` - Load specific version
- `GET /api/diagram/repository-info` - Get ADO repository details

**Other**:
- `POST /api/markdown` - Render markdown content
- `GET /api/version` - Application version info

## Key Development Patterns

### Adding Entities to Diagram
1. Get entity data from `DatamodelDataContext`
2. Call `diagramContext.addEntity(entityData, position)`
3. Context creates `EntityElement` via `createEntity()`
4. Element added to graph → triggers router update in worker
5. DiagramEventBridge dispatches selection event if needed

### Handling Entity Selection
1. User clicks entity → JointJS 'element:pointerclick' event
2. EntityElementView dispatches through DiagramEventBridge
3. React components listening via `diagramEvents.onSelectionEvent()`
4. PropertiesPanel updates to show entity details

### Relationship Routing Flow
1. Entity moved on canvas
2. DiagramViewContext detects change
3. Worker receives RouterRequestEvent.Change message
4. libavoid calculates new route avoiding obstacles
5. Worker returns updated vertices
6. Main thread updates link vertices on graph

### Working with Azure DevOps
Authentication uses either:
- **Local dev**: Azure CLI with PAT token (`AZURE_CLI_AUTHENTICATION_ENABLED=true`)
- **Production**: Managed Identity (`ManagedIdentityAuthService.ts`)

File operations always specify branch (default: 'main') and commit messages.

## Important Notes

- **Path aliases**: `@/*` maps to root directory (see `tsconfig.json`)
- **Next.js config**: Uses standalone output mode for containerized deployment
- **Worker thread**: libavoid runs in Web Worker - avoid blocking main thread with routing logic
- **Selection transformations**: Must be calculated relative to paper transformation matrix (see `Selection.ts:applyTransformation`)
- **Entity deduplication**: Always check `diagramContext.isEntityInDiagram()` before adding
- **JointJS integration**: Custom elements defined with `dia.Element.define()`, custom views with `dia.ElementView.extend()`
15 changes: 15 additions & 0 deletions Website/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Local developer setup
Create local file `.env.local`. Fill variables:
WebsitePassword=
WebsiteSessionSecret=
ADO_PROJECT_NAME=
ADO_ORGANIZATION_URL=
ADO_REPOSITORY_NAME=
AZURE_CLI_AUTHENTICATION_ENABLED=true
ADO_PAT=

### Node
Run `npm i`

### Authentication to Dev Ops
Go to a DevOps instance and create a PAT token for at least read/write to repos
Loading
Loading