Skip to content

🚀 [Feature]: Norwegian legal data, no account needed - #16

Merged
Marius Storhaug (MariusStorhaug) merged 7 commits into
build-lovdata-modulefrom
key-free-lovdata-surface
Aug 2, 2026
Merged

🚀 [Feature]: Norwegian legal data, no account needed#16
Marius Storhaug (MariusStorhaug) merged 7 commits into
build-lovdata-modulefrom
key-free-lovdata-surface

Conversation

@MariusStorhaug

@MariusStorhaug Marius Storhaug (MariusStorhaug) commented Aug 2, 2026

Copy link
Copy Markdown
Member

Norwegian legal data is now available from PowerShell with no account, no API key, and nothing to configure. This first release wraps Lovdata's open, key-free surface: the current acts and central regulations published as free open data under NLOD 2.0, plus the service endpoints that report whether the API is up and which build is deployed. Install the module and pull the full corpus of Norwegian acts in two commands.

New: List and download Lovdata's free open data packages

Get-LovdataPublicDataset lists the packages Lovdata publishes, each with the filename, size, and last-modified date so you can decide what to fetch. Save-LovdataPublicDataset downloads one to a directory, streaming large packages straight to disk with progress and refusing to overwrite an existing file unless you pass -Force. It takes a dataset from the pipeline, so listing and downloading compose directly:

Get-LovdataPublicDataset -FileName 'gjeldende-*' | Save-LovdataPublicDataset -Path './lovdata'

The NLOD 2.0 licence and the requirement to credit Lovdata are stated in the command help, not only the README.

New: Check service reachability and the deployed API version

Test-LovdataConnection returns a boolean and never throws on an unreachable service, so it is safe to use directly in a conditional as a first-run diagnostic. Get-LovdataApiVersion returns the deployment name, build timestamp, and revision. Both work with no account.

if (Test-LovdataConnection) { Get-LovdataApiVersion }

New: Session settings with nothing stored on disk

Get-LovdataConfig and Set-LovdataConfig expose the API base URI for the current session. The settings live in memory only; the module stores no secret and writes nothing to disk.

Removed: The API key surface, deferred to a later release

The commands that required a stored API key (Connect-LovdataAccount, Disconnect-LovdataAccount, Get-LovdataContext, Switch-LovdataContext, Get-LovdataLegalSource) are not part of this release. The authenticated surface and the credential store it needs return in #15. Because these commands never shipped in a published release, no upgrade step is needed.

Technical Details

  • Removed the Context dependency entirely: dropped the vault, the LovdataContext and LovdataLegalSource classes, Resolve-LovdataContext, Initialize-LovdataConfig, and all #Requires -Modules Context lines. The implementation is recoverable from build-lovdata-module history for Add the authenticated Lovdata API surface in a later release #15.
  • LovdataConfig keeps only ApiBaseUri; settings are seeded from a module default and held in the $script:Lovdata variable for the session.
  • Invoke-LovdataAPI no longer takes a -Context or injects X-API-Key; it reads the base URI from config, keeps the query-string builder, JSON-with-plain-text fallback, RFC 9457 error translation, and rate-limit verbose output, and drops the 401 branch while keeping 429 and the generic branch.
  • Added a separate streaming download helper, Invoke-LovdataDownload, beside Invoke-LovdataAPI so the JSON transport stays untouched; it uses Invoke-WebRequest -OutFile with Write-Progress.
  • New public classes LovdataPublicDataset (SizeBytes as [long], LastModified as [datetime]) and LovdataApiVersion.
  • The Pester 6 suite (Config, PublicData, Service, Lovdata.Internals) mocks the module-owned boundary and passes offline with no network and no key. Invoke-ScriptAnalyzer is clean against src.
  • Decisions not specified in the request: Save-LovdataPublicDataset -Path must point at an existing directory and throws a clear error otherwise (it does not create directories); the private download helper is named Invoke-LovdataDownload; ToString() on the new classes returns the filename and the name-plus-timestamp respectively; the examples emit status to the output stream rather than Write-Host to stay analyzer-clean.
  • Implementation plan progress: completes Add access to Lovdata's free open data packages #9 and the key-free part of Add service reachability, API version, and account commands #8, and the "remove the credential store" / "rework the suite" / "examples" tasks under Ship Lovdata's open, key-free API surface as the first release #2. Get-LovdataUser (/v1/userinfo) and everything else needing a key remain in Add the authenticated Lovdata API surface in a later release #15.
Related issues

Drop the Context vault, the stored contexts, the authenticated legal-source
command, and the API key injection so the module targets Lovdata's open,
key-free surface. Settings now live in memory for the session only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@MariusStorhaug Marius Storhaug (MariusStorhaug) added the minor New feature or enhancement, version 0.x.0 increase label Aug 2, 2026
Add Get-LovdataPublicDataset and Save-LovdataPublicDataset for Lovdata's free
open data packages, and Test-LovdataConnection and Get-LovdataApiVersion for
the open service endpoints. Introduce the LovdataPublicDataset and
LovdataApiVersion classes and a streaming download helper.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Cover Config, PublicData, and Service commands plus the internal transport and
download helpers with mocks at the module boundary, so the suite runs offline
with no network and no key.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Lead the README with the key-free story and Install-PSResource, replace the
context-based examples with open-data and service scenarios, and update
SECURITY.md to state that the module stores no secret.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…int clean

PlatyPS renders example command lines without a code fence, so a literal URL
in an example trips markdownlint MD034. Use a variable instead.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The API reports lastModified as an ISO-8601 Z string, but a bare [datetime]
cast converts it to the runner's local time, so the same package can report a
different calendar day depending on the machine's timezone. Parse with
InvariantCulture and RoundtripKind so the value stays UTC and machine-independent.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
FileName flowed unchecked into Join-Path and the download endpoint, so a value
like '../../evil.txt' wrote outside -Path, bypassed the overwrite check against
the un-normalised path, and escaped the URL segment. Validate it as a bare
filename at the parameter, which closes the path and the URL in one place.

Also resolve -Path with -LiteralPath and use its ProviderPath so a directory
name containing wildcard characters resolves correctly, and drop the module's
own static progress bar since Invoke-WebRequest -OutFile already renders one.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@MariusStorhaug

Copy link
Copy Markdown
Member Author

Reviewed and verified independently before merge.

The ValidateScript guard was checked against every traversal form I could think of, not just the one in the test: ../escape.txt, ..\escape.txt, /etc/passwd, C:\evil.txt, ., .., and sub/file.txt are all rejected, while gjeldende-lover.tar.bz2 and a..b.tar.bz2 still pass — worth confirming the second one, since a filename may legitimately contain .. without being a traversal.

The UTC parse returns 2026-08-01T01:31:00.0000000Z with Kind=Utc and compares equal to the expected [datetime], so LastModified is now machine-independent.

CI green on 152b4a7: 35 jobs, 31 success, 4 skipped.

@MariusStorhaug
Marius Storhaug (MariusStorhaug) marked this pull request as ready for review August 2, 2026 13:36
@MariusStorhaug
Marius Storhaug (MariusStorhaug) merged commit d6edce1 into build-lovdata-module Aug 2, 2026
46 checks passed
@MariusStorhaug
Marius Storhaug (MariusStorhaug) deleted the key-free-lovdata-surface branch August 2, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor New feature or enhancement, version 0.x.0 increase

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant