diff --git a/bin/generate-client-docs.php b/bin/generate-client-docs.php index c23ef7a..d1194cd 100644 --- a/bin/generate-client-docs.php +++ b/bin/generate-client-docs.php @@ -267,17 +267,26 @@ function frontmatter(string $title, string $description): string // --------------------------------------------------------------------- // 4. Walk the tree and write files +// +// Recursive to any depth: a node with no child accessors renders as a +// flat NN.slug.md file; a node with children renders as a folder with +// its own 00.index.md (API methods + a Related list) plus one file +// (flat or, recursively, another folder) per child. // --------------------------------------------------------------------- -@mkdir($outDir, 0777, true); -file_put_contents($outDir . '/.navigation.yml', "title: Client Reference\nicon: i-lucide-terminal\n"); - -$topIndex = 0; -foreach ($topLevel as $accessorName => $class) { +function writeNode( + DocBlockFactory $docFactory, + string $accessorName, + string $accessorChain, + string $class, + string $dir, + int $prefixIndex, + string $urlPath +): void { [$rc, $apiMethods, $children] = analyzeClass($class); $slug = kebab($accessorName); - $prefix = str_pad((string) $topIndex++, 2, '0', STR_PAD_LEFT); + $prefix = str_pad((string) $prefixIndex, 2, '0', STR_PAD_LEFT); $title = humanize($accessorName); $classDoc = docblockFor($docFactory, $rc); @@ -285,21 +294,21 @@ function frontmatter(string $title, string $description): string $classSummary = $realSummary !== '' ? $realSummary : "{$title} endpoint reference."; if (empty($children)) { - // flat file: docs/content/2.client/NN.slug.md + // flat file: .../NN.slug.md $body = frontmatter($title, $classSummary); if ($realSummary !== '') { $body .= $realSummary . "\n\n"; } foreach ($apiMethods as $m) { - $body .= renderMethodSection($docFactory, $m, "{$accessorName}()"); + $body .= renderMethodSection($docFactory, $m, $accessorChain); } - file_put_contents("{$outDir}/{$prefix}.{$slug}.md", $body); - continue; + file_put_contents("{$dir}/{$prefix}.{$slug}.md", $body); + return; } - // folder with index.md + one file per child - $dir = "{$outDir}/{$prefix}.{$slug}"; - @mkdir($dir, 0777, true); + // folder with index.md + one file (or subfolder) per child + $nodeDir = "{$dir}/{$prefix}.{$slug}"; + @mkdir($nodeDir, 0777, true); $body = frontmatter($title, $classSummary); if ($realSummary !== '') { @@ -307,47 +316,65 @@ function frontmatter(string $title, string $description): string } foreach ($apiMethods as $m) { - $body .= renderMethodSection($docFactory, $m, "{$accessorName}()"); + $body .= renderMethodSection($docFactory, $m, $accessorChain); } - if (!empty($children)) { - $body .= "## Related\n\n"; - foreach ($children as $childAccessor => $childClass) { - $childTitle = humanize($childAccessor); - $body .= "- [{$childTitle}](/client/{$slug}/" . kebab($childAccessor) . ")\n"; - } - $body .= "\n"; + $body .= "## Related\n\n"; + foreach ($children as $childAccessor => $childClass) { + $childTitle = humanize($childAccessor); + $body .= "- [{$childTitle}]({$urlPath}/" . kebab($childAccessor) . ")\n"; } + $body .= "\n"; - file_put_contents("{$dir}/00.index.md", $body); + file_put_contents("{$nodeDir}/00.index.md", $body); $childIndex = 1; foreach ($children as $childAccessor => $childClass) { - [$childRc, $childApiMethods, $grandchildren] = analyzeClass($childClass); - - $childSlug = kebab($childAccessor); - $childPrefix = str_pad((string) $childIndex++, 2, '0', STR_PAD_LEFT); - $childTitle = humanize($childAccessor); - - $childClassDoc = docblockFor($docFactory, $childRc); - $realChildSummary = $childClassDoc?->getSummary() ?: ''; - $childSummary = $realChildSummary !== '' ? $realChildSummary : "{$childTitle} endpoint reference."; - - $childBody = frontmatter($childTitle, $childSummary); - if ($realChildSummary !== '') { - $childBody .= $realChildSummary . "\n\n"; - } + writeNode( + $docFactory, + $childAccessor, + "{$accessorChain}->{$childAccessor}()", + $childClass, + $nodeDir, + $childIndex++, + "{$urlPath}/" . kebab($childAccessor) + ); + } +} - foreach ($childApiMethods as $m) { - $childBody .= renderMethodSection($docFactory, $m, "{$accessorName}()->{$childAccessor}()"); +function rrmdir(string $dir): void +{ + if (!is_dir($dir)) { + return; + } + foreach (scandir($dir) as $entry) { + if ($entry === '.' || $entry === '..') { + continue; } + $path = "{$dir}/{$entry}"; + is_dir($path) ? rrmdir($path) : unlink($path); + } + rmdir($dir); +} - if (!empty($grandchildren)) { - fwrite(STDERR, "WARNING: {$childClass} has its own child accessors — grandchild nesting isn't handled, methods skipped: " . implode(', ', array_keys($grandchildren)) . "\n"); - } +// Wipe the generated tree first so renumbered/removed accessors don't +// leave stale files behind (this whole directory is mechanical output; +// hand-curated docs live under 2.api instead). +rrmdir($outDir); +@mkdir($outDir, 0777, true); +file_put_contents($outDir . '/.navigation.yml', "title: Client Reference\nicon: i-lucide-terminal\n"); - file_put_contents("{$dir}/{$childPrefix}.{$childSlug}.md", $childBody); - } +$topIndex = 0; +foreach ($topLevel as $accessorName => $class) { + writeNode( + $docFactory, + $accessorName, + "{$accessorName}()", + $class, + $outDir, + $topIndex++, + '/client/' . kebab($accessorName) + ); } echo "Generated client reference docs in {$outDir}\n"; diff --git a/docs/content/2.client/00.accounts/00.index.md b/docs/content/2.client/00.accounts/00.index.md index 38864b8..6ab127f 100644 --- a/docs/content/2.client/00.accounts/00.index.md +++ b/docs/content/2.client/00.accounts/00.index.md @@ -132,9 +132,81 @@ $response = $client->accounts()->delete('ACCOUNT_ID'); View this operation on the Cloudflare API Reference :: +## Profile + +Get account profile. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." +--- +:: + +```php [php] +$response = $client->accounts()->profile('ACCOUNT_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/Accounts_getAccountProfile"} +View this operation on the Cloudflare API Reference +:: + +## Update Profile + +Modify account profile. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "values" + type: "array" + required: false + description: "Account profile values." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->updateProfile('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/Accounts_modifyAccountProfile"} +View this operation on the Cloudflare API Reference +:: + +## Organizations + +List account organizations. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." +--- +:: + +```php [php] +$response = $client->accounts()->organizations('ACCOUNT_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/Accounts_listAccountOrganizations"} +View this operation on the Cloudflare API Reference +:: + ## Related -- [Roles](/client/accounts/roles) - [Members](/client/accounts/members) -- [Audit Logs](/client/accounts/audit-logs) +- [Logs](/client/accounts/logs) +- [Subscriptions](/client/accounts/subscriptions) +- [Tokens](/client/accounts/tokens) diff --git a/docs/content/2.client/00.accounts/02.members.md b/docs/content/2.client/00.accounts/01.members.md similarity index 100% rename from docs/content/2.client/00.accounts/02.members.md rename to docs/content/2.client/00.accounts/01.members.md diff --git a/docs/content/2.client/00.accounts/01.roles.md b/docs/content/2.client/00.accounts/01.roles.md deleted file mode 100644 index d64ddf7..0000000 --- a/docs/content/2.client/00.accounts/01.roles.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Roles -description: "Roles endpoint reference." -navigation: - title: Roles ---- - -## List - -Get all available roles for an account. - -::params-table ---- -params: - - name: "accountId" - type: "string" - required: true - description: "Account identifier." - - name: "params" - type: "array" - required: false - description: "Array containing the necessary params." - default: "[]" ---- -:: - -```php [php] -$response = $client->accounts()->roles()->list('ACCOUNT_ID', []); -``` - -::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-roles-list-roles"} -View this operation on the Cloudflare API Reference -:: - -## Get - -Get information about a specific role for an account. - -::params-table ---- -params: - - name: "accountId" - type: "string" - required: true - description: "Account identifier." - - name: "roleId" - type: "string" - required: true - description: "Role identifier." ---- -:: - -```php [php] -$response = $client->accounts()->roles()->get('ACCOUNT_ID', 'ROLE_ID'); -``` - -::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-roles-role-details"} -View this operation on the Cloudflare API Reference -:: - diff --git a/docs/content/2.client/00.accounts/02.logs/00.index.md b/docs/content/2.client/00.accounts/02.logs/00.index.md new file mode 100644 index 0000000..ac2d630 --- /dev/null +++ b/docs/content/2.client/00.accounts/02.logs/00.index.md @@ -0,0 +1,11 @@ +--- +title: Logs +description: "Logs endpoint reference." +navigation: + title: Logs +--- + +## Related + +- [Audit](/client/accounts/logs/audit) + diff --git a/docs/content/2.client/00.accounts/02.logs/01.audit.md b/docs/content/2.client/00.accounts/02.logs/01.audit.md new file mode 100644 index 0000000..217e65c --- /dev/null +++ b/docs/content/2.client/00.accounts/02.logs/01.audit.md @@ -0,0 +1,87 @@ +--- +title: Audit +description: "Audit endpoint reference." +navigation: + title: Audit +--- + +## List + +Gets a list of audit logs for an account. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "params" + type: "array" + required: false + description: "Array containing the necessary params, requires since and before." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->logs()->audit()->list('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/audit-logs-v2-get-account-audit-logs"} +View this operation on the Cloudflare API Reference +:: + +## History + +Returns the chronological change history for the resource identified by the given audit log entry. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "id" + type: "string" + required: true + description: "Audit log entry identifier used to locate the resource." + - name: "params" + type: "array" + required: false + description: "Array containing the necessary params, requires action_time, since and before." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->logs()->audit()->history('ACCOUNT_ID', 'ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/audit-logs-v2-get-account-audit-log-history"} +View this operation on the Cloudflare API Reference +:: + +## Product Categories + +Lists the available audit log product categories and the resource products each one expands to. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." +--- +:: + +```php [php] +$response = $client->accounts()->logs()->audit()->productCategories('ACCOUNT_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/audit-logs-v2-list-account-product-categories"} +View this operation on the Cloudflare API Reference +:: + diff --git a/docs/content/2.client/00.accounts/03.audit-logs.md b/docs/content/2.client/00.accounts/03.audit-logs.md deleted file mode 100644 index 37d77f9..0000000 --- a/docs/content/2.client/00.accounts/03.audit-logs.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Audit Logs -description: "Audit Logs endpoint reference." -navigation: - title: Audit Logs ---- - -## List - -Gets a list of audit logs for an account. Can be filtered by who made the change, on which zone, and the timeframe of the change. - -::params-table ---- -params: - - name: "accountId" - type: "string" - required: true - description: "Account identifier." - - name: "params" - type: "array" - required: false - description: "Array containing the necessary params." - default: "[]" ---- -:: - -```php [php] -$response = $client->accounts()->auditLogs()->list('ACCOUNT_ID', []); -``` - -::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/audit-logs-get-account-audit-logs"} -View this operation on the Cloudflare API Reference -:: - diff --git a/docs/content/2.client/00.accounts/03.subscriptions.md b/docs/content/2.client/00.accounts/03.subscriptions.md new file mode 100644 index 0000000..405e2b0 --- /dev/null +++ b/docs/content/2.client/00.accounts/03.subscriptions.md @@ -0,0 +1,113 @@ +--- +title: Subscriptions +description: "Subscriptions endpoint reference." +navigation: + title: Subscriptions +--- + +## List + +Lists all of an account's subscriptions. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." +--- +:: + +```php [php] +$response = $client->accounts()->subscriptions()->list('ACCOUNT_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-subscriptions-list-subscriptions"} +View this operation on the Cloudflare API Reference +:: + +## Create + +Creates an account subscription. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "values" + type: "array" + required: false + description: "Subscription values, e.g. frequency, rate_plan." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->subscriptions()->create('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-subscriptions-create-subscription"} +View this operation on the Cloudflare API Reference +:: + +## Update + +Updates an account subscription. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "subscriptionId" + type: "string" + required: true + description: "Subscription identifier tag." + - name: "values" + type: "array" + required: false + description: "Subscription values, e.g. frequency, rate_plan." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->subscriptions()->update('ACCOUNT_ID', 'SUBSCRIPTION_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-subscriptions-update-subscription"} +View this operation on the Cloudflare API Reference +:: + +## Delete + +Deletes an account's subscription. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "subscriptionId" + type: "string" + required: true + description: "Subscription identifier tag." +--- +:: + +```php [php] +$response = $client->accounts()->subscriptions()->delete('ACCOUNT_ID', 'SUBSCRIPTION_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-subscriptions-delete-subscription"} +View this operation on the Cloudflare API Reference +:: + diff --git a/docs/content/2.client/00.accounts/04.tokens/00.index.md b/docs/content/2.client/00.accounts/04.tokens/00.index.md new file mode 100644 index 0000000..01edfdb --- /dev/null +++ b/docs/content/2.client/00.accounts/04.tokens/00.index.md @@ -0,0 +1,169 @@ +--- +title: Tokens +description: "Tokens endpoint reference." +navigation: + title: Tokens +--- + +## List + +List all Account Owned API tokens created for this account. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "params" + type: "array" + required: false + description: "Array containing the necessary params." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->list('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-list-tokens"} +View this operation on the Cloudflare API Reference +:: + +## Create + +Create a new Account Owned API token. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "values" + type: "array" + required: true + description: "Token values, requires name and policies." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->create('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-create-token"} +View this operation on the Cloudflare API Reference +:: + +## Get + +Get information about a specific Account Owned API token. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "tokenId" + type: "string" + required: true + description: "Token identifier." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->get('ACCOUNT_ID', 'TOKEN_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-token-details"} +View this operation on the Cloudflare API Reference +:: + +## Update + +Update an existing token. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "tokenId" + type: "string" + required: true + description: "Token identifier." + - name: "values" + type: "array" + required: true + description: "Token values, e.g. name, policies, condition, status." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->update('ACCOUNT_ID', 'TOKEN_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-update-token"} +View this operation on the Cloudflare API Reference +:: + +## Delete + +Destroy an Account Owned API token. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "tokenId" + type: "string" + required: true + description: "Token identifier." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->delete('ACCOUNT_ID', 'TOKEN_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-delete-token"} +View this operation on the Cloudflare API Reference +:: + +## Verify + +Test whether a token works. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->verify('ACCOUNT_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-verify-token"} +View this operation on the Cloudflare API Reference +:: + +## Related + +- [Permission Groups](/client/accounts/tokens/permission-groups) +- [Value](/client/accounts/tokens/value) + diff --git a/docs/content/2.client/00.accounts/04.tokens/01.permission-groups.md b/docs/content/2.client/00.accounts/04.tokens/01.permission-groups.md new file mode 100644 index 0000000..0eaa3a9 --- /dev/null +++ b/docs/content/2.client/00.accounts/04.tokens/01.permission-groups.md @@ -0,0 +1,34 @@ +--- +title: Permission Groups +description: "Permission Groups endpoint reference." +navigation: + title: Permission Groups +--- + +## List + +Find all available permission groups for Account Owned API Tokens. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "params" + type: "array" + required: false + description: "Array containing the necessary params, e.g. name, scope." + default: "[]" +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->permissionGroups()->list('ACCOUNT_ID', []); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-list-permission-groups"} +View this operation on the Cloudflare API Reference +:: + diff --git a/docs/content/2.client/00.accounts/04.tokens/02.value.md b/docs/content/2.client/00.accounts/04.tokens/02.value.md new file mode 100644 index 0000000..3ae9cb7 --- /dev/null +++ b/docs/content/2.client/00.accounts/04.tokens/02.value.md @@ -0,0 +1,33 @@ +--- +title: Value +description: "Value endpoint reference." +navigation: + title: Value +--- + +## Roll + +Roll the Account Owned API token secret. + +::params-table +--- +params: + - name: "accountId" + type: "string" + required: true + description: "Account identifier." + - name: "tokenId" + type: "string" + required: true + description: "Token identifier." +--- +:: + +```php [php] +$response = $client->accounts()->tokens()->value()->roll('ACCOUNT_ID', 'TOKEN_ID'); +``` + +::callout{icon="i-simple-icons-cloudflare" to="https://developers.cloudflare.com/api/operations/account-api-tokens-roll-token"} +View this operation on the Cloudflare API Reference +:: + diff --git a/src/Endpoints/Accounts.php b/src/Endpoints/Accounts.php index 769e4ee..077d243 100644 --- a/src/Endpoints/Accounts.php +++ b/src/Endpoints/Accounts.php @@ -3,9 +3,10 @@ namespace Cloudflare\Endpoints; use Cloudflare\Contracts\ResponseInterface; -use Cloudflare\Endpoints\Accounts\Roles; use Cloudflare\Endpoints\Accounts\Members; -use Cloudflare\Endpoints\Accounts\AuditLogs; +use Cloudflare\Endpoints\Accounts\Logs; +use Cloudflare\Endpoints\Accounts\Subscriptions; +use Cloudflare\Endpoints\Accounts\Tokens; /** * @link https://developers.cloudflare.com/api/operations/accounts-list-accounts @@ -107,13 +108,46 @@ public function delete(string $accountId): ResponseInterface } /** - * Account Roles + * Get account profile. * - * @return \Cloudflare\Endpoints\Accounts\Roles + * @link https://developers.cloudflare.com/api/operations/Accounts_getAccountProfile + * + * @param string $accountId Account identifier. + * + * @return \Cloudflare\Contracts\ResponseInterface Account Profile response. */ - public function roles(): Roles + public function profile(string $accountId): ResponseInterface { - return new Roles($this->getClient()); + return $this->getHttpClient()->get("/accounts/{$accountId}/profile"); + } + + /** + * Modify account profile. + * + * @link https://developers.cloudflare.com/api/operations/Accounts_modifyAccountProfile + * + * @param string $accountId Account identifier. + * @param array $values Account profile values. + * + * @return \Cloudflare\Contracts\ResponseInterface Update Account Profile response. + */ + public function updateProfile(string $accountId, array $values = []): ResponseInterface + { + return $this->getHttpClient()->put("/accounts/{$accountId}/profile", $values); + } + + /** + * List account organizations. + * + * @link https://developers.cloudflare.com/api/operations/Accounts_listAccountOrganizations + * + * @param string $accountId Account identifier. + * + * @return \Cloudflare\Contracts\ResponseInterface List Account Organizations response. + */ + public function organizations(string $accountId): ResponseInterface + { + return $this->getHttpClient()->get("/accounts/{$accountId}/organizations"); } /** @@ -127,12 +161,32 @@ public function members(): Members } /** - * Account audit logs + * Account Logs + * + * @return \Cloudflare\Endpoints\Accounts\Logs + */ + public function logs(): Logs + { + return new Logs($this->getClient()); + } + + /** + * Account Subscriptions + * + * @return \Cloudflare\Endpoints\Accounts\Subscriptions + */ + public function subscriptions(): Subscriptions + { + return new Subscriptions($this->getClient()); + } + + /** + * Account Owned API Tokens * - * @return \Cloudflare\Endpoints\Accounts\AuditLogs + * @return \Cloudflare\Endpoints\Accounts\Tokens */ - public function auditLogs(): AuditLogs + public function tokens(): Tokens { - return new AuditLogs($this->getClient()); + return new Tokens($this->getClient()); } } diff --git a/src/Endpoints/Accounts/Logs.php b/src/Endpoints/Accounts/Logs.php new file mode 100644 index 0000000..01a51f8 --- /dev/null +++ b/src/Endpoints/Accounts/Logs.php @@ -0,0 +1,19 @@ +getClient()); + } +} diff --git a/src/Endpoints/Accounts/Logs/Audit.php b/src/Endpoints/Accounts/Logs/Audit.php new file mode 100644 index 0000000..6f28724 --- /dev/null +++ b/src/Endpoints/Accounts/Logs/Audit.php @@ -0,0 +1,58 @@ +requiredParams(['since', 'before'], $params); + + return $this->getHttpClient()->get("/accounts/{$accountId}/logs/audit", $params); + } + + /** + * Returns the chronological change history for the resource identified by the given audit log entry. + * + * @link https://developers.cloudflare.com/api/operations/audit-logs-v2-get-account-audit-log-history + * + * @param string $accountId Account identifier. + * @param string $id Audit log entry identifier used to locate the resource. + * @param array $params Array containing the necessary params, requires action_time, since and before. + * + * @return ResponseInterface Audit Log History response. + */ + public function history(string $accountId, string $id, array $params = []): ResponseInterface + { + $this->requiredParams(['action_time', 'since', 'before'], $params); + + return $this->getHttpClient()->get("/accounts/{$accountId}/logs/audit/{$id}/history", $params); + } + + /** + * Lists the available audit log product categories and the resource products each one expands to. + * + * @link https://developers.cloudflare.com/api/operations/audit-logs-v2-list-account-product-categories + * + * @param string $accountId Account identifier. + * + * @return ResponseInterface List Product Categories response. + */ + public function productCategories(string $accountId): ResponseInterface + { + return $this->getHttpClient()->get("/accounts/{$accountId}/logs/audit/product_categories"); + } +} diff --git a/src/Endpoints/Accounts/Roles.php b/src/Endpoints/Accounts/Roles.php deleted file mode 100644 index 31722c2..0000000 --- a/src/Endpoints/Accounts/Roles.php +++ /dev/null @@ -1,39 +0,0 @@ -getHttpClient()->get("/accounts/{$accountId}/roles", $params); - } - - /** - * Get information about a specific role for an account. - * - * @link https://developers.cloudflare.com/api/operations/account-roles-role-details - * - * @param string $accountId Account identifier. - * @param string $roleId Role identifier. - * - * @return ResponseInterface Role Details response - */ - public function get(string $accountId, string $roleId): ResponseInterface - { - return $this->getHttpClient()->get("/accounts/{$accountId}/roles/{$roleId}"); - } -} diff --git a/src/Endpoints/Accounts/Subscriptions.php b/src/Endpoints/Accounts/Subscriptions.php new file mode 100644 index 0000000..b76aa11 --- /dev/null +++ b/src/Endpoints/Accounts/Subscriptions.php @@ -0,0 +1,69 @@ +getHttpClient()->get("/accounts/{$accountId}/subscriptions"); + } + + /** + * Creates an account subscription. + * + * @link https://developers.cloudflare.com/api/operations/account-subscriptions-create-subscription + * + * @param string $accountId Account identifier. + * @param array $values Subscription values, e.g. frequency, rate_plan. + * + * @return ResponseInterface Create Subscription response. + */ + public function create(string $accountId, array $values = []): ResponseInterface + { + return $this->getHttpClient()->post("/accounts/{$accountId}/subscriptions", $values); + } + + /** + * Updates an account subscription. + * + * @link https://developers.cloudflare.com/api/operations/account-subscriptions-update-subscription + * + * @param string $accountId Account identifier. + * @param string $subscriptionId Subscription identifier tag. + * @param array $values Subscription values, e.g. frequency, rate_plan. + * + * @return ResponseInterface Update Subscription response. + */ + public function update(string $accountId, string $subscriptionId, array $values = []): ResponseInterface + { + return $this->getHttpClient()->put("/accounts/{$accountId}/subscriptions/{$subscriptionId}", $values); + } + + /** + * Deletes an account's subscription. + * + * @link https://developers.cloudflare.com/api/operations/account-subscriptions-delete-subscription + * + * @param string $accountId Account identifier. + * @param string $subscriptionId Subscription identifier tag. + * + * @return ResponseInterface Delete Subscription response. + */ + public function delete(string $accountId, string $subscriptionId): ResponseInterface + { + return $this->getHttpClient()->delete("/accounts/{$accountId}/subscriptions/{$subscriptionId}"); + } +} diff --git a/src/Endpoints/Accounts/Tokens.php b/src/Endpoints/Accounts/Tokens.php new file mode 100644 index 0000000..d31d8ba --- /dev/null +++ b/src/Endpoints/Accounts/Tokens.php @@ -0,0 +1,123 @@ +getHttpClient()->get("/accounts/{$accountId}/tokens", $params); + } + + /** + * Create a new Account Owned API token. + * + * @link https://developers.cloudflare.com/api/operations/account-api-tokens-create-token + * + * @param string $accountId Account identifier. + * @param array $values Token values, requires name and policies. + * + * @return ResponseInterface Create Token response. + */ + public function create(string $accountId, array $values): ResponseInterface + { + $this->requiredParams(['name', 'policies'], $values); + + return $this->getHttpClient()->post("/accounts/{$accountId}/tokens", $values); + } + + /** + * Get information about a specific Account Owned API token. + * + * @link https://developers.cloudflare.com/api/operations/account-api-tokens-token-details + * + * @param string $accountId Account identifier. + * @param string $tokenId Token identifier. + * + * @return ResponseInterface Token Details response. + */ + public function get(string $accountId, string $tokenId): ResponseInterface + { + return $this->getHttpClient()->get("/accounts/{$accountId}/tokens/{$tokenId}"); + } + + /** + * Update an existing token. + * + * @link https://developers.cloudflare.com/api/operations/account-api-tokens-update-token + * + * @param string $accountId Account identifier. + * @param string $tokenId Token identifier. + * @param array $values Token values, e.g. name, policies, condition, status. + * + * @return ResponseInterface Update Token response. + */ + public function update(string $accountId, string $tokenId, array $values): ResponseInterface + { + return $this->getHttpClient()->put("/accounts/{$accountId}/tokens/{$tokenId}", $values); + } + + /** + * Destroy an Account Owned API token. + * + * @link https://developers.cloudflare.com/api/operations/account-api-tokens-delete-token + * + * @param string $accountId Account identifier. + * @param string $tokenId Token identifier. + * + * @return ResponseInterface Delete Token response. + */ + public function delete(string $accountId, string $tokenId): ResponseInterface + { + return $this->getHttpClient()->delete("/accounts/{$accountId}/tokens/{$tokenId}"); + } + + /** + * Test whether a token works. + * + * @link https://developers.cloudflare.com/api/operations/account-api-tokens-verify-token + * + * @param string $accountId Account identifier. + * + * @return ResponseInterface Verify Token response. + */ + public function verify(string $accountId): ResponseInterface + { + return $this->getHttpClient()->get("/accounts/{$accountId}/tokens/verify"); + } + + /** + * Account Owned API Token Permission Groups + * + * @return \Cloudflare\Endpoints\Accounts\Tokens\PermissionGroups + */ + public function permissionGroups(): PermissionGroups + { + return new PermissionGroups($this->getClient()); + } + + /** + * Account Owned API Token Value + * + * @return \Cloudflare\Endpoints\Accounts\Tokens\Value + */ + public function value(): Value + { + return new Value($this->getClient()); + } +} diff --git a/src/Endpoints/Accounts/AuditLogs.php b/src/Endpoints/Accounts/Tokens/PermissionGroups.php similarity index 50% rename from src/Endpoints/Accounts/AuditLogs.php rename to src/Endpoints/Accounts/Tokens/PermissionGroups.php index a7a605a..b10d3b4 100644 --- a/src/Endpoints/Accounts/AuditLogs.php +++ b/src/Endpoints/Accounts/Tokens/PermissionGroups.php @@ -1,24 +1,24 @@ getHttpClient()->get("/accounts/{$accountId}/audit_logs", $params); + return $this->getHttpClient()->get("/accounts/{$accountId}/tokens/permission_groups", $params); } } diff --git a/src/Endpoints/Accounts/Tokens/Value.php b/src/Endpoints/Accounts/Tokens/Value.php new file mode 100644 index 0000000..9336f4a --- /dev/null +++ b/src/Endpoints/Accounts/Tokens/Value.php @@ -0,0 +1,24 @@ +getHttpClient()->put("/accounts/{$accountId}/tokens/{$tokenId}/value", []); + } +} diff --git a/test/Tests/Endpoints/Accounts/Logs/AuditTest.php b/test/Tests/Endpoints/Accounts/Logs/AuditTest.php new file mode 100644 index 0000000..e24f696 --- /dev/null +++ b/test/Tests/Endpoints/Accounts/Logs/AuditTest.php @@ -0,0 +1,72 @@ +mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'audit_log_id']]])), + ]); + + $response = $client->accounts()->logs()->audit()->list('account_id', [ + 'since' => '2024-10-30', + 'before' => '2024-10-31', + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/logs/audit', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldThrowExceptionWhenListParamsAreMissing() + { + $client = $this->mockClient([]); + + $this->expectException(\Cloudflare\Exceptions\MissingArgumentException::class); + + $client->accounts()->logs()->audit()->list('account_id'); + } + + #[Test] + public function shouldGetHistory() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'audit_log_id']]])), + ]); + + $response = $client->accounts()->logs()->audit()->history('account_id', 'audit_log_id', [ + 'action_time' => '2024-10-30T15:00:00Z', + 'since' => '2024-10-30', + 'before' => '2024-10-31', + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/logs/audit/audit_log_id/history', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldListProductCategories() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['product' => 'access']]])), + ]); + + $response = $client->accounts()->logs()->audit()->productCategories('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/logs/audit/product_categories', $this->lastRequest()->getUri()->getPath()); + } +} diff --git a/test/Tests/Endpoints/Accounts/RolesTest.php b/test/Tests/Endpoints/Accounts/RolesTest.php deleted file mode 100644 index 8f2be20..0000000 --- a/test/Tests/Endpoints/Accounts/RolesTest.php +++ /dev/null @@ -1,41 +0,0 @@ -mockClient([ - new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'role_id']]])), - ]); - - $response = $client->accounts()->roles()->list('account_id'); - - $this->assertTrue($response->successful()); - $this->assertSame('GET', $this->lastRequest()->getMethod()); - $this->assertSame('/client/v4/accounts/account_id/roles', $this->lastRequest()->getUri()->getPath()); - } - - #[Test] - public function shouldGetDetails() - { - $client = $this->mockClient([ - new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'role_id']])), - ]); - - $response = $client->accounts()->roles()->get('account_id', 'role_id'); - - $this->assertTrue($response->successful()); - $this->assertSame('GET', $this->lastRequest()->getMethod()); - $this->assertSame('/client/v4/accounts/account_id/roles/role_id', $this->lastRequest()->getUri()->getPath()); - } -} diff --git a/test/Tests/Endpoints/Accounts/SubscriptionsTest.php b/test/Tests/Endpoints/Accounts/SubscriptionsTest.php new file mode 100644 index 0000000..ce95c22 --- /dev/null +++ b/test/Tests/Endpoints/Accounts/SubscriptionsTest.php @@ -0,0 +1,75 @@ +mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'subscription_id']]])), + ]); + + $response = $client->accounts()->subscriptions()->list('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/subscriptions', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldCreate() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'subscription_id']])), + ]); + + $response = $client->accounts()->subscriptions()->create('account_id', [ + 'frequency' => 'monthly', + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('POST', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/subscriptions', $this->lastRequest()->getUri()->getPath()); + $this->assertSame(['frequency' => 'monthly'], json_decode((string) $this->lastRequest()->getBody(), true)); + } + + #[Test] + public function shouldUpdate() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'subscription_id']])), + ]); + + $response = $client->accounts()->subscriptions()->update('account_id', 'subscription_id', [ + 'frequency' => 'yearly', + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('PUT', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/subscriptions/subscription_id', $this->lastRequest()->getUri()->getPath()); + $this->assertSame(['frequency' => 'yearly'], json_decode((string) $this->lastRequest()->getBody(), true)); + } + + #[Test] + public function shouldDelete() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['subscription_id' => 'subscription_id']])), + ]); + + $response = $client->accounts()->subscriptions()->delete('account_id', 'subscription_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('DELETE', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/subscriptions/subscription_id', $this->lastRequest()->getUri()->getPath()); + } +} diff --git a/test/Tests/Endpoints/Accounts/AuditLogsTest.php b/test/Tests/Endpoints/Accounts/Tokens/PermissionGroupsTest.php similarity index 57% rename from test/Tests/Endpoints/Accounts/AuditLogsTest.php rename to test/Tests/Endpoints/Accounts/Tokens/PermissionGroupsTest.php index 54430fa..e30ffe4 100644 --- a/test/Tests/Endpoints/Accounts/AuditLogsTest.php +++ b/test/Tests/Endpoints/Accounts/Tokens/PermissionGroupsTest.php @@ -1,13 +1,13 @@ mockClient([ - new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'log_id']]])), + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'permission_group_id']]])), ]); - $response = $client->accounts()->auditLogs()->list('account_id'); + $response = $client->accounts()->tokens()->permissionGroups()->list('account_id'); $this->assertTrue($response->successful()); $this->assertSame('GET', $this->lastRequest()->getMethod()); - $this->assertSame('/client/v4/accounts/account_id/audit_logs', $this->lastRequest()->getUri()->getPath()); + $this->assertSame('/client/v4/accounts/account_id/tokens/permission_groups', $this->lastRequest()->getUri()->getPath()); } } diff --git a/test/Tests/Endpoints/Accounts/Tokens/ValueTest.php b/test/Tests/Endpoints/Accounts/Tokens/ValueTest.php new file mode 100644 index 0000000..474f534 --- /dev/null +++ b/test/Tests/Endpoints/Accounts/Tokens/ValueTest.php @@ -0,0 +1,27 @@ +mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['value' => 'new_token_secret']])), + ]); + + $response = $client->accounts()->tokens()->value()->roll('account_id', 'token_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('PUT', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens/token_id/value', $this->lastRequest()->getUri()->getPath()); + } +} diff --git a/test/Tests/Endpoints/Accounts/TokensTest.php b/test/Tests/Endpoints/Accounts/TokensTest.php new file mode 100644 index 0000000..d3c337d --- /dev/null +++ b/test/Tests/Endpoints/Accounts/TokensTest.php @@ -0,0 +1,112 @@ +mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'token_id']]])), + ]); + + $response = $client->accounts()->tokens()->list('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldCreate() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'token_id']])), + ]); + + $response = $client->accounts()->tokens()->create('account_id', [ + 'name' => 'readonly token', + 'policies' => [['effect' => 'allow']], + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('POST', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldThrowExceptionWhenCreateParamsAreMissing() + { + $client = $this->mockClient([]); + + $this->expectException(\Cloudflare\Exceptions\MissingArgumentException::class); + + $client->accounts()->tokens()->create('account_id', ['name' => 'readonly token']); + } + + #[Test] + public function shouldGetDetails() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'token_id']])), + ]); + + $response = $client->accounts()->tokens()->get('account_id', 'token_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens/token_id', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldUpdate() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'token_id']])), + ]); + + $response = $client->accounts()->tokens()->update('account_id', 'token_id', [ + 'name' => 'renamed token', + ]); + + $this->assertTrue($response->successful()); + $this->assertSame('PUT', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens/token_id', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldDelete() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'token_id']])), + ]); + + $response = $client->accounts()->tokens()->delete('account_id', 'token_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('DELETE', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens/token_id', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldVerify() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'token_id', 'status' => 'active']])), + ]); + + $response = $client->accounts()->tokens()->verify('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/tokens/verify', $this->lastRequest()->getUri()->getPath()); + } +} diff --git a/test/Tests/Endpoints/AccountsTest.php b/test/Tests/Endpoints/AccountsTest.php index 205011d..3614b69 100644 --- a/test/Tests/Endpoints/AccountsTest.php +++ b/test/Tests/Endpoints/AccountsTest.php @@ -116,4 +116,47 @@ public function shouldDelete() $this->assertSame('DELETE', $this->lastRequest()->getMethod()); $this->assertSame('/client/v4/accounts/account_id', $this->lastRequest()->getUri()->getPath()); } + + #[Test] + public function shouldGetProfile() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'account_id']])), + ]); + + $response = $client->accounts()->profile('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/profile', $this->lastRequest()->getUri()->getPath()); + } + + #[Test] + public function shouldUpdateProfile() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => ['id' => 'account_id']])), + ]); + + $response = $client->accounts()->updateProfile('account_id', ['name' => 'renamed']); + + $this->assertTrue($response->successful()); + $this->assertSame('PUT', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/profile', $this->lastRequest()->getUri()->getPath()); + $this->assertSame(['name' => 'renamed'], json_decode((string) $this->lastRequest()->getBody(), true)); + } + + #[Test] + public function shouldListOrganizations() + { + $client = $this->mockClient([ + new Response(200, [], json_encode(['success' => true, 'result' => [['id' => 'organization_id']]])), + ]); + + $response = $client->accounts()->organizations('account_id'); + + $this->assertTrue($response->successful()); + $this->assertSame('GET', $this->lastRequest()->getMethod()); + $this->assertSame('/client/v4/accounts/account_id/organizations', $this->lastRequest()->getUri()->getPath()); + } }