From 2dff6a13ebdf95279621f5ef321bc0bff0ae25af Mon Sep 17 00:00:00 2001 From: Rob Rudin Date: Wed, 19 Nov 2025 11:15:10 -0500 Subject: [PATCH] MLE-24194 Adding TS for releaseClient and checkConnection Also added a test-project for manual testing. --- .copyrightconfig | 2 +- CONTRIBUTING.md | 5 + marklogic.d.ts | 36 ++++- test-typescript/README.md | 34 ----- test-typescript/connection-methods.test.ts | 152 +++++++++++++++++++++ typescript-test-project/.gitignore | 19 +++ typescript-test-project/README.md | 79 +++++++++++ typescript-test-project/package.json | 25 ++++ typescript-test-project/setup.sh | 27 ++++ typescript-test-project/test.ts | 98 +++++++++++++ typescript-test-project/tsconfig.json | 40 ++++++ 11 files changed, 480 insertions(+), 37 deletions(-) create mode 100644 test-typescript/connection-methods.test.ts create mode 100644 typescript-test-project/.gitignore create mode 100644 typescript-test-project/README.md create mode 100644 typescript-test-project/package.json create mode 100755 typescript-test-project/setup.sh create mode 100644 typescript-test-project/test.ts create mode 100644 typescript-test-project/tsconfig.json diff --git a/.copyrightconfig b/.copyrightconfig index a4fe0e9c..939a8b60 100644 --- a/.copyrightconfig +++ b/.copyrightconfig @@ -11,4 +11,4 @@ startyear: 2015 # - Dotfiles already skipped automatically # Enable by removing the leading '# ' from the next line and editing values. # filesexcluded: third_party/*, docs/generated/*.md, assets/*.png, scripts/temp_*.py, vendor/lib.js -filesexcluded: .github/*, README.md, Jenkinsfile, test-app/*, *.md, docker-compose.yaml, test-complete-app-mlDeploy/*, *.json +filesexcluded: .github/*, README.md, Jenkinsfile, test-app/*, *.md, docker-compose.yaml, test-complete-app-mlDeploy/*, *.json, *.sh diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 045b429c..29a9ab5a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -58,6 +58,11 @@ or There are also tests in the `test-complete` folder. The setup for these is more complicated and can be found in the `Jenkinsfile` file in this repository in the `runE2ETests` function. +## Testing TypeScript support + +The 4.1.0 release will add TypeScript support for the client. To try this out locally and manually, +see the `README.md` file in the `./typescript-test-project` directory. + ## Generating documentation After installing the project dependencies, you can build the reference documentation locally from the root diff --git a/marklogic.d.ts b/marklogic.d.ts index 8c42e4e3..c91aa0b0 100644 --- a/marklogic.d.ts +++ b/marklogic.d.ts @@ -60,13 +60,36 @@ declare module 'marklogic' { apiVersion?: string; } + /** + * Result object returned by checkConnection method. + */ + export interface ConnectionCheckResult { + /** Whether the connection was successful */ + connected: boolean; + /** HTTP status code if connection failed */ + httpStatusCode?: number; + /** HTTP status message if connection failed */ + httpStatusMessage?: string; + } + /** * A database client object returned by createDatabaseClient. * Provides access to document, graph, and query operations. */ export interface DatabaseClient { - // Methods will be added as we expand the type definitions - // For now, this is a placeholder to enable basic typing + /** + * Tests if a connection is successful. + * @since 2.1 + * @returns A promise that resolves to an object indicating connection status + */ + checkConnection(): Promise; + + /** + * Releases the client and destroys the agent. + * Call this method when you're done with the client to free up resources. + * @since 3.0.0 + */ + release(): void; } /** @@ -76,8 +99,17 @@ declare module 'marklogic' { */ export function createDatabaseClient(config: DatabaseClientConfig): DatabaseClient; + /** + * Releases a client and destroys its agent. + * This is a standalone function equivalent to calling client.release(). + * @since 3.0.0 + * @param client - The DatabaseClient to release + */ + export function releaseClient(client: DatabaseClient): void; + const marklogic: { createDatabaseClient: typeof createDatabaseClient; + releaseClient: typeof releaseClient; }; export default marklogic; diff --git a/test-typescript/README.md b/test-typescript/README.md index aa6af9ab..2f9bcf4f 100644 --- a/test-typescript/README.md +++ b/test-typescript/README.md @@ -12,40 +12,6 @@ npm run test:types This command runs `tsc --noEmit`, which checks for TypeScript errors without generating JavaScript files. -## What Gets Tested - -### ✅ Type Constraints -- Valid values for `authType` (basic, digest, application-level, certificate, kerberos, saml, cloud) -- Optional vs required properties -- Union types (string | Buffer for certificates) -- Array types (string[] for multiple certificates) - -### ✅ Type Safety -If you uncomment the error examples in the test files, TypeScript will catch: -- Invalid `authType` values -- Incorrect property types -- Missing required properties - -## Test Files - -### `type-constraints.test.ts` -Tests the `DatabaseClientConfig` interface constraints without importing the module. This works immediately without needing to simulate package installation. - -### `basic-types.test.ts` (currently excluded) -Full integration test that imports the `marklogic` module. To use this: -1. Build/link the package locally (`npm link`) -2. Remove it from the exclude list in `tsconfig.json` -3. Run `npm run test:types` again - -## Adding More Type Tests - -As you add more interfaces to `marklogic.d.ts`, add corresponding test files here. The pattern is: - -1. Create a `.test.ts` file -2. Write TypeScript code that uses the types -3. Include examples that should work AND commented examples that should fail -4. Run `npm run test:types` to verify - ## Why This Approach? TypeScript's compiler is the best way to test type definitions because: diff --git a/test-typescript/connection-methods.test.ts b/test-typescript/connection-methods.test.ts new file mode 100644 index 00000000..c3571ba4 --- /dev/null +++ b/test-typescript/connection-methods.test.ts @@ -0,0 +1,152 @@ +/* +* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +*/ + +/** + * TypeScript type checking tests for connection-related methods. + * + * This file validates: + * - checkConnection() return type + * - release() method + * - releaseClient() standalone function + * + * These tests are compiled but NOT executed - they verify type correctness only. + * Run with: npm run test:types + */ + +// Create test types that should match the actual marklogic types +type ConnectionCheckResult = { + connected: boolean; + httpStatusCode?: number; + httpStatusMessage?: string; +}; + +type DatabaseClient = { + checkConnection(): Promise; + release(): void; +}; + +type DatabaseClientConfig = { + host?: string; + port?: number; + user?: string; + password?: string; +}; + +// Simulate the marklogic module interface +type MarkLogicModule = { + createDatabaseClient(config: DatabaseClientConfig): DatabaseClient; + releaseClient(client: DatabaseClient): void; +}; + +// Test checkConnection() return type +async function testCheckConnection(marklogic: MarkLogicModule) { + const client = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin' + }); + + // Should return a Promise + const result = await client.checkConnection(); + + // result.connected should be boolean + const isConnected: boolean = result.connected; + + // When not connected, should have optional error properties + if (!result.connected) { + const statusCode: number | undefined = result.httpStatusCode; + const statusMessage: string | undefined = result.httpStatusMessage; + + console.log(`Connection failed: ${statusCode} - ${statusMessage}`); + } + + return isConnected; +} + +// Test release() method on client +function testRelease(marklogic: MarkLogicModule) { + const client = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin' + }); + + // Should be callable with no return value + client.release(); + + // This is the correct usage pattern +} + +// Test releaseClient() standalone function +function testReleaseClientFunction(marklogic: MarkLogicModule) { + const client = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin' + }); + + // Should accept a DatabaseClient and return void + marklogic.releaseClient(client); + + // This is equivalent to client.release() +} + +// Test proper cleanup pattern +async function testProperCleanupPattern(marklogic: MarkLogicModule) { + const client = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin' + }); + + try { + const result = await client.checkConnection(); + if (result.connected) { + console.log('Connected successfully!'); + // Do database operations... + } else { + console.error(`Connection failed: ${result.httpStatusCode}`); + } + } finally { + // Always clean up resources + client.release(); + } +} + +// Test ConnectionCheckResult structure +const successResult: ConnectionCheckResult = { + connected: true +}; + +const failureResult: ConnectionCheckResult = { + connected: false, + httpStatusCode: 401, + httpStatusMessage: 'Unauthorized' +}; + +// These should cause errors if uncommented: +// const invalidResult1: ConnectionCheckResult = { +// connected: 'yes' // Error: should be boolean +// }; + +// const invalidResult2: ConnectionCheckResult = { +// connected: true, +// httpStatusCode: 'error' // Error: should be number +// }; + +console.log('✅ Connection method types validated!'); + +// Export to prevent "unused" errors +export { + testCheckConnection, + testRelease, + testReleaseClientFunction, + testProperCleanupPattern, + successResult, + failureResult +}; diff --git a/typescript-test-project/.gitignore b/typescript-test-project/.gitignore new file mode 100644 index 00000000..31041d06 --- /dev/null +++ b/typescript-test-project/.gitignore @@ -0,0 +1,19 @@ +# Dependencies +node_modules/ +package-lock.json + +# Compiled output +*.js +*.js.map +*.d.ts + +# Build artifacts +dist/ +build/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ diff --git a/typescript-test-project/README.md b/typescript-test-project/README.md new file mode 100644 index 00000000..0a2039b0 --- /dev/null +++ b/typescript-test-project/README.md @@ -0,0 +1,79 @@ +# Test Project - Realistic TypeScript Usage + +This is a **standalone npm project** that demonstrates how users will consume the MarkLogic Node.js Client with TypeScript support. + +## Purpose + +This folder serves as: +- A realistic demo of TypeScript integration for colleagues and users +- A quick testing ground for new TypeScript features +- Validation that the library works as an installed npm package +- A showcase of IntelliSense and type safety in action + +## Quick Start + +### 1. Setup (First Time Only) +```bash +cd test-project +npm run setup +``` + +This will: +- Install the parent `marklogic` package (via `file:..`) +- Install TypeScript and type dependencies +- Set up everything needed for type checking + +### 2. Test TypeScript Types +```bash +npm test +# or +npm run typecheck +``` + +This runs TypeScript's compiler in check-only mode (no JS output). + +### 3. Edit and Experiment +Open `test.ts` in your IDE (VS Code, IntelliJ, etc.) and start typing: +- You'll get **autocomplete** for all config properties +- **Hover** over methods to see their signatures +- **IntelliSense** shows available options (like authType values) +- Type **errors are highlighted** in real-time + +## How It Works + +This project installs the parent `marklogic` package using npm's `file:..` protocol, which: +- Simulates a real npm install +- Picks up the `types` field from `package.json` +- Loads `marklogic.d.ts` automatically +- Provides full TypeScript support + +## Demo This to Colleagues + +1. **Show autocomplete**: In `test.ts`, type `marklogic.createDatabaseClient({` and watch the suggestions appear +2. **Show type safety**: Try setting `authType: 'invalid'` - TypeScript catches it! +3. **Show IntelliSense**: Hover over `checkConnection()` to see its return type +4. **Show error detection**: Run `npm test` with an invalid config to see type errors + +## Files + +- `package.json` - Standard npm package configuration +- `tsconfig.json` - TypeScript compiler configuration +- `test.ts` - Example TypeScript code using the marklogic client +- `.gitignore` - Ignores node_modules and build artifacts + +## Updating After Changes + +If you make changes to the parent `marklogic` package: + +```bash +# Quick way - just reinstall the local package +npm install .. + +# Or full clean reinstall +rm -rf node_modules package-lock.json +npm run setup +``` + +## Note + +This is a **development/testing project only** - it's not meant to be published. The `"private": true` flag prevents accidental publishing to npm. diff --git a/typescript-test-project/package.json b/typescript-test-project/package.json new file mode 100644 index 00000000..7f38c134 --- /dev/null +++ b/typescript-test-project/package.json @@ -0,0 +1,25 @@ +{ + "name": "marklogic-typescript-test-project", + "version": "1.0.0", + "description": "Test project for validating MarkLogic Node.js Client TypeScript definitions", + "private": true, + "scripts": { + "setup": "npm install .. && npm install", + "typecheck": "tsc --noEmit", + "test": "npm run typecheck" + }, + "keywords": [ + "marklogic", + "typescript", + "test" + ], + "author": "", + "license": "Apache-2.0", + "devDependencies": { + "@types/node": "^22.10.1", + "typescript": "^5.7.2" + }, + "dependencies": { + "marklogic": "file:.." + } +} diff --git a/typescript-test-project/setup.sh b/typescript-test-project/setup.sh new file mode 100755 index 00000000..4b809471 --- /dev/null +++ b/typescript-test-project/setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# Quick setup script for test-project + +echo "🚀 Setting up TypeScript test project..." +echo "" +echo "This will:" +echo " 1. Install the parent marklogic package (file:..)" +echo " 2. Install TypeScript and @types/node" +echo " 3. Verify type checking works" +echo "" + +npm install + +if [ $? -eq 0 ]; then + echo "" + echo "✅ Setup complete!" + echo "" + echo "Try these commands:" + echo " npm test - Check TypeScript types" + echo " npm run typecheck - Same as above" + echo "" + echo "Open test.ts in your IDE to see autocomplete and IntelliSense in action!" +else + echo "" + echo "❌ Setup failed. Check the errors above." + exit 1 +fi diff --git a/typescript-test-project/test.ts b/typescript-test-project/test.ts new file mode 100644 index 00000000..2c58e4ba --- /dev/null +++ b/typescript-test-project/test.ts @@ -0,0 +1,98 @@ +/* +* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. +*/ + +/** + * Realistic TypeScript usage example for MarkLogic Node.js Client + * + * This file demonstrates how users will consume the library with TypeScript. + * + * Setup: + * npm run setup # First time only + * npm test # Check types + * + * Try this: + * - Start typing to see autocomplete + * - Hover over methods to see documentation + * - Uncomment error examples to see TypeScript catch mistakes + */ + +import * as marklogic from 'marklogic'; + +// Example: Create a client with autocomplete support +const client = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin', + authType: 'digest' // Try typing here - autocomplete suggests all valid auth types! +}); + +// Example: Test connection with proper typing +async function testConnection() { + const result = await client.checkConnection(); + + // TypeScript knows the shape of ConnectionCheckResult + if (result.connected) { + console.log('✅ Connected successfully!'); + } else { + // These optional properties only exist when connected = false + console.error(`❌ Connection failed: ${result.httpStatusCode} - ${result.httpStatusMessage}`); + } + + return result; +} + +// Example: Proper cleanup pattern +async function properUsagePattern() { + const db = marklogic.createDatabaseClient({ + host: 'localhost', + port: 8000, + user: 'admin', + password: 'admin' + }); + + try { + const status = await db.checkConnection(); + if (status.connected) { + console.log('Ready to work with database'); + // Do your database operations here... + } + } finally { + // Always clean up - TypeScript knows this method exists + db.release(); + + // Alternative: use the standalone function + // marklogic.releaseClient(db); + } +} + +// ============================================================================= +// ERROR EXAMPLES - Uncomment these to see TypeScript catch mistakes! +// ============================================================================= + +// ❌ Error: Invalid authType value +// const badAuthType = marklogic.createDatabaseClient({ +// authType: 'invalid-type' +// }); + +// ❌ Error: port should be number, not string +// const badPort = marklogic.createDatabaseClient({ +// port: '8000' +// }); + +// ❌ Error: ConnectionCheckResult.connected must be boolean +// const badResult: marklogic.ConnectionCheckResult = { +// connected: 'yes' // Error: string is not assignable to boolean +// }; + +// ❌ Error: httpStatusCode must be number +// const badStatusCode: marklogic.ConnectionCheckResult = { +// connected: false, +// httpStatusCode: 'error' // Error: string is not assignable to number +// }; + +console.log('✅ TypeScript validation complete!'); + +// Export functions to prevent unused warnings +export { testConnection, properUsagePattern }; diff --git a/typescript-test-project/tsconfig.json b/typescript-test-project/tsconfig.json new file mode 100644 index 00000000..bdc0577a --- /dev/null +++ b/typescript-test-project/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + /* Language and Environment */ + "target": "ES2020", + "lib": ["ES2020"], + + /* Modules */ + "module": "commonjs", + "moduleResolution": "node", + "resolveJsonModule": true, + + /* Type Checking */ + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "strictBindCallApply": true, + "strictPropertyInitialization": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + + /* Emit */ + "noEmit": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + + /* Completeness */ + "skipLibCheck": true + }, + "include": [ + "*.ts" + ], + "exclude": [ + "node_modules" + ] +}