Skip to content

Commit 7b08e20

Browse files
committed
feat: Add dnsRecords(options).find(params)
1 parent aa77049 commit 7b08e20

12 files changed

+698
-557
lines changed

.github/workflows/pull_request.yml

+10-2
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,18 @@ on: [pull_request]
77

88
env:
99
GOOGLE_CLOUD_CREDENTIALS: ${{ secrets.GOOGLE_CLOUD_CREDENTIALS }}
10+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
11+
CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}
12+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
1013

1114
jobs:
1215
build:
1316
runs-on: ubuntu-latest
1417
steps:
1518
- uses: actions/checkout@v3
19+
- uses: actions/setup-node@v3
20+
with:
21+
node-version: 18
1622
- uses: actions/cache@v3
1723
with:
1824
path: |
@@ -23,8 +29,10 @@ jobs:
2329
restore-keys: ${{ runner.os }}-yarn-
2430

2531
# Install dependencies
26-
- run: yarn config set enableGlobalCache false
27-
- run: yarn install
32+
- name: yarn install
33+
run: |
34+
yarn config set enableGlobalCache false
35+
yarn install
2836
2937
# Analyze code for potential problems
3038
- run: yarn prettier --check .

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@
2626
"**/.yarn": true,
2727
"**/.pnp.*": true
2828
},
29-
"cSpell.ignoreWords": ["endregion", "proxiable", "proxied"]
29+
"cSpell.ignoreWords": ["endregion", "proxiable", "proxied", "zipcode"]
3030
}

