Giter Site home page Giter Site logo

cloudflare-rs's Introduction

cloudflare-rs

Rust library for the Cloudflare v4 API

Cloudflare's crates.io badge Cloudflare's docs.rs badge

⚠️ This library is a Work in Progress! ⚠️

This library provides convenience functions that wrap the Cloudflare API.

It provides two client implementations (asynchronous vs blocking). However, projects targeting wasm32 only get the asynchronous one (as it does not make sense to block in that target).

cloudflare-rs's People

Contributors

adamchalmers avatar ashleygwilliams avatar ashleymichal avatar atul9 avatar colerar avatar exvuma avatar gabbifish avatar goncalogarcia avatar jbampton avatar jeff-hiner avatar joliveirinha avatar jspspike avatar keehun avatar markpritchard avatar nataliescottdavidson avatar nilslice avatar nmldiegues avatar noah-kennedy avatar ocsfrank avatar rakshith-ravi avatar rishabh-bector avatar shalzz avatar sssilver avatar taylorlee avatar vvilhonen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cloudflare-rs's Issues

JSON deserialization doesn't work for zones with free plans

A free zone has the following plan object:

{
    "id": "0feeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    "name": "Free Website",
    "price": 0,
    "currency": "USD",
    "frequency": "",
    "is_subscribed": true,
    "can_subscribe": false,
    "legacy_id": "free",
    "legacy_discount": false,
    "externally_managed": false
}

Notably, frequency: "". This causes Rust deserialization to fail:

unknown variant ``, expected one of `weekly`, `monthly`, `quarterly`, `yearly`

I think this is technically in the Cloudflare API docs. The section https://api.cloudflare.com/#zone-properties has this to say about frequency:

valid values: weekly, monthly, quarterly, yearly,

Note the trailing comma. I think the API documentation-generator is faithfully stripping the quotes from every possible value, including the empty string...

Add contributing guide

As more folks use this library, repeating guidelines for PRs and issues will become tedious. We should add a contributing guide at the root.

This crate does not work with nicely with a WASM worker

I'm evaluating https://workers.dev as a solution for a Rust based web application.

If you run the following:

wrangler generate my-worker --type=rust
cd my-worker
vim Cargo.toml      # Then add `cloudflare = "0.6.4"`
wrangler build

The build step will fail and give the following error:

   Compiling cloudflare v0.6.4
error[E0432]: unresolved import `reqwest::blocking`
 --> /home/username/.cargo/registry/src/github.com-1ecc6299dc9ec823/cloudflare-0.6.4/src/framework/auth.rs:1:14
  |
1 | use reqwest::blocking::RequestBuilder;
  |              ^^^^^^^^ could not find `blocking` in `reqwest`

I haven't looked deeply into this. But I believe this could due to wasm-bindgen.

How do you use cloudflare-rs within a Rust Cloudflare worker?

Fix brittle test

we have a unit test that checks that our ApiFailure struct does not evaluate as equal to an Invalid Request error raised by reqwest, however it uses a real url that, while not associated with a DNS record, nevertheless was picked up by a service and returns a 200 when called. Let's test with a completely invalid url, as that is a similar error thrown by reqwest.

Expose write access to workers' scripts "create tail" endpoint as a separate token permission

/// https://api.cloudflare.com/#worker-create-tail

Originally filed as cloudflare/wrangler-legacy#1563 which has more context.

It would be nice to grant permission to "read" logs from workers independently of granting permission to write arbitrary workers scripts. It would be great if this could be brought in under account permission 1a71c399035b4950a1bd1466bbe4f420: Workers Scripts Read.

Querying for a specific type of DNS record is broken

You currently can't list DNS records of a specific type only, because the ListDnsRecordsParams takes a DnsContent enum as the parameter for filtering the type, but DnsContent not only contains the record type, but also the record content, which is why reqwest fails to build a query string from it. For the application I'm using this in, I've implemented the filtering locally after fetching the records like this, but I'd be happy about a proper solution for this.

Add async ApiClient

Now that async/await is in stable Rust 1.39, we should support async. I know Tunnelmon would be much cheaper to run if we used an event loop instead of parallelizing across cores.

