CongoBcc.ExchangeRate
1.0.1
dotnet add package CongoBcc.ExchangeRate --version 1.0.1
NuGet\Install-Package CongoBcc.ExchangeRate -Version 1.0.1
<PackageReference Include="CongoBcc.ExchangeRate" Version="1.0.1" />
<PackageVersion Include="CongoBcc.ExchangeRate" Version="1.0.1" />
<PackageReference Include="CongoBcc.ExchangeRate" />
paket add CongoBcc.ExchangeRate --version 1.0.1
#r "nuget: CongoBcc.ExchangeRate, 1.0.1"
#:package CongoBcc.ExchangeRate@1.0.1
#addin nuget:?package=CongoBcc.ExchangeRate&version=1.0.1
#tool nuget:?package=CongoBcc.ExchangeRate&version=1.0.1
Bcc.ExchangeRate
Fetches the Banque Centrale du Congo's (BCC's) published USD/CDF exchange rate, with a fallback to BCC's official X/Twitter account when the website is unreachable or its markup changes.
Origin
Extracted from an internal Cloud Run Job that scraped BCC and wrote straight into that product's own snapshot table, so other consumers needing the same rate don't have to re-implement, and separately maintain, the same fragile HTML scraper.
What moved here: the fetch + parse + fallback-orchestration logic only (BccExchangeRateClient,
BccFetcher, BccRateParser). What stayed behind, deliberately: persistence. Each consumer
has its own schema/domain model for storing a rate — baking a schema-specific write into this
package would defeat the point of sharing it. Call FetchLatestAsync, then persist the
BccExchangeRateResult however your own product does.
Note on naming: the client's original brief called this "BCDC," but the actual central bank being scraped is BCC (Banque Centrale du Congo) — BCDC (Banque Commerciale Du Congo) is a different, private bank. This package is named for the real source.
Note on the nuget.org package id: the C# namespace/assembly is Bcc.ExchangeRate
throughout — that's what every using statement below actually is. The nuget.org PackageId is
CongoBcc.ExchangeRate instead, because nuget.org rejects Bcc.ExchangeRate outright (409,
"The package ID is reserved" — an unrelated company already holds a formal ID Prefix Reservation
on "Bcc," see BccCode.*/BccPay.* on nuget.org; nothing to do with the Congolese central bank,
just an unlucky collision). PackageId/AssemblyName are independent properties in NuGet, so
only the csproj's <PackageId> needed to change — no C# code anywhere references the package id
directly.
Usage
using Bcc.ExchangeRate;
using var client = new BccExchangeRateClient(); // or new BccExchangeRateClient(httpClient) with your own HttpClient
var result = await client.FetchLatestAsync(new BccExchangeRateOptions
{
// XBearerToken = "...", // optional — makes the X fallback actually reliable
});
Console.WriteLine($"1 USD = {result.RateUsdToCdf} CDF (source: {result.SourceDetail})");
FetchLatestAsync throws InvalidOperationException (naming both underlying errors) if neither
the website nor the X fallback produced a parseable rate — a missing rate surfaces loudly, it is
never silently reported as success.
Building and packing locally
No private registry is wired up yet — for now this ships as a .nupkg dropped into a shared local
folder feed that consumers point their own NuGet.Config at:
dotnet pack src/Bcc.ExchangeRate -c Release -o /path/to/local/feed
(GeneratePackageOnBuild is already on, so a plain dotnet build -c Release produces the same
package under src/Bcc.ExchangeRate/bin/Release/.)
Revisit: once this is trusted by more consumers, or once a consuming repo's CI needs it without
a manual copy step, push it to a real registry instead (GitLab Package Registry is a natural fit if
consumers are on gitlab.com) rather than continuing to hand-copy a .nupkg around.
Publishing to nuget.org
CongoBcc.ExchangeRate is unclaimed on nuget.org as of this writing (checked directly against
the registry — Bcc.ExchangeRate, the first choice, is not: see the naming note above).
nuget.org is public and effectively permanent — anyone can install
this package once pushed, and a version can be unlisted (hidden from search/new installs) but
never fully deleted. Worth being sure this is the audience you want before the first push, not
after — though the package itself has nothing sensitive in it (no secrets, no consumer-specific
business logic; see "What moved here" above).
Already done, ready before either publishing path below: the package is MIT-licensed
(LICENSE at the repo root, PackageLicenseExpression in the csproj) and includes its own
README — nuget.org's "missing license/readme" warnings are gone as of the last dotnet pack.
Path A — GitLab CI/CD, Trusted Publishing (no stored secret, current setup)
This repo's .gitlab-ci.yml publishes via nuget.org's
Trusted Publishing — GitLab
issues a short-lived OIDC token per pipeline run, nuget.org exchanges it for a 1-hour API key,
and that key is what actually pushes. No API key is ever stored in GitLab or committed anywhere.
One-time setup, then repeatable for every future version with no credential to rotate:
- ✅ Done — this repo lives at
thubadigital/bcc-fx-rate. - On nuget.org: your username (top right) → Trusted Publishing → Add a new policy →
choose GitLab. The exact field labels on that form weren't spelled out anywhere I could
check directly (only a screenshot in Microsoft's own docs, not transcribable text) — match by
meaning against GitLab's own OIDC claim names (
namespace_path/project_path), which is what nuget.org validates the token against either way:- namespace path:
thubadigital(the group that owns the project) - project path:
thubadigital/bcc-fx-rate(the full path) - Scopes: Push new packages and package versions, glob
CongoBcc.ExchangeRate* - Leave environment and ref blank unless you want to restrict which branch/environment can publish. If a field doesn't take the value as shown, it's a labeling difference, not a wrong value — the group/project path above is what identifies this repo either way.
- namespace path:
- In the GitLab project: Settings → CI/CD → Variables → add
NUGET_USERNAME(your nuget.org profile name — not your email address). Not a secret, but keeping it out of the committed YAML means changing accounts later doesn't need a code change. - Tag a release matching the version in
Bcc.ExchangeRate.csproj(v1.0.0for the current one) and push the tag:git tag v1.0.0 && git push origin v1.0.0. - In GitLab → CI/CD → Pipelines, find the pipeline that tag triggered and click Run on
the
publish_nugetjob — it'swhen: manualdeliberately, so nothing publishes to a public, permanent registry as a side effect of an ordinary push. - On the next real version, bump
<Version>in the csproj, tagvX.Y.Zto match, push, run the job again — no setup repeated.
A private GitLab repo's Trusted Publishing policy starts in a 7-day "pending" state until the first successful publish provides GitLab's repo/owner IDs to nuget.org (prevents a delete-and-recreate-the-repo attack on the policy) — normal, not an error; just don't let more than 7 days pass without running the job at least once.
Path B — manual push with an API key (no GitLab setup, one-off)
Useful if you want a version out today without wiring up CI/CD Path A above, or as a fallback if the pipeline itself is ever broken:
- Create an account at nuget.org (or sign in) and verify your email — pushing is blocked until it's verified.
- Generate an API key: account menu → API Keys → Create. Shortest expiration offered,
scope Push new packages and package versions, glob
CongoBcc.ExchangeRate*. Copy it immediately — shown once. - Push both packages (already built at
src/Bcc.ExchangeRate/bin/Release/— see "Building and packing locally" above; the files are named for the PackageId, not the project folder):
The matchingdotnet nuget push src/Bcc.ExchangeRate/bin/Release/CongoBcc.ExchangeRate.1.0.0.nupkg \ --api-key <your-api-key> --source https://api.nuget.org/v3/index.json.snupkg(debug symbols) is discovered and pushed automatically alongside it. - Delete the API key from nuget.org afterward — no reason to leave even a short-lived one active once the push succeeded.
Never commit an API key anywhere (not in this repo, not in either consumer, not in GitLab CI/CD variables) — Path A exists specifically so a stored key is never needed at all.
After either path
It appears at https://www.nuget.org/packages/CongoBcc.ExchangeRate within a few minutes
(indexing isn't instant). From there, both consuming repos can drop the local-packages source
from their own NuGet.Config and pull straight from nuget.org instead.
Status
The upstream scraper this was extracted from is still staging-only as of the extraction date —
BCC's page markup and the regexes matching it (BccRateParser) are not yet proven reliable in
production for any consumer. Treat a FetchLatestAsync failure as an expected, not exceptional,
occurrence for now, and keep tests/.../BccRateParserTests.cs (which run offline, no network) as
the first thing to extend if BCC changes their page format.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.