README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[![Donate](https://img.shields.io/badge/dynamic/json?color=%23ff424d&label=Patreon&style=flat-square&query=data.attributes.patron_count&suffix=%20patrons&url=https%3A%2F%2Fwww.patreon.com%2Fapi%2Fcampaigns%2F233228)](http://patreon.com/koistya)
77
[![Discord](https://img.shields.io/discord/643523529131950086?label=Chat&style=flat-square)](https://discord.gg/bSsv7XM)
88

9-
An HTTP client for Cloudflare REST API that works in Node.js, browser, and CF Workers environment.
9+
An HTTP client for [Cloudflare API](https://api.cloudflare.com/) that works in Node.js, browser, and CF Workers environment.
1010

1111
```bash
1212
# Install using NPM
@@ -29,6 +29,14 @@ await cf.user({ accessToken: "xxx" }).get();
2929
// https://api.cloudflare.com/#user-api-tokens-verify-token
3030
await cf.userTokens({ accessToken: "xxx" }).verify();
3131

32+
// Get DNS Record details
33+
// https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
34+
await cf.dnsRecords({ zoneId: "xxx", accessToken: "xxx" }).get("xxx");
35+
36+
// Find a single DNS Record matching the search parameters
37+
// https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
38+
await cf.dnsRecords({ zoneId: "xxx", accessToken: "xxx" }).find({ type: "A" });
39+
3240
// Get the list of DNS Records for the target zone
3341
// https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
3442
await cf

package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cloudflare-client",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"packageManager": "[email protected]",
55
"description": "Universal HTTP client for Cloudflare API",
66
"license": "MIT",
@@ -64,19 +64,19 @@
6464
},
6565
"devDependencies": {
6666
"@babel/cli": "^7.17.10",
67-
"@babel/core": "^7.18.2",
67+
"@babel/core": "^7.18.5",
6868
"@babel/preset-env": "^7.18.2",
6969
"@babel/preset-typescript": "^7.17.12",
70-
"@types/jest": "^28.1.0",
71-
"@typescript-eslint/eslint-plugin": "^5.27.0",
72-
"@typescript-eslint/parser": "^5.27.0",
73-
"babel-jest": "^28.1.0",
70+
"@types/jest": "^28.1.3",
71+
"@typescript-eslint/eslint-plugin": "^5.29.0",
72+
"@typescript-eslint/parser": "^5.29.0",
73+
"babel-jest": "^28.1.1",
7474
"babel-plugin-replace-import-extension": "^1.1.3",
7575
"dotenv": "^16.0.1",
76-
"eslint": "^8.16.0",
77-
"jest": "^28.1.0",
78-
"prettier": "^2.6.2",
79-
"typescript": "^4.7.2"
76+
"eslint": "^8.18.0",
77+
"jest": "^28.1.1",
78+
"prettier": "^2.7.1",
79+
"typescript": "^4.7.4"
8080
},
8181
"babel": {
8282
"presets": [

src/dnsRecords.test.ts

+64-17
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
/* SPDX-FileCopyrightText: 2022-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4-
import * as cf from "./dnsRecords.js";
4+
import { dnsRecords, type DnsRecord } from "./dnsRecords.js";
55

6-
const dnsRecords = cf.dnsRecords({
6+
const options = {
77
zoneId: process.env.CLOUDFLARE_ZONE_ID as string,
88
accessToken: process.env.CLOUDFLARE_API_TOKEN as string,
9-
});
9+
};
1010

11-
test("dnsRecords({ zoneId, accessToken }).findMany()", async () => {
12-
const res = await dnsRecords.findMany({ type: "A" });
11+
test("dnsRecords(options).findMany()", async () => {
12+
const res = await dnsRecords(options).findMany({ type: "A" });
1313

1414
if (res.result) {
15+
// Limit the result set
1516
res.result.length = 1;
16-
// Anonymize the response
17-
res.result.forEach((record) => {
18-
record.id = record.id?.replace(/\w/g, "x");
19-
record.name = record.name?.replace(/[\w-]+/g, "xxx");
20-
record.content = record.content && "192.0.2.1";
21-
record.zone_id = record.zone_id?.replace(/\w/g, "x");
22-
record.zone_name = record.zone_name?.replace(/[\w-]+/g, "xxx");
23-
record.created_on = record.created_on?.replace(/\d/g, "0");
24-
record.modified_on = record.modified_on?.replace(/\d/g, "0");
25-
});
17+
anonymize(res.result[0]);
18+
19+
if (typeof res.result_info.count === "number") {
20+
res.result_info.count = 10;
21+
}
22+
23+
if (typeof res.result_info.total_count === "number") {
24+
res.result_info.total_count = 10;
25+
}
26+
// #endregion
2627
}
2728

2829
expect(res).toMatchInlineSnapshot(`
@@ -52,13 +53,59 @@ test("dnsRecords({ zoneId, accessToken }).findMany()", async () => {
5253
},
5354
],
5455
"result_info": Object {
55-
"count": 40,
56+
"count": 10,
5657
"page": 1,
5758
"per_page": 100,
58-
"total_count": 40,
59+
"total_count": 10,
5960
"total_pages": 1,
6061
},
6162
"success": true,
6263
}
6364
`);
6465
});
66+
67+
test("dnsRecords({ zoneId, accessToken }).findMany()", async () => {
68+
const res = await dnsRecords(options).find({ type: "A" });
69+
anonymize(res.result);
70+
71+
expect(res).toMatchInlineSnapshot(`
72+
Object {
73+
"errors": Array [],
74+
"messages": Array [],
75+
"result": Object {
76+
"content": "192.0.2.1",
77+
"created_on": "0000-00-00T00:00:00.000000Z",
78+
"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
79+
"locked": false,
80+
"meta": Object {
81+
"auto_added": false,
82+
"managed_by_apps": false,
83+
"managed_by_argo_tunnel": false,
84+
"source": "primary",
85+
},
86+
"modified_on": "0000-00-00T00:00:00.000000Z",
87+
"name": "xxx.xxx.xxx",
88+
"proxiable": true,
89+
"proxied": true,
90+
"ttl": 1,
91+
"type": "A",
92+
"zone_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
93+
"zone_name": "xxx.xxx",
94+
},
95+
"success": true,
96+
}
97+
`);
98+
});
99+
100+
function anonymize(record?: DnsRecord | null) {
101+
if (record) {
102+
record.id = record.id.replace(/\w/g, "x");
103+
record.name = record.name.replace(/[\w-]+/g, "xxx");
104+
record.content = record.content && "192.0.2.1";
105+
record.zone_id = record.zone_id.replace(/\w/g, "x");
106+
record.zone_name = record.zone_name.replace(/[\w-]+/g, "xxx");
107+
record.created_on = record.created_on.replace(/\d/g, "0");
108+
record.modified_on = record.modified_on.replace(/\d/g, "0");
109+
if (record.proxied === false) record.proxied = true;
110+
}
111+
}

src/dnsRecords.ts

+39-27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* SPDX-FileCopyrightText: 2022-present Kriasoft */
22
/* SPDX-License-Identifier: MIT */
33

4-
import { baseUrl, createFetch, type Res } from "./fetch.js";
4+
import { baseUrl, createFetch, type ListRes, type Res } from "./fetch.js";
55

66
// #region TypeScript
77

@@ -10,6 +10,26 @@ type Options = {
1010
accessToken: string;
1111
};
1212

13+
export type DnsRecordType =
14+
| "A"
15+
| "AAAA"
16+
| "CNAME"
17+
| "HTTPS"
18+
| "TXT"
19+
| "SRV"
20+
| "LOC"
21+
| "MX"
22+
| "NS"
23+
| "CERT"
24+
| "DNSKEY"
25+
| "DS"
26+
| "NAPTR"
27+
| "SMIMEA"
28+
| "SSHFP"
29+
| "SVCB"
30+
| "TLSA"
31+
| "URI";
32+
1333
type FindOptions = {
1434
/**
1535
* Whether to match all search requirements or at least one (`any`)
@@ -40,7 +60,7 @@ type FindOptions = {
4060
* DNS record type
4161
* @example "A"
4262
*/
43-
type?: RecordType;
63+
type?: DnsRecordType;
4464
/**
4565
* DNS record proxied status
4666
*/
@@ -51,29 +71,9 @@ type FindOptions = {
5171
direction?: "asc" | "desc";
5272
};
5373

54-
type RecordType =
55-
| "A"
56-
| "AAAA"
57-
| "CNAME"
58-
| "HTTPS"
59-
| "TXT"
60-
| "SRV"
61-
| "LOC"
62-
| "MX"
63-
| "NS"
64-
| "CERT"
65-
| "DNSKEY"
66-
| "DS"
67-
| "NAPTR"
68-
| "SMIMEA"
69-
| "SSHFP"
70-
| "SVCB"
71-
| "TLSA"
72-
| "URI";
73-
74-
type DnsRecord = {
74+
export type DnsRecord = {
7575
id: string;
76-
type: RecordType;
76+
type: DnsRecordType;
7777
name: string;
7878
content: string;
7979
proxiable: boolean;
@@ -93,8 +93,9 @@ type DnsRecord = {
9393
data?: Record<string, unknown>;
9494
};
9595

96-
type DnsRecordResponse = Res<DnsRecord>;
97-
type DnsRecordsResponse = Res<DnsRecord[]>;
96+
export type DnsRecordResponse = Res<DnsRecord>;
97+
export type DnsRecordsResponse = ListRes<DnsRecord>;
98+
export type DnsRecordsOptions = Options;
9899

99100
// #endregion
100101

@@ -108,10 +109,21 @@ export function dnsRecords(options: Options) {
108109
*/
109110
get: createFetch<string, DnsRecordResponse>({
110111
method: "GET",
111-
url: `${url}/:id`,
112+
url: (id) => `${url}/${id}`,
112113
accessToken: options.accessToken,
113114
}) as (id: string) => Promise<DnsRecordResponse>,
114115

116+
/**
117+
* Find DNS Record
118+
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
119+
*/
120+
find: createFetch<FindOptions, DnsRecordResponse>({
121+
method: "GET",
122+
url,
123+
accessToken: options.accessToken,
124+
single: true,
125+
}),
126+
115127
/**
116128
* List DNS Records
117129
* @see https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records

0 commit comments

Comments
 (0)