Skip to content
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

Small bug fixes, refactors in workflow invocation based on Danny's tests #14054

Merged
merged 18 commits into from
Sep 23, 2024

Conversation

dylburger
Copy link
Contributor

@dylburger dylburger commented Sep 22, 2024

Summary by CodeRabbit

  • New Features

    • Streamlined API client configuration by making publicKey and secretKey optional.
    • Enhanced request handling to support various body types, improving flexibility in API requests.
    • Introduced a mock implementation for the simple-oauth2 library for easier testing.
    • Added Jest configuration and setup files to facilitate testing.
    • Comprehensive unit tests for the ServerClient class to ensure expected functionality.
    • Automated testing workflow introduced for continuous integration.
  • Bug Fixes

    • Improved error handling in request processing, allowing for better response parsing.
  • Documentation

    • Updated comments for clarity and consistency across methods.

Copy link

vercel bot commented Sep 22, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Ignored (Inspect) Visit Preview Sep 23, 2024 6:43pm
pipedream-docs ⬜️ Ignored (Inspect) Sep 23, 2024 6:43pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Sep 23, 2024 6:43pm

Copy link
Contributor

coderabbitai bot commented Sep 22, 2024

Caution

Review failed

The pull request is closed.

Walkthrough

The changes introduce a new mock implementation for the simple-oauth2 library, enhancing testing capabilities without real OAuth requests. A Jest configuration file and setup for mocking the fetch API are added. The ServerClient class and CreateServerClientOpts type are modified to streamline configuration, making keys optional and improving request handling. Additionally, a comprehensive suite of unit tests for the ServerClient class is introduced, ensuring robust functionality across various methods.

Changes

Files Change Summary
packages/sdk/mocks/simple-oauth2.ts New Jest mock for simple-oauth2 simulating ClientCredentials behavior with a mock token.
packages/sdk/jest.config.js New Jest configuration for TypeScript, specifying test environment and file matching patterns.
packages/sdk/jest.setup.js New setup file to initialize jest-fetch-mock for mocking the fetch API in tests.
packages/sdk/package.json Updated to remove "type": "module", added "engines" field for Node.js version, and new test script. New devDependencies added for testing.
packages/sdk/src/server/index.ts Updated CreateServerClientOpts to remove environment and make keys optional. Enhanced request handling with new body property.
packages/sdk/src/server/tests/server.test.ts New unit tests for ServerClient class covering various methods and OAuth token handling.
packages/sdk/tsconfig.node.json Updated TypeScript compiler options for module handling and type definitions.
.github/workflows/pipedream-sdk-test.yaml New GitHub Actions workflow for automating SDK tests on pull requests.

Possibly related PRs

Suggested labels

enhancement, dependencies, javascript, prioritized, strategic-app

Poem

🐰 In the code where bunnies play,
Keys are now optional, hip-hip-hooray!
No more environment, just a simpler way,
With requests so flexible, they dance and sway.
Let’s hop along, with joy we convey,
A brighter SDK, come join the fray! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

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.
    -- Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    -- @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 generate unit testing code.
    -- @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.

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 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: 1

Outside diff range and nitpick comments (2)
packages/sdk/__mocks__/simple-oauth2.ts (1)

1-14: LGTM! The mock implementation looks good.

The mock correctly simulates the behavior of the ClientCredentials class from the simple-oauth2 library. It provides a getToken method that returns a mock token object with the necessary properties and methods.

This setup allows for testing components that depend on the simple-oauth2 library without making actual OAuth requests.

Consider adding a way to customize the access_token value and the expired method's return value in the mock. This would allow for more flexible testing scenarios.

For example, you could accept optional parameters in the mock implementation to set these values:

jest.mock("simple-oauth2", () => {
  return {
    ClientCredentials: jest.fn().mockImplementation((options) => {
      const { access_token = "mocked-oauth-token", expired = false } = options || {};
      return {
        getToken: jest.fn().mockResolvedValue({
          token: {
            access_token,
          },
          expired: jest.fn().mockReturnValue(expired),
        }),
      };
    }),
  };
});

This way, you can customize the mock behavior when needed:

const clientCredentials = new ClientCredentials({
  access_token: "custom-token",
  expired: true,
});
packages/sdk/src/server/__tests__/server.test.ts (1)

