Skip to content

Commit 9964c4a

Browse files
committed
Initialization
1 parent 65e79d1 commit 9964c4a

File tree

12 files changed

+1163
-1
lines changed

12 files changed

+1163
-1
lines changed

components/wordpress_com/common/methods.mjs

Lines changed: 478 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Returns a new object containing only standard prop fields, removing any custom keys.
2+
// Considered using JSON deep cloning, but opted for a manual approach to safely preserve functions or complex types in future data structures.
3+
export function removeCustomPropFields(input) {
4+
const blacklist = new Set(["extendedType", "postBody"]);
5+
const clean = {};
6+
7+
for (const key of Object.keys(input)) {
8+
const prop = input[key];
9+
const cloned = {};
10+
11+
for (const field of Object.keys(prop)) {
12+
if (!blacklist.has(field)) {
13+
cloned[field] = prop[field];
14+
}
15+
}
16+
17+
clean[key] = cloned;
18+
}
19+
20+
return clean;
21+
}
22+
23+
24+

components/wordpress_com/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
"access": "public"
1414
},
1515
"dependencies": {
16-
"@pipedream/platform": "^3.0.0"
16+
"@pipedream/platform": "^3.0.0",
17+
"http": "^0.0.1-security",
18+
"open": "^10.1.1"
1719
}
1820
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Local Tests Directory
2+
3+
This folder contains **non-production test scripts** used for developing and validating Pipedream components locally.
4+
**You can safely delete the entire folder** — it does not affect runtime or deployment.
5+
6+
---
7+
8+
## Folder Structure
9+
10+
### `action-tests/`
11+
Component-level test runners that simulate a real Pipedream `$` context using `mockery-dollar.mjs`.
12+
13+
- `test-retrieve-site-performance-data.mjs` – Tests the Search Analytics action
14+
- `test-submit-url-for-indexing.mjs` – Tests the Indexing API action
15+
16+
---
17+
18+
### `methods/`
19+
Unit tests for reusable validation and utility methods found in `.app.mjs`.
20+
21+
- `test-checkIfUrlValid.mjs` – Tests URL validation helper
22+
- `test-throwIfNotYMDDashDate.mjs` – Tests strict date validation
23+
24+
#### `bogus-data/`
25+
Mocked data used to simulate edge-case user inputs and trigger validations:
26+
- `bogus-data-url.mjs` – Invalid or suspicious URLs
27+
- `bogus-data-google-date.mjs` – Date values to test against expected format
28+
29+
---
30+
31+
### Root-level Utilities
32+
33+
- `mockery-dollar.mjs` – Mocks the `$` object Pipedream injects into actions
34+
- `get-token.mjs` – Script for manually supplying a Google OAuth token during local testing
35+
36+
---
37+
38+
## ⚠️ Notes
39+
40+
- Some files may contain **hardcoded tokens** — be sure to exclude them from commits.
41+
- All files here are meant for **local testing only**.
42+
- Delete this folder any time before publishing — it's safe and has no impact on your app.
43+
44+
---
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* ─────────────────────────────────────────────────────────────────────────────
3+
* LOCAL TEST RUNNER – DO NOT DEPLOY
4+
*
5+
* This file is used to run and debug a Pipedream action **locally** outside
6+
* of the Pipedream platform. Safe to delete. It does **not** affect production.
7+
*
8+
* It:
9+
* - Injects mocked `$` object (logs, summary)
10+
* - Bypasses OAuth by using hardcoded access token (get-token.mjs)
11+
* - Validates, builds, and sends the Search Console request
12+
*
13+
* You MUST:
14+
* - Replace `Authorization` token with a valid one manually
15+
* - Ensure `siteUrl` is verified in your Search Console
16+
*
17+
* ─────────────────────────────────────────────────────────────────────────────
18+
*/
19+
20+
import mockery$ from "../mockery-dollar.mjs";
21+
import { axios } from "@pipedream/platform";
22+
import {removeCustomPropFields} from "../../common/utils.mjs";
23+
import wpress from "../../wordpress_com.app.mjs";
24+
25+
26+
27+
// TEST (FIX IN PRODUCTION) Remove completely
28+
const mockeryData = {
29+
site: "testsit38.wordpress.com",
30+
title : "New Post",
31+
content : "<div> HELLO WORLD </div>",
32+
status : "draft",
33+
};
34+
35+
36+
// Define prop metadata separately and spread it into the props object.
37+
// Useful for accessing extended metadata during runtime — available because it stays in closure.
38+
39+
const propsMeta = {
40+
41+
site: {
42+
type: "string",
43+
label: "Site ID or domain",
44+
extendedType : "domainOrId",
45+
description: "Enter a site ID or domain (e.g. testsit38.wordpress.com). Do not include 'https://' or 'www'."
46+
},
47+
title: {
48+
type: "string",
49+
label: "Post Title",
50+
description: "The title of the post.",
51+
postBody : true,
52+
},
53+
content: {
54+
type: "string",
55+
label: "Post Content",
56+
description: "The content of the post (HTML or text).",
57+
postBody : true,
58+
},
59+
status: {
60+
type: "string",
61+
label: "Status",
62+
description: "The status of the post.",
63+
options: [
64+
"publish",
65+
"draft",
66+
"private",
67+
"pending",
68+
],
69+
default: "draft",
70+
postBody : true,
71+
},
72+
};
73+
74+
75+
76+
//TEST (FIX IN PRODUCTION). Replace to export default.
77+
const testAction = {
78+
79+
...mockeryData,
80+
key: "worpress_com-create-post",
81+
name: "Create New Post",
82+
description: "Retrieves the authenticated user's account information.",
83+
version: "0.0.1",
84+
type: "action",
85+
props: {
86+
wpress,
87+
// Remove custom prop metadata and spread only valid prop fields
88+
...removeCustomPropFields(propsMeta),
89+
},
90+
91+
async run({ $ }) {
92+
93+
94+
/*Validate props (check if they are of a valid data type).
95+
Returns an array with warnings.
96+
If there are no warnings, it returns an empty array.*/
97+
const warnings = wpress.methods.validationLoop(propsMeta, this); // TEST (FIX IN PRODUCTION) replace wpress.methods for this.wpress
98+
99+
// Returns an object with props that has postBody === true
100+
const payload = wpress.methods.preparePayload(propsMeta, this); // TEST (FIX IN PRODUCTION) replace wpress.methods for this.wpress
101+
102+
let response;
103+
try {
104+
response = await axios($, {
105+
method: "POST",
106+
url : `https://public-api.wordpress.com/rest/v1.1/sites/${this.site}/posts/new`,
107+
headers: {
108+
Authorization: `Bearer v6Dp%5xWzh(rsgW9PR&xY4C#Ibs&Jo3W12nv@6Te3cZOrI5XACdoate$DQ#pIyYP`,
109+
},
110+
data : payload,
111+
});
112+
113+
$.export("$summary", `Post "${response.title}" created (ID: ${response.ID})` + "\n- " + warnings.join("\n- "));
114+
return response;
115+
} catch (error) {
116+
117+
const thrower = wpress.methods.checkWhoThrewError(error); //TEST
118+
119+
throw new Error(`Failed to fetch data ( ${thrower.whoThrew} error ) : ${error.message}. ` + "\n- " + warnings.join("\n- "));
120+
121+
}
122+
},
123+
};
124+
125+
126+
127+
//TEST (FIX IN PRODUCTION)
128+
// await is just in case if node wants to finish its job before time =)
129+
async function runTest() {
130+
await testAction.run(mockery$);
131+
}
132+
133+
runTest();
134+
135+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Bare-bones OAuth script to fetch a token.
2+
// For testing purposes only — no timeouts, no browser response.
3+
// Manually terminate the script from the console when finished.
4+
// Access token v6Dp%5xWzh(rsgW9PR&xY4C#Ibs&Jo3W12nv@6Te3cZOrI5XACdoate$DQ#pIyYP
5+
import open from "open";
6+
import http from "http";
7+
8+
// --- Google OAuth Config ---
9+
const clientId = "116795";
10+
const redirectUri = "http://localhost:3000";
11+
const scopes = "global";
12+
13+
const authUrl = new URL("https://public-api.wordpress.com/oauth2/authorize");
14+
authUrl.searchParams.set("client_id", clientId);
15+
authUrl.searchParams.set("redirect_uri", redirectUri);
16+
authUrl.searchParams.set("response_type", "code");
17+
authUrl.searchParams.set("scope", scopes);
18+
authUrl.searchParams.set("access_type", "offline");
19+
authUrl.searchParams.set("prompt", "consent");
20+
21+
22+
new Promise((resolve, reject) => {
23+
// Start a local HTTP server to catch Google's OAuth redirect
24+
http.createServer((incomingRequest, outgoingResponse) => {
25+
const fullRequestUrl = new URL(incomingRequest.url, `http://${incomingRequest.headers.host}`);
26+
const authorizationCode = fullRequestUrl.searchParams.get("code");
27+
28+
if (authorizationCode) {
29+
console.log("AUTH CODE RECEIVED:", authorizationCode);
30+
resolve(authorizationCode);
31+
} else {
32+
console.log("Unexpected request received — no code param found.");
33+
reject("Unexpected request received — no code param.");
34+
}
35+
}).listen(3000, () => {
36+
console.log("🌐 Listening on http://localhost:3000");
37+
console.log("🔗 Opening browser for Google OAuth...");
38+
open(authUrl.toString());
39+
});
40+
})
41+
.then((authorizationCode) => {
42+
console.log("✅ Got the code! Exchanging for tokens...");
43+
44+
45+
fetch("https://public-api.wordpress.com/oauth2/token", {
46+
method: "POST",
47+
headers: {
48+
"Content-Type": "application/x-www-form-urlencoded",
49+
},
50+
body: new URLSearchParams({
51+
code: authorizationCode,
52+
client_id: clientId,
53+
client_secret: "E7BSLxmS3u572GBn3wZ82LTLTGs3VIzWxLtim2yuVI0wKYdH8Y7cXTRXtO7zj5yK",
54+
redirect_uri: redirectUri,
55+
grant_type: "authorization_code",
56+
}),
57+
})
58+
.then((res) => res.json())
59+
.then((data) => {
60+
if (data.access_token) {
61+
console.log("🎉 Tokens received!");
62+
console.log("Access Token:", data.access_token);
63+
console.log("Refresh Token:", data.refresh_token);
64+
console.log("Expires In:", data.expires_in);
65+
console.log("Token retrieval complete. Shutting down server...");
66+
process.exit(0);
67+
} else {
68+
console.error(" Failed to get tokens:", data);
69+
process.exit(1);
70+
}
71+
})
72+
.catch((err) => {
73+
console.error("Error while exchanging code:", err);
74+
process.exit(1);
75+
});
76+
})
77+
.catch((error) => {
78+
console.error("Error during authorization:", error);
79+
process.exit(1);
80+
});

0 commit comments

Comments
 (0)