-
Notifications
You must be signed in to change notification settings - Fork 5.5k
DataForSEO new components #18095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DataForSEO new components #18095
Conversation
The latest updates on your projects. Learn more about Vercel for GitHub. 3 Skipped Deployments
|
WalkthroughAdded many new DataForSEO action modules and expanded the DataForSEO app client with numerous new methods; adjusted prop types (limit → integer, locationCode → integer), removed async from several internal methods, and applied multiple version bumps across actions and package. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant Action as Pipedream Action
participant App as DataForSEO App Client
participant API as DataForSEO API
User->>Action: Provide props (keyword/target/appIds/etc.)
Action->>App: this.dataforseo.getXxx({ $, data: [...] })
App->>API: HTTP request via _makeRequest()
API-->>App: Response { status_code, tasks: [...] }
App-->>Action: Response
Action->>Action: Validate response.status_code and response.tasks[0].status_code
alt error
Action-->>User: throw ConfigurationError(status_message)
else success
Action-->>User: $.export("$summary") + return full response
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 25
🔭 Outside diff range comments (4)
components/dataforseo/actions/parse-page-content/parse-page-content.mjs (1)
37-38
: Fix broken Markdown link in user-facing descriptionThe link text/URL are reversed. This will render as a broken link.
Apply this diff to correct the Markdown:
- description: "Set to `true` if you want to get the HTML of the page using the [https://docs.dataforseo.com/v3/on_page/raw_html/](OnPage Raw HTML endpoint)", + description: "Set to `true` if you want to get the HTML of the page using the [OnPage Raw HTML endpoint](https://docs.dataforseo.com/v3/on_page/raw_html/)",components/dataforseo/dataforseo.app.mjs (3)
14-21
: Null-safe options for Location Code and clearer namingAvoid exceptions when the API returns an empty
tasks
array and use a neutral variable name.- const response = await this.getLocations(); - const languageCodes = response.tasks[0].result; - return languageCodes.map(({ + const response = await this.getLocations(); + const results = response?.tasks?.[0]?.result || []; + return results.map(({ location_name, location_code, }) => ({ value: location_code, label: location_name, }));
79-87
: Null-safe options for Language CodeMirror the defensive approach used for Location Code to prevent runtime errors.
- const response = await this.getLanguageCode(); - const languageCodes = response.tasks[0].result; - return languageCodes.map(({ + const response = await this.getLanguageCode(); + const languageCodes = response?.tasks?.[0]?.result || []; + return languageCodes.map(({ language_name, language_code, }) => ({ value: language_code, label: language_name, }));
167-168
: Close the parenthesis in the targets descriptionMinor documentation typo in a user-facing field.
- description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`", + description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
🧹 Nitpick comments (77)
components/dataforseo/actions/parse-page-content/parse-page-content.mjs (1)
41-44
: Capitalize “JavaScript” in label and descriptionMinor UX polish in user-facing text.
- label: "Enable Javascript", - description: "Set to `true` if you want to load the scripts available on a page", + label: "Enable JavaScript", + description: "Set to `true` if you want to load the scripts available on a page",components/dataforseo/actions/get-business-listings/get-business-listings.mjs (1)
62-62
: Harden $summary construction to avoid runtime errors when tasks is missing/emptyIf the API returns an unexpected shape (e.g., no tasks), accessing
response.tasks[0]
will throw.Apply this diff:
- $.export("$summary", `Successfully sent the request. Status: ${response.tasks[0].status_message}`); + const statusMsg = response?.tasks?.[0]?.status_message ?? response?.status_message ?? "Request sent"; + $.export("$summary", `Successfully sent the request. Status: ${statusMsg}`);components/dataforseo/actions/get-ranked-keywords/get-ranked-keywords.mjs (1)
41-42
: Defensive summary construction to avoid potential TypeErrorIf the API returns an unexpected payload (e.g., empty tasks array), indexing tasks[0] will throw. Recommend a safe fallback.
Apply this diff:
- $.export("$summary", `Successfully sent the request. Status: ${response.tasks[0].status_message}`); + const status = response?.tasks?.[0]?.status_message ?? "Request sent"; + $.export("$summary", `Successfully sent the request. Status: ${status}`);components/dataforseo/actions/get-bulk-ranks/get-bulk-ranks.mjs (1)
11-18
: Minor naming nit: method name could be simplifiedMethod name
getBacklinksBulkRanks
is accurate but slightly verbose within this action whose key is already “get-bulk-ranks”. ConsidergetBulkRanks
for clarity.Apply this diff if you agree:
- getBacklinksBulkRanks(args = {}) { + getBulkRanks(args = {}) { return this.dataforseo._makeRequest({ path: "/backlinks/bulk_ranks/live", method: "post", ...args, }); },And update the caller:
- const response = await this.getBacklinksBulkRanks({ + const response = await this.getBulkRanks({components/dataforseo/actions/search-business-listings/search-business-listings.mjs (1)
62-78
: Consider validating minimum input to prevent empty searchesPer typical DataForSEO patterns, at least one of the query-defining fields should be provided (e.g., categories, description, title, or a location constraint). Add a guard to catch empty criteria early.
Apply this diff:
async run({ $ }) { - const response = await this.searchBusinessListings({ + if ( + (!this.categories || this.categories.length === 0) + && !this.description + && !this.title + && !this.locationCoordinate + ) { + throw new Error("Provide at least one of: categories, description, title, or locationCoordinate."); + } + const response = await this.searchBusinessListings({ $, data: [ { categories: this.categories, description: this.description, title: this.title, location_coordinate: this.locationCoordinate, tag: this.tag, ...parseObjectEntries(this.additionalOptions), }, ], });components/dataforseo/actions/get-categories-aggregation/get-categories-aggregation.mjs (1)
62-78
: Optional: validate inputs to avoid overly broad aggregationsTo reduce unexpected API calls, validate that at least one scoping field is present (e.g., categories/description/title or a location).
Apply this diff:
async run({ $ }) { - const response = await this.getCategoriesAggregation({ + if ( + (!this.categories || this.categories.length === 0) + && !this.description + && !this.title + && !this.locationCoordinate + ) { + throw new Error("Provide at least one of: categories, description, title, or locationCoordinate."); + } + const response = await this.getCategoriesAggregation({ $, data: [ { categories: this.categories, description: this.description, title: this.title, location_coordinate: this.locationCoordinate, tag: this.tag, ...parseObjectEntries(this.additionalOptions), }, ], });components/dataforseo/actions/get-bulk-referring-domains/get-bulk-referring-domains.mjs (1)
34-46
: Optional: validate batch size and inputs fortargets
before sending the request.
DataForSEO bulk endpoints typically accept a bounded list of targets. Pre-validating helps fail fast with actionable messages and prevents API rejections.Apply this minimal guard:
async run({ $ }) { - const response = await this.getBulkReferringDomains({ + if (!Array.isArray(this.targets) || this.targets.length === 0) { + throw new Error("Please provide at least one target."); + } + // Optional: enforce an upper bound if DataForSEO applies one (e.g., 1000) + if (this.targets.length > 1000) { + throw new Error(`Too many targets: ${this.targets.length}. Maximum supported is 1000.`); + } + const response = await this.getBulkReferringDomains({ $, data: [ { targets: this.targets, tag: this.tag, }, ], });components/dataforseo/actions/get-backlinks-history/get-backlinks-history.mjs (1)
53-68
: Optional: validate date inputs and ordering before calling the API.
The endpoint expectsYYYY-MM-DD
format, anddate_from
should not exceeddate_to
. Guarding here prevents avoidable API errors.Add lightweight validation:
async run({ $ }) { - const response = await this.getBacklinksHistory({ + const fmt = /^\d{4}-\d{2}-\d{2}$/; + if (this.dateFrom && !fmt.test(this.dateFrom)) { + throw new Error("dateFrom must be in YYYY-MM-DD format."); + } + if (this.dateTo && !fmt.test(this.dateTo)) { + throw new Error("dateTo must be in YYYY-MM-DD format."); + } + if (this.dateFrom && this.dateTo && this.dateFrom > this.dateTo) { + throw new Error("dateFrom must be less than or equal to dateTo."); + } + const response = await this.getBacklinksHistory({ $, data: [ { target: this.target, date_from: this.dateFrom, date_to: this.dateTo, rank_scale: this.rankScale, tag: this.tag, }, ], });components/dataforseo/actions/get-domain-pages-summary/get-domain-pages-summary.mjs (2)
70-77
: Double-checkadditionalOptions
parsing behavior.
You’re spreadingparseObjectEntries(this.additionalOptions)
. Ensure the utility:
- Returns
{}
for falsy input,- Parses JSON-like values where needed,
- Does not include keys with empty string values unless intended.
If any of the above isn’t guaranteed, a defensive fallback is trivial.
Optionally, add a local fallback:
additionalOptions: { propDefinition: [ dataforseo, "additionalOptions", ], description: "Additional parameters to send in the request. [See the documentation](https://docs.dataforseo.com/v3/backlinks/domain_pages_summary/live/) for all available parameters. Values will be parsed as JSON where applicable.", }, }, async run({ $ }) { - const response = await this.getDomainPagesSummary({ + const extra = parseObjectEntries(this.additionalOptions) || {}; + const response = await this.getDomainPagesSummary({ $, data: [ { target: this.target, include_subdomains: this.includeSubdomains, include_indirect_links: this.includeIndirectLinks, exclude_internal_backlinks: this.excludeInternalBacklinks, backlinks_status_type: this.backlinksStatusType, backlinks_filters: this.backlinksFilters, rank_scale: this.rankScale, tag: this.tag, - ...parseObjectEntries(this.additionalOptions), + ...extra, }, ], });
52-63
: Consider light validation forbacklinksFilters
shape.
DataForSEO often expects an array of filter triplets. If the app propDefinition already enforces schema, ignore. Otherwise, a quick check avoids API errors.Example minimal guard:
- backlinks_filters: this.backlinksFilters, + backlinks_filters: Array.isArray(this.backlinksFilters) ? this.backlinksFilters : undefined,components/dataforseo/actions/get-bulk-spam-score/get-bulk-spam-score.mjs (1)
34-46
: Optional: add basic input checks fortargets
.
Same rationale as other bulk endpoints: ensure non-empty list and cap to a reasonable max to prevent API rejects.Suggested guard:
async run({ $ }) { - const response = await this.getBulkSpamScore({ + if (!Array.isArray(this.targets) || this.targets.length === 0) { + throw new Error("Please provide at least one target."); + } + if (this.targets.length > 1000) { + throw new Error(`Too many targets: ${this.targets.length}. Maximum supported is 1000.`); + } + const response = await this.getBulkSpamScore({ $, data: [ { targets: this.targets, tag: this.tag, }, ], });components/dataforseo/actions/get-bulk-backlinks/get-bulk-backlinks.mjs (1)
34-46
: Optional: enforce simpletargets
validation for a better UX.
Same guidance as the other bulk actions to avoid avoidable API errors.Apply:
async run({ $ }) { - const response = await this.getBulkBacklinks({ + if (!Array.isArray(this.targets) || this.targets.length === 0) { + throw new Error("Please provide at least one target."); + } + if (this.targets.length > 1000) { + throw new Error(`Too many targets: ${this.targets.length}. Maximum supported is 1000.`); + } + const response = await this.getBulkBacklinks({ $, data: [ { targets: this.targets, tag: this.tag, }, ], });components/dataforseo/actions/get-business-listings-categories/get-business-listings-categories.mjs (1)
18-24
: Guard against missingtasks[0]
to avoid runtime TypeError.
If DataForSEO returns no tasks, accessingresponse.tasks[0]
will throw. Add a length check before dereferencing.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks || response.tasks.length === 0) { + throw new ConfigurationError("Error: no tasks returned in response"); + } + if (response.tasks[0].status_code !== 20000) { + throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); + }components/dataforseo/actions/get-google-ads-keywords-for-site-completed-tasks/get-google-ads-keywords-for-site-completed-tasks.mjs (1)
22-24
: Prevent potential crash whentasks
is empty.
Add a guard before accessingresponse.tasks[0]
.- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks || response.tasks.length === 0) { + throw new ConfigurationError("Error: no tasks returned in response"); + } + if (response.tasks[0].status_code !== 20000) { + throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); + }components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs (1)
28-34
: Add a guard for emptytasks
to avoid runtime errors.
This matches the pattern recommended across the new actions and prevents TypeErrors whentasks
is empty.- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks || response.tasks.length === 0) { + throw new ConfigurationError("Error: no tasks returned in response"); + } + if (response.tasks[0].status_code !== 20000) { + throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); + }components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (1)
39-45
: Guardtasks
access to avoid a potential TypeError.
Add a defensive check before usingresponse.tasks[0]
.- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks || response.tasks.length === 0) { + throw new ConfigurationError("Error: no tasks returned in response"); + } + if (response.tasks[0].status_code !== 20000) { + throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); + }components/dataforseo/actions/get-google-ads-ad-traffic-by-keyword/get-google-ads-ad-traffic-by-keyword.mjs (2)
5-7
: Prefer pluralized key/name to reflect multiple keywordsThis action accepts an array of keywords and calls a pluralized API method. Align the key/name to reduce ambiguity and keep consistency with other actions (e.g., “Keywords For Keywords”).
- key: "dataforseo-get-google-ads-ad-traffic-by-keyword", - name: "Get Google Ads Ad Traffic By Keyword", + key: "dataforseo-get-google-ads-ad-traffic-by-keywords", + name: "Get Google Ads Ad Traffic By Keywords",
70-71
: Make the success summary more informativeShow how many keywords were processed and handle pluralization.
- $.export("$summary", "Successfully retrieved Google Ads ad traffic by keyword."); + const count = this.keywords?.length ?? 0; + $.export("$summary", `Successfully retrieved Google Ads ad traffic for ${count} keyword${count === 1 ? "" : "s"}.`);components/dataforseo/dataforseo.app.mjs (2)
69-73
: Enforce numeric bounds for the limit propThe description notes a maximum; reflect that in the schema.
limit: { type: "integer", label: "Limit", - description: "The maximum number of results to return. Maximum: 1000", + description: "The maximum number of results to return. Maximum: 1000", + min: 1, + max: 1000, optional: true, },
210-221
: Location codes are consistent, but use the Keywords Data endpoint for Keywords‐Data UIsLocation codes (e.g. United States = 2840) are shared between the SERP and Keywords Data APIs, but to surface the correct list (and any API-specific caveats) you should:
- In components/dataforseo/dataforseo.app.mjs, leave
getLocations()
pointed at/serp/google/ads_search/locations
for SERP/Ads-Search use- Add (or update) a dedicated method for Keywords Data, e.g.:
getKeywordsLocations(args = {}) { return this._makeRequest({ path: "/keywords_data/google_ads/locations", ...args, }); }- If you really need a single shared UI, either
• fetch from the matching endpoint at runtime, or
• fetch both/serp/.../locations
and/keywords_data/.../locations
once, merge/normalize, and cacheThis ensures you get any Keywords-API–specific differences (e.g., “Okrug” or postal-code support).
components/dataforseo/actions/get-bulk-traffic-analytics/get-bulk-traffic-analytics.mjs (1)
51-52
: Polish the success summary with pluralization- $.export("$summary", `Successfully retrieved bulk traffic analytics for ${this.targets.length} domains.`); + const count = this.targets?.length ?? 0; + $.export("$summary", `Successfully retrieved bulk traffic analytics for ${count} domain${count === 1 ? "" : "s"}.`);components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs (1)
29-35
: Set default for depth to match the descriptionThe description states a default of 100; reflect that in the schema.
depth: { type: "integer", label: "Depth", description: "The parsing depth. Default: 100", max: 700, + default: 100, optional: true, },
components/dataforseo/actions/get-google-ads-traffic-by-keywords-completed-tasks/get-google-ads-traffic-by-keywords-completed-tasks.mjs (1)
14-16
: Optionally validate all tasks, not just the firstIf multiple tasks are returned, consider failing fast on any task with a non-20000 status to surface partial failures.
- const response = await this.dataforseo.getGoogleAdsAdTrafficByKeywordsCompletedTasks({ + const response = await this.dataforseo.getGoogleAdsAdTrafficByKeywordsCompletedTasks({ $, }); @@ - const firstTask = response.tasks && response.tasks[0]; - if (!firstTask) { - throw new ConfigurationError("No tasks returned by DataForSEO."); - } - if (firstTask.status_code !== 20000) { - throw new ConfigurationError(`Error: ${firstTask.status_message}`); - } + const tasks = response.tasks || []; + if (!tasks.length) { + throw new ConfigurationError("No tasks returned by DataForSEO."); + } + const failed = tasks.find(t => t.status_code !== 20000); + if (failed) { + throw new ConfigurationError(`Error: ${failed.status_message}`); + }Also applies to: 18-24
components/dataforseo/actions/get-google-ads-locations/get-google-ads-locations.mjs (2)
12-16
: Make Country Code optional (API supports broad fetch without filter)Locations can often be fetched without a filter; forcing a value reduces usability. Mark as optional so users can fetch all locations or filter by country when needed.
countryCode: { type: "string", label: "Country Code", - description: "The country code to get locations for. Ex: `US`", + description: "Optional ISO 3166-1 alpha-2 country code to filter locations. Ex: `US`", + optional: true, },
32-33
: Tiny consistency nit: add trailing period to summaryMatch the punctuation style used in sibling actions.
- $.export("$summary", "Successfully retrieved Google Ads locations"); + $.export("$summary", "Successfully retrieved Google Ads locations.");components/dataforseo/actions/get-google-ads-search-volume/get-google-ads-search-volume.mjs (2)
19-31
: Confirm requiredness of locationCode and languageCode for this endpointFor DataForSEO Search Volume,
location_code
andlanguage_code
are typically required. Marking them optional may cause avoidable API errors. If docs confirm they are required, removeoptional: true
for both props to align with other Google Ads actions in this PR.locationCode: { propDefinition: [ dataforseo, "locationCode", ], - optional: true, }, languageCode: { propDefinition: [ dataforseo, "languageCode", ], - optional: true, },If the docs indicate they are indeed optional, no change needed—this is just to maintain consistency and reduce user confusion.
33-43
: Consider chunking large keyword lists to respect API limitsIf users pass more than the API’s allowed number of keywords per request, pre-chunking and auto-pagination can improve UX and reliability. This can be added later; not blocking.
Happy to propose a chunking utility if you want to support batching transparently.
components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs (2)
46-48
: Defensive check for missing tasks to avoid runtime errorIf the API responds with an empty tasks array,
response.tasks[0]
will throw. Guard before dereferencing.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: Empty tasks array in API response"); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
42-48
: Deduplicate two-tier error handling via a shared helperThis exact status_code + first task status_code pattern is repeated across actions. Consider centralizing in
dataforseo.app.mjs
(e.g.,await app.requestAndValidate(method, payload, $)
), returning{ task, response }
. This keeps actions thin and consistent.components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs (3)
36-41
: Validate minimum number of apps for intersectionAPI requires comparing at least two apps. Fail fast with a clear message.
Apply this diff:
async run({ $ }) { - const appIds = {}; + if (!Array.isArray(this.appIds) || this.appIds.length < 2) { + throw new ConfigurationError("Please provide at least 2 app IDs for intersection."); + } + const appIds = {}; for (let i = 0; i < this.appIds.length; i++) { appIds[`${i + 1}`] = this.appIds[i]; }
44-44
: Remove debug flag or make it a prop
debug: true
likely shouldn’t ship by default. Either remove or expose as an optional prop.Apply this diff:
- debug: true,
59-61
: Guard against empty tasks array to prevent crashSame defensive pattern as other actions: check for
tasks?.[0]
.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: Empty tasks array in API response"); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-google-ads-search-volume-completed-tasks/get-google-ads-search-volume-completed-tasks.mjs (1)
22-24
: Handle empty tasks when none are readyCompleted-tasks endpoints can return no tasks; guard to avoid
tasks[0]
access errors.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + $.export("$summary", "No completed tasks ready."); + return response; + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-top-serp-results/get-top-serp-results.mjs (2)
37-38
: Validate non-empty keywords array before API callAvoid sending an empty
keywords
list which will return an API error.Apply this diff:
async run({ $ }) { + if (!Array.isArray(this.keywords) || this.keywords.length === 0) { + throw new ConfigurationError("Please provide at least one keyword."); + } const response = await this.dataforseo.getTopSerpResults({
54-56
: Defensive check for missing tasksPrevent crash on empty tasks and reuse the task variable.
Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: Empty tasks array in API response"); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (2)
57-59
: Guard tasks access to avoid runtime error on empty responseAdd a check before reading
tasks[0]
.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: Empty tasks array in API response"); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
12-16
: Consider basic input validation for keywordReject blank/whitespace-only keywords to avoid avoidable API calls.
Example insertion just before the API call:
if (!this.keyword?.trim()) { throw new ConfigurationError("Keyword is required."); }components/dataforseo/actions/get-domain-rank-overview/get-domain-rank-overview.mjs (2)
43-49
: Guard against missing tasks to avoid runtime errorsAccessing
response.tasks[0]
without checking presence can throw if the array is empty or missing. Add a defensive check.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: no tasks returned by DataForSEO."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
31-53
: Reduce duplication of status checks across actionsMany actions in this PR duplicate the same two-tier status check. Consider extracting a small helper to validate responses, improving maintainability and consistency.
You could add a utility in the app (or a nearby module) like:
export function ensureDataForSeoSuccess(response) { if (response?.status_code !== 20000) { const msg = response?.status_message || "Unknown API error"; throw new ConfigurationError(`Error: ${msg}`); } const task = response?.tasks?.[0]; if (!task) { throw new ConfigurationError("Error: no tasks returned by DataForSEO."); } if (task.status_code !== 20000) { const msg = task?.status_message || "Unknown task error"; throw new ConfigurationError(`Error: ${msg}`); } return task; }Then call it inside actions and optionally return
task.result
if that’s the primary payload consumers need.components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (2)
42-48
: Harden task error handling to avoid undefined accessAdd a guard around
response.tasks[0]
to prevent runtime errors when tasks is empty or missing.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: no tasks returned by DataForSEO."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
30-40
: Confirm supported parameters and recommend place_id/cid for precision
- File:
components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs
(lines 30–40)- The “Google Reviews (live)” endpoint does accept
so the current implementation is valid.{ keyword: this.keyword, location_coordinate: this.locationCoordinate, language_code: this.languageCode, }- For unambiguous review retrieval, DataForSEO recommends supplying a
place_id
orcid
in thekeyword
field (e.g.keyword: "place_id:GhIJQWDl0CIeQUARxks3icF8U8A"
).- If you can obtain a Place ID or CID upstream, consider extending this action to:
• accept aplaceId
(orcid
) prop
• set(you may still include- keyword: this.keyword, + keyword: `place_id:${this.placeId}`,location_coordinate
for verification/narrowing, but it isn’t required when usingplace_id
/cid
).- Otherwise, no changes are required—the existing keyword+coordinates approach is supported.
components/dataforseo/actions/get-historical-serp-data/get-historical-serp-data.mjs (3)
29-40
: Optional: Validate date inputs earlyConsider validating
dateFrom
/dateTo
format and order to fail fast with clearer messages, instead of relying on API-side validation.Add a lightweight check in
run()
to ensureYYYY-MM-DD
format and thatdateFrom <= dateTo
when both are provided.
42-54
: Build params conditionally and validate date rangeAvoid sending undefined fields and provide a clearer validation error if the date range is invalid.
Apply this diff:
- async run({ $ }) { - const response = await this.dataforseo.getHistoricalSerpData({ - $, - data: [ - { - keyword: this.keyword, - location_code: this.locationCode, - language_code: this.languageCode, - date_from: this.dateFrom, - date_to: this.dateTo, - }, - ], - }); + async run({ $ }) { + if (this.dateFrom && this.dateTo && new Date(this.dateFrom) > new Date(this.dateTo)) { + throw new ConfigurationError('"Date From" must be before or equal to "Date To".'); + } + const params = { + keyword: this.keyword, + location_code: this.locationCode, + language_code: this.languageCode, + }; + if (this.dateFrom) params.date_from = this.dateFrom; + if (this.dateTo) params.date_to = this.dateTo; + const response = await this.dataforseo.getHistoricalSerpData({ + $, + data: [ params ], + });
60-62
: Guard against missing tasks before dereferencingSame pattern as in other actions: avoid accessing
tasks[0]
blindly.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: no tasks returned by DataForSEO."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-domain-whois-overview/get-domain-whois-overview.mjs (1)
33-35
: Add guard for potentially missing tasksProtect against
tasks
being absent to avoid runtime errors.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: no tasks returned by DataForSEO."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-content-summary/get-content-summary.mjs (1)
32-34
: Defensive check for tasksSame small hardening suggestion for
tasks[0]
.Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: no tasks returned by DataForSEO."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-domain-keywords/get-domain-keywords.mjs (2)
50-56
: DRY up repeated response validation across actionsThis two-tier response validation repeats in many new actions. Consider centralizing in the app to reduce duplication and keep behavior consistent.
Example in
components/dataforseo/dataforseo.app.mjs
:// inside the exported app object methods: { assertOk(response) { if (response?.status_code !== 20000) { throw new this.ConfigurationError(`Error: ${response?.status_message || 'Unknown error'}`); } const task = response.tasks?.[0]; if (!task) { throw new this.ConfigurationError("Unexpected API response: no tasks returned"); } if (task.status_code !== 20000) { throw new this.ConfigurationError(`Error: ${task.status_message || 'Unknown task error'}`); } return task; }, }Then in actions:
const response = await this.dataforseo.getDomainKeywords({ $, data: [ /*...*/ ] }); this.dataforseo.assertOk(response);
58-59
: Optional: Make the summary more informative by including item countIf available, include the number of items returned to help users quickly assess results.
- $.export("$summary", `Successfully retrieved domain keywords for "${this.target}".`); + const task = response.tasks?.[0]; + const count = task?.result?.[0]?.items?.length ?? 0; + $.export("$summary", `Retrieved ${count} domain keywords for "${this.target}".`);components/dataforseo/actions/get-technologies-domain-list/get-technologies-domain-list.mjs (1)
10-18
: Exposelimit
as an optional prop to match API capabilitiesThe endpoint supports pagination/limits. Surfacing
limit
improves usability and parity with other actions.props: { dataforseo, target: { propDefinition: [ dataforseo, "target", ], }, + limit: { + propDefinition: [ + dataforseo, + "limit", + ], + }, }, async run({ $ }) { const response = await this.dataforseo.getTechnologiesDomainList({ $, data: [ { target: this.target, + limit: this.limit, }, ], });Also applies to: 23-26
components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs (1)
12-16
: If the endpoint truly requires IDs, adjust props and summary accordinglyShould the docs confirm IDs are required, expose
businessId
/placeId
/googleId
props and tailor the summary.I can draft a revised version with multiple lookup options and conditional payload construction once the correct required fields are confirmed.
Also applies to: 50-51
components/dataforseo/actions/get-keyword-ideas-live/get-keyword-ideas-live.mjs (1)
58-59
: Minor: Make the summary resilient ifkeywords
isn’t an arrayIf
keywords
ever comes through as a string (depending on propDefinition behavior), accessing.length
will misleadingly report character count.- $.export("$summary", `Successfully retrieved keyword ideas for ${this.keywords.length} keywords.`); + const kwCount = Array.isArray(this.keywords) ? this.keywords.length : 1; + $.export("$summary", `Successfully retrieved keyword ideas for ${kwCount} keyword${kwCount === 1 ? "" : "s"}.`);components/dataforseo/actions/get-content-citations/get-content-citations.mjs (1)
17-22
: Optional: Add pagination controls (offset
) to support larger result setsThe endpoint supports pagination; exposing
offset
improves utility for workflows fetching more thanlimit
.props: { dataforseo, keyword: { type: "string", label: "Keyword", description: "The keyword to search for", }, limit: { propDefinition: [ dataforseo, "limit", ], }, + offset: { + propDefinition: [ + dataforseo, + "offset", + ], + }, }, async run({ $ }) { const response = await this.dataforseo.getContentCitations({ $, data: [ { keyword: this.keyword, limit: this.limit, + offset: this.offset, }, ], });Also applies to: 28-31
components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs (2)
58-64
: Guard against empty tasks and accept 20100 (no results) from DataForSEOPrevent a potential runtime error when tasks is empty and treat 20100 as a successful “no results” outcome, consistent with other actions in this PR.
Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks?.length) { + throw new ConfigurationError("Error: Empty tasks array in DataForSEO response"); + } + const task = response.tasks[0]; + if (![20000, 20100].includes(task.status_code)) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
29-35
: Align prop description with actual default behavior fordepth
The description says “Default: 100” but no default is set. Either set the default or remove the note to avoid confusion. Suggest setting a default.
depth: { type: "integer", label: "Depth", - description: "The parsing depth. Default: 100", + description: "The parsing depth", max: 700, optional: true, + default: 100, },components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (1)
46-48
: Add a guard for missing tasks to avoid a crashAvoid indexing
tasks[0]
whentasks
could be empty, and reuse the extracted task for readability.- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks?.length) { + throw new ConfigurationError("Error: Empty tasks array in DataForSEO response"); + } + const task = response.tasks[0]; + if (task.status_code !== 20000 && task.status_code !== 20100) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (2)
45-47
: Guard against empty tasksPrevent a crash if
tasks
is empty and improve readability.- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks?.length) { + throw new ConfigurationError("Error: Empty tasks array in DataForSEO response"); + } + const task = response.tasks[0]; + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
12-28
: Optional: Validate time range before calling the APIYou could pre-validate that
dateTimeFrom <= dateTimeTo
to fail fast with a clearer message.If you want, I can propose a small validation snippet to add before the API call.
components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs (1)
46-48
: Consider accepting 20100 (no results) and guard against empty tasksOther actions in this PR treat 20100 as acceptable; also avoid indexing into an empty
tasks
array.- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks?.length) { + throw new ConfigurationError("Error: Empty tasks array in DataForSEO response"); + } + const task = response.tasks[0]; + if (![20000, 20100].includes(task.status_code)) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs (2)
57-59
: Guard against empty tasks and reuse the task objectAvoid a potential crash and keep the code tidy.
- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (!response.tasks?.length) { + throw new ConfigurationError("Error: Empty tasks array in DataForSEO response"); + } + const task = response.tasks[0]; + if (task.status_code !== 20000 && task.status_code !== 20100) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
13-16
: ClarifyappId
description to match the linked Apple endpointThe doc link points to the Apple endpoint. If this action targets Apple only, the description “App ID or package name” can mislead (Android uses package name). Suggest clarifying.
- description: "App ID or package name for the mobile app", + description: "App Store app ID (numeric). Example: 544007664",If the underlying app client supports both Apple and Google stores, consider updating the docs link and description to reflect that.
components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (2)
7-7
: Use singular “keyword” in description to match the propsThe action takes a single
keyword
, but the description says “keywords”.Apply this diff:
- description: "Retrieve Google Images search results for specified keywords. [See the documentation](https://docs.dataforseo.com/v3/serp/google/images/live/?bash)", + description: "Retrieve Google Images search results for a specified keyword. [See the documentation](https://docs.dataforseo.com/v3/serp/google/images/live/?bash)",
46-48
: Guard against missing tasks array before indexingIf
response.tasks
is empty/undefined, accessing[0]
can throw. Add a defensive check.Apply this diff:
+ if (!response.tasks?.length) { + throw new ConfigurationError("Error: No tasks were returned by DataForSEO."); + } if (response.tasks[0].status_code !== 20000) { throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); }components/dataforseo/actions/get-competitor-domains/get-competitor-domains.mjs (3)
7-7
: Tighten description: this action uses a target domain, not keywordsThe description mentions “keywords or target domain,” but the payload only supports
target
.Apply this diff:
- description: "Find competing domains for specified keywords or target domain. [See the documentation](https://docs.dataforseo.com/v3/dataforseo_labs/google/competitors_domain/live/?bash)", + description: "Find competing domains for the specified target domain. [See the documentation](https://docs.dataforseo.com/v3/dataforseo_labs/google/competitors_domain/live/?bash)",
38-48
: Optionally sanitize the target to strip protocol/wwwDocs typically expect domains without scheme or www. We can normalize input defensively.
Apply this diff:
async run({ $ }) { - const response = await this.dataforseo.getCompetitorDomains({ + const target = String(this.target) + .replace(/^https?:\/\/(www\.)?/i, "") + .replace(/^www\./i, ""); + const response = await this.dataforseo.getCompetitorDomains({ $, data: [ { - target: this.target, + target, location_code: this.locationCode, language_code: this.languageCode, limit: this.limit, }, ], });
54-56
: Guard against missing tasks array before indexingAvoid a potential runtime error when
tasks
is empty/undefined.Apply this diff:
+ if (!response.tasks?.length) { + throw new ConfigurationError("Error: No tasks were returned by DataForSEO."); + } if (response.tasks[0].status_code !== 20000) { throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); }components/dataforseo/actions/get-keyword-suggestions/get-keyword-suggestions.mjs (2)
7-7
: Align description with single-input propThe description says “seed keywords” (plural), but the action takes a single
keyword
.Apply this diff:
- description: "Get keyword ideas and related terms for specified seed keywords. [See the documentation](https://docs.dataforseo.com/v3/dataforseo_labs/google/keyword_suggestions/live/?bash)", + description: "Get keyword ideas and related terms for a specified seed keyword. [See the documentation](https://docs.dataforseo.com/v3/dataforseo_labs/google/keyword_suggestions/live/?bash)",
53-55
: Guard against missing tasks array before indexingPrevents a crash when
tasks
is empty/undefined.Apply this diff:
+ if (!response.tasks?.length) { + throw new ConfigurationError("Error: No tasks were returned by DataForSEO."); + } if (response.tasks[0].status_code !== 20000) { throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); }components/dataforseo/actions/get-domain-intersection/get-domain-intersection.mjs (3)
46-51
: Normalize domains before sending to the APIStrip protocol and www from both inputs to reduce user error; matches the prop descriptions.
Apply this diff:
- target1: this.target1, - target2: this.target2, + target1: String(this.target1).replace(/^https?:\/\/(www\.)?/i, "").replace(/^www\./i, ""), + target2: String(this.target2).replace/^https?:\/\/(www\.)?/i, "").replace(/^www\./i, ""),Note: If you prefer readability, compute `const t1`/`t2` above the payload instead. --- `59-61`: **Guard against missing tasks array before indexing** Avoid a potential runtime error when `tasks` is empty. Apply this diff: ```diff + if (!response.tasks?.length) { + throw new ConfigurationError("Error: No tasks were returned by DataForSEO."); + } if (response.tasks[0].status_code !== 20000) { throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); }
63-63
: Make summary consistent with other actions (quote the domains)Other actions quote user-provided strings in the summary.
Apply this diff:
- $.export("$summary", `Successfully retrieved domain intersection for domains ${this.target1} and ${this.target2}.`); + $.export("$summary", `Successfully retrieved domain intersection for domains "${this.target1}" and "${this.target2}".`);components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs (3)
7-7
: Use singular “keyword” in description to match the propsThis action accepts a single
keyword
.Apply this diff:
- description: "Retrieve Yahoo organic search results for specified keywords. [See the documentation](https://docs.dataforseo.com/v3/serp/yahoo/organic/live/?bash)", + description: "Retrieve Yahoo organic search results for a specified keyword. [See the documentation](https://docs.dataforseo.com/v3/serp/yahoo/organic/live/?bash)",
30-35
: Optional: Set default depth to 100 to match descriptionThe description claims a default of 100. You can encode that as the prop default for clarity.
Apply this diff:
depth: { type: "integer", label: "Depth", description: "The parsing depth. Default: 100", max: 700, optional: true, + default: 100, },
62-64
: Guard against missing tasks array before indexingPrevents a crash when
tasks
is empty/undefined.Apply this diff:
+ if (!response.tasks?.length) { + throw new ConfigurationError("Error: No tasks were returned by DataForSEO."); + } if (response.tasks[0].status_code !== 20000) { throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); }components/dataforseo/actions/get-google-ads-keywords-for-keywords-completed-tasks/get-google-ads-keywords-for-keywords-completed-tasks.mjs (1)
26-26
: Optional: Summarize the number of ready tasks for better UXA dynamic summary helps users immediately see what was returned.
- $.export("$summary", "Successfully retrieved Google Ads keywords for keywords completed tasks."); + $.export("$summary", `Successfully retrieved ${response.tasks?.length ?? 0} completed task(s).`);components/dataforseo/actions/get-google-ads-keywords-for-keywords/get-google-ads-keywords-for-keywords.mjs (3)
36-43
: Avoid sending undefined fields by conditionally including optional paramsDataForSEO will ignore missing fields; being explicit avoids sending undefined values and keeps requests clean.
data: [ { keywords: this.keywords, - location_code: this.locationCode, - language_code: this.languageCode, + ...(this.locationCode != null && { location_code: this.locationCode }), + ...(this.languageCode != null && { language_code: this.languageCode }), }, ],
49-51
: Make nested status checks resilient to unexpected response shapesWhile the live endpoint typically returns one task, adding a simple guard prevents brittle failures if the API shape changes or an upstream error returns no tasks.
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: No task information found in response."); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
53-54
: Optional: Provide a more informative summary (items count)Surfacing the number of related keywords returned can improve usability.
- $.export("$summary", "Successfully retrieved Google Ads keywords for keywords."); + $.export("$summary", `Successfully retrieved ${(response.tasks?.[0]?.result?.[0]?.items?.length) ?? 0} related keyword(s).`);
components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
Outdated
Show resolved
Hide resolved
components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-bulk-traffic-analytics/get-bulk-traffic-analytics.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-bulk-traffic-analytics/get-bulk-traffic-analytics.mjs
Show resolved
Hide resolved
...nts/dataforseo/actions/get-business-listings-categories/get-business-listings-categories.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-ranked-keywords/get-ranked-keywords.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-technologies-domain-list/get-technologies-domain-list.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🔭 Outside diff range comments (2)
components/dataforseo/dataforseo.app.mjs (2)
12-21
: Fix variable misnaming and ensure option value type matches prop type
- Variable
languageCodes
is misnamed in thelocationCode
options loader; it actually holds locations.locationCode
prop is typed as a string, butlocation_code
may be numeric. Cast to string to avoid type mismatch issues in the UI/runtime.- Add null-safe access to avoid crashes if the response shape changes or is empty.
Apply this diff:
- async options() { - const response = await this.getLocations(); - const languageCodes = response.tasks[0].result; - return languageCodes.map(({ - location_name, location_code, - }) => ({ - value: location_code, - label: location_name, - })); - }, + async options() { + const response = await this.getLocations(); + const results = response?.tasks?.[0]?.result ?? []; + return results.map(({ location_name, location_code }) => ({ + value: String(location_code), + label: location_name, + })); + },
164-168
: Close the parenthesis and add a period in the Targets descriptionUser-facing copy has an unmatched parenthesis.
Apply this diff:
- description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`", + description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
♻️ Duplicate comments (2)
components/dataforseo/dataforseo.app.mjs (2)
42-42
: Add a period at the end of the backlinks target descriptionParenthesis was fixed previously; just add the final period to keep user-facing copy polished.
Apply this diff:
- description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`)", + description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
119-135
: Descriptions say “Default is true” but no defaults are setAlign runtime behavior with documentation by setting
default: true
on all three boolean props.Apply this diff:
includeSubdomains: { type: "boolean", label: "Include Subdomains", description: "Whether the subdomains of the `target` will be included in the search. Default is `true`", + default: true, optional: true, }, includeIndirectLinks: { type: "boolean", label: "Include Indirect Links", description: "Whether indirect links to the target will be included in the results. Default is `true`", + default: true, optional: true, }, excludeInternalBacklinks: { type: "boolean", label: "Exclude Internal Backlinks", description: "Indicates if internal backlinks from subdomains to the target will be excluded from the results. Default is `true`", + default: true, optional: true, },
🧹 Nitpick comments (7)
components/dataforseo/dataforseo.app.mjs (7)
37-38
: Tighten up the Target description (consistency and readability)Use “URL” and keep formatting consistent with other props.
Apply this diff:
- description: "The domain name or the url of the target website or page. The domain should be specified without https:// and www.", + description: "The domain name or the URL of the target website or page. The domain should be specified without `https://` and `www`.",
79-87
: Harden the languageCode options loader with null-safe accessAvoid potential runtime errors when
tasks
orresult
are absent.Apply this diff:
- const response = await this.getLanguageCode(); - const languageCodes = response.tasks[0].result; - return languageCodes.map(({ + const response = await this.getLanguageCode(); + const languageCodes = response?.tasks?.[0]?.result ?? []; + return languageCodes.map(({ language_name, language_code, }) => ({ value: language_code, label: language_name, }));
210-215
: Deduplicate language endpoints: make getLanguageCode an aliasBoth
getLanguageCode
andgetGoogleAdsLanguages
hit the same endpoint. Prefer a single source of truth.Apply this diff:
- getLanguageCode(args = {}) { - return this._makeRequest({ - path: "/keywords_data/google_ads/languages", - ...args, - }); - }, + getLanguageCode(args = {}) { + // Backwards-compatible alias to `getGoogleAdsLanguages` + return this.getGoogleAdsLanguages(args); + },
242-249
: Validate required parameter for getGoogleAdsLocationsThrow early if
countryCode
is missing to avoid sending an invalid URL.Apply this diff:
- getGoogleAdsLocations({ - countryCode, ...args - }) { - return this._makeRequest({ - path: `/keywords_data/google_ads/locations/${countryCode}`, - ...args, - }); - }, + getGoogleAdsLocations({ countryCode, ...args }) { + if (!countryCode) { + throw new Error("countryCode is required"); + } + return this._makeRequest({ + path: `/keywords_data/google_ads/locations/${countryCode}`, + ...args, + }); + },
427-447
: Add convenience “tasks_ready” getters for review task_post endpoints (consistency)You added tasks-ready helpers for several Google Ads endpoints. Consider doing the same for Trustpilot/Tripadvisor/Google review tasks for parity.
Follow the existing pattern:
- trustpilot: GET
/business_data/trustpilot/reviews/tasks_ready
- tripadvisor: GET
/business_data/tripadvisor/reviews/tasks_ready
- google: GET
/business_data/google/reviews/tasks_ready
454-474
: Consider adding tasks_ready helpers for App Store / Google Play tasksThese use
task_post
; providing correspondingtasks_ready
helpers would simplify client actions, similar to your Google Ads helpers.Suggested additions:
getAppStoreSearchCompletedTasks
:/app_data/apple/app_searches/tasks_ready
getGooglePlaySearchCompletedTasks
:/app_data/google/app_searches/tasks_ready
getAppReviewsSummaryCompletedTasks
:/app_data/apple/app_reviews/tasks_ready
69-73
: Enforce numeric bounds onlimit
prop
Addmin
andmax
to the integer prop so it can’t exceed the documented “Maximum: 1000.”• File:
components/dataforseo/dataforseo.app.mjs
(around lines 69–73)
• Prop:limit
Suggested diff:
label: "Limit", description: "The maximum number of results to return. Maximum: 1000", + min: 1, + max: 1000, optional: true,
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (4)
components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs
(1 hunks)components/dataforseo/dataforseo.app.mjs
(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
- components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs
- components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.
Applied to files:
components/dataforseo/dataforseo.app.mjs
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Lint Code Base
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
🔇 Additional comments (5)
components/dataforseo/dataforseo.app.mjs (5)
101-111
: LGTM: Rank scale defaults and options read clearlyThe options and default value are sensible and match the description.
137-157
: LGTM: Backlinks status type options and defaultClear options with a reasonable default of “live”.
174-188
: LGTM: Non-async _makeRequest is fine; axios already returns a PromiseSignature simplification is OK and keeps auth centralized.
222-496
: LGTM: Consistent method expansion and endpoint coverageThe new methods follow a uniform pattern, align with DataForSEO paths, and clearly separate live vs task-based endpoints. Naming is consistent and easy to reason about.
216-221
: Confirm you’re using the right DataForSEO locations endpoint
YourgetLocations
call currently uses the SERP Ads Search endpoint (/serp/google/ads_search/locations
), which is correct if you need location codes for Google Ads SERP Ads Search.
If your goal is to fetch location codes for Google Ads Keywords Data (keyword metrics, planning, etc.), you should instead call:/keywords_data/google_ads/locations
Using the wrong endpoint can lead to code mismatches or runtime errors. Please verify your intended use case and update accordingly.
• File:
components/dataforseo/dataforseo.app.mjs
Lines: 216–221 (getLocations
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/dataforseo/dataforseo.app.mjs (1)
164-168
: Close the parenthesis in the targets description.Minor documentation typo in a user-facing field.
Apply this diff:
- description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`", + description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
♻️ Duplicate comments (4)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (2)
15-16
: Fix time format in descriptions (use hh:mm:ss, not hh-mm-ss)Use the correct time separator and keep wording consistent.
- description: "The start time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. If include_metadata is set to true, minimum value: a month before the current datetime. If include_metadata is set to false, minimum value: six months before the current datetime", + description: "The start time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. If include_metadata is set to true, minimum value: a month before the current datetime. If include_metadata is set to false, minimum value: six months before the current datetime", @@ - description: "The finish time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`", + description: "The finish time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`",Also applies to: 20-21
85-91
: Harden error handling against missing/empty tasksAvoid accessing response.tasks[0] without verifying existence.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
15-21
: Fix datetime format in descriptions (prior feedback still pending).The descriptions still show hh-mm-ss (hyphens) while your validation error message expects hh:mm:ss (colons). Aligning both avoids confusion.
Apply this diff:
- description: "The start time for filtering results in the format "yyyy-mm-dd hh-mm-ss +00:00". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", + description: "The start time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", ... - description: "The finish time for filtering results in the format "yyyy-mm-dd hh-mm-ss +00:00". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", + description: "The finish time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.",components/dataforseo/dataforseo.app.mjs (1)
119-135
: Boolean props state “Default is true” but don’t set default (prior feedback still pending).Set default: true to match the description and avoid surprises.
Apply this diff:
includeSubdomains: { type: "boolean", label: "Include Subdomains", description: "Whether the subdomains of the `target` will be included in the search. Default is `true`", + default: true, optional: true, }, includeIndirectLinks: { type: "boolean", label: "Include Indirect Links", description: "Whether indirect links to the target will be included in the results. Default is `true`", + default: true, optional: true, }, excludeInternalBacklinks: { type: "boolean", label: "Exclude Internal Backlinks", description: "Indicates if internal backlinks from subdomains to the target will be excluded from the results. Default is `true`", + default: true, optional: true, },
🧹 Nitpick comments (12)
components/dataforseo/common/utils.mjs (1)
9-12
: Optionally avoid hard failure on malformed JSON input in parseObjectEntriesCurrent code will throw if value is a string that isn’t valid JSON. If you prefer resilience, use optionalParseAsJSON here too and guard against non-objects.
-export function parseObjectEntries(value = {}) { - const obj = typeof value === "string" - ? JSON.parse(value) - : value; +export function parseObjectEntries(value = {}) { + const obj = typeof value === "string" + ? optionalParseAsJSON(value) + : value; + const safeObj = obj && typeof obj === "object" ? obj : {}; - return Object.fromEntries( - Object.entries(obj).map(([ + return Object.fromEntries( + Object.entries(safeObj).map(([ key, value, ]) => [ key, optionalParseAsJSON(value), ]), ); }components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (1)
53-59
: Guard against missing/empty tasks in API responseAvoid accessing response.tasks[0] without checking it exists.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || (task.status_code !== 20000 && task.status_code !== 20100)) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (2)
10-29
: Props look good for keyword + location_coordinate flow; consider supporting direct business identifiersMany users also have place_id/cid/g_cid/data_id to target a specific business without keyword + location coordinate. Consider optional mutually exclusive props for those identifiers to broaden applicability.
If you’d like, I can draft a diff adding optional props (placeId, cid, gCid, dataId) and select the first provided to send to the API.
42-48
: Null-safe error handling for response and tasksPrevent potential runtime errors when tasks is empty or undefined.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || (task.status_code !== 20000 && task.status_code !== 20100)) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (1)
46-49
: Normalize to ISO 8601 before Date parsing to avoid engine-dependent failuresThe provided format with a space (yyyy-mm-dd hh:mm:ss +00:00) isn’t reliably parsed by JS Date across environments. Normalize to ISO by inserting “T”.
- validateDateRange(dateTimeFrom, dateTimeTo, includeMetadata) { - const from = new Date(dateTimeFrom); - const to = new Date(dateTimeTo); + validateDateRange(dateTimeFrom, dateTimeTo, includeMetadata) { + const toIso = (s) => (typeof s === "string" ? s.replace(" ", "T") : s); + const from = new Date(toIso(dateTimeFrom)); + const to = new Date(toIso(dateTimeTo));components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (1)
42-48
: Add null-safe checks for robustnessPrevent runtime errors if tasks is missing/empty.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (2)
66-72
: Guard against missing tasks to avoid runtime errors.Accessing response.tasks[0] can throw if tasks is empty or undefined. Add a defensive check.
Apply this diff:
- if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("No tasks returned in response"); + } + if (task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
52-64
: Consider DRYing date validation across actions.Other actions (e.g., get-keyword-data-id-list) implement similar date-range validation. Centralize this helper (e.g., components/dataforseo/common/date.mjs) and import it to reduce duplication and inconsistencies.
I can factor out a shared validator and update all usages in this PR if you want.
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (3)
73-75
: Guard against missing tasks in the initial response.Avoid response.tasks[0] access if tasks is empty or undefined.
Apply this diff:
- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("No tasks returned in response"); + } + if (![20000, 20100].includes(task.status_code)) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
53-56
: Nit: remove extra space in assignment.Minor formatting for readability.
Apply this diff:
- let postbackUrl = this.postbackUrl; + let postbackUrl = this.postbackUrl;
36-41
: Clarify postback URL behavior.Description says it’s only applicable when “Wait for Results” = FALSE, but the code sets a postback_url to the Pipedream resume URL when waiting = TRUE (overriding any user-provided value). Consider clarifying the description to reflect this behavior.
Apply this diff:
- description: "The URL to receive the search results. Only applicable when \"Wait for Results\" = `FALSE`", + description: "Optional custom URL to receive results when \"Wait for Results\" = `FALSE`. When \"Wait for Results\" = `TRUE`, this action will set a Pipedream resume URL automatically and ignore any value provided here.",components/dataforseo/dataforseo.app.mjs (1)
13-21
: Nit: rename local var to reflect locations, not language codes.Using languageCodes in the locations options is confusing.
Apply this diff:
- const languageCodes = response.tasks[0].result; - return languageCodes.map(({ + const locations = response.tasks[0].result; + return locations.map(({ location_name, location_code, }) => ({ value: location_code, label: location_name, }));
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (18)
components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
(1 hunks)components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs
(1 hunks)components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs
(1 hunks)components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs
(1 hunks)components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs
(1 hunks)components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs
(1 hunks)components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs
(1 hunks)components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs
(1 hunks)components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs
(1 hunks)components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs
(1 hunks)components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs
(1 hunks)components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs
(1 hunks)components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs
(1 hunks)components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs
(1 hunks)components/dataforseo/common/utils.mjs
(1 hunks)components/dataforseo/dataforseo.app.mjs
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs
- components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs
- components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs
- components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs
- components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs
- components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs
- components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs
- components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs
- components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
- components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.
Applied to files:
components/dataforseo/dataforseo.app.mjs
🧬 Code Graph Analysis (6)
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (3)
from
(47-47)to
(48-48)response
(72-83)
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (3)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs (1)
response
(31-40)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (1)
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (3)
from
(31-31)to
(32-32)response
(55-64)
components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (3)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs (1)
response
(31-40)
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (1)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)
components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (2)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs (1)
response
(31-40)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
- GitHub Check: Lint Code Base
🔇 Additional comments (9)
components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
30-40
: LGTM overallAction shape, mapping, and acceptance of 20100 for task_post endpoints are consistent with the rest of the suite.
Also applies to: 50-52
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (1)
69-83
: Solid structure and validation strategyNice internal validateDateRange helper and clear $summary. Mapping to API fields looks correct.
Also applies to: 93-96
components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (1)
10-29
: Well-structured action; aligns with live/advanced endpointProps, payload mapping, and single 20000 task status check (live endpoint) look good.
Also applies to: 31-40, 50-52
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
74-76
: LGTM: Clear summary and return of full API response.The $summary and return shape follow the established pattern in this integration.
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (1)
78-84
: LGTM: Async callback handling and clear summary.The run.runs pattern and summary export are consistent with other actions.
components/dataforseo/dataforseo.app.mjs (4)
42-43
: Backlinks target description parentheses now correct — thanks.The missing closing parenthesis issue is resolved here.
69-73
: Limit prop: integer type and description look good.This aligns with usage across actions and the API’s expectations.
210-221
: Non-async wrappers returning Promises via axios look good.Switching to non-async methods is fine since _makeRequest returns a Promise; callers remain unaffected.
8-12
: No lingering string-typed definitions or casts for locationCode or limitThe repo-wide search shows:
- No
type: "string"
definitions forlocationCode
orlimit
.- No manual
String(...)
or.toString()
casts used when passing these props.All downstream components/actions already expect integers.
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/dataforseo/dataforseo.app.mjs (1)
167-168
: Close the parenthesis in targets description and end the sentenceThe user-facing description is missing a closing parenthesis and period.
- description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`", + description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
♻️ Duplicate comments (7)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (2)
15-21
: Fix datetime format in user-facing descriptions (hh:mm:ss, not hh-mm-ss)Align with the example and standard formatting.
- description: "The start time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. If include_metadata is set to true, minimum value: a month before the current datetime. If include_metadata is set to false, minimum value: six months before the current datetime", + description: "The start time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. If include_metadata is set to true, minimum value: a month before the current datetime. If include_metadata is set to false, minimum value: six months before the current datetime", ... - description: "The finish time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`", + description: "The finish time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`",
85-91
: Harden error handling against missing/empty tasksAvoid direct indexing of tasks[0]; this can throw if tasks is missing.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
15-21
: Fix datetime format in user-facing descriptions (hh:mm:ss, not hh-mm-ss)Match the example and standard time separator.
- description: "The start time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", + description: "The start time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", ... - description: "The finish time for filtering results in the format \"yyyy-mm-dd hh-mm-ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.", + description: "The finish time for filtering results in the format \"yyyy-mm-dd hh:mm:ss +00:00\". Example: `2023-01-15 12:57:46 +00:00`. Allows filtering results by the datetime parameter within the range of the last 7 days.",components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (2)
10-38
: TripAdvisor reviews/live requires url_path (not keyword/location) and doesn’t support sort_byThe current props don’t match the API. The TripAdvisor “reviews/task_post” endpoint expects a business identifier via url_path; keyword + location_code won’t return reviews directly. Also, sort_by isn’t supported for this endpoint.
Apply this diff to align with the API:
props: { dataforseo, - keyword: { - type: "string", - label: "Keyword", - description: "The keyword to search for", - }, - locationCode: { - propDefinition: [ - dataforseo, - "locationCode", - ], - }, + urlPath: { + type: "string", + label: "TripAdvisor URL Path", + description: "The `url_path` portion of the TripAdvisor URL that uniquely identifies the business (e.g., \"Hotel_Review-g60763-d23462501-Reviews-Margaritaville_Times_Square-New_York_City_New_York.html\").", + }, languageCode: { propDefinition: [ dataforseo, "languageCode", ], }, - sortBy: { - type: "string", - label: "Sort By", - description: "Sort reviews by specific criteria", - options: [ - "most_recent", - "detailed_reviews", - ], - optional: true, - }, + translateReviews: { + type: "boolean", + label: "Translate Reviews", + description: "Translate reviews to the specified language when available. Default is `true`.", + default: true, + optional: true, + }, },
41-51
: Use url_path and translate_reviews in payload; remove unsupported paramsMap props to the correct request body fields and drop unsupported ones.
const response = await this.dataforseo.getTripadvisorReviews({ $, data: [ { - keyword: this.keyword, - location_code: this.locationCode, - language_code: this.languageCode, - sort_by: this.sortBy, + url_path: this.urlPath, + language_code: this.languageCode, + translate_reviews: this.translateReviews, }, ], });components/dataforseo/dataforseo.app.mjs (2)
42-43
: Fix minor punctuation in backlinksTarget descriptionClose the sentence with a period.
- description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`)", + description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
121-135
: Boolean props state “Default is true” but don’t set a defaultSet default: true to match the description and avoid surprising behavior.
includeSubdomains: { type: "boolean", label: "Include Subdomains", description: "Whether the subdomains of the `target` will be included in the search. Default is `true`", + default: true, optional: true, }, includeIndirectLinks: { type: "boolean", label: "Include Indirect Links", description: "Whether indirect links to the target will be included in the results. Default is `true`", + default: true, optional: true, }, excludeInternalBacklinks: { type: "boolean", label: "Exclude Internal Backlinks", description: "Indicates if internal backlinks from subdomains to the target will be excluded from the results. Default is `true`", + default: true, optional: true, },
🧹 Nitpick comments (10)
components/dataforseo/common/utils.mjs (1)
24-30
: Normalize parseArray output and avoid over-eager falsy checksCurrent logic treats 0/false as “empty” and returns [], and may return non-arrays. Normalize to arrays, treat only nullish/empty string as empty, and guard JSON.parse with a CSV fallback.
-export function parseArray(value) { - if (!value) return []; - if (typeof value === "string") { - return JSON.parse(value); - } - return value; -} +export function parseArray(value) { + // Treat only null/undefined/empty-string as empty + if (value == null || value === "") return []; + if (typeof value === "string") { + try { + const parsed = JSON.parse(value); + return Array.isArray(parsed) ? parsed : [parsed]; + } catch { + // Fallback: simple CSV string input + return value.split(",").map((s) => s.trim()).filter(Boolean); + } + } + // Ensure array output + return Array.isArray(value) ? value : [value]; +}components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
42-48
: Harden error handling against missing/empty tasksAvoid potential TypeError when tasks is missing or empty and improve messages.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || (task.status_code !== 20000 && task.status_code !== 20100)) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (2)
46-52
: Make date parsing deterministic across runtimesnew Date("yyyy-mm-dd hh:mm:ss +00:00") isn’t guaranteed portable. Normalize to ISO 8601 before parsing to avoid false “Invalid date” errors.
- validateDateRange(dateTimeFrom, dateTimeTo, includeMetadata) { - const from = new Date(dateTimeFrom); - const to = new Date(dateTimeTo); + validateDateRange(dateTimeFrom, dateTimeTo, includeMetadata) { + const from = new Date(this.toIso8601(dateTimeFrom)); + const to = new Date(this.toIso8601(dateTimeTo)); - if (isNaN(from) || isNaN(to)) { - throw new ConfigurationError("Invalid date format. Use yyyy-mm-dd hh:mm:ss +00:00"); + if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) { + throw new ConfigurationError("Invalid date format. Use yyyy-mm-dd hh:mm:ss +00:00"); }Add this helper inside the methods object (outside the selected lines):
toIso8601(s) { // "2023-01-15 12:57:46 +00:00" -> "2023-01-15T12:57:46+00:00" return String(s).replace(" ", "T").replace(" +", "+"); }
64-66
: Also validate DateTimeTo against the minimum date windowDocs imply the entire range should be within the allowed window. Currently, only DateTimeFrom is checked.
- if (from < minDate) { - throw new ConfigurationError(`DateTimeFrom must not be earlier than ${monthsBack} month(s) ago`); - } + if (from < minDate) { + throw new ConfigurationError(`DateTimeFrom must not be earlier than ${monthsBack} month(s) ago`); + } + if (to < minDate) { + throw new ConfigurationError(`DateTimeTo must not be earlier than ${monthsBack} month(s) ago`); + }components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (1)
39-45
: Harden error handling against missing/empty tasksUse null-safe access and clearer messages to avoid TypeError when tasks is empty.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || (task.status_code !== 20000 && task.status_code !== 20100)) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (2)
31-37
: Make date parsing deterministic across runtimesNormalize to ISO 8601 before parsing to avoid runtime-specific failures.
- validateDateRange(dateTimeFrom, dateTimeTo) { - const from = new Date(dateTimeFrom); - const to = new Date(dateTimeTo); + validateDateRange(dateTimeFrom, dateTimeTo) { + const from = new Date(this.toIso8601(dateTimeFrom)); + const to = new Date(this.toIso8601(dateTimeTo)); const now = new Date(); - if (isNaN(from) || isNaN(to)) { + if (Number.isNaN(from.getTime()) || Number.isNaN(to.getTime())) { throw new ConfigurationError("Invalid date format. Use yyyy-mm-dd hh:mm:ss +00:00"); }Add this helper inside the methods object (outside the selected lines):
toIso8601(s) { // "2023-01-15 12:57:46 +00:00" -> "2023-01-15T12:57:46+00:00" return String(s).replace(" ", "T").replace(" +", "+"); }
66-72
: Harden error handling against missing/empty tasksPrevent potential crashes and improve diagnostics.
- if (response.status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.status_message}`); - } - - if (response.tasks[0].status_code !== 20000) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + if (response?.status_code !== 20000) { + throw new ConfigurationError(`Error: ${response?.status_message || "Unexpected API response"}`); + } + const task = response.tasks?.[0]; + if (!task || task.status_code !== 20000) { + throw new ConfigurationError(`Error: ${task?.status_message || "No task result returned"}`); + }components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (2)
53-59
: Guard against missing tasks array before dereferencingDefensive check prevents runtime errors when response has no tasks.
- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: No task information returned by DataForSEO."); + } + if (task.status_code !== 20000 && task.status_code !== 20100) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
61-62
: Update summary to reflect url_path- $.export("$summary", `Successfully retrieved TripAdvisor reviews for "${this.keyword}".`); + $.export("$summary", `Successfully retrieved TripAdvisor reviews for "${this.urlPath}".`);components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (1)
69-76
: Optional: Harden task access for initial requestA small guard makes the code more robust if the API shape changes.
- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const task = response.tasks?.[0]; + if (!task) { + throw new ConfigurationError("Error: No task information returned by DataForSEO."); + } + if (task.status_code !== 20000 && task.status_code !== 20100) { + throw new ConfigurationError(`Error: ${task.status_message}`); + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (18)
components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
(1 hunks)components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs
(1 hunks)components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs
(1 hunks)components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs
(1 hunks)components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs
(1 hunks)components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs
(1 hunks)components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs
(1 hunks)components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs
(1 hunks)components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs
(1 hunks)components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs
(1 hunks)components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs
(1 hunks)components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs
(1 hunks)components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs
(1 hunks)components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs
(1 hunks)components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs
(1 hunks)components/dataforseo/common/utils.mjs
(1 hunks)components/dataforseo/dataforseo.app.mjs
(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs
- components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs
- components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs
- components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs
- components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs
- components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs
- components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs
- components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs
- components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs
- components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.
Applied to files:
components/dataforseo/dataforseo.app.mjs
🧬 Code Graph Analysis (6)
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (1)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (3)
components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs (1)
response
(41-51)components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
response
(31-40)components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (1)
response
(29-37)
components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (3)
components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs (1)
response
(41-51)components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
response
(31-40)components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (1)
response
(41-51)
components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
components/dataforseo/dataforseo.app.mjs (2)
response
(13-13)response
(79-79)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (1)
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (3)
from
(31-31)to
(32-32)response
(55-64)
components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (3)
from
(47-47)to
(48-48)response
(72-83)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
- GitHub Check: Lint Code Base
🔇 Additional comments (4)
components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
31-40
: Client method and payload mapping verified
- Found
getGoogleReviews(args = {})
in components/dataforseo/dataforseo.app.mjs (lines 441–444), which spreads...args
into the request config.- Your action’s call
correctly passesawait this.dataforseo.getGoogleReviews({ $, data: [ { keyword: this.keyword, location_coordinate: this.locationCoordinate, language_code: this.languageCode, }, ], });$
and adata
array with the expected keys (keyword
,location_coordinate
,language_code
).No further changes required here.
components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (1)
29-37
: getTrustpilotReviews client method and payload mapping are correct
- Located
getTrustpilotReviews(args = {})
in components/dataforseo/dataforseo.app.mjs (lines 427–432), issuing a POST to/business_data/trustpilot/reviews/task_post
and spreading the passedargs
.- Confirmed the action passes a
data
array with objects containingdomain
andsort_by
, matching the client’s accepted payload.No changes needed.
components/dataforseo/dataforseo.app.mjs (2)
174-188
: Non-async _makeRequest is fine; it returns a PromiseReturning the axios Promise without async/await is idiomatic here and doesn’t break callers using await.
468-474
: Method name vs endpoint mismatch: getAppReviewsSummary points to app_reviews endpointThe method name implies a “summary/overview” endpoint, but the path is for raw reviews. Please confirm the intended endpoint and either rename the method or update the path.
Options:
- If you want the summary endpoint:
- path: "/app_data/apple/app_reviews/task_post", + path: "/app_data/apple/app_reviews_summary/task_post",
- If the method should fetch raw reviews, rename it to getAppReviews to avoid confusion.
Also check if you need a Google Play counterpart for parity.
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs
Show resolved
Hide resolved
components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
components/dataforseo/dataforseo.app.mjs (1)
181-186
: Close the parenthesis and end the sentence in “targets” description.User-facing typo; the opening parenthesis before “including” isn’t closed.
- description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`", + description: "Up to 1000 domains, subdomains or webpages to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
♻️ Duplicate comments (5)
components/dataforseo/dataforseo.app.mjs (3)
136-140
: Boolean prop says “Default is true” but default is not set (includeSubdomains).Set default: true to match the description and avoid surprising behavior.
includeSubdomains: { type: "boolean", label: "Include Subdomains", description: "Whether the subdomains of the `target` will be included in the search. Default is `true`", + default: true, optional: true, },
142-146
: Boolean prop says “Default is true” but default is not set (includeIndirectLinks).Same rationale as above.
includeIndirectLinks: { type: "boolean", label: "Include Indirect Links", description: "Whether indirect links to the target will be included in the results. Default is `true`", + default: true, optional: true, },
148-152
: Boolean prop says “Default is true” but default is not set (excludeInternalBacklinks).Same rationale as above.
excludeInternalBacklinks: { type: "boolean", label: "Exclude Internal Backlinks", description: "Indicates if internal backlinks from subdomains to the target will be excluded from the results. Default is `true`", + default: true, optional: true, },
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (2)
12-47
: TripAdvisor reviews/task_post requires url_path (not keyword/location_code); remove unsupported sort_by.The TripAdvisor reviews endpoint expects a business identifier via url_path. keyword + location_code isn’t valid for direct reviews retrieval, and sort_by isn’t supported here. First search to obtain url_path, then request reviews.
Apply this diff to fix props:
props: { dataforseo, - keyword: { - type: "string", - label: "Keyword", - description: "The keyword to search for", - }, - countryCode: { - type: "string", - label: "Country Code", - description: "The country code of the target location. Ex: `US`", - }, - locationCode: { - propDefinition: [ - dataforseo, - "tripAdvisorLocationCode", - (c) => ({ - countryCode: c.countryCode, - }), - ], - }, + urlPath: { + type: "string", + label: "TripAdvisor URL Path", + description: "Path segment that uniquely identifies the business on TripAdvisor, e.g. \"Hotel_Review-g60763-d23462501-Reviews-Margaritaville_Times_Square-New_York_City_New_York.html\"", + }, languageCode: { propDefinition: [ dataforseo, "languageCode", ], optional: true, }, - sortBy: { - type: "string", - label: "Sort By", - description: "Sort reviews by specific criteria", - options: [ - "most_recent", - "detailed_reviews", - ], - optional: true, - }, + translateReviews: { + type: "boolean", + label: "Translate Reviews", + description: "Translate reviews to the specified language when available", + optional: true, + default: true, + },
71-87
: Send url_path (and translate_reviews) to the API; drop unsupported fields.Request payload currently sends keyword, location_code, and sort_by, which won’t return results for reviews. Use url_path and translate_reviews.
- response = await this.dataforseo.getTripadvisorReviews({ + response = await this.dataforseo.getTripadvisorReviews({ $, data: [ { - keyword: this.keyword, - location_code: this.locationCode, - language_code: this.languageCode, - sort_by: this.sortBy, + url_path: this.urlPath, + language_code: this.languageCode, + translate_reviews: this.translateReviews, postback_url: postbackUrl, }, ], });
🧹 Nitpick comments (9)
components/dataforseo/dataforseo.app.mjs (6)
12-21
: Rename misnamed variable to avoid confusion (locations vs languageCodes).The options() for locationCode stores the locations array in a variable named languageCodes. This is misleading and increases cognitive load when reading the code.
Apply this diff:
- const languageCodes = response.tasks[0].result; - return languageCodes.map(({ + const locations = response.tasks[0].result; + return locations.map(({ location_name, location_code, }) => ({ value: location_code, label: location_name, }));
23-39
: Disambiguate label to “TripAdvisor Location Code”.Both locationCode and tripAdvisorLocationCode render with the label “Location Code”, which can be confusing in actions that include multiple selectors. Rename the label here for clarity.
- label: "Location Code", + label: "TripAdvisor Location Code",
54-55
: Unify formatting for protocol/domain guidance in “target” description.Backticks are used elsewhere (backlinksTarget). Align formatting and clarify page vs domain input.
- description: "The domain name or the url of the target website or page. The domain should be specified without https:// and www.", + description: "The domain name or the URL of the target website or page. A domain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
59-60
: Add terminal period for consistency in user-facing text.Minor copy nit to keep descriptions consistent.
- description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`)", + description: "Domain, subdomain or webpage to get data for. A domain or a subdomain should be specified without `https://` and `www`. A page should be specified with absolute URL (including `http://` or `https://`).",
86-90
: Consider enforcing max limit at call sites to prevent rejected tasks.You document “Maximum: 1000” but there’s no enforcement. To avoid API rejections, clamp the limit in actions (before calling _makeRequest) or add validation to props where supported.
If Pipedream props support numeric constraints, add max: 1000 here. Otherwise, add runtime validation in actions that accept limit. Also verify DataForSEO’s per-endpoint max as some differ slightly.
191-205
: Centralize API error shaping for DX and easier debugging.Currently, _makeRequest returns raw axios responses. Consider catching DataForSEO error payloads and rethrowing with status_code/status_message to surface actionable error info in Pipedream UI.
- return axios($, { - ...otherOpts, - url: this._baseUrl() + path, - auth: { - username: `${this.$auth.api_login}`, - password: `${this.$auth.api_password}`, - }, - }); + return axios($, { + ...otherOpts, + url: this._baseUrl() + path, + auth: { + username: `${this.$auth.api_login}`, + password: `${this.$auth.api_password}`, + }, + }).catch((err) => { + const res = err.response?.data; + const status = res?.status_code; + const message = res?.status_message || err.message; + const details = res?.tasks?.[0]?.result || res?.tasks?.[0]?.status_message; + const enriched = new Error(`[DataForSEO] ${message}${status ? ` (code ${status})` : ""}${details ? ` — ${JSON.stringify(details).slice(0, 500)}` : ""}`); + enriched.cause = err; + throw enriched; + });components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (3)
8-8
: Bump version due to breaking prop changes.Switching from keyword/location_code to url_path is a breaking change. Bump version to at least 0.1.0.
- version: "0.0.1", + version: "0.1.0",
89-95
: Guard against missing tasks array to avoid runtime errors.If the API returns a non-standard error payload, response.tasks[0] can throw. Add defensive checks.
- if (response.tasks[0].status_code !== 20000 && response.tasks[0].status_code !== 20100) { - throw new ConfigurationError(`Error: ${response.tasks[0].status_message}`); - } + const firstTask = response.tasks?.[0]; + if (!firstTask) { + throw new ConfigurationError("Error: Missing task metadata in response."); + } + if (firstTask.status_code !== 20000 && firstTask.status_code !== 20100) { + throw new ConfigurationError(`Error: ${firstTask.status_message}`); + }
102-103
: Update summary (no longer using keyword).After switching to url_path, referencing this.keyword is incorrect.
- $.export("$summary", `Successfully retrieved TripAdvisor reviews for "${this.keyword}".`); - return response; + $.export("$summary", "Successfully retrieved TripAdvisor reviews."); + return response;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs
(1 hunks)components/dataforseo/dataforseo.app.mjs
(8 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-08T16:42:59.225Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.
Applied to files:
components/dataforseo/dataforseo.app.mjs
🧬 Code graph analysis (2)
components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (2)
components/dataforseo/dataforseo.app.mjs (3)
response
(13-13)response
(28-30)response
(96-96)components/dataforseo/actions/get-app-store-search/get-app-store-search.mjs (4)
response
(44-44)context
(45-45)run
(46-50)postbackUrl
(53-53)
components/dataforseo/dataforseo.app.mjs (14)
components/dataforseo/actions/get-app-reviews-summary/get-app-reviews-summary.mjs (1)
response
(41-51)components/dataforseo/actions/get-app-intersection/get-app-intersection.mjs (1)
response
(45-56)components/dataforseo/actions/get-bing-organic-results/get-bing-organic-results.mjs (1)
response
(45-56)components/dataforseo/actions/get-google-images-results/get-google-images-results.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-my-business-info/get-google-my-business-info.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-play-search/get-google-play-search.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-news-results/get-google-news-results.mjs (1)
response
(31-40)components/dataforseo/actions/get-google-organic-results/get-google-organic-results.mjs (1)
response
(45-56)components/dataforseo/actions/get-google-reviews/get-google-reviews.mjs (1)
response
(31-40)components/dataforseo/actions/get-keyword-data-errors/get-keyword-data-errors.mjs (1)
response
(55-64)components/dataforseo/actions/get-sentiment-analysis/get-sentiment-analysis.mjs (1)
response
(19-26)components/dataforseo/actions/get-trustpilot-reviews/get-trustpilot-reviews.mjs (1)
response
(29-37)components/dataforseo/actions/get-keyword-data-id-list/get-keyword-data-id-list.mjs (1)
response
(72-83)components/dataforseo/actions/get-yahoo-organic-results/get-yahoo-organic-results.mjs (1)
response
(45-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Lint Code Base
- GitHub Check: pnpm publish
- GitHub Check: Publish TypeScript components
- GitHub Check: Verify TypeScript components
🔇 Additional comments (2)
components/dataforseo/dataforseo.app.mjs (1)
41-44
: Radius units are confirmed as kilometers
Verified against the official DataForSEO documentation: the third value in everylocation_coordinate
triple (including for/business_data/google/reviews/task_post
and/business_data/business_listings/search/live
) is measured in kilometers. No change is needed to the current description.components/dataforseo/actions/get-tripadvisor-reviews/get-tripadvisor-reviews.mjs (1)
98-100
: Confirm postback payload shape and type on rerun path.Some providers POST raw JSON (object) while others send text. If run.callback_request.body is a string, JSON.parse may be required.
You can add a small guard like:
response = typeof run.callback_request?.body === "string" ? JSON.parse(run.callback_request.body) : run.callback_request?.body;Please confirm the postback payload type used by DataForSEO webhooks.
/approve |
Resolves #18048
The issue requests adding new components for each endpoint in the DataForSEO API. This would amount to over 350 potential new components. This PR adds 40 new components aimed at covering most use cases.
Summary by CodeRabbit
New Features
Refactor
Chores