Control Plane
Account setup and deployment are F# programs that call Cloudflare's REST API. Eleven .NET clients have a method for each of 3,437 operations in that API, from creating a database to uploading a Worker.
- Clients 10
Management, 1Tenancy - Shared types
Core.Api - Operations 3,437
- Target
netstandard2.0
API Client
Each client's constructor takes an HttpClient. The one below has the API root as its base address and sends a bearer token from the CLOUDFLARE_API_TOKEN environment variable with every request.
open System
open System.Net.Http
open System.Net.Http.Headers
open FSharp.CloudEdge.Management.Compute
open FSharp.CloudEdge.Management.Storage
open FSharp.CloudEdge.Tenancy
let connect () =
let token = Environment.GetEnvironmentVariable "CLOUDFLARE_API_TOKEN"
let http = new HttpClient(BaseAddress = Uri "https://api.cloudflare.com/client/v4")
http.DefaultRequestHeaders.Authorization <- AuthenticationHeaderValue("Bearer", token)
http
let accountId = Environment.GetEnvironmentVariable "CLOUDFLARE_ACCOUNT_ID"
let http = connect ()
let tenancy = TenancyClient http
let storage = StorageClient http
let compute = ComputeClient http
Needs an API token in CLOUDFLARE_API_TOKEN and your account ID in CLOUDFLARE_ACCOUNT_ID. Credentials covers both values.
Token Check
Every operation returns a Task of a union with one case for each response the operation declares. UserApiTokensVerifyToken declares OK and Status4XX, and match! handles both.
open FSharp.CloudEdge.Core.Api.Types
open FSharp.CloudEdge.Tenancy
let checkToken (tenancy: TenancyClient) =
task {
match! tenancy.UserApiTokensVerifyToken() with
| UserApiTokensVerifyToken.OK payload ->
printfn "Token accepted: %b" payload.success
return true
| UserApiTokensVerifyToken.Status4XX(status, failure) ->
for error in failure.errors do
printfn "HTTP %d, code %d: %s" status error.code error.message
return false
}
The unions and payload types are in FSharp.CloudEdge.Core.Api.Types. A type for a named schema has the schema's name with _u002D_ in place of each hyphen, as in workers_u002D_kv_create_namespace_body. Unions and inline request bodies are named after their operation, such as D1CreateDatabasePayload.
Account Lookup
The client raises an exception for a status outside the declared cases, and for a response body whose JSON differs from its declared type. This lookup returns every outcome as a Result, and try ... with turns exceptions into Error values.
open FSharp.CloudEdge.Core.Api.Types
open FSharp.CloudEdge.Tenancy
let findAccount (tenancy: TenancyClient) (name: string) =
task {
try
match! tenancy.AccountsListAccounts(name = name) with
| AccountsListAccounts.OK payload ->
match Option.defaultValue [] payload.result with
| account :: _ -> return Ok account.id
| [] -> return Error $"No account named {name}"
| AccountsListAccounts.Status4XX(status, failure) ->
let details = failure.errors |> List.map (fun error -> error.message) |> String.concat "; "
return Error $"HTTP {status}: {details}"
with error ->
return Error error.Message
}
Setup Program
This entry point starts with Token Check and then calls examples from Account Setup and Worker Upload in order. It creates the storage and the queue, then uploads the Worker with bindings to them. After the upload it attaches the queue consumer and sets the schedule, and the last call turns on the workers.dev address.
open System.IO
open FSharp.CloudEdge.Core.Api.Http
open FSharp.CloudEdge.Core.Api.Types
open ApiClient
let uploadGuestbook databaseId namespaceId =
let worker: MultipartFile =
{ Bytes = File.ReadAllBytes "dist/worker.js"
FileName = "worker.js"
ContentType = Some "application/javascript+module"
PartName = Some "worker.js" }
let metadata = GuestbookBindings.metadata databaseId namespaceId
compute.WorkerScriptUploadWorkerModule(accountId, "guestbook", metadata, files = [ worker ])
let setup () =
task {
let! tokenAccepted = TokenCheck.checkToken tenancy
let! databaseId = GuestbookDatabase.createDatabase storage accountId
let! namespaceId = SessionStore.createNamespace storage accountId
let! bucketCreated = PhotoBucket.createBucket storage accountId
let! queueId = OrderQueue.createQueue compute accountId
match databaseId, namespaceId, queueId with
| Some databaseId, Some namespaceId, Some queueId when tokenAccepted && bucketCreated ->
match! uploadGuestbook databaseId namespaceId with
| WorkerScriptUploadWorkerModule.OK _ ->
do! OrderConsumer.attach compute accountId queueId
do! NightlyCleanup.schedule compute accountId
do! PublicUrl.publish compute accountId "guestbook"
return 0
| WorkerScriptUploadWorkerModule.Status4XX(status, _) ->
printfn "Upload rejected with HTTP %d" status
return 1
| _ -> return 1
}
[<EntryPoint>]
let main _ = setup().GetAwaiter().GetResult()
Library Table
| Library | Operations | What it covers |
|---|---|---|
Core.Api |
0 | Payload types and HTTP transport |
Management.<Purpose> |
3,137 | Ten clients, listed in the Client Catalog |
Tenancy |
300 | Accounts, members, API tokens, zones |
Related Pages
NuGet packages
Core.Api 0.1.0, Management.Compute 0.1.0, Management.Storage 0.1.0, Tenancy 0.1.0.