diff --git a/README.md b/README.md index a069751..f8b070b 100644 --- a/README.md +++ b/README.md @@ -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.yaml/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 @@ -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 diff --git a/lib/use_paragon.rb b/lib/use_paragon.rb index 671c7c7..3ec974e 100644 --- a/lib/use_paragon.rb +++ b/lib/use_paragon.rb @@ -9,6 +9,9 @@ # UseParagon gem base module module UseParagon + class Error < StandardError; end + class InvalidUserIdError < StandardError; end + # Configuration from initializer class << self def configuration @@ -19,7 +22,4 @@ def configure yield(configuration) end end - - class Error < StandardError; end - class InvalidUserIdError < StandardError; end end diff --git a/lib/use_paragon/configuration.rb b/lib/use_paragon/configuration.rb index 6616870..88ea346 100644 --- a/lib/use_paragon/configuration.rb +++ b/lib/use_paragon/configuration.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "logger" - module UseParagon # Allows configuration using an initializer class Configuration diff --git a/lib/use_paragon/engine.rb b/lib/use_paragon/engine.rb deleted file mode 100644 index 7c08e0f..0000000 --- a/lib/use_paragon/engine.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module UseParagon - # Engine for Rails to include and precompile vendor assets - class Engine < ::Rails::Engine - initializer "use_paragon.assets.precompile" do |app| - app.config.assets.precompile += %w[useparagon/connect.js] - end - end -end diff --git a/lib/use_paragon/integration.rb b/lib/use_paragon/integration.rb index 3defd35..7951997 100644 --- a/lib/use_paragon/integration.rb +++ b/lib/use_paragon/integration.rb @@ -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 diff --git a/lib/use_paragon/user.rb b/lib/use_paragon/user.rb index 87b2725..c9d45c7 100644 --- a/lib/use_paragon/user.rb +++ b/lib/use_paragon/user.rb @@ -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": "sean@useparagon.com", "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 diff --git a/lib/use_paragon/workflow.rb b/lib/use_paragon/workflow.rb index 8122fc9..648f78f 100644 --- a/lib/use_paragon/workflow.rb +++ b/lib/use_paragon/workflow.rb @@ -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 @@ -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 diff --git a/spec/use_paragon/configuration_spec.rb b/spec/use_paragon/configuration_spec.rb index bbde712..34d12ac 100644 --- a/spec/use_paragon/configuration_spec.rb +++ b/spec/use_paragon/configuration_spec.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "logger" - RSpec.describe UseParagon::Configuration do let(:configuration) { described_class.new } diff --git a/spec/use_paragon/integration_spec.rb b/spec/use_paragon/integration_spec.rb index d4143bb..b97eb9d 100644 --- a/spec/use_paragon/integration_spec.rb +++ b/spec/use_paragon/integration_spec.rb @@ -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") @@ -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 diff --git a/spec/use_paragon/workflow_spec.rb b/spec/use_paragon/workflow_spec.rb index f8ccae9..bcf8534 100644 --- a/spec/use_paragon/workflow_spec.rb +++ b/spec/use_paragon/workflow_spec.rb @@ -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")