266-270: Remove unnecessary oauth_app_id field with undefined value

In the request body of the connectTokenCreate test, oauth_app_id is set to undefined. When serializing to JSON with JSON.stringify, properties with undefined values are omitted. Including this field may cause confusion. It's clearer to omit the field entirely when it isn't needed.

Apply this diff to remove the unnecessary field:

           body: JSON.stringify({
             external_id: "user-id",
             app_slug: "test-app",
-            oauth_app_id: undefined,
           }),
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 587f688 and a1f318d.

Files ignored due to path filters (2)
  • packages/sdk/package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing (7)
  • packages/sdk/mocks/simple-oauth2.ts (1 hunks)
  • packages/sdk/jest.config.js (1 hunks)
  • packages/sdk/jest.setup.js (1 hunks)
  • packages/sdk/package.json (2 hunks)
  • packages/sdk/src/server/tests/server.test.ts (1 hunks)
  • packages/sdk/src/server/index.ts (22 hunks)
  • packages/sdk/tsconfig.node.json (1 hunks)
Additional context used
Biome
packages/sdk/src/server/index.ts

[error] 466-466: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Additional comments not posted (31)
packages/sdk/jest.setup.js (2)

1-3: LGTM!

The setup file correctly initializes jest-fetch-mock for mocking the fetch API in Jest tests. This will facilitate easier and more controlled testing of components that rely on the fetch API.


3-3: Verify the impact of the global fetch assignment on the codebase.

Assigning the global fetch variable to the mock implementation provided by jest-fetch-mock could potentially affect other parts of the codebase that rely on the original fetch implementation.

Run the following script to verify the usage of the global fetch variable:

If there are occurrences of fetch outside of test files or setup files, it might indicate that the global assignment could affect the behavior of those parts of the codebase. In such cases, consider using a different approach for mocking fetch in tests, such as using a library like nock or msw that can intercept and mock network requests without modifying the global fetch variable.

Verification successful

Verified: The global fetch assignment is confined to test and setup files.

No usages of fetch outside of test or setup files were found, ensuring that assigning global.fetch to fetchMock does not affect other parts of the codebase.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of the global `fetch` variable in the codebase.

# Test 1: Search for occurrences of `global.fetch` in the codebase.
# Expect: Only occurrences in test files or setup files.
rg --type js $'global\.fetch'

# Test 2: Search for occurrences of `fetch` that are not preceded by `global.` in the codebase.
# Expect: No occurrences outside of test files or setup files.
rg --type js $'(?<!global\.)fetch'

Length of output: 342


Script:

#!/bin/bash
# Description: Re-verify occurrences of `fetch` not preceded by `global.` using PCRE2.

# Test 2: Search for occurrences of `fetch` that are not preceded by `global.` in the codebase.
# Expect: No occurrences outside of test files or setup files.
rg --pcre2 --type js '(?<!global\.)fetch'

Length of output: 348

packages/sdk/tsconfig.node.json (6)

4-4: Verify compatibility with target JavaScript environments.

The change from "ES6" to "es2020" for the target option is approved. However, please ensure that the target JavaScript environments (browsers, Node.js versions, etc.) support ECMAScript 2020 features.


5-5: LGTM!

The change from ["ES6"] to ["esnext"] for the lib option is approved. This allows the use of the latest ECMAScript built-in API declarations in the codebase.


11-11: LGTM!

The addition of the allowSyntheticDefaultImports option set to true is approved. This allows the use of default imports from modules that don't have a default export, which is useful when working with CommonJS modules.


12-12: Update module imports, if necessary.

The change from "NodeNext" to "node" for the moduleResolution option is approved. However, please ensure that the module imports in the codebase are updated to align with the Node.js CommonJS module resolution strategy, if necessary.


14-14: LGTM!

The change from ["node"] to ["node", "jest", "jest-fetch-mock"] for the types option is approved. This allows the use of type declarations for Node.js, Jest, and Jest Fetch Mock in the codebase.


3-3: Verify compatibility and update module imports/exports.

The change from "NodeNext" to "esnext" for the module option is approved. However, please ensure the following:

  • Verify that the target Node.js version supports the latest ECMAScript features.
  • Update the module imports and exports in the codebase to align with the new module code generation, if necessary.

Run the following script to verify the module usage:

packages/sdk/jest.config.js (1)

1-28: LGTM!

The Jest configuration file is well-structured and follows the recommended practices for a TypeScript project. The use of ts-jest preset, along with the specified test environment, setup file, roots, test match patterns, module file extensions, and transformation rule, provides a solid foundation for running tests effectively.

The configuration ensures that TypeScript files are properly transformed during testing, and the setup file allows for any necessary setup or mocking to be performed before running the tests. The roots and test match patterns help Jest locate and run the relevant test files, while the transformation rule ensures that TypeScript files are processed correctly with the specified TypeScript configuration.

Overall, this Jest configuration file is a great addition to the project and should enable smooth testing of the TypeScript codebase.

packages/sdk/package.json (4)

24-26: LGTM!

Specifying the required Node.js version using the "engines" field is a good practice. Setting it to 18.0.0 or higher ensures that the package leverages the latest features and improvements.


38-38: LGTM!

Adding the "build:browser" script improves the build process by generating the browser-specific version of the package using the appropriate TypeScript configuration.


39-39: LGTM!

Adding the "test" script is a great improvement as it enables running tests using Jest. This enhances the testing capabilities of the package and aligns with the PR objective of implementing changes based on tests.


45-51: LGTM!

The added development dependencies significantly enhance the testing setup of the package:

  • @types/fetch-mock and @types/jest provide type definitions for better type checking and IDE support.
  • jest is a powerful testing framework that enables writing and running tests effectively.
  • jest-fetch-mock allows mocking fetch requests in tests, which is crucial for testing network-related functionality.
  • ts-jest enables running TypeScript tests with Jest, ensuring type safety in the test suite.

These additions align perfectly with the PR objective of implementing changes based on tests conducted by Danny.

packages/sdk/src/server/index.ts (12)

111-115: LGTM!

The AuthType enum is correctly defined with appropriate string literal values to represent the authentication types for an app.


227-227: LGTM!

The optional credentials property is correctly added to the Account type with the appropriate type Record<string, string>. The comment clarifies that the property is included only when the include_credentials parameter is set to true.


Line range hint 250-269: LGTM!

The RequestOptions interface is updated to include the body property explicitly, allowing various data types such as objects, strings, FormData, URLSearchParams, or null. This change provides flexibility in specifying the request body.


294-299: LGTM!

The properties of the ServerClient class are updated to make secretKey, publicKey, and oauthClient optional, and the environment property is removed. These changes align with the updates made to the CreateServerClientOpts type.


302-323: LGTM!

The constructor of the ServerClient class is updated to remove the environment property and add an optional oauthClient parameter. The logic for configuring the OAuth client is updated based on the presence of the oauthClient parameter, allowing for providing a pre-configured OAuth client for testing purposes.


355-358: Verify the availability of publicKey and secretKey.

The connectAuthorizationHeader method now directly uses the publicKey and secretKey properties without checking for their presence. Please ensure that these properties are always available when the method is called to avoid potential runtime errors.


Line range hint 360-388: LGTM!

The oauthAuthorizationHeader method is updated to include retry logic for handling expired oauthToken. It introduces a maximum number of attempts and a delay between retries to prevent potential infinite loops and avoid rapid retries. If the maximum number of attempts is reached and the oauthToken is still expired, an appropriate error is thrown.


Line range hint 399-470: LGTM!

The makeRequest method is updated to handle different types of request bodies, including FormData, URLSearchParams, and strings. It introduces a processedBody variable to store the processed request body, which is then assigned to the body property of the requestOptions object. The error handling is improved to attempt parsing the response as JSON if the content type indicates JSON, otherwise, it falls back to raw text.


474-498: LGTM!

The makeAuthorizedRequest method is updated to accept an authType parameter, which determines the type of authorization header to include in the request. It now supports both OAuth and Connect authorization headers based on the authType value. The headers object is updated to include the appropriate authorization header based on the authType.


506-518: LGTM!

The makeApiRequest method is updated to call makeAuthorizedRequest with "oauth" as the authType. This change simplifies the method by delegating the authorization header inclusion to the makeAuthorizedRequest method and ensures that the OAuth authorization header is used for API requests.


520-532: LGTM!

