8000 Apigee (new) by vtalas · Pull Request #579 · clientIO/appmixer-connectors · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Apigee (new) #579

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

Draft
wants to merge 19 commits into
base: dev
Choose a base branch
from
Draft

Apigee (new) #579

wants to merge 19 commits into from

Conversation

vtalas
Copy link
Contributor
@vtalas vtalas commented Jun 13, 2025
  • ListEnvironments
  • ListOrganizations
  • ListKeyValueMaps

Summary by CodeRabbit

  • New Features

    • Introduced Apigee integration, enabling management of IP block entries, environments, organizations, and key-value maps directly within workflows.
    • Added components for adding, finding, and removing IP block entries, as well as listing organizations, environments, and key-value maps in Apigee.
    • Integrated OAuth2 authentication for Google accounts to support secure Apigee access.
    • Added a component for retrieving datasets from Apify.
  • Bug Fixes

    • Updated request configuration to use the correct property for URLs in Apify authentication.
  • Documentation

    • Added usage instructions and component metadata for new Apigee integration features.
  • Tests

    • Introduced tests for listing Apigee environments and organizations.
  • Chores

    • Added development dependency for environment variable management.

@vtalas vtalas marked this pull request as draft June 13, 2025 13:07
Copy link
Contributor
coderabbitai bot commented Jun 13, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update introduces a new Apigee integration package, including authentication, service metadata, quota management, and multiple components for managing Apigee organizations, environments, key-value maps, and IP block entries. It also adds supporting utility libraries, component definitions, and test suites. Minor adjustments are made to Apify authentication and dataset-fetching components.

Changes

Files / Grouped Paths Change Summary
package.json Added "dotenv" to devDependencies.
src/appmixer/apify/auth.js Changed request config keys from uri to url in requestProfileInfo and validate.
src/appmixer/apify/crawlers/FetchDatasets/FetchDatasets.js, component.json Added new FetchDatasets module and component definition for retrieving Apify datasets.
src/appmixer/apigee/README.md Added usage example for Apigee authentication login test.
src/appmixer/apigee/auth.js Introduced new OAuth2 authentication module for Google accounts with methods for authorization, token management, and profile info retrieval.
src/appmixer/apigee/bundle.json Added package metadata and changelog for appmixer.apigee.
src/appmixer/apigee/core/AddEntry/AddEntry.js, component.json Added AddEntry module and component for adding entries to Apigee key-value maps (IP blocking).
src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js, component.json Added FindBlockingEntries module and component for listing IP block entries with multiple output formats.
src/appmixer/apigee/core/FindEntries/FindEntries.js, component.json Added FindEntries module and component for retrieving IP block entries from a key-value map.
src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js, component.json Added ListEnvironments module and component for retrieving Apigee environments.
src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js, component.json Added ListKeyValueMaps module and component for listing key-value maps in an environment.
src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js, component.json Added ListOrganizations module and component for retrieving accessible Apigee organizations.
src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js, component.json Added RemoveEntry module and component for removing IP block entries from key-value maps.
src/appmixer/apigee/lib.generated.js Introduced utility functions for sending output data in various formats and generating output port options.
src/appmixer/apigee/quota.js Added quota configuration limiting requests per second.
src/appmixer/apigee/service.json Added service metadata for the Apigee integration.
test/apigee/listEnvironments.test.js, test/apigee/listOrganizations.test.js Added test suites for Apigee ListEnvironments and ListOrganizations components.

Suggested labels

POC


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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor
@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 21

🧹 Nitpick comments (17)
src/appmixer/apigee/lib.generated.js (1)

25-31: Duplicate comment – minor cleanup

Line 25 repeats “One by one.” twice; trim the redundancy.

src/appmixer/apify/auth.js (1)

26-34: Good switch from uriurl; consider moving token to header

Change aligns with common Axios/fetch options – nice.

Security note: the API token is still sent as a query parameter, which can be logged by proxies. If Apify supports it, prefer Authorization: Bearer {{apiToken}} to avoid leaking credentials.

src/appmixer/apigee/README.md (1)

