|
| 1 | +# Jenkins Client - Refactored Architecture |
| 2 | + |
| 3 | +This directory contains the refactored Jenkins client implementation following TypeScript best practices and design patterns. |
| 4 | + |
| 5 | +## Architecture Overview |
| 6 | + |
| 7 | +The Jenkins client has been refactored using a service-oriented architecture with the following benefits: |
| 8 | + |
| 9 | +- **Single Responsibility Principle**: Each class has one clear purpose |
| 10 | +- **Type Safety**: Comprehensive TypeScript types throughout |
| 11 | +- **Testability**: Smaller, focused classes are easier to unit test |
| 12 | +- **Maintainability**: Changes to specific functionality affect fewer files |
| 13 | +- **Reusability**: Utility classes can be reused across the codebase |
| 14 | +- **Error Handling**: Consistent, typed error handling |
| 15 | +- **Configuration**: Centralized configuration management |
| 16 | +- **Extensibility**: Easy to add new credential types or job configurations |
| 17 | + |
| 18 | +## Directory Structure |
| 19 | + |
| 20 | +``` |
| 21 | +jenkins/ |
| 22 | +├── enums/ # Enums for Jenkins constants |
| 23 | +│ └── jenkins.enums.ts |
| 24 | +├── types/ # TypeScript type definitions |
| 25 | +│ └── jenkins.types.ts |
| 26 | +├── config/ # Configuration constants |
| 27 | +│ └── jenkins.config.ts |
| 28 | +├── errors/ # Custom error classes |
| 29 | +│ └── jenkins.errors.ts |
| 30 | +├── utils/ # Utility classes |
| 31 | +│ └── jenkins.utils.ts |
| 32 | +├── strategies/ # Strategy pattern implementations |
| 33 | +│ └── credential.strategy.ts |
| 34 | +├── http/ # HTTP client abstraction |
| 35 | +│ └── jenkins-http.client.ts |
| 36 | +├── services/ # Business logic services |
| 37 | +│ ├── jenkins-job.service.ts |
| 38 | +│ ├── jenkins-build.service.ts |
| 39 | +│ └── jenkins-credential.service.ts |
| 40 | +├── jenkins.client.ts # Main client facade |
| 41 | +├── index.ts # Module exports |
| 42 | +└── README.md # This file |
| 43 | +``` |
| 44 | + |
| 45 | +## Usage Examples |
| 46 | + |
| 47 | +### Basic Usage (Backwards Compatible) |
| 48 | + |
| 49 | +```typescript |
| 50 | +import { JenkinsClient } from './jenkins'; |
| 51 | + |
| 52 | +const client = new JenkinsClient({ |
| 53 | + baseUrl: 'https://jenkins.example.com', |
| 54 | + username: 'your-username', |
| 55 | + token: 'your-api-token' |
| 56 | +}); |
| 57 | + |
| 58 | +// Create a job (legacy method signature) |
| 59 | +await client.createJob( |
| 60 | + 'my-job', |
| 61 | + 'https://github.com/user/repo.git', |
| 62 | + 'my-folder', |
| 63 | + 'main', |
| 64 | + 'Jenkinsfile', |
| 65 | + 'git-credentials' |
| 66 | +); |
| 67 | + |
| 68 | +// Trigger a build |
| 69 | +await client.build('my-job', 'my-folder', { PARAM1: 'value1' }); |
| 70 | + |
| 71 | +// Get build information |
| 72 | +const build = await client.getBuild('my-job', 123, 'my-folder'); |
| 73 | +``` |
| 74 | + |
| 75 | +### New Options-Based Usage |
| 76 | + |
| 77 | +```typescript |
| 78 | +import { JenkinsClient, CreateJobOptions, BuildOptions } from './jenkins'; |
| 79 | + |
| 80 | +const client = new JenkinsClient({ |
| 81 | + baseUrl: 'https://jenkins.example.com', |
| 82 | + username: 'your-username', |
| 83 | + token: 'your-api-token' |
| 84 | +}); |
| 85 | + |
| 86 | +// Create a job (new options signature) |
| 87 | +const jobOptions: CreateJobOptions = { |
| 88 | + jobName: 'my-job', |
| 89 | + repoUrl: 'https://github.com/user/repo.git', |
| 90 | + folderName: 'my-folder', |
| 91 | + branch: 'main', |
| 92 | + jenkinsfilePath: 'Jenkinsfile', |
| 93 | + credentialId: 'git-credentials' |
| 94 | +}; |
| 95 | +await client.createJob(jobOptions); |
| 96 | + |
| 97 | +// Trigger a build with options |
| 98 | +const buildOptions: BuildOptions = { |
| 99 | + jobName: 'my-job', |
| 100 | + folderName: 'my-folder', |
| 101 | + parameters: { PARAM1: 'value1' } |
| 102 | +}; |
| 103 | +await client.build(buildOptions); |
| 104 | +``` |
| 105 | + |
| 106 | +### Direct Service Access |
| 107 | + |
| 108 | +```typescript |
| 109 | +import { JenkinsClient } from './jenkins'; |
| 110 | + |
| 111 | +const client = new JenkinsClient(config); |
| 112 | + |
| 113 | +// Access individual services for advanced operations |
| 114 | +const jobs = client.jobs; |
| 115 | +const builds = client.builds; |
| 116 | +const credentials = client.credentials; |
| 117 | + |
| 118 | +// Use services directly |
| 119 | +const runningBuilds = await builds.getRunningBuilds('my-job', 'my-folder'); |
| 120 | +const jobExists = await jobs.jobExists('my-job', 'my-folder'); |
| 121 | +await credentials.createSecretTextCredential('my-folder', 'my-secret', 'secret-value'); |
| 122 | +``` |
| 123 | + |
| 124 | +### Error Handling |
| 125 | + |
| 126 | +```typescript |
| 127 | +import { |
| 128 | + JenkinsClient, |
| 129 | + JenkinsJobNotFoundError, |
| 130 | + JenkinsBuildTimeoutError, |
| 131 | + JenkinsAuthenticationError |
| 132 | +} from './jenkins'; |
| 133 | + |
| 134 | +try { |
| 135 | + const build = await client.getBuild('non-existent-job', 123); |
| 136 | +} catch (error) { |
| 137 | + if (error instanceof JenkinsJobNotFoundError) { |
| 138 | + console.log('Job not found:', error.message); |
| 139 | + } else if (error instanceof JenkinsAuthenticationError) { |
| 140 | + console.log('Authentication failed:', error.message); |
| 141 | + } else { |
| 142 | + console.log('Unexpected error:', error); |
| 143 | + } |
| 144 | +} |
| 145 | +``` |
| 146 | + |
| 147 | +## Design Patterns Used |
| 148 | + |
| 149 | +### 1. Facade Pattern |
| 150 | +- `JenkinsClient` acts as a facade providing a simple interface to the complex subsystem |
| 151 | + |
| 152 | +### 2. Strategy Pattern |
| 153 | +- `CredentialStrategy` and implementations for different credential types |
| 154 | +- Easy to add new credential types without modifying existing code |
| 155 | + |
| 156 | +### 3. Service Layer Pattern |
| 157 | +- Business logic separated into focused service classes |
| 158 | +- Each service handles one domain (jobs, builds, credentials) |
| 159 | + |
| 160 | +### 4. Builder Pattern |
| 161 | +- `JenkinsXmlBuilder` for constructing XML configurations |
| 162 | +- `JenkinsPathBuilder` for constructing API paths |
| 163 | + |
| 164 | +### 5. Factory Pattern |
| 165 | +- `CredentialStrategyFactory` for creating credential strategies |
| 166 | + |
| 167 | +### 6. Error Handling Pattern |
| 168 | +- Custom error hierarchy with specific error types |
| 169 | +- Consistent error handling across all services |
| 170 | + |
| 171 | +## Configuration |
| 172 | + |
| 173 | +All configuration constants are centralized in `JenkinsConfig`: |
| 174 | + |
| 175 | +```typescript |
| 176 | +import { JenkinsConfig } from './jenkins'; |
| 177 | + |
| 178 | +// Access default values |
| 179 | +const timeout = JenkinsConfig.DEFAULT_TIMEOUT_MS; |
| 180 | +const headers = JenkinsConfig.HEADERS.JSON; |
| 181 | +const endpoint = JenkinsConfig.ENDPOINTS.API_JSON; |
| 182 | +``` |
| 183 | + |
| 184 | +## Extending the Client |
| 185 | + |
| 186 | +### Adding New Credential Types |
| 187 | + |
| 188 | +1. Add the new type to `CredentialType` enum |
| 189 | +2. Create a new strategy class implementing `CredentialStrategy` |
| 190 | +3. Register it in `CredentialStrategyFactory` |
| 191 | + |
| 192 | +### Adding New Services |
| 193 | + |
| 194 | +1. Create a new service class in `services/` |
| 195 | +2. Add it to the main `JenkinsClient` constructor |
| 196 | +3. Expose it through the facade if needed |
| 197 | + |
| 198 | +### Adding New Error Types |
| 199 | + |
| 200 | +1. Create new error classes extending `JenkinsError` |
| 201 | +2. Export them from `errors/jenkins.errors.ts` |
| 202 | +3. Use them in appropriate services |
| 203 | + |
| 204 | +## Testing |
| 205 | + |
| 206 | +The refactored architecture makes testing much easier: |
| 207 | + |
| 208 | +```typescript |
| 209 | +// Mock individual services |
| 210 | +const mockJobService = { |
| 211 | + createJob: jest.fn(), |
| 212 | + getJob: jest.fn(), |
| 213 | +}; |
| 214 | + |
| 215 | +// Test services in isolation |
| 216 | +const jobService = new JenkinsJobService(mockHttpClient); |
| 217 | +``` |
| 218 | + |
| 219 | +## Performance Considerations |
| 220 | + |
| 221 | +- Services are lightweight and share the same HTTP client instance |
| 222 | +- Path building and XML generation are optimized |
| 223 | +- Error handling is consistent and efficient |
| 224 | +- Configuration is loaded once and reused |
| 225 | + |
| 226 | +## Security |
| 227 | + |
| 228 | +- Credentials are handled through the strategy pattern |
| 229 | +- Sensitive data is not logged |
| 230 | +- XML escaping prevents injection attacks |
| 231 | +- Type-safe parameter handling |
0 commit comments