view release on metacpan or search on metacpan
bin/cloudflare-api view on Meta::CPAN
'param-dumper-file=s' => sub { my ($key, $value)=parse_named('dumper-file', $_[1]); $param{$key}=$value },
'<>' => sub { push(@argument_source, ['bare', $_[0]]) }
)
) || pod2usage(2);
pod2usage(1) if $opt{'help'};
pod2usage(-verbose => 2, -exitval => 0) if $opt{'man'};
$opt{'version'} && do {print "$Script $VERSION\n"; return 0};
# Generate completion before validating a request or resolving credentials
#
if (defined($opt{'generate-completion'})) {
require Cloudflare::API::CLI::Completion;
print Cloudflare::API::CLI::Completion::generate(
$opt{'generate-completion'}, {
resources => \%resource_method,
options => $completion_option_ar
}
);
return 0;
bin/cloudflare-api view on Meta::CPAN
die "action '$opt{'action'}' for resource '$opt{'resource'}' requires at least " .
"$minimum --arg value".($minimum==1 ? '' : 's')."; ".scalar(@arg)." supplied\n"
if @arg<$minimum;
die "action '$opt{'action'}' for resource '$opt{'resource'}' accepts at most " .
"$maximum --arg value".($maximum==1 ? '' : 's')."; ".scalar(@arg)." supplied\n"
if @arg>$maximum;
}
# Validate the composite Worker inspection before resolving credentials
#
if (!$raw_fg&&$opt{'resource'} eq 'workers'&&$opt{'action'} eq 'inspect_script') {
die "full_response is unavailable for inspect_script\n" if $opt{'full-response'};
my @unknown=sort(grep { $_!~/\A(?:name|tag|etag)\z/ } keys(%param));
die "unknown inspect selector: $unknown[0]\n" if @unknown;
my @selector=grep { exists($param{$_}) } qw(name tag etag);
die "inspect_script requires exactly one of name, tag, or etag\n"
unless @selector==1;
my $selector=$selector[0];
my $value=$param{$selector};
bin/cloudflare-api view on Meta::CPAN
* **--method VERB, --path /relative/path**
Call a low-level JSON endpoint through `Cloudflare::API->request()`. Both options are required and cannot be combined with `--resource` or `--action`. The method is uppercased. The path must begin with exactly one slash and cannot be an absolute ...
* **--account-id ID**
Override `CLOUDFLARE_ACCOUNT_ID` and Wrangler account discovery for this invocation. Account-scoped methods require an ID; account and zone lookups do not.
* **--auth=wrangler**
Run `wrangler auth token --json` and use its API token or refreshed OAuth token instead of the environment token. For an account-scoped named method, also run `wrangler whoami --json` and use the account ID when exactly one account is available. ...
## Positional and named arguments ##
* **--arg VALUE**
Append a literal string method argument. Repeat to supply several arguments in order. For named resource actions, bare operands after the resource and action are equivalent; use `--arg` or `--` when a value could be mistaken for an option.
* **--arg-bool true|false, --arg-array JSON, --arg-hash JSON, --arg-json JSON, --arg-json-file FILE**
Append a typed positional argument. Boolean values are case-insensitive; array and hash forms require the matching JSON container. The JSON forms accept any JSON value, directly or read from a file. To pass a private JSON body without putting it ...
bin/cloudflare-api view on Meta::CPAN
cloudflare-api --resource secrets_store --action create_secret \
--arg my-store --arg-json-file /dev/stdin < secrets.json
```
For a Worker version upload, pass the Worker name through `--arg` and prepared `metadata` and `files` through `--param-json-file NAME=FILE`. Version upload does not activate a deployment; consult `Cloudflare::API::Workers` for the staging and deploym...
Worker inspection accepts exactly one of `--param name=...`, `--param tag=...`, or `--param etag=...`. The name is the `id` printed by `list_scripts`; `tag` is Cloudflare's immutable Worker ID, while `etag` identifies current script content. Inspecti...
# RETURN VALUES AND ERRORS #
Successful requests print the result followed by a newline and exit with status zero. JSON output preserves Cloudflare's response shape; a paginated list prints an array of pages. The CLI checks the number of `--arg` values required by supported meth...
# SEE ALSO #
[Cloudflare::API](../lib/Cloudflare/API.pm.md), [Cloudflare::API::Workers](../lib/Cloudflare/API/Workers.pm.md), the other resource module sidecars, and `cloudflare-api --man`.
# AUTHOR #
Andrew Speer <andrew.speer@isolutions.com.au>
# LICENSE and COPYRIGHT
bin/cloudflare-api view on Meta::CPAN
B<--account-id ID>
Override C<CLOUDFLARE_ACCOUNT_ID> and Wrangler account discovery for this invocation. Account-scoped methods require an ID; account and zone lookups do not.
=item *
B<--auth=wrangler>
Run C<wrangler auth token --json> and use its API token or refreshed OAuth token instead of the environment token. For an account-scoped named method, also run C<wrangler whoami --json> and use the account ID when exactly one account is available. Se...
=back
=head2 Positional and named arguments
=over
bin/cloudflare-api view on Meta::CPAN
--param prefix=/docs
cloudflare-api --resource secrets_store --action create_secret \
--arg my-store --arg-json-file /dev/stdin < secrets.json
For a Worker version upload, pass the Worker name through C<--arg> and prepared C<metadata> and C<files> through C<--param-json-file NAME=FILE>. Version upload does not activate a deployment; consult C<Cloudflare::API::Workers> for the staging and de...
Worker inspection accepts exactly one of C<--param name=...>, C<--param tag=...>, or C<--param etag=...>. The name is the C<id> printed by C<list_scripts>; C<tag> is Cloudflare's immutable Worker ID, while C<etag> identifies current script content. I...
=head1 RETURN VALUES AND ERRORS
Successful requests print the result followed by a newline and exit with status zero. JSON output preserves Cloudflare's response shape; a paginated list prints an array of pages. The CLI checks the number of C<--arg> values required by supported met...
=head1 SEE ALSO
L<Cloudflare::API|Cloudflare::API>, L<Cloudflare::API::Workers|Cloudflare::API::Workers>, the other resource module sidecars, and C<cloudflare-api --man>.
=head1 AUTHOR
Andrew Speer L<mailto:andrew.speer@isolutions.com.au>
bin/cloudflare-api.md view on Meta::CPAN
* **--method VERB, --path /relative/path**
Call a low-level JSON endpoint through `Cloudflare::API->request()`. Both options are required and cannot be combined with `--resource` or `--action`. The method is uppercased. The path must begin with exactly one slash and cannot be an absolute ...
* **--account-id ID**
Override `CLOUDFLARE_ACCOUNT_ID` and Wrangler account discovery for this invocation. Account-scoped methods require an ID; account and zone lookups do not.
* **--auth=wrangler**
Run `wrangler auth token --json` and use its API token or refreshed OAuth token instead of the environment token. For an account-scoped named method, also run `wrangler whoami --json` and use the account ID when exactly one account is available. ...
## Positional and named arguments ##
* **--arg VALUE**
Append a literal string method argument. Repeat to supply several arguments in order. For named resource actions, bare operands after the resource and action are equivalent; use `--arg` or `--` when a value could be mistaken for an option.
* **--arg-bool true|false, --arg-array JSON, --arg-hash JSON, --arg-json JSON, --arg-json-file FILE**
Append a typed positional argument. Boolean values are case-insensitive; array and hash forms require the matching JSON container. The JSON forms accept any JSON value, directly or read from a file. To pass a private JSON body without putting it ...
bin/cloudflare-api.md view on Meta::CPAN
cloudflare-api --resource secrets_store --action create_secret \
--arg my-store --arg-json-file /dev/stdin < secrets.json
```
For a Worker version upload, pass the Worker name through `--arg` and prepared `metadata` and `files` through `--param-json-file NAME=FILE`. Version upload does not activate a deployment; consult `Cloudflare::API::Workers` for the staging and deploym...
Worker inspection accepts exactly one of `--param name=...`, `--param tag=...`, or `--param etag=...`. The name is the `id` printed by `list_scripts`; `tag` is Cloudflare's immutable Worker ID, while `etag` identifies current script content. Inspecti...
# RETURN VALUES AND ERRORS #
Successful requests print the result followed by a newline and exit with status zero. JSON output preserves Cloudflare's response shape; a paginated list prints an array of pages. The CLI checks the number of `--arg` values required by supported meth...
# SEE ALSO #
[Cloudflare::API](../lib/Cloudflare/API.pm.md), [Cloudflare::API::Workers](../lib/Cloudflare/API/Workers.pm.md), the other resource module sidecars, and `cloudflare-api --man`.
# AUTHOR #
Andrew Speer <andrew.speer@isolutions.com.au>
# LICENSE and COPYRIGHT #
doc/cloudflare-api.md view on Meta::CPAN
# Introduction {#introduction}
Want to list your R2 buckets, inspect a Worker, or create a D1 database from a Perl script? `Cloudflare::API` gives you a small client for those everyday Cloudflare management tasks. It uses `HTTP::API::Core` for the HTTPS transport, authentication h...
You can call the module from Perl or use the accompanying `cloudflare-api` command when a shell is more convenient. Both talk to Cloudflare's management API. Neither builds a Worker project or runs npm. The command can use a Wrangler login when you r...
If you only need a quick look, the next example shows the whole idea. The later sections explain credentials, resource methods, uploads, responses, and command-line options. A module-by-module reference is near the end.
``` perl
use strict;
use warnings;
use Cloudflare::API;
my $api_or=Cloudflare::API->new();
my $buckets_ar=$api_or->r2()->list_buckets();
my $scripts_ar=$api_or->workers()->list_scripts();
```
With `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` set, the client supplies the account path and bearer token. List methods follow Cloudflare's pages and return a flat array reference. Other JSON methods normally return the decoded `result`.
!!! note
The distribution requires Perl 5.10 or later, `HTTP::API::Core` 1.08 or
later, and Perl HTTPS support through `IO::Socket::SSL`. Install it with
a CPAN client in the usual way.
# Credentials and account context {#credentials}
Create an API token in the Cloudflare dashboard with only the permissions and account or zone access your script needs. Cloudflare offers user tokens and account tokens; the endpoint and your account permissions determine which is appropriate. Copy t...
``` sh
# Load CLOUDFLARE_API_TOKEN from your secret manager first.
export CLOUDFLARE_ACCOUNT_ID='your-account-id'
cloudflare-api --resource r2 --action list_buckets
```
The account ID above is a placeholder. Load the token through your secret manager so it does not enter a repository or a pasted command. The module also accepts explicit `token` and `account_id` constructor options:
doc/cloudflare-api.md view on Meta::CPAN
it, paste it into command-line arguments, print it in logs, or include
it in error reports. Use a narrowly scoped token; a token that can edit
a resource can change or delete it through the relevant methods.
If you have logged into Wrangler locally, the command can ask Wrangler for a token for each invocation. Wrangler refreshes an expired OAuth token before returning it:
``` sh
cloudflare-api --auth=wrangler --resource workers --action list_scripts
```
For an account-scoped named method, `--auth=wrangler` also uses `wrangler whoami --json` to obtain the account ID when the login has exactly one available account. If it has several, select one with `--account-id` or `CLOUDFLARE_ACCOUNT_ID`; either v...
!!! tip
For unattended scripts, use a dedicated, limited API token supplied by a
secret manager. The Wrangler shortcut is best suited to an interactive
session you control.
# A first Perl script {#perl-synopsis}
Let's list two resources without changing anything. Resource accessors are named for the Cloudflare service, and their methods follow the action you want to perform:
doc/cloudflare-api.md view on Meta::CPAN
!!! important
Paths must start with one slash and cannot be absolute URLs, so a
caller-supplied path cannot send the bearer token to another host. If
you put a dynamic value in a low-level path, percent-encode that segment
yourself. Named resource methods encode their own path segments.
# Using the command-line script {#command-line}
`cloudflare-api` is useful for a quick lookup or a shell script that already has credentials in its environment. Choose a resource and action for a named method. Output is pretty JSON unless you ask for `dumper`:
``` sh
cloudflare-api --resource zones --action list --param status=active
cloudflare-api --resource r2 --action list_buckets --full-response
cloudflare-api --resource zones --action list --output dumper
```
A `--param` passes a named string argument, usually a list filter. `--arg` passes a positional string argument, in the order given. For a method expecting a JSON body, use a typed argument rather than a string:
``` sh
doc/cloudflare-api.xml view on Meta::CPAN
<author>
<personname><firstname>Andrew</firstname><surname>Speer</surname></personname>
</author>
<copyright><year>2026</year><holder>Andrew Speer</holder></copyright>
</info>
<section xml:id="introduction">
<title>Introduction</title>
<para>Want to list your R2 buckets, inspect a Worker, or create a D1 database from a Perl script? <classname>Cloudflare::API</classname> gives you a small client for those everyday Cloudflare management tasks. It uses <classname>HTTP::API::Core</...
<para>You can call the module from Perl or use the accompanying <command>cloudflare-api</command> command when a shell is more convenient. Both talk to Cloudflare's management API. Neither builds a Worker project or runs npm. The command can use ...
<para>If you only need a quick look, the next example shows the whole idea. The later sections explain credentials, resource methods, uploads, responses, and command-line options. A module-by-module reference is near the end.</para>
<programlisting language="perl">use strict;
use warnings;
use Cloudflare::API;
my $api_or=Cloudflare::API->new();
my $buckets_ar=$api_or->r2()->list_buckets();
my $scripts_ar=$api_or->workers()->list_scripts();</programlisting>
<para>With <envar>CLOUDFLARE_API_TOKEN</envar> and <envar>CLOUDFLARE_ACCOUNT_ID</envar> set, the client supplies the account path and bearer token. List methods follow Cloudflare's pages and return a flat array reference. Other JSON methods norma...
<note><para>The distribution requires Perl 5.10 or later, <classname>HTTP::API::Core</classname> 1.08 or later, and Perl HTTPS support through <classname>IO::Socket::SSL</classname>. Install it with a CPAN client in the usual way.</para></note>
</section>
<section xml:id="credentials">
<title>Credentials and account context</title>
<para>Create an API token in the Cloudflare dashboard with only the permissions and account or zone access your script needs. Cloudflare offers user tokens and account tokens; the endpoint and your account permissions determine which is appropria...
<programlisting language="sh"># Load CLOUDFLARE_API_TOKEN from your secret manager first.
export CLOUDFLARE_ACCOUNT_ID='your-account-id'
cloudflare-api --resource r2 --action list_buckets</programlisting>
<para>The account ID above is a placeholder. Load the token through your secret manager so it does not enter a repository or a pasted command. The module also accepts explicit <literal>token</literal> and <literal>account_id</literal> constructor...
<programlisting language="perl">my $api_or=Cloudflare::API->new(
token => $ENV{'CLOUDFLARE_API_TOKEN'},
account_id => $ENV{'CLOUDFLARE_ACCOUNT_ID'},
timeout => 30
);</programlisting>
<para>You can omit <literal>account_id</literal> for account and zone lookup, but account-scoped methods need it. The command has <option>--account-id</option> to choose another account while continuing to read the token from the environment.</pa...
<warning><para>An API token is a credential, even when it is short lived. Do not commit it, paste it into command-line arguments, print it in logs, or include it in error reports. Use a narrowly scoped token; a token that can edit a resource can ...
<para>If you have logged into Wrangler locally, the command can ask Wrangler for a token for each invocation. Wrangler refreshes an expired OAuth token before returning it:</para>
<programlisting language="sh">cloudflare-api --auth=wrangler --resource workers --action list_scripts</programlisting>
<para>For an account-scoped named method, <option>--auth=wrangler</option> also uses <command>wrangler whoami --json</command> to obtain the account ID when the login has exactly one available account. If it has several, select one with <option>-...
<tip><para>For unattended scripts, use a dedicated, limited API token supplied by a secret manager. The Wrangler shortcut is best suited to an interactive session you control.</para></tip>
</section>
<section xml:id="perl-synopsis">
<title>A first Perl script</title>
<para>Let's list two resources without changing anything. Resource accessors are named for the Cloudflare service, and their methods follow the action you want to perform:</para>
<programlisting language="perl">use strict;
use warnings;
use Cloudflare::API;
doc/cloudflare-api.xml view on Meta::CPAN
query => { per_page => 20 }
);
my $decoded_hr=$api_or->request_full('GET', '/accounts');
my $response_or=$api_or->response('GET', '/accounts');</programlisting>
<para><methodname>request()</methodname> unwraps <literal>result</literal>, <methodname>request_full()</methodname> keeps the complete decoded Cloudflare response, and <methodname>response()</methodname> returns the <classname>HTTP::API::Core::Re...
<important><para>Paths must start with one slash and cannot be absolute URLs, so a caller-supplied path cannot send the bearer token to another host. If you put a dynamic value in a low-level path, percent-encode that segment yourself. Named reso...
</section>
<section xml:id="command-line">
<title>Using the command-line script</title>
<para><command>cloudflare-api</command> is useful for a quick lookup or a shell script that already has credentials in its environment. Choose a resource and action for a named method. Output is pretty JSON unless you ask for <literal>dumper</lit...
<programlisting language="sh">cloudflare-api --resource zones --action list --param status=active
cloudflare-api --resource r2 --action list_buckets --full-response
cloudflare-api --resource zones --action list --output dumper</programlisting>
<para>A <option>--param</option> passes a named string argument, usually a list filter. <option>--arg</option> passes a positional string argument, in the order given. For a method expecting a JSON body, use a typed argument rather than a string:...
<programlisting language="sh">cloudflare-api --resource kv --action create_namespace \
--arg-json '{"title":"my-app-cache"}'
cloudflare-api --resource workers --action upload_assets \
--arg my-app --arg dist/site --param prefix=/docs</programlisting>
<para>For selected files, give the Worker name with <option>--arg</option>, then combine repeatable <option>--asset FILE</option>, <option>--asset-list-json FILE</option>, and <option>--asset-list-text FILE</option>. Use <option>--asset-list-stdi...
<programlisting language="sh">cloudflare-api --resource workers --action upload_assets \
lib/Cloudflare/API.pm view on Meta::CPAN
#
1;
#============================================================================
sub new {
# Resolve credentials and account context from options or environment
#
my ($class, %opt)=@_;
my $token=exists($opt{'token'}) ? delete($opt{'token'}) : $ENV{'CLOUDFLARE_API_TOKEN'};
die "token is required\n" unless defined($token)&&!ref($token)&&length($token);
my $account_id=exists($opt{'account_id'})
? delete($opt{'account_id'}) : $ENV{'CLOUDFLARE_ACCOUNT_ID'};
die "account_id must be a non-empty scalar\n"
if defined($account_id)&&(ref($account_id)||!length($account_id));
lib/Cloudflare/API.pm view on Meta::CPAN
}
sub account_id { return $_[0]->{'account_id'} }
sub response {
# Require a relative API path so credentials stay on the configured host
#
my ($self, $method, $path, %opt)=@_;
die "path must begin with one slash\n"
unless defined($path)&&!ref($path)&&$path=~m{\A/(?!/)};
return $self->{'core_or'}->request($method, $path, %opt);
}
sub raw_request {
lib/Cloudflare/API.pm view on Meta::CPAN
* **[Cloudflare::API::Queues](API/Queues.pm.md)** (`queues()`) manages queues and consumers.
* **[Cloudflare::API::Hyperdrive](API/Hyperdrive.pm.md)** (`hyperdrive()`) manages database connection configurations.
* **[Cloudflare::API::SecretsStore](API/SecretsStore.pm.md)** (`secrets_store()`) manages stores and write-only secrets.
* **[Cloudflare::API::Error](API/Error.pm.md)** represents a Cloudflare JSON response reporting failure despite HTTP success.
* **[Cloudflare::API::Resource](API/Resource.pm.md)** is the shared base class for resource objects; applications normally use the accessors above rather than constructing it.
# METHODS #
* **new(%options)**
Construct the client. `token` is a non-empty scalar and is mandatory after environment fallback. `account_id` is an optional non-empty scalar. `base_url` defaults to `https://api.cloudflare.com/client/v4` and must be an HTTPS URL. `timeout`, `ret...
* **account_id()**
Return the configured account ID, or `undef` if none was supplied.
* **workers(), r2(), kv(), d1(), queues(), hyperdrive(), secrets_store(), accounts(), zones()**
Return a new object of the corresponding resource class, retaining this client. Resource modules are loaded when their accessor is called.
* **request($method, $path, %options)**
lib/Cloudflare/API.pm view on Meta::CPAN
=head1 METHODS
=over
=item *
B<new(%options)>
Construct the client. C<token> is a non-empty scalar and is mandatory after environment fallback. C<account_id> is an optional non-empty scalar. C<base_url> defaults to C<https://api.cloudflare.com/client/v4> and must be an HTTPS URL. C<timeout>, C<r...
=item *
B<account_id()>
Return the configured account ID, or C<undef> if none was supplied.
lib/Cloudflare/API.pm.md view on Meta::CPAN
* **[Cloudflare::API::Queues](API/Queues.pm.md)** (`queues()`) manages queues and consumers.
* **[Cloudflare::API::Hyperdrive](API/Hyperdrive.pm.md)** (`hyperdrive()`) manages database connection configurations.
* **[Cloudflare::API::SecretsStore](API/SecretsStore.pm.md)** (`secrets_store()`) manages stores and write-only secrets.
* **[Cloudflare::API::Error](API/Error.pm.md)** represents a Cloudflare JSON response reporting failure despite HTTP success.
* **[Cloudflare::API::Resource](API/Resource.pm.md)** is the shared base class for resource objects; applications normally use the accessors above rather than constructing it.
# METHODS #
* **new(%options)**
Construct the client. `token` is a non-empty scalar and is mandatory after environment fallback. `account_id` is an optional non-empty scalar. `base_url` defaults to `https://api.cloudflare.com/client/v4` and must be an HTTPS URL. `timeout`, `ret...
* **account_id()**
Return the configured account ID, or `undef` if none was supplied.
* **workers(), r2(), kv(), d1(), queues(), hyperdrive(), secrets_store(), accounts(), zones()**
Return a new object of the corresponding resource class, retaining this client. Resource modules are loaded when their accessor is called.
* **request($method, $path, %options)**
lib/Cloudflare/API/Hyperdrive.pm view on Meta::CPAN
# SYNOPSIS #
```perl
my $configs=$api->hyperdrive()->list_configs();
my $config=$api->hyperdrive()->get_config($config_id);
```
# DESCRIPTION #
These account-scoped REST methods manage Hyperdrive configurations for external PostgreSQL or MySQL databases. They do not run SQL. Applications query through a Worker Hyperdrive binding and a database driver. Cloudflare's origin password is write-on...
# METHODS #
* **list_configs(%query)** â List every configuration. Named filters become query parameters. The method follows all pages and returns one flat array reference; a large result set can require many requests and substantial memory.
* **list_configs_page(%query)** â Return a lazy `HTTP::API::Core::Pagination` object. Use `next()` to consume one configuration at a time.
* **list_configs_page_response(%query)** â Make one list request and return the complete decoded Cloudflare response hash, including `result_info` when supplied.
* **get_config($id, %options)** â Retrieve a configuration by ID and return its `result`.
* **create_config(\%body, %options)** â POST a Cloudflare configuration body and return its `result`.
* **replace_config($id, \%body, %options)** â PUT a replacement configuration and return its `result`.
* **update_config($id, \%body, %options)** â PATCH a configuration and return its `result`.
lib/Cloudflare/API/Hyperdrive.pm view on Meta::CPAN
=head1 SYNOPSIS
my $configs=$api->hyperdrive()->list_configs();
my $config=$api->hyperdrive()->get_config($config_id);
=head1 DESCRIPTION
These account-scoped REST methods manage Hyperdrive configurations for external PostgreSQL or MySQL databases. They do not run SQL. Applications query through a Worker Hyperdrive binding and a database driver. Cloudflare's origin password is write-on...
=head1 METHODS
=over
=item *
B<list_configs(%query)> â List every configuration. Named filters become query parameters. The method follows all pages and returns one flat array reference; a large result set can require many requests and substantial memory.
lib/Cloudflare/API/Hyperdrive.pm.md view on Meta::CPAN
# SYNOPSIS #
```perl
my $configs=$api->hyperdrive()->list_configs();
my $config=$api->hyperdrive()->get_config($config_id);
```
# DESCRIPTION #
These account-scoped REST methods manage Hyperdrive configurations for external PostgreSQL or MySQL databases. They do not run SQL. Applications query through a Worker Hyperdrive binding and a database driver. Cloudflare's origin password is write-on...
# METHODS #
* **list_configs(%query)** â List every configuration. Named filters become query parameters. The method follows all pages and returns one flat array reference; a large result set can require many requests and substantial memory.
* **list_configs_page(%query)** â Return a lazy `HTTP::API::Core::Pagination` object. Use `next()` to consume one configuration at a time.
* **list_configs_page_response(%query)** â Make one list request and return the complete decoded Cloudflare response hash, including `result_info` when supplied.
* **get_config($id, %options)** â Retrieve a configuration by ID and return its `result`.
* **create_config(\%body, %options)** â POST a Cloudflare configuration body and return its `result`.
* **replace_config($id, \%body, %options)** â PUT a replacement configuration and return its `result`.
* **update_config($id, \%body, %options)** â PATCH a configuration and return its `result`.
$ENV{'MOCK_WRANGLER_WHOAMI_JSON'}='{"loggedIn":true,"accounts":[]}';
$error=eval { wrangler_account_id(); 1 };
ok(!$error && $@=~/no available accounts/, 'empty Wrangler account list rejected');
$ENV{'MOCK_WRANGLER_WHOAMI_JSON'}='{"loggedIn":true}';
$error=eval { wrangler_account_id(); 1 };
ok(!$error && $@=~/no account list/, 'missing Wrangler account list rejected');
$ENV{'MOCK_WRANGLER_JSON'}='{"type":"api_token","token":"api-test-token"}';
is(wrangler_token(), 'api-test-token', 'Wrangler API token accepted');
$ENV{'MOCK_WRANGLER_JSON'}='{"type":"api_key","key":"secret-key","email":"x@example.test"}';
$error=eval { wrangler_token(); 1 };
ok(!$error && $@=~/unsupported credential type/, 'API key credentials rejected');
unlike($@, qr/secret-key/, 'API key is absent from error');
$ENV{'MOCK_WRANGLER_JSON'}='secret-output';
$error=eval { wrangler_token(); 1 };
ok(!$error && $@=~/invalid JSON/, 'invalid Wrangler output rejected');
unlike($@, qr/secret-output/, 'invalid output is absent from error');
$ENV{'MOCK_WRANGLER_JSON'}='{"type":"oauth","token":"bad\\nheader"}';
$error=eval { wrangler_token(); 1 };
ok(!$error && $@=~/no usable token/, 'token containing a control character rejected');
local $ENV{'MOCK_WRANGLER_FAIL'}=1;
$error=eval { wrangler_token(); 1 };
ok(!exists($client_opt{'account_id'}), 'zone-scoped action needs no account ID');
}
($exit, $output)=run_cli(undef, qw(--auth=other --resource accounts --action
list --dump-opt));
isnt($exit, 0, 'unknown authentication source rejected');
like($output, qr/auth must be wrangler/, 'authentication source error is clear');
($exit, $output)=run_cli(undef, qw(--auth=wrangler --resource accounts --action
list --dump-opt));
is($exit, 0, 'Wrangler option accepted without fetching credentials in dump mode');
($exit, $output)=run_cli(undef, qw(--resource r2 --action delete_bucket --arg x
--paginate --dump-opt));
isnt($exit, 0, 'pagination rejected for non-list action');
like($output, qr/--paginate requires a list or search action/, 'pagination error is clear');
($exit, $output)=run_cli(undef, '--resource', 'secrets_store', '--action',
'create_secret', '--arg', 'store', '--arg-json',
'[{"name":"x","value":"secret","scopes":["workers"]}]', '--dump-opt');
isnt($exit, 0, 'secret-bearing action cannot dump parsed options');