I'm not sure it's possible to use the same ApiClient trait for both sync and async (because one will return Future and one will return T). So we might need a second trait.

Add issue templates

As more people use the library, we will get more issues reported. Providing a template helps us get the necessary information up front from community issue creators.

Owner deserialization error

Starting yesterday we started seeing errors where the response from ListZones were failing to deserialize correctly. It looks to be related to the owner field in the API response. The field in the response looks like:

{
  "owner": {
    "id": null,
    "type": "user",
    "email": null
  }
}

This conflicts with the Owner enum which expects the id and email fields to be non-null:

pub enum Owner {
    User { id: String, email: String },
    Organization { id: String, name: String },
}

Unsure if this was a recent change in the API, but presumably these should now be Option<String> if null is a valid response value? (Unclear if this is an API issue returning null when it shouldn't or if this is a library update)

Add dev environments (with custom URLs)

Currently Cloudflare-rs only supports production environments. But users need to test their code against other environments too, e.g. a local developer instance of the Cloudflare API. Users should be able to provide a custom URL.

Zone listing endpoint not working

Hi, I tried to call ListZones and it doesn't work. The error is:

invalid value: integer `-64315`, expected u8 at line 1 column 142

I checked the response and it's development_mode field.

After serialization of request payload bytes, verify payload size is <100MB

There are certain API endpoints on the Cloudflare API that allow users to send large payloads (e.g. the Workers KV bulk upload endpoint).

The API gateway will block any payloads over 100MB in size: this means we get an APIFailure like Error(413, ApiErrors { other: {}, errors: [] }). I think it could be worth introducing a general request validator function that checks for things like appropriate payload size, given that this will affect all API requests.

cc @ashleymichal

HttpApiClient should be able to handle non-JSON request and response bodies

There are some APIs at Cloudflare that do not follow our common use of json for request and response bodies: Two examples of this:

  1. Reading a Workers KV value by key returns a raw string body: https://api.cloudflare.com/#workers-kv-namespace-read-key-value-pair
  2. Setting a Workers KV key-value pair requires a raw string body: https://api.cloudflare.com/#workers-kv-namespace-write-key-value-pair

Because cloudflare-rs expects to serialize/deserialize JSON for all CF API endpoints, these Workers KV API endpoints are not compatible with this library.

Given that this library surfaces an HttpApiClient, not an HttpJsonApiClient, should this trait be built more extensibly, so the parsing of request bodies is directly tied to the "content-type" header associated with it? Perhaps the default serialization/deserialization will be for json, but if a request/response specifies a specific "content-type" header, the body bytes will be parsed as such.

Broken API Link

The commented link here is broken and does not go to a valid API section.
Furthermore, I can't even find the section in the docs, so I don't know what you should update it with.

Maybe the link is correct and you should ping your API docs team and let them know the section disappeared for some reason.😉

Add crate metadata

Presently the package is missing some metadata in the TOML file that's required for publishing the package to crates.io.

Separate modules for the API framework and endpoint implementations

Currently our main src/ directory contains two kinds of modules:

  • modules for the API framework (e.g. response or apiclient
  • modules for requests/responses from particular Cloudflare APIs (e.g. dns or workerskv)

We should sort these into two different modules (my suggested names: framework and endpoints). That way, most people can ignore the framework directory and only focus on adding their own endpoints.

ApiFailure should impl Error

To integrate with Failure and the rest of the Rust error ecosystem, std::error::Error should be implemented for cloudflare::framework::response::apifail::ApiFailure. Otherwise, it's hard to use cloudflare-rs with ? in a function that returns Fallible.

Add Workers Routes endpoints

Let's start out support for the Workers API with three endpoints for working with Workers Routes: Create Route, Delete Route, and List Routes.

Make 'Content-Type' header configurable per Endpoint

Added create namespace to example and attempted to run (actual auth values changed in snippet):

$ cargo run -- --auth-key $CLOUDFLARE_AUTH_KEY --email $CLOUDFLARE_AUTH_EMAIL kv $CLOUDFLARE_ACCOUNT_ID "another great namespace"
   Compiling cloudflare v0.1.0 (/Users/ashleylewis/cloudflare-rs)
    Finished dev [unoptimized + debuginfo] target(s) in 4.74s
     Running `target/debug/cloudflare-rust-examples --auth-key $CLOUDFLARE_AUTH_KEY --email $CLOUDFLARE_AUTH_EMAIL kv $CLOUDFLARE_ACCOUNT_ID 'another great namespace'`
Error 400 Bad Request:
Error 10010: could not parse Namespace from request body: 'mime: no media type'

This can be fixed by adding the Content-Type: application/json header to the request. The question is whether to require Content-type on all endpoints, provide a default of application/json, etc. Note that not all Cloudflare endpoints use JSON for upload requests; notably the Workers PUT /script endpoint requires either application/javascript or multipart form.

ListZones not work due to deserialize json fail!

        let req = ListZones { params };
        let resp: Vec<Zone> = self.api_client.request_handle(&req).await?.result;
      //this fail with: error decoding response body: invalid type: null, expected a string at line 1 column 615)

parse fail happens at this line
ListZones

        let parsed: Result<ApiSuccess<ResultType>, reqwest::Error> = resp.json();

the response json which fail to be parsed

{
    "result": [
        {
            "id": "xxxxxxxxxxxxxxxxxxx",
            "name": "xxxx.com",
            "status": "active",
            "paused": false,
            "type": "full",
            "development_mode": 0,
            "name_servers": [
                "frank.ns.cloudflare.com",
                "sandy.ns.cloudflare.com"
            ],
            "original_name_servers": null,
            "original_registrar": "name.com, inc. (id: 625)",
            "original_dnshost": null,
            "modified_on": "2021-11-12T21:01:17.744476Z",
            "created_on": "2020-11-18T10:40:49.839936Z",
            "activated_on": "2020-11-18T10:43:07.087867Z",
            "meta": {
                "step": 2,
                "custom_certificate_quota": 0,
                "page_rule_quota": 3,
                "phishing_detected": false,
                "multiple_railguns_allowed": false
            },
            "owner": {
                "id": null,
                "type": "user",
                "email": null
            },
            "account": {
                "id": "xxxxxxxxxxxxxxxxxxxx",
                "name": "xxxxxxxxxxxxx"
            },
            "tenant": {
                "id": null,
                "name": null
            },
            "tenant_unit": {
                "id": null
            },
            "permissions": [
                "#access:edit",
                "#access:read",
                "#analytics:read",
                "#app:edit",
                "#auditlogs:read",
                "#billing:edit",
                "#billing:read",
                "#blocks:edit",
                "#blocks:read",
                "#cache_purge:edit",
                "#dash_sso:edit",
                "#dash_sso:read",
                "#dns_records:edit",
                "#dns_records:read",
                "#fbm_acc:edit",
                "#fbm:edit",
                "#fbm:read",
                "#healthchecks:edit",
                "#healthchecks:read",
                "#http_applications:edit",
                "#http_applications:read",
                "#image:edit",
                "#image:read",
                "#lb:edit",
                "#lb:read",
                "#legal:edit",
                "#legal:read",
                "#logs:edit",
                "#logs:read",
                "#magic:edit",
                "#magic:read",
                "#member:edit",
                "#member:read",
                "#organization:edit",
                "#organization:read",
                "#ssl:edit",
                "#ssl:read",
                "#stream:edit",
                "#stream:read",
                "#subscription:edit",
                "#subscription:read",
                "#teams:edit",
                "#teams:pii",
                "#teams:read",
                "#teams:report",
                "#waf:edit",
                "#waf:read",
                "#waitingroom:edit",
                "#waitingroom:read",
                "#webhooks:edit",
                "#webhooks:read",
                "#worker:edit",
                "#worker:read",
                "#zaraz:edit",
                "#zaraz:publish",
                "#zaraz:read",
                "#zone:edit",
                "#zone:read",
                "#zone_settings:edit",
                "#zone_settings:read",
                "#zone_versioning:edit",
                "#zone_versioning:read"
            ],
            "plan": {
                "id": "0feeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
                "name": "Free Website",
                "price": 0,
                "currency": "USD",
                "frequency": "",
                "is_subscribed": false,
                "can_subscribe": false,
                "legacy_id": "free",
                "legacy_discount": false,
                "externally_managed": false
            }
        }
    ],
    "result_info": {
        "page": 1,
        "per_page": 20,
        "total_pages": 1,
        "count": 1,
        "total_count": 1
    },
    "success": true,
    "errors": [],
    "messages": []
}

build with wasm target works, but fail to run because CORS

Access to fetch at 'https://api.cloudflare.com/client/v4/zones' from origin 'http://lo:8000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Do api.cloudflare.com have any setting to bypass CORS restriction?

Bump dependencies

  1. reqwest now defaults to async, not blocking. We should update and change our code to use reqwest::blocking instead of reqwest.
  2. serde_derive is no longer recommended, the Serde docs now say to use the derive feature of the serde crate.
  3. We depend on serde_yaml for no reason, no part of the code uses it

Secret endpoints only show/update/delete `secret_text`

Description

Not sure what's going on here but all of these endpoints only seem to affect encrypted secrets. It works as expected when the secrets are encrypted but nothing happens when it's in plain text.

  1. List secrets – GET /accounts/{account_id}/workers/scripts/{script_name}/secrets
  2. Create secrets – PUT /accounts/{account_id}/workers/scripts/{script_name}/secrets
  3. Delete secrets – DELETE /accounts/{account_id}/workers/scripts/{script_name}/secrets/{secret_name}

Steps to reproduce:

  1. Create a cloudflare worker in the dashboard
  2. Add new environment variables to the project but don't encrypt it
  3. Make a GET request to the list secrets endpoint.

Expected result:

{
    "result": [
        {
            "name": "hello",
            "type": "plain_text"
        }
    ],
    "success": true,
    "errors": [],
    "messages": []
}

Actual result:

{
    "result": [],
    "success": true,
    "errors": [],
    "messages": []
}

DNS: Support for additional record types

It seems like this crate currently only supports the most common DNS record types:

// in cloudflare/src/endpoints/dns.rs
pub enum DnsContent {
    A { content: Ipv4Addr },
    AAAA { content: Ipv6Addr },
    CNAME { content: String },
    NS { content: String },
    MX { content: String, priority: u16 },
    TXT { content: String },
}

However, the Cloudflare API lists several additional record types that are currently not implemented, including CAA, CERT, DNSKEY and more.

From what I can tell, adding any of these records to a zone causes the ListDnsRecords query to fail with an unknown variant error during deserialization, effectively making the ListDnsRecords query unavailable for such a zone (example below).
I'm running into this issue on my own zones, since I have a CAA record set on them.

I have a fork that fixes this for CAA records, but I'd be interested in seeing support for these record types in this main crate.
Adding simple enum variants for these records with the content field should be sufficient to allow the ListDnsRecords request to succeed (the API also returns a structured data field for some record types, but I don't think this is necessary for basic support).

If support for these record types is desired, I'd be more than happy to make the required changes and open a PR!


Steps to reproduce:

  1. Create a zone and create a CAA record through the Web Interface (such as 0 issue letsencrypt.org).
  2. Attempt to list all records in that zone using the cloudflare-examples utility like so:
cargo run -p cloudflare-examples -- --auth-token <redacted> dns <zone-id>
  1. The client will return the following error:
Error: error decoding response body: unknown variant `CAA`, expected one of `A`, `AAAA`, `CNAME`, `NS`, `MX`, `TXT`, `SRV` at line 1 column 8705

Use cargo workspaces

At the moment, crates which depend on cloudflare-rs will pull in clap and tokio, which are large crates that take a long time to download and compile. This sucks, because cloudflare-rs' library doesn't even use them! They're only used in the example/testing binaries. We shouldn't make users compile times unnecessarily long.

Solution: use cargo workspaces, and make the library a separate project from the example and the e2e tests. That way the library will only declare dependencies it actually uses.

wildcard_proxiable not included in zones listing response

The GET /client/v4/zone response doesn't include the wildcard_proxiable anymore.

This produces a deserialization error: Error: error decoding response body: missing field `wildcard_proxiable` at line 1 column 591

I don't know if the field has become optional or simply removed from the response.

Removing the field from the struct or converting it to an Option<bool> works.

pre-beta r2 endpoints

stub out the r2 endpoint locations for create/delete/listing of buckets, even though these endpoints are not yet publicly available.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.