1-4: Wrap bare URL in markdown to silence MD034 and improve readability
Markdown-lint flags the naked URL. Enclose it in angle brackets or make it an explicit link.

-TEST_SERVER_URL=http://localhost:2200 appmixer …
+TEST_SERVER_URL=<http://localhost:2200> appmixer …
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

1-1: Bare URL used
null

(MD034, no-bare-urls)


1-1: Bare URL used
null

(MD034, no-bare-urls)

src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js (2)

6-7: Validate required parameters before firing the request
org or env being undefined will currently yield a hard-to-debug 404 from Apigee. Guard early and surface a clear error.

-const { org, env } = context.messages.in.content;
+const { org, env } = context.messages.in.content || {};
+if (!org || !env) {
+    throw new Error('Both "org" and "env" parameters are required.');
+}

12-14: Header-key quoting inconsistent with sibling modules
Here the key is unquoted while in ListEnvironments.js it is quoted. Pick one style across the package for consistency.

-    headers: {
-        Authorization: `Bearer ${context.auth.accessToken}`
-    }
+    headers: {
+        'Authorization': `Bearer ${context.auth.accessToken}`
+    }
src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js (3)

6-7: Parameter presence check recommended
Same rationale as for ListKeyValueMaps; fail fast when org is missing to avoid opaque remote errors.


9-15: Potential pagination not handled
The environments list also supports pagination (pageSize, pageToken). Decide whether to auto-paginate or at least document the limitation.


12-14: Quote the Authorization header for uniformity
Match the quoting style used in other files.

test/apigee/listEnvironments.test.js (1)

30-32: sendJson mock should mirror actual signature
Production signature is (data, port). Include the port parameter to avoid surprises if logic branches on it later.

