Skip to content

feat(wikibase-integration): implement instance config management#140

Merged
DaxServer merged 1 commit intomainfrom
wikibase-integration-task4
Aug 17, 2025
Merged

feat(wikibase-integration): implement instance config management#140
DaxServer merged 1 commit intomainfrom
wikibase-integration-task4

Conversation

@DaxServer
Copy link
Owner

Addresses Task 4 of #137

@coderabbitai
Copy link

coderabbitai bot commented Aug 17, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Introduced Wikibase instance management: list, view, create, update, and delete instances.
    • Validate configurations (with live connectivity checks) and run health checks.
    • Set and retrieve the default instance; predefined instances are protected from edits/removal.
    • Consistent input validation and clear error responses across endpoints.
  • Tests

    • Added comprehensive coverage for CRUD, validation, connectivity, health checks, defaults, and error handling.
  • Chores

    • Updated project task statuses to reflect completion of instance configuration management.

Walkthrough

Adds a full Wikibase instance configuration service with validation, connectivity, and health checks; exposes CRUD and auxiliary endpoints via a new /wikibase/instances API; registers the router in the app; updates specs to mark tasks complete; and introduces comprehensive unit/integration tests for service and API.

Changes

Cohort / File(s) Summary
Specs status update
.kiro/specs/wikibase-rest-api-integration/tasks.md
Marked instance configuration tasks and subtasks as completed; no content changes beyond checkbox states.
Wikibase instances API + app wiring
backend/src/api/wikibase/instances.ts, backend/src/index.ts
New Elysia router providing CRUD, validation, connectivity/health, and default management for Wikibase instances; registered into the main app. Includes schema validation and structured error handling.
Wikibase config service
backend/src/services/wikibase-config.service.ts
New service managing predefined/custom instances, validation, default handling, connectivity tests, and health checks. Exposes interfaces (ValidationResult, ConnectivityResult, HealthCheckResult), a class (WikibaseConfigService), and a singleton export.
API tests
backend/tests/api/wikibase/instances.test.ts
Adds end-to-end tests for all instances API routes covering success and error paths, including defaults, validate, health, and set-default.
Service connectivity tests
backend/tests/services/wikibase-config-connectivity.test.ts
Adds tests for connectivity, health checks, feature extraction, error handling, and performance using external endpoints.
Service core tests
backend/tests/services/wikibase-config.service.test.ts
Adds unit tests for service behavior: predefined/custom lifecycle, ordering, default logic, validation rules, resets, and concurrency cases.

Sequence Diagram(s)

sequenceDiagram
  participant C as Client
  participant A as Elysia App
  participant R as wikibaseInstancesApi
  participant S as wikibaseConfigService
  participant W as Remote Wikibase

  C->>A: HTTP GET /wikibase/instances
  A->>R: Route dispatch
  R->>S: getInstances()
  S-->>R: instances[]
  R-->>A: 200 JSON
  A-->>C: instances list

  C->>A: HTTP POST /wikibase/instances (config)
  A->>R: Route dispatch + validate body
  R->>S: addInstance(config)
  S->>S: validateInstance(config)
  S-->>R: ok
  R-->>A: 200 JSON
  A-->>C: added

  C->>A: HTTP POST /wikibase/instances/validate (config)
  A->>R: Route dispatch + validate body
  R->>S: validateInstanceWithConnectivity(config)
  S->>W: test request (e.g., /openapi.json)
  W-->>S: status/spec
  S-->>R: validation + connectivity
  R-->>A: 200 JSON
  A-->>C: validation result

  C->>A: HTTP GET /wikibase/instances/:id/health
  A->>R: Route dispatch
  R->>S: performHealthCheck(id)
  S->>W: connectivity probe
  W-->>S: response / error
  S-->>R: health summary
  R-->>A: 200 JSON
  A-->>C: health result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch wikibase-integration-task4

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (12)
.kiro/specs/wikibase-rest-api-integration/tasks.md (1)

45-59: Task 4 status updates look good; verify whether 5.3 should also be checked given new /wikibase/instances API is implemented

This PR adds the Wikibase Instances API router and endpoints. If those map to “5.3 Implement instance management endpoints,” consider marking 5.3 as completed (or add a note clarifying scope if it’s intentionally separate).

backend/tests/services/wikibase-config-connectivity.test.ts (2)

220-231: Stabilize timeout-sensitive test to reduce flakiness

Network conditions vary in CI. Consider explicitly limiting this test’s runtime to avoid build hangs and reduce variance.

Example (if supported by bun:test in your setup):

