Skip to content

Commit

Permalink
Merge pull request #6 from candanedo/feature/improvements_and_isolati…
Browse files Browse the repository at this point in the history
…on_of_api_wrapper

[FEATURE] Improvements, isolation of API Wrapper
  • Loading branch information
candanedo authored Feb 28, 2024
2 parents 3e4b789 + 389d8da commit 744344b
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 205 deletions.
65 changes: 21 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# UseParagon

The UseParagon Ruby Gem simplifies the interaction with Paragon's service through RESTful API calls, enabling seamless integration of native features into your Ruby applications. With this gem, product and engineering teams can effortlessly incorporate Paragon's SDK and embedded Integration Platform as a Service (iPaaS) to accelerate the development of native integrations.
[![example workflow](https://github.com/candanedo/use_paragon/actions/workflows/main.yml/badge.svg)](https://github.com/candanedo/use_paragon/actions?query=branch%3Amain)
[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)

The UseParagon Ruby Gem simplifies the interaction with Paragon's service through RESTful API calls, enabling seamless integration of native features into your Ruby applications. With this gem, engineering teams can effortlessly incorporate Paragon's API to accelerate the development of native integrations.

| API Method | REST Method | Supported | Gem's Class | Method |
|--------------------------------|-------------|-----------|-------------------------|---------------------------------------------------------------------------------|
| Disable Workflow | DELETE | [x] | UseParagon::Workflow | disable(workflow_id) |
| Get Integrations Metadata | GET | [x] | UseParagon::Integration | metadata |
| Get User | GET | [x] | UseParagon::User | get |
| Workflow Event (App Events) | POST | [x] | UseParagon::Workflow | event(event_name, payload = {}) |
| Proxy Request | | [x] | UseParagon::Workflow | proxy_request(request_method, integration_type, integration_path, payload = {}) |
| Set User Metadata | PATCH | [x] | UseParagon::User | metadata=(metadata) |
| Uninstall Integration | DELETE | [x] | UseParagon::Integration | uninstall(integration_id) |
| Workflow Request (Trigger) | POST | [x] | UseParagon::Workflow | request(workflow_id, payload = {}) |
| Get Project's Integrations | GET | [x] | UseParagon::Integration | list |
| Get User's Connect Credentials | GET | [x] | UseParagon::User | credentials |

## Installation

Expand All @@ -22,56 +38,17 @@ If Bundler is not used to manage dependencies, install the gem with:

## Configuration

To use the UseParagon gem, you need to configure it with your private key and project ID. Here's how to do it:
To use the UseParagon gem, you need to configure it with your private key and project ID. Here's an example on how to do it:

#### For Rails
Create a new initializer in your Rails project:

touch config/initializers/use_paragon.rb

Provide your private key and project ID in the initializer:
Provide your private key and project ID:

```ruby
# config/initializers/use_paragon.rb

UseParagon.configure do |config|
config.private_key = Rails.application.credentials.paragon.private_key
config.project_id = Rails.application.credentials.paragon.project_id
config.private_key = YOUR_PRIVATE_KEY
config.project_id = YOUR_PROJECT_ID
end
```

## Usage

To use UseParagon, add your project's ID along with your private key to your Rails credentials.

The private key is generated on the UseParagon Dashboard. Follow [these instructions](https://docs.useparagon.com/getting-started/installing-the-connect-sdk#setup-with-your-own-authentication-backend).

Rails.application.credentials.paragon.private_key

Find your project ID in the Overview tab of any Integration.

Rails.application.credentials.paragon.project_id

Once this information is configured in your Rails project, you can start using the gem as needed.

## Javascript assets (Paragon SDK)

The UseParagon gem includes the JavaScript assets necessary for you to utilize the Paragon SDK. To use window.paragon.authenticate(project, token) or window.connect(integration_name, options) in your JavaScript, you need to require the assets.

#### Using Importmaps
If you're using importmaps, add the following line to your config/importmap.rb file:

```ruby
pin "useparagon/connect" # 3.1.2
```
This will ensure that the necessary JavaScript asset is included in your application.

If you're using Stimulus controllers, you can require the asset in your desired controllers:

```js
import "useparagon/connect"
```

### Workflow triggers
#### Request trigger

Expand Down
14 changes: 3 additions & 11 deletions lib/use_paragon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,17 @@

# UseParagon gem base module
module UseParagon
class Error < StandardError; end
class InvalidUserIdError < StandardError; end

# Configuration from initializer
class << self
def load!
register_rails_engine if rails?
end

def configuration
@configuration ||= Configuration.new
end

def configure
yield(configuration)
end

def register_rails_engine
require "use_paragon/engine"
end
end

class Error < StandardError; end
class InvalidUserIdError < StandardError; end
end
2 changes: 0 additions & 2 deletions lib/use_paragon/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require "logger"

module UseParagon
# Allows configuration using an initializer
class Configuration
Expand Down
10 changes: 0 additions & 10 deletions lib/use_paragon/engine.rb

This file was deleted.

42 changes: 33 additions & 9 deletions lib/use_paragon/integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,49 @@
module UseParagon
# https://docs.useparagon.com/api/users#disconnecting-integrations
class Integration < Base
VALID_HTTP_METHODS = %w[post get put patch delete].freeze

# Returns a list of the integrations enabled for the Paragon project by the ID in the URL.
def list
connection.get(path("sdk/integrations"))
end

# Get the name, brandColor, and icon, for any of your active integration providers.
def metadata
endpoint = path("sdk/metadata")

connection.get(endpoint)
connection.get(path("sdk/metadata"))
end

# Returns a list of the integrations enabled for the Paragon project by the ID in the URL.
def list
endpoint = path("sdk/integrations")
# Call proxy_request to send an API request to a third-party integration on behalf of
# one of your users
# https://docs.useparagon.com/api/making-api-requests#server-side-usage
# This endpoint accepts any HTTP verb you want to use with the API:
# post, get, put, patch or delete.
# Body contents must be specified as application/json.
def proxy_request(request_method, integration_type, integration_path, payload = {})
formatted_method = request_method&.downcase

validate_proxy_http_method(formatted_method)

connection.get(endpoint)
connection.send(
formatted_method,
path("sdk/proxy/#{integration_type}/#{integration_path}"),
payload
)
end

# Integrations can be disconnected using uninstall via REST API.
def uninstall(integration_id)
endpoint = path("sdk/integrations/#{integration_id}")
connection.delete(path("sdk/integrations/#{integration_id}"))
end

private

def validate_proxy_http_method(formatted_method)
return if VALID_HTTP_METHODS.include?(formatted_method)

connection.delete(endpoint)
raise ArgumentError,
"Invalid request method: #{formatted_method}. " \
"Allowed methods: #{VALID_HTTP_METHODS.join(", ")}"
end
end
end
12 changes: 3 additions & 9 deletions lib/use_paragon/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@ module UseParagon
# Retrieve the currently authenticated user and their connected integration state
class User < Base
def get
endpoint = path("sdk/me")

connection.get(endpoint)
connection.get(path("sdk/me"))
end

# Call set_metadata to associate the authenticated user with metadata from
# your application
# { "meta": { "Email": "[email protected]", "apiKey": "key_Y0kBVldPFInxK" } }
def metadata=(metadata)
endpoint = path("sdk/me")

connection.patch(endpoint, metadata)
connection.patch(path("sdk/me"), metadata)
end

def credentials
endpoint = path("sdk/credentials")

connection.get(endpoint)
connection.get(path("sdk/credentials"))
end
end
end
37 changes: 3 additions & 34 deletions lib/use_paragon/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,19 @@
module UseParagon
# https://docs.useparagon.com/workflows/triggers#request
class Workflow < Base
VALID_HTTP_METHODS = %w[post get put patch delete].freeze
# The Request trigger can be used to run workflows by sending it an HTTP request
def request(workflow_id, payload = {})
endpoint = path("sdk/triggers/#{workflow_id}")

connection.post(endpoint, payload)
end

# Call proxy_request to send an API request to a third-party integration on behalf of
# one of your users
# https://docs.useparagon.com/api/making-api-requests#server-side-usage
# This endpoint accepts any HTTP verb you want to use with the API:
# post, get, put, patch or delete.
# Body contents must be specified as application/json.
def proxy_request(request_method, integration_type, integration_path, payload = {})
formatted_method = request_method&.downcase

validate_proxy_http_method(formatted_method)

endpoint = path("sdk/proxy/#{integration_type}/#{integration_path}")

connection.send(formatted_method, endpoint, payload)
connection.post(path("sdk/triggers/#{workflow_id}"), payload)
end

# App Events can be sent from your application using the Paragon REST API.
def event(event_name, payload = {})
endpoint = path("sdk/events/trigger")

connection.post(endpoint, event_payload(event_name, payload))
connection.post(path("sdk/events/trigger"), event_payload(event_name, payload))
end

# Call disable Workflow to turn off a workflow for a user by ID.
def disable(workflow_id)
endpoint = path("sdk/workflows/#{workflow_id}")

connection.delete(endpoint)
connection.delete(path("sdk/workflows/#{workflow_id}"))
end

private
Expand All @@ -51,13 +28,5 @@ def event_payload(event_name, payload)
payload: payload
}
end

def validate_proxy_http_method(formatted_method)
return if VALID_HTTP_METHODS.include?(formatted_method)

raise ArgumentError,
"Invalid request method: #{formatted_method}. " \
"Allowed methods: #{VALID_HTTP_METHODS.join(", ")}"
end
end
end
2 changes: 0 additions & 2 deletions spec/use_paragon/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

require "logger"

RSpec.describe UseParagon::Configuration do
let(:configuration) { described_class.new }

Expand Down
50 changes: 43 additions & 7 deletions spec/use_paragon/integration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
RSpec.describe UseParagon::Integration do
let(:integration) { described_class.new(123) }

describe "#list" do
it "calls the correct endpoint" do
expect(integration).to receive(:path).with("sdk/integrations")
.and_return("/projects/123/sdk/integrations")
expect(integration).to receive(:connection)
.and_return(double("connection", get: true))
integration.list
end
end

describe "#metadata" do
it "calls the correct endpoint" do
expect(integration).to receive(:path).with("sdk/metadata")
Expand All @@ -13,13 +23,39 @@
end
end

describe "#list" do
it "calls the correct endpoint" do
expect(integration).to receive(:path).with("sdk/integrations")
.and_return("/projects/123/sdk/integrations")
expect(integration).to receive(:connection)
.and_return(double("connection", get: true))
integration.list
describe "#proxy_request" do
context "with valid HTTP method" do
it "calls the correct endpoint with post method" do
expect(integration).to receive(:path).with("sdk/proxy/some_type/some_path")
.and_return(
"/projects/123/sdk/proxy/some_type/some_path"
)
expect(integration).to receive(:connection).and_return(double("connection", post: true))
integration.proxy_request("post", "some_type", "some_path", {})
end

it "calls the correct endpoint with delete method" do
expect(integration).to receive(:path).with("sdk/proxy/some_type/some_path")
.and_return(
"/projects/123/sdk/proxy/some_type/some_path"
)
expect(integration).to receive(:connection).and_return(double("connection", delete: true))
integration.proxy_request("delete", "some_type", "some_path", {})
end
end

context "with invalid HTTP method" do
it "raises ArgumentError" do
expect { integration.proxy_request("invalid_method", "some_type", "some_path", {}) }
.to raise_error(ArgumentError, /Invalid request method/)
end
end

context "with missing HTTP method" do
it "raises ArgumentError" do
expect { integration.proxy_request(nil, "some_type", "some_path", {}) }
.to raise_error(ArgumentError, /Invalid request method/)
end
end
end

Expand Down
32 changes: 0 additions & 32 deletions spec/use_paragon/workflow_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,38 +12,6 @@
end
end

describe "#proxy_request" do
context "with valid HTTP method" do
it "calls the correct endpoint with post method" do
expect(workflow).to receive(:path).with("sdk/proxy/some_type/some_path")
.and_return("/projects/123/sdk/proxy/some_type/some_path")
expect(workflow).to receive(:connection).and_return(double("connection", post: true))
workflow.proxy_request("post", "some_type", "some_path", {})
end

it "calls the correct endpoint with delete method" do
expect(workflow).to receive(:path).with("sdk/proxy/some_type/some_path")
.and_return("/projects/123/sdk/proxy/some_type/some_path")
expect(workflow).to receive(:connection).and_return(double("connection", delete: true))
workflow.proxy_request("delete", "some_type", "some_path", {})
end
end

context "with invalid HTTP method" do
it "raises ArgumentError" do
expect { workflow.proxy_request("invalid_method", "some_type", "some_path", {}) }
.to raise_error(ArgumentError, /Invalid request method/)
end
end

context "with missing HTTP method" do
it "raises ArgumentError" do
expect { workflow.proxy_request(nil, "some_type", "some_path", {}) }
.to raise_error(ArgumentError, /Invalid request method/)
end
end
end

describe "#event" do
it "calls the correct endpoint with post method" do
expect(workflow).to receive(:path).with("sdk/events/trigger")
Expand Down
Loading

0 comments on commit 744344b

Please sign in to comment.