The makeConnectRequest method is updated to call makeAuthorizedRequest with "connect" as the authType. This change simplifies the method by delegating the authorization header inclusion to the makeAuthorizedRequest method and ensures that the Connect authorization header is used for Connect API requests. Prepending /connect to the path parameter ensures that the correct API endpoint is called for Connect requests.


Line range hint 699-765: LGTM!

The getProjectInfo method is added to retrieve project information by making a GET request to the /projects/info endpoint using the makeConnectRequest method. It returns a promise that resolves to the project info response.

The invokeWorkflow method is updated to accept a body parameter as part of the opts object. The body parameter is included in the request options when making the request to the workflow URL. The Authorization header is set using the oauthAuthorizationHeader method to include the OAuth access token.

packages/sdk/src/server/__tests__/server.test.ts (6)

128-176: Tests for makeApiRequest correctly verify OAuth authorization handling

The tests effectively mock the OAuth client and validate that the makeApiRequest method includes the correct Authorization header. This ensures that API requests are properly authenticated using OAuth tokens.


200-232: Validation of Connect API requests is thorough

The test for makeConnectRequest correctly checks that the necessary authorization headers are included for Connect API requests. This ensures that requests to the Connect API are properly authenticated.


280-315: Retrieving accounts with query parameters is correctly tested

The getAccounts method test accurately includes query parameters and checks that accounts are retrieved as expected. Passing parameters like include_credentials ensures the API handles query parameters properly.


422-442: Deletion of accounts is properly verified

The test for deleteAccount confirms that the correct HTTP method (DELETE) is used and that the client handles the 204 No Content response appropriately.


530-588: Workflow invocation test correctly handles OAuth authentication

The invokeWorkflow test ensures that workflows are invoked with the proper URL, body, and headers, including the OAuth token. This verifies that the client can invoke workflows while maintaining valid authentication.


590-653: OAuth token refresh logic is correctly tested

The test for token refreshing accurately simulates an expired token scenario and validates that a new token is fetched and used in subsequent requests. This ensures the client can maintain authentication even when tokens expire.

packages/sdk/src/server/__tests__/server.test.ts Outdated Show resolved Hide resolved
*/
environment?: string;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a weird diff but I was removing environment

secretKey: "test-secret-key",
});

const result = await client["makeRequest"]("/test-path", {
Copy link
Contributor

@jverce jverce Sep 23, 2024

Choose a reason for hiding this comment

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

Why do we have to do client["makeRequest"] instead of client.makeRequest? Is it because of mocking? Or because the method is private?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather focus the tests on the public interface instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it makes sense to make makeRequest public as a mechanism for people making their own Pipedream API requests that aren't covered by the SDK anyway, so I changed that and corrected these tests

Comment on lines +493 to +497
if (authType === "oauth") {
headers["Authorization"] = await this.oauthAuthorizationHeader();
} else {
headers["Authorization"] = this.connectAuthorizationHeader();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm now more convinced that we should split these clients into ConnectClient and ApiClient, WDYT?
We could also rename ServerClient (too verbose IMHO) to just Client, make it abstract, and implement the HTTP calling logic only.

abstract class Client {
  private baseUrl: string;

  constructor(baseUrl: string) {
    this.baseUrl = baseUrl;
  }

  makeRequest(
    method: "GET" | "POST",
    path: string,
    headers: object,
    payload?: unknown,
  ) {
    // Do all the stuff that it does now
  }
}

class ConnectClient extends Client {
  private publicKey: string;
  private secretKey: string;

  constructor(publicKey: string, secretKey: string) {
    super("https://api.pipedream.com/v1/connect");
    this.publicKey = publicKey;
    this.privateKey = privateKey;
  }

  private makeRequest(
    method: "GET" | "POST",
    path: string,
    headers: object,
    payload?: unknown,
  ) {
    headers["Authorization"] = this.authorizationHeader();
    return super(method, path, headers, payload);
  }
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Not now of course.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

100%, maybe after we ship all the JWT validation in catcher and Rails we can work on this next sprint

@dylburger dylburger merged commit 6a7ab4f into master Sep 23, 2024
8 checks passed
@dylburger dylburger deleted the connect/invocation-sdk-fixes branch September 23, 2024 18:44
@coderabbitai coderabbitai bot mentioned this pull request Sep 26, 2024
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.

2 participants