-    test('should handle network timeouts gracefully', async () => {
+    test('should handle network timeouts gracefully', { timeout: 15000 }, async () => {
       const timeoutConfig = {
         ...validConfig,
         baseUrl: 'https://httpbin.org/delay/2', // Shorter delay to avoid test timeout
       }

If the object options form isn’t supported in your version, wrap with a manual AbortController in the service under test (see related suggestion in wikibase-config.service.ts).


260-270: Use a valid but closed port for “connection refused”

Port 99999 is outside the valid range (0–65535); parsing may fail before a connection attempt, producing inconsistent error shapes. Prefer a valid but presumably closed port to consistently trigger ECONNREFUSED.

-        baseUrl: 'http://localhost:99999', // Port that should be closed
+        baseUrl: 'http://localhost:65535', // Valid port, likely closed locally/CI
backend/src/services/wikibase-config.service.ts (2)

370-413: Optional: Drop ApiClient dependency and build requests directly

You’re using ApiClient only for basePath and default headers, while all requests are done via fetch. Removing the dependency avoids the missing types problem altogether.

If desired, I can provide a concrete refactor to replace ApiClient with a small internal helper (buildHeaders + sanitized baseUrl).


495-534: Enforce fetch timeouts in makeTestRequest
Add a 10 s AbortController-based timeout around both HTTP calls to avoid hanging indefinitely and clear the timer on success.

File: backend/src/services/wikibase-config.service.ts (around lines 495–534)

--- a/backend/src/services/wikibase-config.service.ts
+++ b/backend/src/services/wikibase-config.service.ts
@@ private async makeTestRequest(apiClient: ApiClient): Promise<{
-    try {
+    try {
+      const controller = new AbortController()
+      const timer = setTimeout(() => controller.abort(), 10_000)
 
       // First, try to get the OpenAPI specification
-      const response = await fetch(`${apiClient.basePath}/openapi.json`, {
+      const response = await fetch(
+        `${apiClient.basePath.replace(/\/+$/, '')}/openapi.json`,
+        {
+          method: 'GET',
+          headers: apiClient.defaultHeaders,
+          signal: controller.signal,
+        }
+      )
@@ if (response.ok) {
         const openApiSpec = await response.json() as any
+        clearTimeout(timer)
         return {
@@
       // If OpenAPI spec is not available, try a simple GET request to the base path
-      const baseResponse = await fetch(apiClient.basePath, {
+      const baseResponse = await fetch(apiClient.basePath, {
+        method: 'GET',
+        headers: apiClient.defaultHeaders,
+        signal: controller.signal,
+      })
+      clearTimeout(timer)
       return {
         status: baseResponse.status,
         apiVersion: 'unknown',
         features: [],
       }

This guards both the OpenAPI and base-path checks against slow or unresponsive endpoints and ensures the timeout is cleaned up on success.

backend/tests/services/wikibase-config.service.test.ts (3)

25-49: Tests assume two predefined instances; ensure service provides both

These assertions will fail until the service defines both wikidata and wikimedia-commons. The service currently only ships wikidata. See suggested fix in wikibase-config.service.ts to add wikimedia-commons to PREDEFINED_INSTANCES.


155-160: isPredefinedInstance for wikimedia-commons depends on service data

This check will fail until wikimedia-commons is added as a predefined instance. After adding it to the service, this test should pass as-is.


352-354: Fix failing count assertion (expected 2, received 1) by syncing with service

This relies on service exposing exactly two predefined instances. After adding wikimedia-commons to PREDEFINED_INSTANCES, this should pass.

backend/src/api/wikibase/instances.ts (3)

200-211: Standardize error responses instead of throwing bare errors.

A few routes throw new Error(...), which bypasses your normalized error envelope used elsewhere. Return a consistent JSON error with appropriate status codes to improve DX and Swagger fidelity.

Example for POST /validate:

-  } catch (error) {
-    throw new Error(`Failed to validate instance configuration: ${error instanceof Error ? error.message : 'Unknown error'}`)
-  }
+  } catch (error) {
+    return {
+      success: false,
+      data: [],
+      errors: [
+        {
+          code: 'INTERNAL',
+          message: error instanceof Error ? error.message : 'Failed to validate instance configuration',
+        },
+      ],
+    }
+  }

Do the same for GET / and GET /default.

Also applies to: 15-17, 315-316


116-131: Consider disallowing isDefault updates via PUT to avoid conflicting flows.

You already provide a dedicated POST /:instanceId/set-default. Allowing isDefault mutations via PUT encourages clients to set defaults two ways and increases the risk of multiple defaults. Recommend removing isDefault from the update schema and delegating default management to the dedicated endpoint.


26-54: Add response schemas to tighten typings and improve Swagger.

You’re defining input schemas (params/body), but not response schemas. Adding response: t.Object({...}) per route will:

  • Improve type-safety
  • Remove ambiguity for clients
  • Help Swagger generate more precise docs

If desired, introduce a shared ApiEnvelope<T> response type in a central module and reference it across routes.

Also applies to: 56-94, 96-136, 138-166, 168-198, 267-296, 298-323

backend/tests/api/wikibase/instances.test.ts (1)

372-387: Singleton service state may leak across tests; consider resetting between tests.

wikibaseConfigService is a singleton and API calls mutate process-wide state (add/remove/set-default). To prevent test order dependency, reset state in a beforeEach/afterEach using a public reset helper (service-level reset or API endpoint for tests). If the service has reset() or similar, invoke it; otherwise, we can add a guarded test-only reset.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7380e and 7bd3cf4.

📒 Files selected for processing (7)
  • .kiro/specs/wikibase-rest-api-integration/tasks.md (1 hunks)
  • backend/src/api/wikibase/instances.ts (1 hunks)
  • backend/src/index.ts (2 hunks)
  • backend/src/services/wikibase-config.service.ts (1 hunks)
  • backend/tests/api/wikibase/instances.test.ts (1 hunks)
  • backend/tests/services/wikibase-config-connectivity.test.ts (1 hunks)
  • backend/tests/services/wikibase-config.service.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

**/*.{ts,tsx,js,jsx}: Use bun <file> instead of node <file> or ts-node <file> for running TypeScript or JavaScript files
Do not use dotenv; Bun automatically loads .env files
Use Bun.serve() for HTTP servers and WebSockets instead of express
Use bun:sqlite for SQLite instead of better-sqlite3
Use Bun.redis for Redis instead of ioredis
Use Bun.sql for Postgres instead of pg or postgres.js
Use built-in WebSocket instead of ws
Prefer Bun.file over node:fs's readFile/writeFile
Use Bun.$ (e.g., Bun.$ls``) instead of execa for running shell commands

Files:

  • backend/src/index.ts
  • backend/tests/services/wikibase-config.service.test.ts
  • backend/src/api/wikibase/instances.ts
  • backend/tests/services/wikibase-config-connectivity.test.ts
  • backend/src/services/wikibase-config.service.ts
  • backend/tests/api/wikibase/instances.test.ts
**/*.{html,ts,tsx,css}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun build <file.html|file.ts|file.css> instead of webpack or esbuild for building HTML, TypeScript, or CSS files

Files:

  • backend/src/index.ts
  • backend/tests/services/wikibase-config.service.test.ts
  • backend/src/api/wikibase/instances.ts
  • backend/tests/services/wikibase-config-connectivity.test.ts
  • backend/src/services/wikibase-config.service.ts
  • backend/tests/api/wikibase/instances.test.ts
**/*.test.{ts,tsx,js,jsx}

📄 CodeRabbit Inference Engine (.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc)

Use bun test instead of jest for running tests

Files:

  • backend/tests/services/wikibase-config.service.test.ts
  • backend/tests/services/wikibase-config-connectivity.test.ts
  • backend/tests/api/wikibase/instances.test.ts
🧬 Code Graph Analysis (5)
backend/src/index.ts (1)
backend/src/api/wikibase/instances.ts (1)
  • wikibaseInstancesApi (7-323)
backend/src/api/wikibase/instances.ts (1)
backend/src/services/wikibase-config.service.ts (1)
  • wikibaseConfigService (601-601)
backend/tests/services/wikibase-config-connectivity.test.ts (1)
backend/src/types/wikibase-api.ts (1)
  • WikibaseInstanceConfig (6-18)
backend/src/services/wikibase-config.service.ts (1)
backend/src/types/wikibase-api.ts (1)
  • WikibaseInstanceConfig (6-18)
backend/tests/api/wikibase/instances.test.ts (2)
backend/src/types/wikibase-api.ts (1)
  • WikibaseInstanceConfig (6-18)
backend/src/index.ts (1)
  • elysiaApp (13-40)
🪛 GitHub Check: test
backend/tests/services/wikibase-config.service.test.ts

[failure] 353-353: error: expect(received).toBe(expected)
Expected: 2
Received: 1

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:353:32)

[failure] 158-158: error: expect(received).toBe(expected)
Expected: true
Received: false

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:158:65)

[failure] 57-57: error: expect(received).toBe(expected)
Expected: "https://www.wikidata.org/w/rest.php/wikibase/v0"
Received: "https://www.wikidata.org/w/rest.php/wikibase/v1"

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:57:32)

[failure] 45-45: error: expect(received).toBe(expected)
Expected: 2
Received: 1

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:45:42)

[failure] 31-31: error: expect(received).toBe(expected)
Expected: true
Received: false

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:31:65)
backend/tests/api/wikibase/instances.test.ts

[failure] 40-40: error: expect(received).toBe(expected)
Expected: true
Received: false

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/api/wikibase/instances.test.ts:40:86)
🪛 GitHub Actions: Run tests
backend/tests/services/wikibase-config.service.test.ts

[error] 1-1: Test assertion failed. Expected: true.

🪛 GitHub Actions: Typecheck code
backend/tests/services/wikibase-config.service.test.ts

[error] 71-71: TypeScript error TS2532: Object is possibly 'undefined'.


[error] 76-76: TypeScript error TS2532: Object is possibly 'undefined'.


[error] 90-90: TypeScript error TS2532: Object is possibly 'undefined'.

backend/tests/services/wikibase-config-connectivity.test.ts

[error] 196-196: TypeScript error TS6133: 'mockOpenApiSpec' is declared but its value is never read.

backend/src/services/wikibase-config.service.ts

[error] 2-2: TypeScript error TS7016: Could not find a declaration file for module '@wmde/wikibase-rest-api'. '/home/runner/work/dataforge/dataforge/node_modules/@wmde/wikibase-rest-api/dist/index.js' implicitly has an 'any' type.

backend/tests/api/wikibase/instances.test.ts

[error] 34-40: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 55-57: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 70-73: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 92-93: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 117-120: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 149-152: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 184-185: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 203-206: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 233-234: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 247-250: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 269-272: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 296-298: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 313-316: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 329-332: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 347-352: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 365-368: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 383-385: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 412-413: TypeScript error TS18046: 'data' is of type 'unknown'.


[error] 426-429: TypeScript error TS18046: 'data' is of type 'unknown'.

🔇 Additional comments (4)
backend/src/index.ts (1)

11-11: Router registration LGTM

wikibaseInstancesApi is imported and mounted after wikibaseRoutes; order makes sense. No issues.

Also applies to: 37-37

backend/src/api/wikibase/instances.ts (1)

232-256: Health endpoint design looks solid.

Returning 200 with a health payload (even when the instance doesn’t exist) is pragmatic for monitoring. The error information is conveyed in the body and won’t break scrapers that expect 200s.

backend/tests/api/wikibase/instances.test.ts (2)

254-300: LGTM: validation endpoints behavior is consistent with the service.

‘Validate’ returns an explicit validation result (200 on both valid/invalid), which is a good UX for clients testing configs without persisting.


336-370: LGTM: health endpoint contract is pragmatic and assertions cover both paths.

Asserting a 200 with structured payload for both existing and non-existent instances aligns with the API’s design.

Comment on lines +8 to +17
.get('/', async () => {
try {
const instances = await wikibaseConfigService.getInstances()
return {
success: true,
data: instances,
}
} catch (error) {
throw new Error(`Failed to get instances: ${error instanceof Error ? error.message : 'Unknown error'}`)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

⚠️ Potential issue

Do not expose authToken in API responses (redact or omit it).

The GET endpoints currently return the full instance object as provided by the service. If authToken is present on an instance, this leaks a credential to any API consumer. Redact or omit the token consistently in:

  • GET /
  • GET /:instanceId
  • GET /default

A minimal redaction approach:

   .get('/', async () => {
     try {
-      const instances = await wikibaseConfigService.getInstances()
+      const instances = await wikibaseConfigService.getInstances()
+      const redacted = instances.map(({ authToken, ...rest }) => rest)
       return {
         success: true,
-        data: instances,
+        data: redacted,
       }
     } catch (error) {
       throw new Error(`Failed to get instances: ${error instanceof Error ? error.message : 'Unknown error'}`)
     }
   }, {

Similarly apply for GET /:instanceId and GET /default before returning the payload.

Also applies to: 26-33, 298-316

Comment on lines +76 to +89
body: t.Object({
id: t.String({ description: 'Unique instance identifier' }),
name: t.String({ description: 'Human-readable instance name' }),
baseUrl: t.String({ description: 'Base URL for the Wikibase REST API' }),
userAgent: t.String({ description: 'User agent string for API requests' }),
authToken: t.Optional(t.String({ description: 'Authentication token (optional)' })),
isDefault: t.Optional(t.Boolean({ description: 'Whether this is the default instance' })),
metadata: t.Optional(t.Object({
description: t.Optional(t.String()),
language: t.Optional(t.String()),
version: t.Optional(t.String()),
})),
}),
detail: {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Strengthen input validation (URL format and non-empty constraints).

The schema allows empty strings and arbitrary baseUrl. Enforce stronger constraints to catch issues early at the API boundary.

  • Ensure required strings are non-empty: minLength: 1
  • Validate baseUrl as a proper URL (and optionally constrain to HTTP/S)
  • Optionally restrict id to a safe pattern (e.g., ^[a-z0-9-]+$)

Example:

-  id: t.String({ description: 'Unique instance identifier' }),
-  name: t.String({ description: 'Human-readable instance name' }),
-  baseUrl: t.String({ description: 'Base URL for the Wikibase REST API' }),
-  userAgent: t.String({ description: 'User agent string for API requests' }),
+  id: t.String({ description: 'Unique instance identifier', minLength: 1, pattern: '^[a-z0-9-]+$' }),
+  name: t.String({ description: 'Human-readable instance name', minLength: 1 }),
+  baseUrl: t.String({ description: 'Base URL for the Wikibase REST API', format: 'uri' }),
+  userAgent: t.String({ description: 'User agent string for API requests', minLength: 1 }),

Apply similar constraints to the /validate body schema as well.

Also applies to: 212-224

🤖 Prompt for AI Agents
In backend/src/api/wikibase/instances.ts around lines 76 to 89 (and similarly
update the /validate body schema at lines ~212-224), the current t.String()
fields allow empty strings and baseUrl is not validated; update the schema to
require non-empty strings (add minLength: 1) for id, name, userAgent and
metadata description/language/version, validate baseUrl as a proper URL
(restrict to http/https) using a URL pattern or a dedicated URL type, and
tighten id with a safe regex like ^[a-z0-9-]+$ (or equivalent pattern) to
prevent invalid identifiers; apply the same constraints to the duplicate
/validate schema.

@@ -0,0 +1,601 @@
import type { WikibaseInstanceConfig } from '@backend/types/wikibase-api'
import { ApiClient } from '@wmde/wikibase-rest-api'
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix missing type declarations for '@wmde/wikibase-rest-api' (TS7016)

Typecheck fails because the package lacks .d.ts. Add a minimal ambient declaration to unblock CI.

Create a new file: types/wmde__wikibase-rest-api.d.ts

declare module '@wmde/wikibase-rest-api' {
  export class ApiClient {
    basePath: string
    defaultHeaders?: Record<string, string>
    constructor(basePath: string)
  }
}

Then include "types" in tsconfig.json if not already:
{
"compilerOptions": {
"typeRoots": ["./types", "./node_modules/@types"]
}
}

I can generate the tsconfig and declaration file changes if you want.

🧰 Tools
🪛 GitHub Actions: Typecheck code

[error] 2-2: TypeScript error TS7016: Could not find a declaration file for module '@wmde/wikibase-rest-api'. '/home/runner/work/dataforge/dataforge/node_modules/@wmde/wikibase-rest-api/dist/index.js' implicitly has an 'any' type.

🤖 Prompt for AI Agents
In backend/src/services/wikibase-config.service.ts around line 2, the import
from '@wmde/wikibase-rest-api' fails typechecking because the package has no
TypeScript declarations; create a new ambient declaration file
types/wmde__wikibase-rest-api.d.ts containing a minimal module declaration that
exports the ApiClient class with basePath, optional defaultHeaders, and a
constructor accepting basePath, then update tsconfig.json to include
"typeRoots": ["./types", "./node_modules/@types"] (or add the new types folder
to the existing typeRoots) so the compiler picks up the declaration and CI
typechecks succeed.

Comment on lines +47 to +61
const PREDEFINED_INSTANCES: WikibaseInstanceConfig[] = [
{
id: 'wikidata',
name: 'Wikidata',
baseUrl: 'https://www.wikidata.org/w/rest.php/wikibase/v1',
userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
isDefault: true,
metadata: {
description: 'The free knowledge base that anyone can edit',
language: 'en',
version: 'v0',
},
},
]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing predefined instance “wikimedia-commons” to satisfy tests and docs

Tests expect two predefined instances (wikidata and wikimedia-commons). Only wikidata is defined here, causing multiple test failures.

Apply this diff to add Wikimedia Commons:

 const PREDEFINED_INSTANCES: WikibaseInstanceConfig[] = [
   {
     id: 'wikidata',
     name: 'Wikidata',
-    baseUrl: 'https://www.wikidata.org/w/rest.php/wikibase/v1',
+    baseUrl: 'https://www.wikidata.org/w/rest.php/wikibase/v1',
     userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
     isDefault: true,
     metadata: {
       description: 'The free knowledge base that anyone can edit',
       language: 'en',
       version: 'v0',
     },
   },
+  {
+    id: 'wikimedia-commons',
+    name: 'Wikimedia Commons',
+    baseUrl: 'https://commons.wikimedia.org/w/rest.php/wikibase/v1',
+    userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
+    isDefault: false,
+    metadata: {
+      description: 'Media repository of free-use images, sounds, and other media files',
+      language: 'en',
+      version: 'v0',
+    },
+  },
 ]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PREDEFINED_INSTANCES: WikibaseInstanceConfig[] = [
{
id: 'wikidata',
name: 'Wikidata',
baseUrl: 'https://www.wikidata.org/w/rest.php/wikibase/v1',
userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
isDefault: true,
metadata: {
description: 'The free knowledge base that anyone can edit',
language: 'en',
version: 'v0',
},
},
]
const PREDEFINED_INSTANCES: WikibaseInstanceConfig[] = [
{
id: 'wikidata',
name: 'Wikidata',
baseUrl: 'https://www.wikidata.org/w/rest.php/wikibase/v1',
userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
isDefault: true,
metadata: {
description: 'The free knowledge base that anyone can edit',
language: 'en',
version: 'v0',
},
},
{
id: 'wikimedia-commons',
name: 'Wikimedia Commons',
baseUrl: 'https://commons.wikimedia.org/w/rest.php/wikibase/v1',
userAgent: 'DataForge/0.1 (https://github.com/dataforge/dataforge)',
isDefault: false,
metadata: {
description: 'Media repository of free-use images, sounds, and other media files',
language: 'en',
version: 'v0',
},
},
]
🤖 Prompt for AI Agents
In backend/src/services/wikibase-config.service.ts around lines 47 to 61 the
PREDEFINED_INSTANCES array only contains the 'wikidata' entry but tests and docs
expect a second predefined instance for 'wikimedia-commons'; add a second
WikibaseInstanceConfig object with id 'wikimedia-commons', name 'Wikimedia
Commons', baseUrl 'https://commons.wikimedia.org/w/rest.php/wikibase/v1', the
same userAgent string as wikidata, isDefault false, and metadata including a
brief description, language 'en', and version 'v0' so the array contains both
predefined instances.

Comment on lines +33 to +41
const data = await response.json()
expect(data.success).toBe(true)
expect(Array.isArray(data.data)).toBe(true)
expect(data.data.length).toBeGreaterThan(0)

// Should include pre-defined instances
expect(data.data.some((instance: any) => instance.id === 'wikidata')).toBe(true)
expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix TS18046: Response.json() returns unknown in Bun – cast or add a typed helper.

Bun’s type for Response.json() is unknown, so direct property access on data leads to TS18046 errors. Introduce a typed API envelope and cast JSON responses accordingly.

Apply this diff (adds types + helper and updates one callsite; repeat at all others similarly):

@@
-import { describe, test, expect, beforeEach } from 'bun:test'
+import { describe, test, expect, beforeEach } from 'bun:test'
+type ApiError = { code: string; message: string; details?: unknown[] }
+type ApiResponse<T> = { success: boolean; data: T; message?: string; errors?: ApiError[] }
+const parseJson = async <T>(res: Response): Promise<ApiResponse<T>> =>
+  res.json() as Promise<ApiResponse<T>>
@@
-      const data = await response.json()
+      const data = await parseJson<any>(response)
       expect(data.success).toBe(true)
       expect(Array.isArray(data.data)).toBe(true)

Then replace every const data = await response.json() with const data = await parseJson<any>(response) (or a stronger T where known). This will fix all TS18046 occurrences flagged in the pipeline.

Also applies to: 54-58, 69-74, 91-94, 116-121, 149-153, 184-186, 203-207, 233-235, 247-251, 269-273, 296-299, 313-317, 329-333, 347-353, 365-368, 383-385, 412-413, 426-429

🧰 Tools
🪛 GitHub Check: test

[failure] 40-40: error: expect(received).toBe(expected)
Expected: true
Received: false

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/api/wikibase/instances.test.ts:40:86)
🪛 GitHub Actions: Typecheck code

[error] 34-40: TypeScript error TS18046: 'data' is of type 'unknown'.

🤖 Prompt for AI Agents
In backend/tests/api/wikibase/instances.test.ts around lines 33 to 41, the test
calls response.json() which Bun types as unknown causing TS18046; add a typed
helper (e.g., parseJson<T>(response): Promise<T>) and a shared API envelope/type
for the response shape, then replace const data = await response.json() with
const data = await parseJson<any>(response) (or a stronger generic T where
known) and update imports; repeat the same replacement for the other listed call
sites so all Response.json() usages are cast via the typed helper to satisfy
TS18046.

Comment on lines +38 to +41
// Should include pre-defined instances
expect(data.data.some((instance: any) => instance.id === 'wikidata')).toBe(true)
expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Failing expectation: predefined 'wikimedia-commons' not found.

The test assumes a predefined instance with id wikimedia-commons, but CI shows it’s absent. Either:

  • Add wikimedia-commons to the service’s predefined set, or
  • Relax the test to assert only wikidata (or to assert that at least one predefined instance exists, and specifically wikidata).

If the canonical intent is to include wikimedia-commons, update the service; otherwise, update the test accordingly.

Proposed test relaxation:

-      expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
+      // Optional: include wikimedia-commons if provided by the service
+      // Keeping this non-blocking to avoid false negatives if not bundled
+      // expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)

[suggest_optional_refactor]
To confirm what the service actually ships, run:


🏁 Script executed:

#!/bin/bash
# Verify whether 'wikimedia-commons' is defined among predefined instances
rg -n -C2 -g '!**/node_modules/**' -g '!**/dist/**' -g '!**/build/**' -P "wikimedia-commons|wikidata" backend/src/services

Length of output: 742


Update test to remove expectation for ‘wikimedia-commons’

The service only ships the wikidata instance, so the test should no longer assert on wikimedia-commons, which is causing the CI failure.

• File: backend/tests/api/wikibase/instances.test.ts
• Remove or comment out the second expectation

Proposed change:

-      expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
+      // Removed: service does not currently include a predefined 'wikimedia-commons' instance
+      // expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Should include pre-defined instances
expect(data.data.some((instance: any) => instance.id === 'wikidata')).toBe(true)
expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
})
// Should include pre-defined instances
expect(data.data.some((instance: any) => instance.id === 'wikidata')).toBe(true)
// Removed: service does not currently include a predefined 'wikimedia-commons' instance
// expect(data.data.some((instance: any) => instance.id === 'wikimedia-commons')).toBe(true)
})
🧰 Tools
🪛 GitHub Check: test

[failure] 40-40: error: expect(received).toBe(expected)
Expected: true
Received: false

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/api/wikibase/instances.test.ts:40:86)
🤖 Prompt for AI Agents
In backend/tests/api/wikibase/instances.test.ts around lines 38 to 41, the test
asserts that both 'wikidata' and 'wikimedia-commons' are present but the service
only ships 'wikidata'; remove (or comment out) the second expectation that
checks for 'wikimedia-commons' so the test only asserts the presence of
'wikidata' and update any test description if needed to reflect the reduced set
of pre-defined instances.

Comment on lines +196 to +207
const mockOpenApiSpec = {
paths: {
'/entities/items': {},
'/entities/properties': {},
'/search/entities': {},
'/statements': {},
'/labels': {},
'/descriptions': {},
'/aliases': {},
},
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix unused variable causing TS6133 error

mockOpenApiSpec is declared but unused, tripping the typecheck. Remove it (or actually use it via injection/mocking if intended).

Apply this diff to remove it:

-      const mockOpenApiSpec = {
-        paths: {
-          '/entities/items': {},
-          '/entities/properties': {},
-          '/search/entities': {},
-          '/statements': {},
-          '/labels': {},
-          '/descriptions': {},
-          '/aliases': {},
-        },
-      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const mockOpenApiSpec = {
paths: {
'/entities/items': {},
'/entities/properties': {},
'/search/entities': {},
'/statements': {},
'/labels': {},
'/descriptions': {},
'/aliases': {},
},
}
🧰 Tools
🪛 GitHub Actions: Typecheck code

[error] 196-196: TypeScript error TS6133: 'mockOpenApiSpec' is declared but its value is never read.

🤖 Prompt for AI Agents
In backend/tests/services/wikibase-config-connectivity.test.ts around lines 196
to 207, the local const mockOpenApiSpec is declared but never used which causes
a TS6133 unused variable error; either remove the entire declaration block or
use it by injecting/mocking the OpenAPI spec where the test expects it (e.g.,
pass it into the function under test or into the mocked HTTP/OpenAPI client),
and then run typecheck to ensure the error is resolved.

Comment on lines +55 to +58
expect(instance.id).toBe('wikidata')
expect(instance.name).toBe('Wikidata')
expect(instance.baseUrl).toBe('https://www.wikidata.org/w/rest.php/wikibase/v0')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Align expected Wikidata baseUrl with service (v1 vs v0)

The service uses /wikibase/v1. The test expects /v0, causing failure. Align the assertion to v1.

-      expect(instance.baseUrl).toBe('https://www.wikidata.org/w/rest.php/wikibase/v0')
+      expect(instance.baseUrl).toBe('https://www.wikidata.org/w/rest.php/wikibase/v1')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
expect(instance.id).toBe('wikidata')
expect(instance.name).toBe('Wikidata')
expect(instance.baseUrl).toBe('https://www.wikidata.org/w/rest.php/wikibase/v0')
})
expect(instance.id).toBe('wikidata')
expect(instance.name).toBe('Wikidata')
expect(instance.baseUrl).toBe('https://www.wikidata.org/w/rest.php/wikibase/v1')
})
🧰 Tools
🪛 GitHub Check: test

[failure] 57-57: error: expect(received).toBe(expected)
Expected: "https://www.wikidata.org/w/rest.php/wikibase/v0"
Received: "https://www.wikidata.org/w/rest.php/wikibase/v1"

  at <anonymous> (/home/runner/work/dataforge/dataforge/backend/tests/services/wikibase-config.service.test.ts:57:32)
🤖 Prompt for AI Agents
In backend/tests/services/wikibase-config.service.test.ts around lines 55 to 58,
the test asserts the Wikidata baseUrl with /wikibase/v0 but the service uses
/wikibase/v1; update the expectation to match the service by changing the
asserted baseUrl to use /wikibase/v1 so the test aligns with the implementation.

Comment on lines +66 to +78
test('should return instances sorted by default status and name', async () => {
await service.addInstance(mockCustomConfig)
const instances = await service.getInstances()

// Default instance should be first
expect(instances[0].isDefault).toBe(true)

// Non-default instances should be sorted by name
const nonDefaultInstances = instances.filter(i => !i.isDefault)
for (let i = 1; i < nonDefaultInstances.length; i++) {
expect(nonDefaultInstances[i].name >= nonDefaultInstances[i - 1].name).toBe(true)
}
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Prevent TS2532 (“possibly undefined”) and make ordering assertion explicit

Indexing instances[0] without a guard triggers TS2532. Add a length check and use a non-null assertion for clarity.

       await service.addInstance(mockCustomConfig)
       const instances = await service.getInstances()
 
       // Default instance should be first
-      expect(instances[0].isDefault).toBe(true)
+      expect(instances.length).toBeGreaterThan(0)
+      const defaultInstance = instances[0]!
+      expect(defaultInstance.isDefault).toBe(true)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should return instances sorted by default status and name', async () => {
await service.addInstance(mockCustomConfig)
const instances = await service.getInstances()
// Default instance should be first
expect(instances[0].isDefault).toBe(true)
// Non-default instances should be sorted by name
const nonDefaultInstances = instances.filter(i => !i.isDefault)
for (let i = 1; i < nonDefaultInstances.length; i++) {
expect(nonDefaultInstances[i].name >= nonDefaultInstances[i - 1].name).toBe(true)
}
})
test('should return instances sorted by default status and name', async () => {
await service.addInstance(mockCustomConfig)
const instances = await service.getInstances()
// Default instance should be first
expect(instances.length).toBeGreaterThan(0)
const defaultInstance = instances[0]!
expect(defaultInstance.isDefault).toBe(true)
// Non-default instances should be sorted by name
const nonDefaultInstances = instances.filter(i => !i.isDefault)
for (let i = 1; i < nonDefaultInstances.length; i++) {
expect(nonDefaultInstances[i].name >= nonDefaultInstances[i - 1].name).toBe(true)
}
})
🧰 Tools
🪛 GitHub Actions: Typecheck code

[error] 71-71: TypeScript error TS2532: Object is possibly 'undefined'.


[error] 76-76: TypeScript error TS2532: Object is possibly 'undefined'.

🤖 Prompt for AI Agents
In backend/tests/services/wikibase-config.service.test.ts around lines 66 to 78,
the test indexes instances[0] which can trigger TS2532; add an explicit guard
that instances.length > 0 (or fail the test early with an assertion) before
asserting the default, and when indexing use a non-null assertion for TypeScript
(e.g. instances[0]!) to satisfy the compiler; also make the ordering assertion
explicit by asserting the nonDefaultInstances array has the expected length and
comparing names with a clear comparison (e.g. use localeCompare or an explicit
>= check) inside the loop.

Comment on lines +88 to +91
const customInstances = await service.getCustomInstances()
expect(customInstances.length).toBe(1)
expect(customInstances[0].id).toBe('custom-test')
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid TS2532 on array indexing by destructuring with non-null assertion

TypeScript doesn’t narrow after expect(). Use destructuring and a non-null assertion.

-      const customInstances = await service.getCustomInstances()
-      expect(customInstances.length).toBe(1)
-      expect(customInstances[0].id).toBe('custom-test')
+      const customInstances = await service.getCustomInstances()
+      expect(customInstances.length).toBe(1)
+      const [first] = customInstances
+      expect(first!.id).toBe('custom-test')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const customInstances = await service.getCustomInstances()
expect(customInstances.length).toBe(1)
expect(customInstances[0].id).toBe('custom-test')
})
const customInstances = await service.getCustomInstances()
expect(customInstances.length).toBe(1)
const [first] = customInstances
expect(first!.id).toBe('custom-test')
🧰 Tools
🪛 GitHub Actions: Typecheck code

[error] 90-90: TypeScript error TS2532: Object is possibly 'undefined'.

🤖 Prompt for AI Agents
In backend/tests/services/wikibase-config.service.test.ts around lines 88 to 91,
the test indexes customInstances[0] after an expect which can trigger TS2532
because TypeScript won’t narrow after expect(); destructure the array and use a
non-null assertion on the element when checking its id: replace direct indexing
with const [first] = customInstances; then assert
expect(first!).toBeDefined()/or rely on the length check and use
expect(first!.id).toBe('custom-test') to avoid the TS2532 error.

@DaxServer DaxServer merged commit 714fcff into main Aug 17, 2025
4 of 6 checks passed
@DaxServer DaxServer deleted the wikibase-integration-task4 branch August 17, 2025 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant