Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4f1d705
docs: add convertible-bond market-data design spec
MatthewHsu1 Jun 30, 2026
a78fae2
chore: stop tracking /docs (add to .gitignore)
MatthewHsu1 Jun 30, 2026
a790862
refactor: remove UserRepository and UsersBridge
MatthewHsu1 Jul 1, 2026
037e216
feat: implement TWSE MIS bond quote client and integrate into market …
MatthewHsu1 Jul 2, 2026
f6e1d0d
feat: add methods to retrieve all issuance terms and stock/bond quote…
MatthewHsu1 Jul 2, 2026
7905ba1
feat: add BondValuationSnapshot and BondValuationRefreshState entitie…
MatthewHsu1 Jul 2, 2026
d1a8b3d
feat: implement ValuationSnapshotRepository with upsert and retrieval…
MatthewHsu1 Jul 8, 2026
427c119
feat: implement BondValuationRefreshStateRepository with GetAsync and…
MatthewHsu1 Jul 8, 2026
ebdff13
feat: implement RefreshAllBondsUseCase and related interfaces, add Ta…
MatthewHsu1 Jul 8, 2026
4b6a55e
feat: add GetValuationsUseCase and corresponding tests for conversion…
MatthewHsu1 Jul 8, 2026
84e29b3
feat: enhance bond valuation features with new bridge and data source…
MatthewHsu1 Jul 13, 2026
43fffcc
feat(api): scaffold AppName.Api project and register central package …
MatthewHsu1 Jul 13, 2026
4376a3d
refactor(infra): switch persistence provider from SQLite to PostgreSQ…
MatthewHsu1 Jul 13, 2026
13c6a03
feat(api): add valuation/refresh endpoints, JWT auth stub, CORS, OpenAPI
MatthewHsu1 Jul 13, 2026
c4b7d7b
refactor(maui): make MAUI a DB-free API client (drop Infrastructure, …
MatthewHsu1 Jul 13, 2026
f272df2
test(infra): restore SQLite provider reference for tests after Npgsql…
MatthewHsu1 Jul 13, 2026
45ff103
fix(api): reject empty (not just null) JWT config values at startup
MatthewHsu1 Jul 13, 2026
8a63863
feat(infra): add user secrets and JSON configuration support for AppD…
MatthewHsu1 Jul 13, 2026
9f39dea
feat(infra): add YugabyteDB CA certificate handling and connection st…
MatthewHsu1 Jul 13, 2026
67cc16c
feat(infra): refactor infrastructure setup to use IConfiguration for …
MatthewHsu1 Jul 13, 2026
a3d4504
feat(auth): implement JWT authentication setup and configuration vali…
MatthewHsu1 Jul 13, 2026
1aa6924
feat(cors): add CORS policy registration extension method and refacto…
MatthewHsu1 Jul 13, 2026
800139a
feat(api): implement HTTP client configuration and authentication for…
MatthewHsu1 Jul 15, 2026
e951a76
feat(migrations): remove obsolete migration files and add new initial…
MatthewHsu1 Jul 15, 2026
5aee887
feat(database): update Npgsql.EntityFrameworkCore.PostgreSQL to versi…
MatthewHsu1 Jul 15, 2026
ccb9835
feat(ci): update MAUI build command to specify TargetFrameworks property
MatthewHsu1 Jul 15, 2026
39fbab0
feat(dependencies): add Microsoft.OpenApi package to project references
MatthewHsu1 Jul 15, 2026
730d5ba
feat(ci): update macOS runner to use Xcode 26.6 for iOS and MacCataly…
MatthewHsu1 Jul 15, 2026
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
16 changes: 16 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// .devcontainer/devcontainer.json
//
// Dev container for building the AppName.Maui Android head without an Android
// SDK / Java / MAUI workload on the host. Builds docker/android.Dockerfile,
// which carries the JDK, Android cmdline-tools, Node.js, and the
// `maui-android` workload.
{
"name": "AppName MAUI (Android, dotnet 10)",
"build": {
"dockerfile": "../docker/android.Dockerfile",
"context": ".."
},
"workspaceFolder": "/src",
"workspaceMount": "source=${localWorkspaceFolder},target=/src,type=bind",
"remoteUser": "root"
}
45 changes: 45 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,48 @@ jobs:

- name: Build
run: npm run build

# Builds the MAUI host (src/AppName.Maui) per target platform. Each platform's
# SDK is only available on certain OSes: iOS and MacCatalyst require Xcode on a
# macOS runner, Windows (WinUI) requires the Windows SDK on a windows runner, and
# Android can build anywhere but is included here too. This is why these builds
# can't run on the single ubuntu runner used by dotnet-tests above.
maui-build:
name: maui-build
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
tfm: net10.0-android
- os: windows-latest
tfm: net10.0-windows10.0.19041.0
- os: macos-26
tfm: net10.0-ios
- os: macos-26
tfm: net10.0-maccatalyst
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

- name: Select Xcode 26.6
if: runner.os == 'macOS'
run: sudo xcode-select -s /Applications/Xcode_26.6.app

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '10.0.x'

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
cache-dependency-path: src/web/package-lock.json

- name: Install MAUI workloads
run: dotnet workload restore src/AppName.Maui/AppName.Maui.csproj

- name: Build MAUI head (${{ matrix.tfm }})
run: dotnet build src/AppName.Maui/AppName.Maui.csproj -f ${{ matrix.tfm }} -c Release
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,10 @@ node_modules/
dist/
src/AppName.Maui/Resources/Raw/web/

# Docs (not tracked)
/docs/

# OS
.DS_Store

.playwright-cli
15 changes: 15 additions & 0 deletions AppName.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppName.Infrastructure.Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppName.Maui", "src\AppName.Maui\AppName.Maui.csproj", "{89E211BB-FCAF-4A7D-8252-314AE63C4930}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppName.Api", "src\AppName.Api\AppName.Api.csproj", "{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -115,6 +117,18 @@ Global
{89E211BB-FCAF-4A7D-8252-314AE63C4930}.Release|x64.Build.0 = Release|Any CPU
{89E211BB-FCAF-4A7D-8252-314AE63C4930}.Release|x86.ActiveCfg = Release|Any CPU
{89E211BB-FCAF-4A7D-8252-314AE63C4930}.Release|x86.Build.0 = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|x64.ActiveCfg = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|x64.Build.0 = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|x86.ActiveCfg = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Debug|x86.Build.0 = Debug|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|Any CPU.Build.0 = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|x64.ActiveCfg = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|x64.Build.0 = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|x86.ActiveCfg = Release|Any CPU
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -127,5 +141,6 @@ Global
{B2CEEB9C-E4D3-4578-ABF3-261A249E0611} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{0F95507E-D2E4-48DD-AB43-70A0E26195E9} = {0AB3BF05-4346-4AA6-1389-037BE0695223}
{89E211BB-FCAF-4A7D-8252-314AE63C4930} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
{A1F39E8D-3C9A-49A7-916A-A1F537AA8861} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
EndGlobalSection
EndGlobal
29 changes: 29 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Project conventions

## C# documentation & display naming

These rules apply **only to the C# project** (not the React/web code).

### XML documentation

- **Comment shape**: write every `<summary>` (and other XML doc tags) in the multi-line form — the opening tag, the text, and the closing tag each on their own `///` line. Do NOT collapse a summary onto a single line.

```csharp
/// <summary>
/// Number of shares the bond converts into (par value / conversion price).
/// </summary>
[DisplayName("Conversion Shares")]
public decimal ConversionShares { get; }
```

- **Interfaces**: every interface and its members (methods, properties, events) MUST have a concise `/// <summary>` describing the contract.
- **Model properties**: every property on a model/DTO MUST have a `/// <summary>`. Also document `enum` types and their values.
- **Implementations**: classes that implement an interface (or override/inherit a documented member) MUST use `/// <inheritdoc/>` on the implementing members instead of repeating the summary. Only write a fresh `/// <summary>` when the implementation adds behavior the interface doc doesn't cover.

### Display naming

- **Model properties**: every property on a model MUST carry a `[DisplayName("Human Readable Name")]` attribute so it renders correctly in UI, reports, exports, and filters.

## C# unit testing

- When writing, reviewing, or refactoring **C# unit tests**, follow [docs/TestingConventionGuidance.md](docs/TestingConventionGuidance.md) — it is the authoritative guidance for this solution's xUnit + Moq conventions (the nested `Fixture` pattern, when to mock vs. use a real/hand-written double, test naming, and file placement). Read it before adding or changing tests.
24 changes: 18 additions & 6 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
<Project>
<ItemGroup>
<!-- Versions added by later tasks. Example shape: -->
<!-- <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="..." /> -->
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="10.0.20" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="Microsoft.OpenApi" Version="2.11.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
<PackageVersion Include="Scalar.AspNetCore" Version="2.16.11" />
<PackageVersion Include="Scrutor" Version="7.0.0" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="10.0.20" />
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0" />
</ItemGroup>
</Project>
55 changes: 55 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Docker Android build

Build the `AppName.Maui` Android head without installing the Android SDK, Java,
or the MAUI workload on your host machine. Everything needed lives in
`docker/android.Dockerfile`; the repo is bind-mounted into the container so
NuGet restores and build outputs land directly in your working tree.

## Quick start

```bash
# 1. Build the image (slow the first time: downloads the .NET SDK base image,
# JDK, Node.js, and the Android cmdline-tools + platform/build-tools, then
# runs `dotnet workload install maui-android`).
just docker-android-image

# 2. Build the Android head.
just docker-build-android

# 3. Or just get a shell with the SDK/workload available.
just docker-shell
```

These map to plain `docker build` / `docker run` commands — see the
`# ---- Docker (Android) ----` section of the root `justfile` for the exact
commands if you want to run them without `just`.

## Where the APK lands

```
src/AppName.Maui/bin/Release/net10.0-android/
```

(Because the repo is bind-mounted at `/src` inside the container, this is the
same path on your host once the build finishes.)

## Caveats

- **Image size**: the image is several GB (dotnet SDK + JDK + Android SDK
components + Node.js). The first `docker-android-image` build will take a
while and use real disk space.
- **No emulator support**: this image does not set up an Android emulator.
Running one inside Docker needs `--device /dev/kvm` (hardware
virtualization passthrough) plus an AVD/system-image setup that isn't
included here. The supported workflow is to build the APK in the container
and deploy it to a **physical device** via `adb install` (or `adb` over
USB/network from the host, since `adb` itself isn't bundled in this image),
or to use `just docker-shell` to poke around interactively.
- **Android only**: this image and devcontainer cover the Android head only.
iOS, Mac Catalyst, and Windows heads need their native OS toolchains and are
not buildable in this Linux container — see the CI matrix
(`.github/workflows/ci.yml`) for how those are/aren't covered.
- **Versions may drift**: the Android cmdline-tools build number and
platform/build-tools versions pinned in `android.Dockerfile` will go stale
over time. See the comments at the top of the `ENV ANDROID_HOME=...` block
in the Dockerfile for how to find current values.
77 changes: 77 additions & 0 deletions docker/android.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# docker/android.Dockerfile
#
# Builds the AppName.Maui Android head WITHOUT installing the Android SDK,
# Java, or the MAUI workload on the host machine. Everything lives in this
# image; the repo is bind-mounted in at build/run time (see justfile recipes
# docker-android-image / docker-build-android / docker-shell).
#
# Usage (from repo root):
# just docker-android-image # build this image (slow the first time, several GB)
# just docker-build-android # build the Android head into the mounted repo
# just docker-shell # interactive shell with the SDK/workload available
#
# See docker/README.md for details and caveats (no emulator support here).

FROM mcr.microsoft.com/dotnet/sdk:10.0

# ---- OS packages -------------------------------------------------------------
# openjdk-17-jdk: required by the Android SDK build-tools / d8 / aapt2 toolchain.
# unzip/curl: needed to fetch and extract the Android cmdline-tools archive.
# Other bits (git, ca-certificates) are common build-time needs.
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
openjdk-17-jdk \
unzip \
curl \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# ---- Node.js 24 (NodeSource) --------------------------------------------------
# Matches the Node version used in CI (.github/workflows/ci.yml) and is needed
# because AppName.Maui.csproj's BuildReactApp MSBuild target runs `npm install`
# + `npm run build` in ../web as part of a normal (non-SkipReactBuild) build.
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*

# ---- Android SDK (command-line tools only; no Android Studio) ----------------
# NOTE: cmdline-tools version/build number changes periodically. The value
# below ("11076708", commandlinetools-linux package) was current as of late
# 2024/2025 cmdline-tools releases targeting compileSdk 35. If the download
# 404s, check https://developer.android.com/studio#command-line-tools-only
# for the latest "commandlinetools-linux-XXXXXXXX_latest.zip" build number
# and bump it here.
ENV ANDROID_HOME=/opt/android-sdk
ENV ANDROID_SDK_ROOT=${ANDROID_HOME}
ARG ANDROID_CMDLINE_TOOLS_VERSION=11076708
ARG ANDROID_PLATFORM=android-35
ARG ANDROID_BUILD_TOOLS=35.0.0

RUN mkdir -p ${ANDROID_HOME}/cmdline-tools \
&& curl -fsSL -o /tmp/cmdline-tools.zip \
"https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_CMDLINE_TOOLS_VERSION}_latest.zip" \
&& unzip -q /tmp/cmdline-tools.zip -d ${ANDROID_HOME}/cmdline-tools \
&& mv ${ANDROID_HOME}/cmdline-tools/cmdline-tools ${ANDROID_HOME}/cmdline-tools/latest \
&& rm /tmp/cmdline-tools.zip

ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}"

# Accept licenses, then install only what's needed to build net10.0-android
# (compileSdk 35, min API 21 per SupportedOSPlatformVersion in the csproj).
RUN yes | sdkmanager --licenses >/dev/null \
&& sdkmanager \
"platform-tools" \
"platforms;${ANDROID_PLATFORM}" \
"build-tools;${ANDROID_BUILD_TOOLS}"

# ---- .NET MAUI Android workload -----------------------------------------------
# This is the step that lets the container build the Android head without any
# host-side Android SDK install: the workload (incl. the Java/Android bindings)
# is installed straight into this image's dotnet install.
RUN dotnet workload install maui-android

WORKDIR /src
Loading
Loading