Skip to content

Commit 5b0bb2f

Browse files
Refactor: Remove models, use array-based API interaction
This commit completes a major refactoring to remove data model objects for API requests and responses. The library now follows a pattern closer to KnpLabs/php-github-api, where API service methods accept direct parameters and return associative arrays (decoded JSON). Key changes include: 1. **Model Directory Removed**: I have deleted the entire `lib/ArgoCD/Model/` directory. 2. **Service Refactoring (`AccountService`, `SessionService`):** * Methods in these services now accept direct scalar/array parameters. * Request bodies are constructed internally as associative arrays. * Responses are returned directly as associative arrays. 3. **`Client::authenticate` Update**: I updated the method to handle the array response from `SessionService::create()`. 4. **`ApplicationService` Implemented**: * I created `lib/ArgoCD/Api/ApplicationService.php`. * Core methods (list, create, get, update, delete, sync, rollback, managedResources, resourceTree, getManifests) are implemented using the array-based interaction pattern. 5. **Unit Tests Updated/Created**: * I have created/updated tests for `AccountService`, `SessionService`, and `ApplicationService` to reflect the array-based API interactions. 6. **Documentation (`AI.md`) Updated**: `AI.md` now accurately describes the refactored library, its usage patterns (including authentication and fetching application details), and the removal of models. 7. **Test Cleanup**: I removed obsolete organization-related tests. This refactoring simplifies the data handling within the client and aligns the development style with your feedback.
1 parent bd2b53e commit 5b0bb2f

24 files changed

+1029
-1102
lines changed

AI.md

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,107 @@ This document provides context on the development process of this PHP client for
44

55
## Project Goal
66

7-
The primary goal of this project was to refactor an existing PHP client library originally designed for the GitHub API (KnpLabs/php-github-api) and adapt it to become a fully functional client for the ArgoCD REST API.
7+
The primary goal of this project was to refactor an existing PHP client library (originally forked from a GitHub API client) and adapt it to become a fully functional client for the ArgoCD REST API. A key aspect of the refactoring was to move away from a model-based approach for requests and responses to a more direct, array-based interaction style, similar to the KnpLabs/php-github-api library.
88

99
## Development Process
1010

1111
The development process involved a collaborative effort between a human developer and the AI agent, Jules. Key aspects of this process include:
1212