-context.sendJson = (data) => {
+context.sendJson = (data, _port) => {
     result = data;
 };
src/appmixer/apify/crawlers/FetchDatasets/FetchDatasets.js (2)

1-1: Insert 'use strict'; for consistency

All other Appmixer components start with the directive and guidelines explicitly state it is desirable.
Add it before the first statement to keep the style uniform.

+'use strict';
+
 const lib = require('../../lib.generated');

4-4: desc is extracted but never used

Leaving an unused variable will trigger linters and confuses readers.

-        const { desc } = context.messages.in.content;
+        // No input parameters required at the moment
src/appmixer/apigee/core/FindEntries/FindEntries.js (1)

4-4: Unused lib import

lib is required but never referenced; drop it to avoid dead code.

-const lib = require('../../lib.generated');
test/apigee/listOrganizations.test.js (1)

35-35: Remove debugging output

console.log in unit tests pollutes CI logs.

-        console.log(result);
src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js (1)

4-4: Unused lib import

lib isn’t referenced; remove to keep the file lean.

-const lib = require('../../lib.generated');
src/appmixer/apigee/core/AddEntry/AddEntry.js (1)

3-3: lib is imported but never used

lib is required yet no symbol from it is referenced in this module. Drop the import to keep the file lean or use it if there is planned functionality.

-const lib = require('../../lib.generated');
+// const lib = require('../../lib.generated'); // uncomment when helpers are needed
src/appmixer/apigee/core/ListEnvironments/component.json (1)

39-43: Consider defining an explicit output schema

outPorts lacks a schema definition, which weakens type-safety and inspector UX. Adding at least a minimal array schema (e.g. {"type":"array","items":{"type":"string"}}) makes the component integration clearer.

src/appmixer/apigee/core/FindEntries/component.json (1)

1-3: Add unit tests for this component.
No corresponding test file exists under test/apigee.

Cover scenarios for empty maps, multiple entries, and error conditions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b562c8 and 1ab40fb.

📒 Files selected for processing (26)
  • package.json (1 hunks)
  • src/appmixer/apify/auth.js (1 hunks)
  • src/appmixer/apify/crawlers/FetchDatasets/FetchDatasets.js (1 hunks)
  • src/appmixer/apify/crawlers/FetchDatasets/component.json (1 hunks)
  • src/appmixer/apigee/README.md (1 hunks)
  • src/appmixer/apigee/auth.js (1 hunks)
  • src/appmixer/apigee/bundle.json (1 hunks)
  • src/appmixer/apigee/core/AddEntry/AddEntry.js (1 hunks)
  • src/appmixer/apigee/core/AddEntry/component.json (1 hunks)
  • src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js (1 hunks)
  • src/appmixer/apigee/core/FindBlockingEntries/component.json (1 hunks)
  • src/appmixer/apigee/core/FindEntries/FindEntries.js (1 hunks)
  • src/appmixer/apigee/core/FindEntries/component.json (1 hunks)
  • src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js (1 hunks)
  • src/appmixer/apigee/core/ListEnvironments/component.json (1 hunks)
  • src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js (1 hunks)
  • src/appmixer/apigee/core/ListKeyValueMaps/component.json (1 hunks)
  • src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js (1 hunks)
  • src/appmixer/apigee/core/ListOrganizations/component.json (1 hunks)
  • src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js (1 hunks)
  • src/appmixer/apigee/core/RemoveEntry/component.json (1 hunks)
  • src/appmixer/apigee/lib.generated.js (1 hunks)
  • src/appmixer/apigee/quota.js (1 hunks)
  • src/appmixer/apigee/service.json (1 hunks)
  • test/apigee/listEnvironments.test.js (1 hunks)
  • test/apigee/listOrganizations.test.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
`src/appmixer/**/*.js`: Not necessary to add try/catch blocks in their `receive` function. Appmixer engine automatically handles any exceptions that originate in these async functi...

src/appmixer/**/*.js: Not necessary to add try/catch blocks in their receive function. Appmixer engine automatically handles any exceptions that originate in these async functions.

The directive "use strict"; is desirable here

  • src/appmixer/apify/auth.js
  • src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js
  • src/appmixer/apigee/quota.js
  • src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js
  • src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js
  • src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js
  • src/appmixer/apigee/core/FindEntries/FindEntries.js
  • src/appmixer/apify/crawlers/FetchDatasets/FetchDatasets.js
  • src/appmixer/apigee/core/AddEntry/AddEntry.js
  • src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js
  • src/appmixer/apigee/lib.generated.js
  • src/appmixer/apigee/auth.js
`**/*/bundle.json`: Make sure `version` also matches the last entry in `changelog`

**/*/bundle.json: Make sure version also matches the last entry in changelog

  • src/appmixer/apigee/bundle.json
🧬 Code Graph Analysis (6)
src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js (2)
src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js (2)
  • context (6-6)
  • context (9-15)
src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js (1)
  • context (7-13)
src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js (3)
src/appmixer/apigee/core/AddEntry/AddEntry.js (2)
  • lib (3-3)
  • context (17-23)
src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js (3)
  • lib (4-4)
  • context (10-10)
  • context (17-23)
src/appmixer/apigee/core/FindEntries/FindEntries.js (3)
  • lib (4-4)
  • context (8-8)
  • context (11-17)
src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js (1)
src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js (2)
  • context (6-6)
  • context (9-15)
test/apigee/listOrganizations.test.js (1)
src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js (1)
  • context (7-13)
src/appmixer/apigee/core/AddEntry/AddEntry.js (3)
src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js (3)
  • lib (4-4)
  • context (10-10)
  • context (17-23)
src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js (3)
  • lib (4-4)
  • context (8-8)
  • context (11-17)
src/appmixer/apigee/auth.js (2)
  • data (56-62)
  • data (82-87)
src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js (3)
src/appmixer/apigee/core/AddEntry/AddEntry.js (2)
  • lib (3-3)
  • context (17-23)
src/appmixer/apigee/core/FindEntries/FindEntries.js (3)
  • lib (4-4)
  • context (8-8)
  • context (11-17)
src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js (3)
  • lib (4-4)
  • context (8-8)
  • context (11-17)
🪛 markdownlint-cli2 (0.17.2)
src/appmixer/apigee/README.md

1-1: Bare URL used
null

(MD034, no-bare-urls)


1-1: Bare URL used
null

(MD034, no-bare-urls)

🪛 Biome (1.9.4)
src/appmixer/apigee/core/ListEnvironments/ListEnvironments.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/quota.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/RemoveEntry/RemoveEntry.js

[error] 1-2: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/FindEntries/FindEntries.js

[error] 1-2: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/AddEntry/AddEntry.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/core/FindBlockingEntries/FindBlockingEntries.js

[error] 1-2: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/auth.js

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

🔇 Additional comments (7)
package.json (1)

14-14:

✅ Verification successful

Confirm dotenv is only used in test code; promote if used at runtime

dotenv is declared under devDependencies, which is correct as long as it’s only required by the test suite.
If any production modules load it (e.g. via require('dotenv').config()), it must move to "dependencies" or those imports will break in production deployments where devDeps aren’t installed.

Run a quick search to ensure no non-test file imports dotenv:


🏁 Script executed:

#!/bin/bash
rg --glob '!test/**' --glob '!**/*.spec.js' --glob '!**/*.test.js' $'require[(]..dotenv.' || echo "✅ no runtime usage"

Length of output: 137


dotenv usage verified – safe to keep as devDependency
No non-test files import dotenv; it’s only used in your test suite, so leaving it under devDependencies is correct.

src/appmixer/apigee/quota.js (1)

1-13: 'use strict' is fine – ignore the lint false-positive

The file is CommonJS, not an ES module. CommonJS isn’t strict by default, so keeping 'use strict'; is legitimate. No changes required.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

src/appmixer/apigee/service.json (1)

1-8: Metadata JSON LGTM

The descriptor is syntactically valid and complete. Nice job embedding the icon to avoid extra assets.

src/appmixer/apigee/bundle.json (1)

1-8: Version & changelog are correctly aligned – good catch
The version field matches the last key in changelog, satisfying the repository guideline for bundle.json files.

src/appmixer/apigee/core/ListOrganizations/ListOrganizations.js (1)

11-11: Token property differs from other components

Here we use accessToken, elsewhere (FindEntries, RemoveEntry) use apiToken. Standardise the field name across Apigee components to avoid integration bugs.

src/appmixer/apigee/core/ListOrganizations/component.json (1)

7-10: Component configuration aligns with authentication requirements.
The auth section correctly specifies both the service and required OAuth scope.

src/appmixer/apigee/auth.js (1)

1-1: 'use strict'; should stay – guideline overrides the linter noise

Appmixer’s own coding-guideline explicitly asks for "use strict";.
Ignore Biome’s “redundant” warning; this file is CommonJS and strict mode is desirable.

🧰 Tools
🪛 Biome (1.9.4)

[error] 1-1: Redundant use strict directive.

The entire contents of JavaScript modules are automatically in strict mode, with no statement needed to initiate it.
Safe fix: Remove the redundant use strict directive.

(lint/suspicious/noRedundantUseStrict)

Comment on lines +41 to +44
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Potential path-traversal via componentName

componentName is taken from user-configurable flowDescriptor labels. If a label contains ../ or absolute paths, pathModule.normalize could escape the intended directory.

Mitigate by stripping path separators:

-const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
+const safeName = componentName.replace(/[\\\/]/g, '_');
+const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${safeName}.csv`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
8000
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const safeName = componentName.replace(/[\\\/]/g, '_');
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${safeName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);
🤖 Prompt for AI Agents
In src/appmixer/apigee/lib.generated.js around lines 41 to 44, the componentName
is derived from user-configurable labels and may contain path traversal
characters like ../ which pathModule.normalize won't fully prevent. To fix this,
sanitize componentName by removing or replacing any path separators or traversal
sequences before using it to build the fileName, ensuring the final path cannot
escape the intended directory.

Comment on lines +35 to +49
} else if (outputType === 'file') {

// Into CSV file.
const csvString = toCsv(records);

let buffer = Buffer.from(csvString, 'utf8');
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);

await context.log({ step: 'File was saved', fileName, fileId: savedFile.fileId });
await context.sendJson({ fileId: savedFile.fileId }, outputPortName);
} else {
throw new context.CancelError('Unsupported outputType ' + outputType);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Empty‐records path crashes for outputType === "file"

toCsv(records) blindly accesses records[0]; passing an empty array causes TypeError: Cannot convert undefined or null to object.

Add the same empty-array guard used for "first":

 } else if (outputType === 'file') {
-    // Into CSV file.
-    const csvString = toCsv(records);
+    if (records.length === 0) {
+        throw new context.CancelError('No records available for file output type');
+    }
+    // Into CSV file.
+    const csvString = toCsv(records);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (outputType === 'file') {
// Into CSV file.
const csvString = toCsv(records);
let buffer = Buffer.from(csvString, 'utf8');
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);
await context.log({ step: 'File was saved', fileName, fileId: savedFile.fileId });
await context.sendJson({ fileId: savedFile.fileId }, outputPortName);
} else {
throw new context.CancelError('Unsupported outputType ' + outputType);
}
} else if (outputType === 'file') {
if (records.length === 0) {
throw new context.CancelError('No records available for file output type');
}
// Into CSV file.
const csvString = toCsv(records);
let buffer = Buffer.from(csvString, 'utf8');
const componentName = context.flowDescriptor[context.componentId].label || context.componentId;
const fileName = `${context.config.outputFilePrefix || DEFAULT_PREFIX}-${componentName}.csv`;
const savedFile = await context.saveFileStream(pathModule.normalize(fileName), buffer);
await context.log({ step: 'File was saved', fileName, fileId: savedFile.fileId });
await context.sendJson({ fileId: savedFile.fileId }, outputPortName);
} else {
throw new context.CancelError('Unsupported outputType ' + outputType);
}
🤖 Prompt for AI Agents
In src/appmixer/apigee/lib.generated.js between lines 35 and 49, the code calls
toCsv(records) without checking if records is empty, causing a TypeError when
records is an empty array. Add a guard to check if records is empty before
calling toCsv, similar to the existing guard used for the "first" outputType. If
records is empty, handle that case appropriately to avoid calling toCsv with an
empty array.

Comment on lines +9 to +15
const { data } = await context.httpRequest({
method: 'GET',
url: `https://apigee.googleapis.com/v1/organizations/${org}/environments/${env}/keyvaluemaps`,
headers: {
Authorization: `Bearer ${context.auth.accessToken}`
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

API is paginated – component returns only the first page
/keyvaluemaps supports pageSize & pageToken; the response may contain nextPageToken. Consider iterating until token is empty or expose pagination controls to the user so data isn’t silently truncated.

🤖 Prompt for AI Agents
In src/appmixer/apigee/core/ListKeyValueMaps/ListKeyValueMaps.js around lines 9
to 15, the API call to fetch keyvaluemaps only retrieves the first page of
results, ignoring pagination. To fix this, implement a loop that repeatedly
calls the API using the nextPageToken from the response until no more tokens
remain, aggregating all pages of data before returning. Alternatively, modify
the component to expose pagination controls so users can request additional
pages explicitly.

Comment on lines +27 to +38
it('should handle parameters.ADD with empty object', async () => {

let result;
context.sendJson = (data) => {
result = data;
};
context.messages = {
in: {
content: {
org: 'new-edge-team',
outputType: 'array'
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Test description is misleading & may hit live network

  1. Title says parameters.ADD which is unrelated.
  2. axios is passed straight through; unless you stub Apigee endpoints, the test will perform real HTTP calls and become flaky. Swap axios for a nock/fetch-mock stub or inject a fake httpRequest.
🤖 Prompt for AI Agents
In test/apigee/listEnvironments.test.js around lines 27 to 38, the test title
incorrectly references parameters.ADD which is unrelated and misleading; update
the test description to accurately reflect what is being tested. Also, replace
the direct use of axios with a stub or mock HTTP client like nock or fetch-mock
to prevent real network calls during testing, or inject a fake httpRequest to
ensure the test is isolated and reliable.

const component = require('../../src/appmixer/apigee/core/ListEnvironments/ListEnvironments');
const { createMockContext } = require('../utils');

dotenv.config({ path: __dirname + '../../.env' });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

.env path concatenation is missing a path separator
__dirname + '../../.env' produces /tests/apigee../.env. Use path.join.

-dotenv.config({ path: __dirname + '../../.env' });
+const path = require('path');
+dotenv.config({ path: path.join(__dirname, '../../.env') });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dotenv.config({ path: __dirname + '../../.env' });
const path = require('path');
dotenv.config({ path: path.join(__dirname, '../../.env') });
🤖 Prompt for AI Agents
In test/apigee/listEnvironments.test.js at line 8, the dotenv config path
concatenation is incorrect because it lacks a path separator, causing an invalid
path. Replace the string concatenation with path.join(__dirname, '../../.env')
to correctly construct the path to the .env file.

Comment on lines 26 to 29
"mapname": {
"type": "string"
},
"outputType": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent property naming: use mapName instead of mapname.
Align this key with other Apigee components (RemoveEntry, FindEntries) for schema consistency.

 "properties": {
-    "mapname": {
-        "type": "string"
-    },
+    "mapName": {
+        "type": "string"
+    },
     "outputType": {
         "type": "string"
     }
 }

Remember to update references in the inspector and any downstream code.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"mapname": {
"type": "string"
},
"outputType": {
"mapName": {
"type": "string"
},
"outputType": {
"type": "string"
}
🤖 Prompt for AI Agents
In src/appmixer/apigee/core/FindBlockingEntries/component.json around lines 26
to 29, rename the property key from "mapname" to "mapName" to maintain
consistent camelCase naming with other Apigee components. After updating the
key, ensure all references to this property in the inspector and any downstream
code are also updated to use "mapName" to avoid breaking changes.

Comment on lines 20 to 28
"organization": {
"type": "string"
},
"environment": {
"type": "string"
},
"mapName": {
"type": "string"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent input property names across components.
Other Apigee actions use org and env – here you use full words. Standardize to one convention for uniformity:

 "properties": {
-    "organization": {"type": "string"},
-    "environment": {"type": "string"},
+    "org": {"type": "string"},
+    "env": {"type": "string"},
     "mapName": {"type": "string"}
 }

Don’t forget to adjust the inspector labels and any implementation code accordingly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"organization": {
"type": "string"
},
"environment": {
"type": "string"
},
"mapName": {
"type": "string"
}
"org": {
"type": "string"
},
"env": {
"type": "string"
},
"mapName": {
"type": "string"
}
🤖 Prompt for AI Agents
In src/appmixer/apigee/core/FindEntries/component.json around lines 20 to 28,
the input property names "organization" and "environment" are inconsistent with
other Apigee actions that use "org" and "env". Rename these properties to "org"
and "env" respectively for consistency. Also update the "inspector" labels and
any related implementation code to reflect these new property names to maintain
uniformity across components.

Comment on lines +24 to +36
authUrl: function(context) {
const params = new URLSearchParams({
client_id: initData.clientId,
redirect_uri: context.callbackUrl,
response_type: 'code',
scope: context.scope.join(' '),
state: context.ticket,
access_type: 'offline',
approval_prompt: 'force'
}).toString();

return `https://accounts.google.com/o/oauth2/auth?${params}`;
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Google OAuth query param approval_prompt is deprecated – switch to prompt=consent

approval_prompt was removed from Google OAuth 2.0 spec years ago. Use the modern prompt param to avoid 400 errors on some tenants.

-                    approval_prompt: 'force'
+                    prompt: 'consent'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
authUrl: function(context) {
const params = new URLSearchParams({
client_id: initData.clientId,
redirect_uri: context.callbackUrl,
response_type: 'code',
scope: context.scope.join(' '),
state: context.ticket,
access_type: 'offline',
approval_prompt: 'force'
}).toString();
return `https://accounts.google.com/o/oauth2/auth?${params}`;
},
authUrl: function(context) {
const params = new URLSearchParams({
client_id: initData.clientId,
redirect_uri: context.callbackUrl,
response_type: 'code',
scope: context.scope.join(' '),
state: context.ticket,
access_type: 'offline',
prompt: 'consent'
}).toString();
return `https://accounts.google.com/o/oauth2/auth?${params}`;
},
🤖 Prompt for AI Agents
In src/appmixer/apigee/auth.js between lines 24 and 36, replace the deprecated
Google OAuth query parameter 'approval_prompt' with the current 'prompt'
parameter set to 'consent'. Update the URLSearchParams object by removing
'approval_prompt: force' and adding 'prompt: consent' to ensure compatibility
and prevent 400 errors.

Comment on lines +80 to +96
refreshAccessToken: async function(context) {

const data = {
client_id: initData.clientId,
client_secret: initData.clientSecret,
refresh_token: context.refreshToken,
grant_type: 'refresh_token'
};

const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Same form-encoding issue in refreshAccessToken

-                const data = {
-                    client_id: initData.clientId,
-                    client_secret: initData.clientSecret,
-                    refresh_token: context.refreshToken,
-                    grant_type: 'refresh_token'
-                };
+                const data = new URLSearchParams({
+                    client_id: initData.clientId,
+                    client_secret: initData.clientSecret,
+                    refresh_token: context.refreshToken,
+                    grant_type: 'refresh_token'
+                }).toString();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
refreshAccessToken: async function(context) {
const data = {
client_id: initData.clientId,
client_secret: initData.clientSecret,
refresh_token: context.refreshToken,
grant_type: 'refresh_token'
};
const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});
refreshAccessToken: async function(context) {
- const data = {
- client_id: initData.clientId,
- client_secret: initData.clientSecret,
- refresh_token: context.refreshToken,
- grant_type: 'refresh_token'
- };
+ const data = new URLSearchParams({
+ client_id: initData.clientId,
+ client_secret: initData.clientSecret,
+ refresh_token: context.refreshToken,
+ grant_type: 'refresh_token'
+ }).toString();
const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});
🤖 Prompt for AI Agents
In src/appmixer/apigee/auth.js between lines 80 and 96, the refreshAccessToken
function sends the token refresh request with data as a JSON object, but the
Content-Type header is 'application/x-www-form-urlencoded'. To fix this, convert
the data object into a URL-encoded string format before sending it in the
request body, ensuring it matches the specified content type.

Comment on lines +56 to +72
const data = {
code: context.authorizationCode,
client_id: initData.clientId,
client_secret: initData.clientSecret,
redirect_uri: context.callbackUrl,
grant_type: 'authorization_code'
};

const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Body is sent as object while header claims application/x-www-form-urlencoded

context.httpRequest (axios-style) will JSON-encode plain objects, yielding a 400 from Google because the header and body mismatch. Encode with URLSearchParams (or drop the header and let axios form-encode).

-                const data = {
-                    code: context.authorizationCode,
-                    client_id: initData.clientId,
-                    client_secret: initData.clientSecret,
-                    redirect_uri: context.callbackUrl,
-                    grant_type: 'authorization_code'
-                };
+                const data = new URLSearchParams({
+                    code: context.authorizationCode,
+                    client_id: initData.clientId,
+                    client_secret: initData.clientSecret,
+                    redirect_uri: context.callbackUrl,
+                    grant_type: 'authorization_code'
+                }).toString();

Same remark for refreshAccessToken below.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const data = {
code: context.authorizationCode,
client_id: initData.clientId,
client_secret: initData.clientSecret,
redirect_uri: context.callbackUrl,
grant_type: 'authorization_code'
};
const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});
const data = new URLSearchParams({
code: context.authorizationCode,
client_id: initData.clientId,
client_secret: initData.clientSecret,
redirect_uri: context.callbackUrl,
grant_type: 'authorization_code'
}).toString();
const response = await context.httpRequest({
method: 'POST',
url: 'https://oauth2.googleapis.com/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data
});
🤖 Prompt for AI Agents
In src/appmixer/apigee/auth.js around lines 56 to 72, the request body is sent
as a plain object while the Content-Type header is set to
'application/x-www-form-urlencoded', causing a mismatch and a 400 error from
Google. To fix this, convert the data object to a URLSearchParams instance
before sending it in the httpRequest call. Apply the same fix to the
refreshAccessToken function where a similar pattern occurs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant
0