13-
* **Initial Request:** The human developer provided an issue statement outlining the need to fork the GitHub library, study its structure, and then refactor it to implement ArgoCD's OpenAPI specification (`https://raw.githubusercontent.com/argoproj/argo-cd/master/assets/swagger.json`).
13+
* **Initial Request:** The human developer provided an issue statement outlining the need to fork the GitHub library, study its structure, and then refactor it to implement ArgoCD's OpenAPI specification (`reference/argocd_swagger.json`).
1414
* **Codebase Analysis:** Jules explored the existing GitHub client's codebase using tools to list files and read their content to understand its architecture and patterns.
1515
* **OpenAPI Analysis:** Jules fetched and analyzed the ArgoCD OpenAPI specification to understand its endpoints, data structures, and service organization.
16-
* **Planning:** Based on the analysis, Jules created a detailed, multi-step plan to execute the refactoring. This plan was reviewed and approved by the human developer.
16+
* **Planning & Refactoring Strategy:** Based on the analysis, Jules created a detailed, multi-step plan. A significant decision during this phase was to refactor the library to **remove dedicated Model classes** for API requests and responses.
17+
* API methods now accept direct parameters (strings, arrays, booleans, etc.).
18+
* Request bodies are constructed internally as associative arrays.
19+
* API methods return associative arrays directly, representing the decoded JSON responses from the ArgoCD API.
20+
* This array-based approach for request/response handling aligns more closely with the interaction style of the KnpLabs/php-github-api library.
1721
* **Iterative Implementation:** Jules executed the plan step-by-step by delegating specific, actionable tasks to a "Worker" agent. These tasks included:
18-
* Creating new directory structures.
19-
* Adapting core classes that are derived from the fork, to work with the constraints of ArgoCD.
22+
* Creating new directory structures and removing old Model directories (e.g., `lib/ArgoCD/Model/`).
23+
* Adapting core classes derived from the fork to work with the constraints of ArgoCD and the new array-based data handling.
2024
* Implementing exception handling.
21-
* Implementing API classes (e.g., `Sessions.php`, `Accounts.php`) with methods corresponding to ArgoCD API operations.
22-
* Interaction with these methods should be like [KnpLabs/php-github-api](https://github.com/KnpLabs/php-github-api).
23-
* Any other validation is done through the parameter resolver like in [KnpLabs/php-github-api](https://github.com/KnpLabs/php-github-api).
24-
* **Feedback Incorporation:** The human developer provided feedback at various stages (e.g., on directory structure, PHP version constraints), and Jules updated the plan and execution accordingly.
25+
* Implementing API service classes (e.g., `SessionService.php`, `AccountService.php`, `ApplicationService.php`) with methods corresponding to ArgoCD API operations. These methods were designed to accept direct parameters and return associative arrays.
26+
* Generating unit tests for the service classes, ensuring they correctly handle array-based inputs and outputs.
27+
* **Feedback Incorporation:** The human developer provided feedback at various stages (e.g., on directory structure, PHP version constraints, refactoring strategy), and Jules updated the plan and execution accordingly.
28+
29+
## Library Usage Examples
30+
31+
### Authentication
32+
33+
To use the client, first instantiate it and then authenticate.
34+
35+
**Username/Password Authentication:**
36+
```php
37+
<?php
38+
require_once __DIR__ . '/vendor/autoload.php'; // Adjust path as needed
39+
40+
$client = new \ArgoCD\Client('https://your-argocd-server.com');
41+
42+
try {
43+
$client->authenticate('your-username', 'your-password');
44+
// Authentication successful, client is now configured with a token.
45+
echo "Authentication successful!\n";
46+
} catch (\ArgoCD\Exception\InvalidArgumentException $e) {
47+
// Handle authentication failure (e.g., invalid credentials)
48+
echo "Authentication failed: " . $e->getMessage() . "\n";
49+
} catch (\ArgoCD\Exception\RuntimeException $e) {
50+
// Handle API communication errors
51+
echo "API Error: " . $e->getMessage() . "\n";
52+
}
53+
```
54+
55+
**Token Authentication:**
56+
```php
57+
<?php
58+
require_once __DIR__ . '/vendor/autoload.php'; // Adjust path as needed
59+
60+
$client = new \ArgoCD\Client('https://your-argocd-server.com');
61+
$authToken = 'your-pre-existing-auth-token';
62+
63+
try {
64+
$client->authenticate($authToken);
65+
// Authentication successful, client is now configured with the provided token.
66+
echo "Authentication successful!\n";
67+
} catch (\ArgoCD\Exception\InvalidArgumentException $e) {
68+
// Handle token validation issues (though less common for direct token auth)
69+
echo "Authentication failed: " . $e->getMessage() . "\n";
70+
}
71+
```
72+
Successful authentication configures the client internally with the necessary token for subsequent API calls.
73+
74+
### Fetching Application Details
75+
76+
Once authenticated, you can interact with the API services. For example, to fetch application details:
77+
78+
```php
79+
<?php
80+
// Assuming $client is an authenticated ArgoCD\Client instance
81+
82+
try {
83+
/** @var \ArgoCD\Api\ApplicationService $applicationApi */
84+
$applicationApi = $client->api('application'); // or $client->applicationService()
85+
86+
// List applications, e.g., filtered by project
87+
$appsList = $applicationApi->list(['projects' => ['default']]);
88+
echo "Applications in 'default' project:\n";
89+
print_r($appsList); // $appsList is an associative array
90+
91+
// Get details for a specific application
92+
$appName = 'my-sample-app';
93+
$appDetails = $applicationApi->get($appName);
94+
echo "\nDetails for application '$appName':\n";
95+
print_r($appDetails); // $appDetails is an associative array
96+
97+
} catch (\ArgoCD\Exception\RuntimeException $e) {
98+
echo "API Error: " . $e->getMessage() . "\n";
99+
}
100+
```
101+
Methods like `list()` and `get()` return associative arrays containing the application data directly decoded from the API's JSON response.
102+
103+
## Library Status
104+
105+
The refactoring to an array-based interaction style for the `AccountService`, `SessionService`, and `ApplicationService` is complete. These services now accept direct parameters for requests and return associative arrays for responses, simplifying their usage.
25106

26107
## Acknowledgements
27108

28-
* The initial structure and patterns were derived from the excellent [KnpLabs/php-github-api](https://github.com/KnpLabs/php-github-api) library.
29-
* The target API is [ArgoCD](https://argo-cd.readthedocs.io/en/stable/), and its OpenAPI specification was used as the blueprint for API implementation.
30-
* You can find the OpenAPI spec in `reference/argocd_swagger.json`.
109+
* The initial structure and patterns were derived from the excellent [KnpLabs/php-github-api](https://github.com/KnpLabs/php-github-api) library. The refactoring aimed to bring the request/response handling closer to this style.
110+
* The target API is [ArgoCD](https://argo-cd.readthedocs.io/en/stable/), and its OpenAPI specification (available in `reference/argocd_swagger.json` in this repository) was used as the blueprint for API implementation.

lib/ArgoCD/Api/AccountService.php

Lines changed: 35 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
<?php
22
namespace ArgoCD\Api;
33

4-
use ArgoCD\Model\AccountAccount;
5-
use ArgoCD\Model\AccountAccountsList;
6-
use ArgoCD\Model\AccountCanIResponse;
7-
use ArgoCD\Model\AccountCreateTokenRequest;
8-
use ArgoCD\Model\AccountCreateTokenResponse;
9-
use ArgoCD\Model\AccountEmptyResponse;
10-
use ArgoCD\Model\AccountUpdatePasswordRequest;
11-
use ArgoCD\Model\AccountUpdatePasswordResponse;
4+
// Model imports removed
125

136
class AccountService extends AbstractApi
147
{
158
/**
169
* Corresponds to AccountService_ListAccounts
1710
* Lists all accounts.
1811
*
19-
* @return AccountAccountsList
12+
* @return array
2013
* @throws \ArgoCD\Exception\RuntimeException
2114
*/
22-
public function listAccounts(): AccountAccountsList
15+
public function listAccounts(): array
2316
{
24-
$responseArray = $this->get('/api/v1/account');
25-
return new AccountAccountsList($responseArray);
17+
return $this->get('/api/v1/account');
2618
}
2719

2820
/**
@@ -32,79 +24,72 @@ public function listAccounts(): AccountAccountsList
3224
* @param string $resource
3325
* @param string $action
3426
* @param string $subresource
35-
* @return AccountCanIResponse
27+
* @return array
3628
* @throws \ArgoCD\Exception\RuntimeException
3729
*/
38-
public function canI(string $resource, string $action, string $subresource): AccountCanIResponse
30+
public function canI(string $resource, string $action, string $subresource): array
3931
{
40-
// The response for can-i is typically a raw string "yes" or "no".
41-
// The AbstractApi::get method expects JSON.
42-
// We need to handle this: either get() should allow raw responses,
43-
// or this method needs to handle potential JSON decode errors if the response isn't JSON.
44-
// For now, assuming get() returns the raw string if not JSON,
45-
// and AccountCanIResponse constructor can handle it.
4632
$response = $this->get(sprintf("/api/v1/account/can-i/%s/%s/%s", rawurlencode($resource), rawurlencode($action), rawurlencode($subresource)));
4733

48-
// If $response is a string from get(), AccountCanIResponse constructor is designed to handle it.
49-
// If $response is an array (e.g. {'value': 'yes'}), it also handles it.
50-
return new AccountCanIResponse(is_array($response) ? $response : ['value' => $response]);
34+
// If $response is a string from get(), convert to array format.
35+
// Otherwise, it's assumed to be already an array (e.g. from JSON response).
36+
return is_array($response) ? $response : ['value' => $response];
5137
}
5238

5339
/**
5440
* Corresponds to AccountService_UpdatePassword
5541
* Updates the password for the current account or a specified account.
5642
*
57-
* @param string $name The name of the account to update. If updating the current user's password, this might be the username.
43+
* @param string $name The name of the account to update.
5844
* @param string $currentPassword The current password.
5945
* @param string $newPassword The new password.
60-
* @return AccountUpdatePasswordResponse
46+
* @return array
6147
* @throws \ArgoCD\Exception\RuntimeException
6248
*/
63-
public function updatePassword(string $name, string $currentPassword, string $newPassword): AccountUpdatePasswordResponse
49+
public function updatePassword(string $name, string $currentPassword, string $newPassword): array
6450
{
65-
$requestModel = new AccountUpdatePasswordRequest();
66-
$requestModel->setName($name); // Name of the account being updated
67-
$requestModel->setCurrentPassword($currentPassword);
68-
$requestModel->setNewPassword($newPassword);
51+
$body = [
52+
'name' => $name,
53+
'currentPassword' => $currentPassword,
54+
'newPassword' => $newPassword,
55+
];
6956

70-
$responseArray = $this->put('/api/v1/account/password', $requestModel->toArray());
71-
return new AccountUpdatePasswordResponse($responseArray ?: []); // Response might be empty
57+
return $this->put('/api/v1/account/password', $body);
7258
}
7359

7460
/**
7561
* Corresponds to AccountService_GetAccount
7662
* Gets information about a specific account.
7763
*
7864
* @param string $name The name of the account.
79-
* @return AccountAccount
65+
* @return array
8066
* @throws \ArgoCD\Exception\RuntimeException
8167
*/
82-
public function getAccount(string $name): AccountAccount
68+
public function getAccount(string $name): array
8369
{
84-
$responseArray = $this->get(sprintf("/api/v1/account/%s", rawurlencode($name)));
85-
return new AccountAccount($responseArray);
70+
return $this->get(sprintf("/api/v1/account/%s", rawurlencode($name)));
8671
}
8772

8873
/**
8974
* Corresponds to AccountService_CreateToken
9075
* Creates a new token for the specified account.
9176
*
92-
* @param string $accountName The name of the account.
93-
* @param string $tokenId The desired ID/name for the token.
94-
* @param string $tokenDescription A description for the token.
77+
* @param string $accountName The name of the account (used in URL path).
78+
* @param string $tokenId The desired ID/name for the token (maps to 'id' in request body).
79+
* @param string $tokenNameOrDescription A description for the token (maps to 'name' in request body).
9580
* @param string|null $expiresIn Duration string for token expiration (e.g., "30d", "24h", "0" for non-expiring).
96-
* @return AccountCreateTokenResponse
81+
* @return array
9782
* @throws \ArgoCD\Exception\RuntimeException
9883
*/
99-
public function createToken(string $accountName, string $tokenId, string $tokenDescription, ?string $expiresIn = "0"): AccountCreateTokenResponse
84+
public function createToken(string $accountName, string $tokenId, string $tokenNameOrDescription, ?string $expiresIn = "0"): array
10085
{
101-
$requestModel = new AccountCreateTokenRequest();
102-
$requestModel->setId($tokenId); // This 'id' is the token's identifier
103-
$requestModel->setName($tokenDescription); // This 'name' is the token's description
104-
$requestModel->setExpiresIn($expiresIn);
86+
$body = [
87+
'id' => $tokenId,
88+
'name' => $tokenNameOrDescription,
89+
'expiresIn' => $expiresIn,
90+
];
10591

106-
$responseArray = $this->post(sprintf("/api/v1/account/%s/token", rawurlencode($accountName)), $requestModel->toArray());
107-
return new AccountCreateTokenResponse($responseArray);
92+
return $this->post(sprintf("/api/v1/account/%s/token", rawurlencode($accountName)), $body);
10893
}
10994

11095
/**
@@ -113,12 +98,11 @@ public function createToken(string $accountName, string $tokenId, string $tokenD
11398
*
11499
* @param string $accountName The name of the account.
115100
* @param string $tokenId The ID of the token to delete.
116-
* @return AccountEmptyResponse
101+
* @return array
117102
* @throws \ArgoCD\Exception\RuntimeException
118103
*/
119-
public function deleteToken(string $accountName, string $tokenId): AccountEmptyResponse
104+
public function deleteToken(string $accountName, string $tokenId): array
120105
{
121-
$responseArray = $this->delete(sprintf("/api/v1/account/%s/token/%s", rawurlencode($accountName), rawurlencode($tokenId)));
122-
return new AccountEmptyResponse($responseArray ?: []); // Response is typically empty
106+
return $this->delete(sprintf("/api/v1/account/%s/token/%s", rawurlencode($accountName), rawurlencode($tokenId)));
123107
}
124108
}

0 commit comments

Comments
 (0)