└── custom_connectors ├── basic_auth ├── onprem_security.rb ├── click_time_connector.rb ├── harvest_connector.rb ├── close_io_connector.rb ├── docparser_connector.rb ├── webmerge_connector.rb ├── watson_tone_analyzer_connector.rb ├── unbounce_connector.rb ├── toggl_connector.rb ├── freshdesk_connector.rb ├── salesforceiq_connector.rb ├── splunk_connector.rb └── clearbit_connector.rb ├── none ├── password_generator.rb ├── statuspage.rb └── rssfeedreader.rb ├── api_key_auth ├── gender_api_connector.rb ├── ops_genie_connector.rb ├── hipchat_connector.rb ├── codeship_connector.rb ├── rightsignature_connector.rb ├── mandrill_connector.rb └── aftership_connector.rb ├── custom_auth ├── knack_hq_connector.rb ├── tsheets_connector.rb ├── infobip_connector.rb ├── lo_jack_connector.rb ├── safetyculture_connector.rb ├── convert_api_connector.rb ├── bill_connector.rb ├── blacklinereports.rb ├── neto_connector.rb └── domo.rb └── oauth2 ├── pushbullet_connector.rb ├── wachete_connector.rb ├── accelo_connector.rb ├── linenotify.rb ├── podio_connector.rb ├── amcards_connector.rb ├── cisco_spark_connector.rb ├── typeform_connector.rb ├── wrike_connector.rb ├── formassembly.rb └── ephesoft_connector.rb /custom_connectors/basic_auth/onprem_security.rb: -------------------------------------------------------------------------------- 1 | { 2 | # Works with the custom security extension profile for 3 | # Workato OPA: https://github.com/workato/opa-extensions/blob/master/src/main/java/com/mycompany/onprem/SecurityExtension.java 4 | title: 'On-prem security', 5 | secure_tunnel: true, 6 | 7 | connection: { 8 | fields: [{ name: 'profile', hint: 'On-prem security connection profile' }], 9 | authorization: { type: 'none'} 10 | }, 11 | 12 | test: ->(connection) { 13 | post("http://localhost/ext/#{connection['profile']}", { payload: 'test' }).headers('X-Workato-Connector': 'enforce') 14 | }, 15 | 16 | actions: { 17 | sha256_digest: { 18 | 19 | title: 'Create SHA-256 digest', 20 | description: 'Create SHA-256 digest', 21 | 22 | input_fields: ->(_) { [{ name: 'payload' }] }, 23 | output_fields: ->(_) { [{name: 'signature'}] }, 24 | 25 | execute: ->(connection, input) { 26 | post("http://localhost/ext/#{connection['profile']}", input).headers('X-Workato-Connector': 'enforce') 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /custom_connectors/none/password_generator.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Password Generator', 3 | 4 | connection: { 5 | authorization: {}, 6 | }, 7 | object_definitions: {}, 8 | 9 | test: ->(connection) { 10 | true 11 | }, 12 | actions: { 13 | generate_password: { 14 | execute: ->() { 15 | letters_list = [ 16 | ('!'..'/').to_a + (':'..'@').to_a + ('['..'`').to_a + ('{'..'~').to_a, 17 | ('0'..'9').to_a, 18 | ('A'..'Z').to_a, 19 | ('a'..'z').to_a 20 | ].sort_by { |n| rand(4) } 21 | 22 | password_size = 8 23 | req_chars_size = letters_list.size 24 | other_chars_size = password_size - req_chars_size 25 | index_list = 26 | (0...req_chars_size).to_a.sort_by { rand(req_chars_size) } + 27 | (0...other_chars_size).to_a.map { rand(other_chars_size) } 28 | 29 | password = (0...password_size).to_a.map do |n| 30 | letters = letters_list[index_list[n]] 31 | letters[rand(letters.size)] 32 | end.join 33 | { 34 | password: password 35 | } 36 | }, 37 | output_fields: ->() { 38 | [ 39 | { name: 'password' } 40 | ] 41 | }, 42 | sample_output: ->() { 43 | { password: 'P@s19ser' } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/click_time_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Click Time', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'username', 8 | optional: true, 9 | hint: 'Your email used for login' 10 | }, 11 | { 12 | name: 'password', 13 | control_type: 'password', 14 | } 15 | ], 16 | 17 | authorization: { 18 | type: 'basic_auth', 19 | 20 | credentials: ->(connection) { 21 | user(connection['username']) 22 | password(connection['password']) 23 | } 24 | } 25 | }, 26 | 27 | object_definitions: { 28 | session: { 29 | fields: ->() { 30 | [ 31 | { name: 'CompanyID' }, 32 | { name: 'SecurityLevel' }, 33 | { name: 'Token' }, 34 | { name: 'UserEmail' }, 35 | { name: 'UserID' }, 36 | { name: 'UserName' } 37 | ] 38 | } 39 | } 40 | }, 41 | 42 | test: ->(connection) { 43 | get("https://app.clicktime.com/api/1.3/session") 44 | }, 45 | 46 | actions: { 47 | get_user_and_company_id: { 48 | input_fields: ->() { 49 | }, 50 | 51 | execute: -> (connection, input) { 52 | get("https://app.clicktime.com/api/1.3/session") 53 | }, 54 | 55 | output_fields: -> (object_definitions) { 56 | object_definitions['session'] 57 | } 58 | } 59 | }, 60 | 61 | triggers: {} 62 | } 63 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/gender_api_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Gender', 3 | 4 | connection: { 5 | 6 | fields: [ 7 | { 8 | name: 'key', 9 | hint: 'Your Gender API Private Server Key', 10 | optional: false 11 | }, 12 | ], 13 | 14 | authorization: { 15 | type: 'api_key', 16 | 17 | credentials: ->(connection) { 18 | params(key: connection['key']) 19 | } 20 | } 21 | }, 22 | 23 | test: ->(connection) { 24 | get("https://gender-api.com/get?name=matthew") 25 | }, 26 | 27 | object_definitions: { 28 | 29 | person: { 30 | fields: ->() { 31 | [ 32 | { name: 'name' }, 33 | { name: 'gender' }, 34 | { name: 'samples', type: :integer }, 35 | { name: 'accuracy', type: :integer } 36 | ] 37 | } 38 | } 39 | }, 40 | 41 | actions: { 42 | 43 | get_gender: { 44 | 45 | input_fields: ->(object_definitions) { 46 | [ 47 | { name: 'name', optional: false}, 48 | { name: 'country', hint: '2 Letter country code. Click here for list.' } 49 | ] 50 | }, 51 | 52 | execute: ->(connection, input) { 53 | { 54 | person: get("https://gender-api.com/get", input) 55 | } 56 | }, 57 | 58 | output_fields: ->(object_definitions) { 59 | [ 60 | { name: "person", type: :object, 61 | properties: object_definitions['person']} 62 | ] 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/knack_hq_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Knack HQ', 3 | 4 | connection: { 5 | fields: [ 6 | { name: 'app_id', optional: false }, 7 | { name: 'api_key', control_type: 'password', optional: false } 8 | ], 9 | 10 | authorization: { 11 | type: 'custom_auth', 12 | 13 | credentials: ->(connection) { 14 | headers('X-Knack-Application-Id': connection['app_id'], 15 | 'X-Knack-REST-API-Key': connection['api_key']) 16 | } 17 | } 18 | }, 19 | 20 | test: ->(connection) { 21 | get("https://api.knackhq.com/v1/objects") 22 | }, 23 | 24 | object_definitions: { 25 | object_1: { 26 | fields: ->(connection) { 27 | get("https://api.knackhq.com/v1/objects/object_1/fields")['fields']. 28 | map { |f| { name: f['key'], label: f['label'] } } 29 | } 30 | } 31 | }, 32 | 33 | actions: { 34 | 35 | get_object_1_by_id: { 36 | input_fields: ->(object_definitions) { 37 | [ 38 | { name: 'id', optional: false } 39 | ] 40 | }, 41 | 42 | execute: ->(connection,input) { 43 | get("https://api.knackhq.com/v1/objects/object_1/records/#{input['id']}") 44 | }, 45 | 46 | output_fields: -> (object_definitions) { 47 | object_definitions['object_1'] 48 | } 49 | } 50 | }, 51 | 52 | triggers: { 53 | new_object_1_record: { 54 | type: :paging_desc, 55 | 56 | input_fields: -> (object_definitions) {}, 57 | 58 | poll: ->(connection,input,page) { 59 | page ||= 1 60 | 61 | response = get("https://api.knackhq.com/v1/objects/object_1/records"). 62 | params(sort_field: 'id', 63 | sort_order: 'desc', 64 | page: page) 65 | 66 | next_page = response['total_pages'] == response['current_page'].to_i ? nil : (page + 1) 67 | 68 | { 69 | events: response['records'], 70 | next_page: next_page 71 | } 72 | }, 73 | 74 | output_fields: -> (object_definitions) { 75 | object_definitions['object_1'] 76 | } 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/pushbullet_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Pushbullet', 3 | 4 | connection: { 5 | authorization: { 6 | type: 'oauth2', 7 | 8 | authorization_url: ->() { 9 | 'https://www.pushbullet.com/authorize?response_type=code' 10 | }, 11 | 12 | token_url: ->() { 13 | 'https://api.pushbullet.com/oauth2/token' 14 | }, 15 | 16 | client_id: 'abababababab', 17 | 18 | client_secret: 'cdcdcdcdcdcd', 19 | 20 | credentials: ->(connection, access_token) { 21 | headers('Authorization': "Bearer #{access_token}") 22 | } 23 | } 24 | }, 25 | 26 | object_definitions: { 27 | push: { 28 | fields: ->() { 29 | [ 30 | { name: 'active', type: :boolean }, 31 | { name: 'body' }, 32 | { name: 'created' }, 33 | { name: 'direction' }, 34 | { name: 'dismissed', type: :boolean }, 35 | { name: 'iden' }, 36 | { name: 'modified' }, 37 | { name: 'receiver_email' }, 38 | { name: 'receiver_email_normalized' }, 39 | { name: 'receiver_iden' }, 40 | { name: 'sender_email' }, 41 | { name: 'sender_email_normalized' }, 42 | { name: 'sender_iden' }, 43 | { name: 'sender_name' }, 44 | { name: 'title' }, 45 | { name: 'type' }, 46 | ] 47 | } 48 | } 49 | }, 50 | 51 | actions: { 52 | create_a_push: { 53 | input_fields: ->() { 54 | [ 55 | { name: 'email', optional: false }, 56 | { name: 'type', type: :select, optional: false, pick_list: [ 57 | ["Note","note"], 58 | ["Link","link"], 59 | ["File","file"] 60 | ]}, 61 | { name: 'title', optional: false }, 62 | { name: 'body', optional: false }, 63 | { name: 'url', hint: 'Required if type set to Link or File' }, 64 | { name: 'file_type', hint: 'Required if type set to File' } 65 | ] 66 | }, 67 | execute: ->(connection,input) { 68 | post("https://api.pushbullet.com/v2/pushes", input) 69 | }, 70 | output_fields: ->(object_definitions) { 71 | object_definitions['push'] 72 | } 73 | } 74 | }, 75 | 76 | triggers: {} 77 | } 78 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/harvest_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Harvest', 3 | 4 | # HTTP basic auth example. 5 | connection: { 6 | fields: [ 7 | { 8 | name: 'subdomain', 9 | control_type: 'subdomain', 10 | url: '.harvestapp.com', 11 | hint: 'The subdomain found in your Harvest URL' 12 | }, 13 | { 14 | name: 'username' 15 | }, 16 | { 17 | name: 'password', 18 | control_type: 'password' 19 | } 20 | ], 21 | 22 | authorization: { 23 | type: 'basic_auth', 24 | 25 | # Basic auth credentials are just the username and password; framework handles adding 26 | # them to the HTTP requests. 27 | credentials: ->(connection) { 28 | user(connection['username']) 29 | password(connection['password']) 30 | } 31 | } 32 | }, 33 | 34 | object_definitions: { 35 | }, 36 | 37 | test: ->(connection) { 38 | get("https://#{connection['subdomain']}.harvestapp.com/account/who_am_i") 39 | }, 40 | 41 | actions: { 42 | search_users: { 43 | # not used 44 | input_fields: ->(object_definitions) { 45 | }, 46 | 47 | execute: ->(connection, input) { 48 | # harvest returns an array with each user wrapped in a hash [ { user: {} }, ..] 49 | # extract the object 50 | results = get("https://#{connection['subdomain']}.harvestapp.com/people.json", input).map do |row| 51 | row['user'] 52 | end 53 | 54 | { results: results } 55 | }, 56 | output_fields: ->(object_definitions) { 57 | [ 58 | { 59 | name: 'users', 60 | type: 'array', 61 | of: 'object', 62 | properties: [ 63 | { name: 'id', type: 'integer' }, 64 | { name: 'first_name' }, 65 | { name: 'last_name' }, 66 | { name: 'email' }, 67 | { name: 'telephone' }, 68 | { name: 'department' }, 69 | { name: 'cost_rate' }, 70 | { name: 'is_admin', type: 'boolean' }, 71 | { name: 'is_active', type: 'boolean' }, 72 | { name: 'is_contractor', type: 'boolean' }, 73 | { name: 'has_access_to_all_future_projects', type: 'boolean' }, 74 | { name: 'created_at', type: 'date_time' }, 75 | { name: 'updated_at', type: 'date_time' }, 76 | ] 77 | } 78 | ] 79 | } 80 | } 81 | }, 82 | } 83 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/wachete_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Wachete", 3 | 4 | connection: { 5 | authorization: { 6 | type: "oauth2", 7 | 8 | authorization_url: lambda do 9 | "https: //www.wachete.com/login" 10 | end, 11 | 12 | token_url: lambda do 13 | "https: //api.wachete.com/v1/oauth/token" 14 | end, 15 | 16 | client_id: "WACHETE_CLIENT_ID", 17 | 18 | client_secret: "WACHETE_CLIENT_SECRET", 19 | 20 | credentials: lambda do |_connection, access_token| 21 | headers("Authorization": "bearer #{access_token}") 22 | end 23 | } 24 | }, 25 | 26 | object_definitions: { 27 | notification: { 28 | fields: lambda do 29 | [ 30 | { 31 | name: "id", 32 | }, 33 | { 34 | name: "task", 35 | type: :object, 36 | properties: [ 37 | { 38 | name: "definition", 39 | type: :object, 40 | properties: [ 41 | { 42 | name: "name", 43 | hint: "Name of wachet you received notification for" 44 | } 45 | ] 46 | } 47 | ] 48 | }, 49 | { 50 | name: "timestampUtc", 51 | label: "Timestamp", 52 | type: :timestamp, 53 | hint: "Time when notification happened" 54 | }, 55 | { 56 | name: "current", 57 | hint: "Current value of wachet" 58 | }, 59 | { 60 | name: "comparand", 61 | hint: "Previous value of wachet" 62 | }, 63 | { 64 | name: "type", 65 | hint: "Type of notification" 66 | } 67 | ] 68 | end 69 | } 70 | }, 71 | 72 | test: lambda do |_connection| 73 | get("https://api.wachete.com/v1/task/get") 74 | end, 75 | 76 | triggers: { 77 | new_notification: { 78 | poll: lambda do |_connection, _input, _last_updated_since| 79 | notifications = get("https://api.wachete.com/v1/alert/range"). 80 | params(from: "2006-01-25T10: 37: 23.574Z", 81 | to: "2030-01-23T10: 37: 23.574Z", 82 | count: 1) 83 | { 84 | events: notifications["data"] 85 | } 86 | end, 87 | 88 | dedup: lambda do |notification| 89 | notification["id"] 90 | end, 91 | 92 | output_fields: lambda do |object_definitions| 93 | object_definitions["notification"] 94 | end 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/ops_genie_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "OpsGenie", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "key", 8 | hint: "Find your OpsGenie API key " \ 9 | "here.", 10 | optional: false 11 | } 12 | ], 13 | 14 | authorization: { 15 | type: "api_key", 16 | 17 | credentials: lambda do |connection| 18 | headers("Authorization": "GenieKey #{connection['key']}") 19 | end 20 | }, 21 | 22 | base_uri: lambda do 23 | "https://api.opsgenie.com" 24 | end 25 | }, 26 | 27 | test: lambda do 28 | get("/v2/alerts") 29 | end, 30 | 31 | object_definitions: { 32 | alert: { 33 | fields: lambda do |_connection, _config_fields| 34 | [ 35 | { name: "message", optional: false }, 36 | { name: "alias" }, 37 | { name: "description" }, 38 | { name: "responders", type: :object, properties: [ 39 | { name: "id" }, 40 | { name: "name" }, 41 | { name: "username" }, 42 | { name: "type" } 43 | ] }, 44 | { name: "visibleTo", type: :object, properties: [ 45 | { name: "id" }, 46 | { name: "name" }, 47 | { name: "username" }, 48 | { name: "type" } 49 | ] }, 50 | { name: "entity" }, 51 | { name: "priority", control_type: "select", pick_list: "priorities" } 52 | ] 53 | end 54 | } 55 | }, 56 | 57 | actions: { 58 | create_alert: { 59 | title: "Create an alert", 60 | description: "Create alert in " \ 61 | "OpsGenie", 62 | 63 | input_fields: lambda do |object_definitions| 64 | object_definitions["alert"] 65 | end, 66 | 67 | execute: lambda do |_connection, input| 68 | post("https://api.opsgenie.com/v2/alerts", input) 69 | end, 70 | 71 | output_fields: lambda do |_object_definitions| 72 | [ 73 | { name: "result" }, 74 | { name: "took", type: "number" }, 75 | { name: "requestId" } 76 | ] 77 | end, 78 | 79 | sample_output: lambda do |_object_definitions| 80 | { 81 | "result": "Request will be processed", 82 | "took": 0.302, 83 | "requestId": "43a29c5c-3dbf-4fa4-9c26-f4f71023e120" 84 | } 85 | end 86 | } 87 | }, 88 | 89 | pick_lists: { 90 | priorities: lambda do |_connection| 91 | [ 92 | %w[P1 P1], 93 | %w[P2 P2], 94 | %w[P3 P3], 95 | %w[P4 P4], 96 | %w[P5 P5] 97 | ] 98 | end 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/hipchat_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Hipchat', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'subdomain', 8 | control_type: 'subdomain', 9 | url: '.hipchat.com', 10 | optional: false, 11 | hint: 'Enter your subdomain' 12 | }, 13 | { 14 | name: 'auth_token', 15 | control_type: 'password', 16 | optional: false, 17 | label: "Access token", 18 | hint: 'Go to Account Settings->API Access. Enter password and get token' 19 | }, 20 | ], 21 | 22 | authorization: { 23 | type: 'auth_token', 24 | credentials: ->(connection) { 25 | params(auth_token: connection['auth_token']) 26 | } 27 | } 28 | }, 29 | 30 | object_definitions: { 31 | 32 | message: { 33 | fields: ->() { 34 | [ 35 | { name: 'id', hint: "ID of the message" }, 36 | { name: 'timestamp' } 37 | ] 38 | } 39 | } 40 | }, 41 | 42 | test: ->(connection) { 43 | get("https://#{connection['subdomain']}.hipchat.com/v2/room") 44 | }, 45 | 46 | actions: { 47 | post_message: { 48 | 49 | description: 'Post Message in Hipchat', 50 | 51 | input_fields: ->() { 52 | [ 53 | { name: 'message', hint: "Valid length range: 1 - 1000", optional: false, label: "Message" }, 54 | { name: 'room', hint: "Give either Room ID or Room name", optional: false, label: "Room" } 55 | ] 56 | }, 57 | 58 | execute: ->(connection, input) { 59 | post("https://#{connection['subdomain']}.hipchat.com/v2/room/#{input['room']}/message",input) 60 | }, 61 | 62 | output_fields: ->(object_definitions) { 63 | object_definitions['message'] 64 | }, 65 | 66 | sample_output: ->(connection) { 67 | room_id = get("https://#{connection['subdomain']}.hipchat.com/v2/room")['items'].last['id'] 68 | get("https://#{connection['subdomain']}.hipchat.com/v2/room/#{room_id}/history")['items'].first || {} 69 | } 70 | }, 71 | 72 | reply_to_message: { 73 | 74 | description: 'Reply to Message in Hipchat', 75 | 76 | input_fields: ->() { 77 | [ 78 | { name: 'parentMessageId', hint: "The ID of the message you are replying to", label: "Message ID", optional: false }, 79 | { name: 'message', hint: "Valid length range: 1 - 1000", optional: false, label: "Message" }, 80 | { name: 'room', hint: "Give either Room ID or Room name", optional: false, label: "Room"} 81 | ] 82 | }, 83 | 84 | execute: ->(connection, input) { 85 | post("https://#{connection['subdomain']}.hipchat.com/v2/room/#{input['room']}/reply",input) 86 | }, 87 | 88 | output_fields: ->(object_definitions) { 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/codeship_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Codeship', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'api_key', 8 | control_type: 'password', 9 | label: 'API key', 10 | optional: false, 11 | hint: 'Go to My Account->Account settings and get Api key' 12 | } 13 | ], 14 | 15 | authorization: { 16 | type: 'api_key', 17 | credentials: ->(connection) { 18 | params(api_key: connection['api_key']) 19 | } 20 | } 21 | }, 22 | 23 | object_definitions: { 24 | build: { 25 | fields: ->() { 26 | [ 27 | { name: 'id', hint: "ID of the particular build" }, 28 | { name: 'uuid' }, 29 | { name: 'status' }, 30 | { name: 'project_id', hint: "ID of the particular project"}, 31 | { name: 'branch', hint: "Name of the branch" }, 32 | { name: 'commit_id', hint: "ID of the commited build" }, 33 | { name: 'github_username', hint: "User of the github for particular build" }, 34 | { name: 'message', hint: "Message of your build" }, 35 | { name: 'started_at' }, 36 | { name: 'finished_at' }, 37 | ] 38 | } 39 | } 40 | }, 41 | 42 | test: ->(connection) { 43 | get("https://codeship.com/api/v1/projects") 44 | }, 45 | 46 | actions: { 47 | list_builds: { 48 | 49 | description: 'List Builds in Codeship', 50 | 51 | input_fields: ->() { 52 | [ 53 | { name: 'project_id', optional: false } 54 | ] 55 | }, 56 | 57 | execute: ->(connection, input) { 58 | get("https://codeship.com/api/v1/projects/#{input['project_id']}") 59 | }, 60 | 61 | output_fields: ->(object_definitions) { 62 | [ 63 | { name:'id' }, 64 | { name:'repository_name' }, 65 | { name:'repository_provider' }, 66 | { name:'uuid' }, 67 | { name: 'builds', type: :array, of: :object, properties: object_definitions['build'] } 68 | ] 69 | }, 70 | 71 | sample_output: ->(connection) { 72 | get("https://codeship.com/api/v1/projects")['projects'].first || {} 73 | }, 74 | }, 75 | 76 | restart_build: { 77 | 78 | description: 'Restart Build in Codeship', 79 | 80 | input_fields: ->() { 81 | [ 82 | { name: 'id', optional: false, hint: 'ID of the particular build', label: 'Build ID' } 83 | ] 84 | }, 85 | 86 | execute: ->(connection, input) { 87 | post("https://codeship.com/api/v1/builds/#{input['id']}/restart") 88 | }, 89 | 90 | output_fields: ->(object_definitions) { 91 | object_definitions['build'] 92 | }, 93 | 94 | sample_output: ->(connection) { 95 | get("https://codeship.com/api/v1/projects")['projects'][0]['builds'].first || {} 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/close_io_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Close.io', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'api_key', 8 | optional: false, 9 | hint: 'Profile (top right) > Settings > Your API Keys' 10 | } 11 | ], 12 | 13 | authorization: { 14 | type: 'basic_auth', 15 | 16 | # close.io uses api key only for authentication. treats apikey as username and password left blank 17 | # curl -u "{your api_key}:" "https://app.close.io/api/v1/me/" 18 | credentials: ->(connection) { 19 | user(connection['api_key']) 20 | password("") 21 | } 22 | } 23 | }, 24 | 25 | object_definitions: { 26 | lead: { 27 | fields: ->() { 28 | [ 29 | { name: 'name' }, 30 | { name: 'display_name' }, 31 | { name: 'id' }, 32 | { name: 'status_id' }, 33 | { name: 'date_updated' }, 34 | { name: 'status_label' }, 35 | { name: 'description' }, 36 | { name: 'html_url' }, 37 | { name: 'created_by' }, 38 | { name: 'organization_id' }, 39 | { name: 'url' }, 40 | { name: 'updated_by' }, 41 | { name: 'created_by_name' }, 42 | { name: 'date_created' }, 43 | { name: 'updated_by_name' } 44 | ] 45 | } 46 | } 47 | }, 48 | 49 | test: ->(connection) { 50 | get("https://app.close.io/api/v1/me/") 51 | }, 52 | 53 | actions: { 54 | 55 | get_lead_by_id: { 56 | input_fields: ->() { 57 | [ 58 | { name: "lead_id", optional: false } 59 | ] 60 | }, 61 | execute: ->(connection, input) { 62 | get("https://app.close.io/api/v1/lead/#{input['lead_id']}/") 63 | }, 64 | output_fields: ->(object_definitions) { 65 | object_definitions['lead'] 66 | } 67 | } 68 | }, 69 | 70 | triggers: { 71 | 72 | new_or_updated_lead: { 73 | 74 | type: :paging_desc, 75 | 76 | input_fields: ->() { 77 | [ 78 | { name: 'since', type: :timestamp, 79 | hint: 'Defaults to leads created after the recipe is first started' } 80 | ] 81 | }, 82 | 83 | poll: ->(connection, input, last_updated_since) { 84 | since = last_updated_since || input['since'] || Time.now 85 | 86 | # Close.io currently does not support _order_by parameter for leads, defaults to order by date_update 87 | results = get("https://app.close.io/api/v1/lead/"). 88 | params(query: "updated > #{since.to_time.iso8601}", 89 | _limit: 5) 90 | 91 | next_updated_since = results['data'].last['date_updated'] unless results['data'].length == 0 92 | 93 | { 94 | events: results['data'], 95 | next_page: next_updated_since, 96 | } 97 | }, 98 | 99 | dedup: ->(lead) { 100 | lead['id'] 101 | }, 102 | 103 | output_fields: ->(object_definitions) { 104 | object_definitions['lead'] 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/accelo_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Accelo', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'deployment', 8 | control_type: 'subdomain', 9 | url: '.accelo.com', 10 | optional: false 11 | } 12 | ], 13 | authorization: { 14 | type: 'oauth2', 15 | 16 | authorization_url: ->(connection) { 17 | "https://#{connection['deployment']}.api.accelo.com/oauth2/v0/authorize?response_type=code" 18 | }, 19 | 20 | token_url: ->(connection) { 21 | "https://#{connection['deployment']}.api.accelo.com/oauth2/v0/token" 22 | }, 23 | 24 | client_id: 'ababababab', 25 | 26 | client_secret: 'cdcdcdcdcd', 27 | 28 | credentials: ->(connection, access_token) { 29 | headers('Authorization': "Bearer #{access_token}") 30 | } 31 | } 32 | }, 33 | 34 | object_definitions: { 35 | company: { 36 | fields: ->() { 37 | [ 38 | { name: 'id', type: :integer }, 39 | { name: 'name' }, 40 | { name: 'website' }, 41 | { name: 'standing' }, 42 | { name: 'status' }, 43 | { name: 'phone' }, 44 | { name: 'fax' }, 45 | { name: 'date_created', type: :integer }, 46 | { name: 'date_modified', type: :integer }, 47 | { name: 'comments' } 48 | ] 49 | }, 50 | } 51 | }, 52 | 53 | actions: { 54 | search_company: { 55 | input_fields: ->(object_definitions) { 56 | [ 57 | { name: 'company_name' } 58 | ] 59 | }, 60 | 61 | execute: ->(connection, input) { 62 | { 63 | 'companies': get("https://#{connection['deployment']}.api.accelo.com/api/v0/companies.json"). 64 | params(_search: input['company_name'])['response'] 65 | } 66 | }, 67 | 68 | output_fields: ->(object_definitions) { 69 | [ 70 | { 71 | name: 'companies', 72 | type: :array, of: :object, 73 | properties: object_definitions['company'] 74 | } 75 | ] 76 | } 77 | } 78 | }, 79 | 80 | triggers: { 81 | new_company: { 82 | 83 | type: :paging_desc, 84 | 85 | input_fields: ->() { 86 | [ 87 | { name: 'created_after', type: :timestamp, optional: false } 88 | ] 89 | }, 90 | poll: ->(connection, input, last_created_since) { 91 | created_since = (last_created_since || input['created_after'] || Time.now).to_i 92 | 93 | companies = get("https://#{connection['deployment']}.api.accelo.com/api/v0/companies.json"). 94 | params(_filters: "date_created_after(#{created_since})", 95 | _limit: 2, 96 | _fields: 'date_created,website,status,phone,fax')['response'] 97 | 98 | next_created_since = companies.last['date_created'] unless companies.blank? 99 | 100 | { 101 | events: companies, 102 | next_page: next_created_since 103 | } 104 | }, 105 | 106 | dedup: ->(company) { 107 | company['id'] 108 | }, 109 | 110 | output_fields: ->(object_definitions) { 111 | object_definitions['company'] 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/tsheets_connector.rb: -------------------------------------------------------------------------------- 1 | # Adds operations missing from the standard adapter. 2 | { 3 | title: "TSheets(custom)", 4 | 5 | connection: { 6 | fields: [ 7 | { 8 | name: "subdomain", 9 | control_type: "subdomain", 10 | url: ".tsheets.com", 11 | label: "TSheets subdomain", 12 | optional: false, 13 | hint: "Your TSheets sub domain name as found in your TSheets URL" 14 | }, 15 | { 16 | name: "api_token", 17 | label: "API token", 18 | control_type: "password", 19 | optional: false, 20 | }, 21 | ], 22 | 23 | authorization: { 24 | type: "custom_auth", 25 | 26 | credentials: lambda do |connection| 27 | headers("Authorization": "Bearer #{connection['api_token']}") 28 | end 29 | } 30 | }, 31 | 32 | test: lambda do |connection| 33 | get("https://#{connection['subdomain']}.tsheets.com/api/v1/timesheets"). 34 | params(per_page: 1)["results"]["timesheets"].values 35 | end, 36 | 37 | actions: { 38 | query_timesheets: { 39 | description: 'Query timesheets in TSheets(custom)', 40 | 41 | input_fields: lambda do 42 | [ 43 | { name: "start_date", type: :timestamp, optional: false }, 44 | { name: "end_date", type: :timestamp, optional: false } 45 | ] 46 | end, 47 | 48 | execute: lambda do |connection, input| 49 | { 50 | timesheets: get("https://#{connection['subdomain']}.tsheets.com/api/v1/timesheets"). 51 | params( 52 | start_date: input["start_date"].to_date.to_s, 53 | end_date: input["end_date"].to_date.to_s, 54 | per_page: 100 55 | )["results"]["timesheets"].values 56 | } 57 | end, 58 | 59 | output_fields: lambda do 60 | [ 61 | { 62 | name: "timesheets", 63 | type: "array", 64 | of: "object", 65 | properties: [ 66 | { name: "id", type: "integer" }, 67 | { name: "user_id", type: "integer" }, 68 | { name: "jobcode_id", type: "integer" }, 69 | { name: "start", type: "timestamp" }, 70 | { name: "end", type: "timestamp" }, 71 | { name: "duration", type: "integer" }, 72 | { name: "date", type: "timestamp" }, 73 | { name: "tz", type: "integer" }, 74 | { name: "tz_str", type: "string" }, 75 | { name: "type", type: "string" }, 76 | { name: "location", type: "string" }, 77 | { name: "on_the_clock", type: "boolean" }, 78 | { name: "locked", type: "integer" }, 79 | { name: "notes", type: "string" }, 80 | { 81 | name: "customfields", 82 | type: "object", 83 | properties: [ 84 | # Add your custom fields here 85 | # { name: "71138", label: "location" }, 86 | ] 87 | } 88 | ] 89 | } 90 | ] 91 | end, 92 | 93 | sample_output: lambda do |connection| 94 | { 95 | timesheets: get("https://#{connection['subdomain']}.tsheets.com/api/v1/timesheets"). 96 | params(per_page: 1)["results"]["timesheets"].values 97 | } 98 | end 99 | }, 100 | }, 101 | } 102 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/infobip_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Infobip', 3 | connection: { 4 | fields: [ 5 | { 6 | name: 'api_key', 7 | control_type: 'password', 8 | optional: false 9 | } 10 | ], 11 | authorization: { 12 | type: 'custom_auth', 13 | credentials: ->(connection) { 14 | headers(:Authorization => "App #{connection['api_key']}", 15 | :'User-Agent' => 'Workato') 16 | } 17 | } 18 | }, 19 | test: ->(connection) { 20 | get('https://api.infobip.com/sms/1/logs').params(limit: 1) 21 | }, 22 | object_definitions: { 23 | send_sms_request: { 24 | fields: ->() { 25 | [ 26 | {name: 'from'}, 27 | {name: 'to', optional: false, control_type: 'phone'}, 28 | {name: 'text'} 29 | ] 30 | } 31 | }, 32 | sent_sms_info: { 33 | fields: ->() { 34 | [ 35 | {name: 'to'}, 36 | {name: 'status', type: :object, properties: [ 37 | {name: 'groupId', type: :integer}, 38 | {name: 'groupName'}, 39 | {name: 'id', type: :integer}, 40 | {name: 'name'}, 41 | {name: 'description'} 42 | ]}, 43 | {name: 'smsCount', type: :integer}, 44 | {name: 'messageId'} 45 | ] 46 | } 47 | }, 48 | received_sms_info: { 49 | fields: ->() { 50 | [ 51 | {name: 'messageId'}, 52 | {name: 'from'}, 53 | {name: 'to'}, 54 | {name: 'text'}, 55 | {name: 'cleanText'}, 56 | {name: 'keyword'}, 57 | {name: 'smsCount', type: :integer}, 58 | {name: 'receivedAt'} 59 | ] 60 | } 61 | } 62 | }, 63 | actions: { 64 | send_sms: { 65 | input_fields: ->(object_definitions) { 66 | object_definitions['send_sms_request'] 67 | }, 68 | execute: ->(connection, input) { 69 | post('https://api.infobip.com/sms/1/text/single', input)['send_sms_request'] 70 | }, 71 | output_fields: ->(object_definitions) {[ 72 | { name: 'messages', type: :array, of: :object, properties: object_definitions['sent_sms_info'] } 73 | ]} 74 | } 75 | }, 76 | triggers: { 77 | sms_received: { 78 | poll: ->(connection, input, last_received_at) { 79 | date_time_format = '%Y-%m-%dT%H:%M:%S.%L+00:00' 80 | received_since = last_received_at || (Time.now - 5 * 60).utc.strftime(date_time_format) 81 | query_param = received_since.gsub(':', '%3A').gsub('+', '%2B') 82 | 83 | received_messages = get("https://api.infobip.com/sms/1/inbox/logs?receivedSince=#{query_param}") 84 | 85 | received_sms_info = received_messages['results'] 86 | last_received_at = received_sms_info.length == 0 ? received_since : received_sms_info[0]['receivedAt'] 87 | { 88 | events: received_sms_info.reverse, 89 | can_poll_more: false, 90 | next_poll: last_received_at[0, last_received_at.length-2] + ':' + last_received_at[last_received_at.length-2, 2] 91 | } 92 | }, 93 | dedup: ->(received_sms_info) { 94 | received_sms_info['messageId'] 95 | }, 96 | output_fields: ->(object_definitions) { 97 | object_definitions['received_sms_info'] 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /custom_connectors/basic_auth/docparser_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Docparser", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "api_key", 8 | label: "Docparser Account API Key", 9 | optional: false, 10 | hint: "Enter your secret Docparser API key. You can find your API key inside your Docparser account or in the settings of your Document Parser." 11 | } 12 | ], 13 | 14 | authorization: { 15 | type: "basic_auth", 16 | credentials: lambda do |connection| 17 | user(connection['api_key']) 18 | password("") 19 | end 20 | } 21 | }, 22 | 23 | pick_lists: { 24 | parsers: lambda do |connection| 25 | get("https://api.docparser.com/v1/parsers"). 26 | map { |parser| [parser["label"], parser["id"]] } 27 | end 28 | }, 29 | 30 | object_definitions: { 31 | document: { 32 | fields: lambda do 33 | [ 34 | { 35 | name: "id", 36 | label: "Document Id" 37 | } 38 | ] 39 | end 40 | }, 41 | parsed_data: { 42 | fields: lambda do |_connection, config_fields| 43 | get("https://api.docparser.com/v1/results/#{config_fields['parser_id']}/schema") 44 | end 45 | } 46 | }, 47 | 48 | test: lambda do |_connection| 49 | get("https://api.docparser.com/v1/ping")["msg"].present? 50 | end, 51 | 52 | triggers: { 53 | parsed_data: { 54 | config_fields: [ 55 | { 56 | name: "parser_id", 57 | label: "Document Parser", 58 | control_type: :select, 59 | pick_list: "parsers", 60 | optional: false 61 | } 62 | ], 63 | poll: lambda do |_connection, input| 64 | parsed_data = get("https://api.docparser.com/v1/results/#{input['parser_id']}") 65 | { 66 | events: parsed_data 67 | } 68 | end, 69 | webhook_subscribe: lambda do |webhook_url, _connection, input, recipe_id| 70 | post("https://api.docparser.com/v1/webhook/subscribe/#{input['parser_id']}/workato", 71 | target_url: webhook_url, 72 | webhook_token: recipe_id) 73 | end, 74 | webhook_unsubscribe: lambda do |webhook| 75 | post("https://api.docparser.com/v1/webhook/unsubscribe/#{webhook['parser_id']}/workato", 76 | id: webhook['webhook_id']) 77 | end, 78 | webhook_notification: lambda do |_input, payload| 79 | payload 80 | end, 81 | dedup: lambda do |parsed_data| 82 | parsed_data["id"] 83 | end, 84 | output_fields: lambda do |object_definitions| 85 | object_definitions["parsed_data"] 86 | end 87 | } 88 | }, 89 | 90 | actions: { 91 | fetch_document_from_url: { 92 | input_fields: lambda do 93 | [ 94 | { 95 | name: "url", 96 | label: "Source URL", 97 | hint: "Upload file from this URL", 98 | optional: false 99 | }, 100 | { 101 | name: "parser_id", 102 | label: "Document Parser", 103 | hint: "The Document Parser the file gets imported to", 104 | control_type: :select, 105 | pick_list: "parsers", 106 | optional: false 107 | }, 108 | { 109 | name: "remote_id", 110 | label: "Your Document ID", 111 | hint: "Use this field to pipe your own document ID through the Docparser process.", 112 | optional: true 113 | } 114 | ] 115 | end, 116 | execute: lambda do |_connection, input| 117 | post("https://api.docparser.com/v1/document/fetch/#{input['parser_id']}?url=#{input['url']}&remote_id=#{input['remote_id']}") 118 | end, 119 | output_fields: lambda do |object_definitions| 120 | object_definitions["document"] 121 | end 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/webmerge_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "WebMerge", 3 | 4 | # HTTP basic auth example. 5 | connection: { 6 | fields: [ 7 | { 8 | name: "api_key", 9 | optional: false, 10 | hint: "Your WebMerge API Key" 11 | }, 12 | { 13 | name: "api_secret", 14 | control_type: "password", 15 | label: "Your WebMerge API Secret" 16 | } 17 | ], 18 | 19 | authorization: { 20 | type: "basic_auth", 21 | 22 | # Basic auth credentials are just the username and password 23 | credentials: lambda do |connection| 24 | user(connection["api_key"]) 25 | password(connection["api_secret"]) 26 | end 27 | } 28 | }, 29 | 30 | object_definitions: { 31 | 32 | document: { 33 | 34 | fields: lambda do |_connection, config_fields| 35 | # here, you can use the input from the input from the user 36 | return [] if config_fields.blank? 37 | d = config_fields["document_id"].split("|") 38 | fields = get("https://www.webmerge.me/api/documents/#{d.first}/fields"). 39 | map { |field| field.slice("name") } 40 | end 41 | }, 42 | 43 | route: { 44 | 45 | fields: lambda do |_connection, config_fields| 46 | # here, you can use the input from the input from the user 47 | return [] if config_fields.blank? 48 | r = config_fields["route_id"].split("|") 49 | fields = get("https://www.webmerge.me/api/routes/#{r.first}/fields"). 50 | map { |field| field.slice("name") } 51 | end 52 | } 53 | }, 54 | 55 | test: lambda do |_connection| 56 | get("https://www.webmerge.me/api/account") 57 | end, 58 | 59 | actions: { 60 | merge_document: { 61 | config_fields: [ 62 | # this field shows up first in recipe as a picklist of documents to use 63 | { 64 | name: "document_id", 65 | label: "Document", 66 | control_type: :select, 67 | pick_list: "documents", 68 | optional: false 69 | } 70 | ], 71 | 72 | input_fields: lambda do |object_definition| 73 | object_definition["document"] 74 | end, 75 | 76 | execute: lambda do |_connection, input| 77 | d = input["document_id"].split("|") 78 | post(d.last.to_s, input) 79 | end, 80 | 81 | output_fields: lambda do |_object_definition| 82 | [ 83 | { 84 | name: "success" 85 | } 86 | ] 87 | end 88 | }, 89 | 90 | merge_route: { 91 | config_fields: [ 92 | # this field shows up first in recipe as a picklist of routes to use 93 | { 94 | name: "route_id", 95 | label: "Route", 96 | control_type: :select, 97 | pick_list: "routes", 98 | optional: false 99 | } 100 | ], 101 | 102 | input_fields: lambda do |object_definition| 103 | object_definition["route"] 104 | end, 105 | 106 | execute: lambda do |_connection, input| 107 | r = input["route_id"].split("|") 108 | post(r.last.to_s, input) 109 | end, 110 | 111 | output_fields: lambda do |_object_definition| 112 | [ 113 | { 114 | name: "success" 115 | } 116 | ] 117 | end 118 | } 119 | }, 120 | 121 | pick_lists: { 122 | documents: lambda do |_connection| 123 | get("https://www.webmerge.me/api/documents").map do |document| 124 | [document["name"], document["id"] + "|" + document["url"]] 125 | end 126 | end, 127 | 128 | routes: lambda do |_connection| 129 | get("https://www.webmerge.me/api/routes").map do |route| 130 | [route["name"], route["id"] + "|" + route["url"]] 131 | end 132 | end 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/rightsignature_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "RightSignature", 3 | 4 | connection: { 5 | fields: [{ 6 | name: "api_key", 7 | label: "Secure token", 8 | hint: "You may find the secure token here", 10 | control_type: "password", 11 | optional: false 12 | }], 13 | 14 | authorization: { 15 | type: "api_key", 16 | 17 | apply: ->(connection) { headers(api_token: connection["api_key"]) } 18 | }, 19 | 20 | base_uri: ->(_connection) { "https://rightsignature.com" } 21 | }, 22 | 23 | object_definitions: { 24 | document: { 25 | fields: lambda { 26 | [ 27 | { name: "guid", label: "Document ID" }, 28 | { name: "created_at", type: "timestamp" }, 29 | { name: "completed_at", type: "timestamp" }, 30 | { name: "last_activity_at", type: "timestamp" }, 31 | { name: "expires_on", type: "timestamp" }, 32 | { name: "is_trashed", control_type: "checkbox", type: "boolean" }, 33 | { name: "size", type: "integer" }, 34 | { name: "content_type" }, 35 | { name: "original_filename" }, 36 | { name: "signed_pdf_checksum" }, 37 | { name: "subject" }, 38 | { name: "message" }, 39 | { name: "processing_state" }, 40 | { name: "merge_state" }, 41 | { name: "state" }, 42 | { name: "callback_location" }, 43 | { name: "tags" }, 44 | { 45 | name: "recipients", 46 | type: "array", 47 | of: "object", 48 | properties: [ 49 | { name: "name" }, 50 | { name: "email", control_type: "email" }, 51 | { name: "must_sign", control_type: "checkbox", type: "boolean" }, 52 | { name: "document_role_id" }, 53 | { name: "role_id" }, 54 | { name: "state" }, 55 | { name: "is_sender", control_type: "checkbox", type: "boolean" }, 56 | { name: "viewed_at", type: "timestamp" }, 57 | { name: "completed_at", type: "timestamp" } 58 | ] 59 | }, 60 | { 61 | name: "audit_trails", 62 | type: "array", 63 | of: "object", 64 | properties: [ 65 | { name: "timestamp", type: "timestamp" }, 66 | { name: "keyword" }, 67 | { name: "message" } 68 | ] 69 | }, 70 | { 71 | name: "pages", 72 | type: "array", 73 | of: "object", 74 | properties: [ 75 | { name: "page_number" }, 76 | { name: "original_template_guid" }, 77 | { name: "original_template_filename" } 78 | ] 79 | }, 80 | { name: "original_url" }, 81 | { name: "pdf_url" }, 82 | { name: "thumbnail_url" }, 83 | { name: "large_url" }, 84 | { name: "signed_pdf_url" } 85 | ] 86 | } 87 | } 88 | }, 89 | 90 | test: ->(_connection) { get("/api/documents.json") }, 91 | 92 | actions: { 93 | get_document_details: { 94 | subtitle: "Get document details by ID", 95 | description: "Get document details " \ 96 | "by ID in RightSignature", 97 | 98 | input_fields: lambda { |object_definitions| 99 | object_definitions["document"].only("guid").required("guid") 100 | }, 101 | 102 | execute: lambda { |_connection, input| 103 | get("/api/documents/#{input['guid']}.json")["document"] || {} 104 | }, 105 | 106 | output_fields: ->(object_definitions) { object_definitions["document"] }, 107 | 108 | sample_output: lambda { |_connection| 109 | get("/api/documents.json").dig("page", "documents", 0) || {} 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/watson_tone_analyzer_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Watson Tone Analyzer', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'username', optional: true, 8 | hint: 'Your username; leave empty if using API key below' 9 | }, 10 | { 11 | name: 'password', control_type: 'password', 12 | label: 'Password or personal API key' 13 | } 14 | ], 15 | 16 | authorization: { 17 | type: 'basic_auth', 18 | 19 | credentials: ->(connection) { 20 | user(connection['username']) 21 | password(connection['password']) 22 | } 23 | } 24 | }, 25 | 26 | test: ->(connection) { 27 | post("https://gateway.watsonplatform.net/tone-analyzer-beta/api/v3/tone?version=2016-02-11"). 28 | payload(text: "this is it") 29 | }, 30 | 31 | object_definitions: { 32 | tone: { 33 | fields: ->(connection) { 34 | [ 35 | { name: 'score', type: :decimal }, 36 | { name: 'tone_id' }, 37 | { name: 'tone_name' } 38 | ] 39 | } 40 | } 41 | }, 42 | 43 | actions: { 44 | analyze_content: { 45 | input_fields: ->(object_definitions) { 46 | [ 47 | { name: 'text', optional: false } 48 | ] 49 | }, 50 | 51 | execute: ->(connection, input) { 52 | response = post("https://gateway.watsonplatform.net/tone-analyzer-beta/api/v3/tone?version=2016-02-11"). 53 | payload(text: input['text'])['document_tone'] 54 | 55 | tones = {} 56 | response['tone_categories'].each do |cat| 57 | if ["emotion_tone", "writing_tone", "social_tone"].include?(cat['category_id']) 58 | tones[cat['category_id']] = cat 59 | end 60 | end 61 | 62 | if tones['emotion_tone'].present? && tones['emotion_tone']['tones'].present? 63 | dominant_emotion_tone = tones['emotion_tone']['tones']. 64 | sort { |a,b| b['score'] <=> a['score'] }. 65 | first 66 | end 67 | 68 | if tones['writing_tone'].present? && tones['writing_tone']['tones'].present? 69 | dominant_writing_tone = tones['writing_tone']['tones']. 70 | sort { |a,b| b['score'] <=> a['score'] }. 71 | first 72 | end 73 | 74 | if tones['social_tone'].present? && tones['social_tone']['tones'].present? 75 | dominant_social_tone = tones['social_tone']['tones']. 76 | sort { |a,b| b['score'] <=> a['score'] }. 77 | first 78 | end 79 | 80 | { 81 | 'dominant_emotion_tone': dominant_emotion_tone, 82 | 'emotion_tones': tones['emotion_tone'], 83 | 'dominant_writing_tone': dominant_writing_tone, 84 | 'writing_tones': tones['writing_tone'], 85 | 'dominant_social_tone': dominant_social_tone, 86 | 'social_tones': tones['social_tone'], 87 | } 88 | }, 89 | 90 | output_fields: ->(object_definitions) { 91 | [ 92 | { 93 | name: 'dominant_emotion_tone', type: :object, 94 | properties: object_definitions['tone'] 95 | }, 96 | { 97 | name: 'emotion_tones', type: :array, 98 | of: :object, properties: object_definitions['tone'] 99 | }, 100 | { 101 | name: 'dominant_writing_tone', type: :object, 102 | properties: object_definitions['tone'] 103 | }, 104 | { 105 | name: 'writing_tones', type: :array, 106 | of: :object, properties: object_definitions['tone'] 107 | }, 108 | { 109 | name: 'dominant_social_tone', type: :object, 110 | properties: object_definitions['tone'] }, 111 | { 112 | name: 'social_tones', type: :array, 113 | of: :object, properties: object_definitions['tone'] 114 | } 115 | ] 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/linenotify.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Line Notify', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "client_id", 8 | label: "Client ID", 9 | optional: false, 10 | hint: "You can find your client ID " \ 11 | "here" 13 | }, 14 | { 15 | name: "client_secret", 16 | label: "Client secret", 17 | control_type: "password", 18 | optional: false, 19 | hint: "You can find your client secret " \ 20 | "here" 22 | } 23 | ], 24 | 25 | authorization: { 26 | type: "oauth2", 27 | 28 | authorization_url: lambda do |connection| 29 | "https://notify-bot.line.me/oauth/authorize?" \ 30 | "client_id=#{connection['client_id']}&response_type=code&scope=notify" 31 | end, 32 | 33 | acquire: lambda do |connection, auth_code| 34 | response = post("https://notify-bot.line.me/oauth/token"). 35 | payload(code: auth_code, 36 | client_id: connection["client_id"], 37 | client_secret: connection["client_secret"], 38 | redirect_uri: "https://www.workato.com/oauth/callback", 39 | grant_type: "authorization_code"). 40 | request_format_www_form_urlencoded 41 | 42 | [response, nil, nil] 43 | end, 44 | 45 | refresh_on: [403], 46 | 47 | refresh: lambda do |connection, refresh_token| 48 | post("https://notify-bot.line.me/oauth/token"). 49 | payload(grant_type: "refresh_token", 50 | client_id: connection["client_id"], 51 | client_secret: connection["client_secret"], 52 | refresh_token: refresh_token, 53 | redirect_uri: "https://www.workato.com/oauth/callback"). 54 | request_format_www_form_urlencoded 55 | end, 56 | 57 | apply: lambda do |_connection, access_token| 58 | headers("Authorization": "Bearer #{access_token}") 59 | end 60 | }, 61 | 62 | base_uri: lambda do 63 | "https://notify-bot.line.me" 64 | end 65 | }, 66 | object_definitions: { 67 | response: { 68 | fields: lambda do 69 | [ 70 | { name: "status", type: "Integer" }, 71 | { name: "message" } 72 | ] 73 | end 74 | } 75 | }, 76 | 77 | actions: { 78 | notify: { 79 | description: "Send notification in " \ 80 | "Line Notify", 81 | title_hint: "Sends notifications to users or groups that" \ 82 | " are related to an access token.", 83 | input_fields: lambda do 84 | [ 85 | { name: "message", label: "Message", optional: false }, 86 | { name: "stickerId", label: "Sticker ID", type: "number", 87 | hint: "You can find sticker list " \ 88 | "here" 90 | }, 91 | { name: "stickerPackageId", label: "Sticker package ID", type: "number", 92 | hint: "You can find sticker package list " \ 93 | "here" 95 | } 96 | ] 97 | end, 98 | 99 | execute: lambda do |_connection, input| 100 | post("https://notify-api.line.me/api/notify"). 101 | payload(input). 102 | request_format_multipart_form 103 | end, 104 | 105 | output_fields: lambda do |object_definitions| 106 | object_definitions['response'] 107 | end, 108 | sample_output: lambda do |_| 109 | { "status": 200, "message": "ok" } 110 | end 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/lo_jack_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'LoJack', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'account', 8 | }, 9 | { 10 | name: 'username', 11 | }, 12 | { 13 | name: 'password', 14 | control_type: 'password', 15 | } 16 | ], 17 | 18 | authorization: { 19 | type: 'custom_auth', 20 | 21 | credentials: ->(connection) { 22 | params(connection.merge(lang: 'en', outputformat: 'json')) 23 | # LoJack API expects a non-standard Accept header 24 | headers(Accept: "*/*") 25 | } 26 | } 27 | }, 28 | 29 | object_definitions: { 30 | }, 31 | 32 | test: ->(connection) { 33 | get("https://csv.business.tomtom.com/extern?action=showObjectReportExtern&filterstring=XXXXXXXX") 34 | }, 35 | 36 | actions: { 37 | get_object_reports: { 38 | # not used 39 | input_fields: ->() { 40 | [ 41 | { 42 | name: "filterstring", 43 | label: "Filter String", 44 | optional: true, 45 | hint: "An arbitrary pattern that is located in the object data." 46 | }, 47 | 48 | { 49 | name: "objectgroupname", 50 | label: "Object Group Name", 51 | optional: true, 52 | hint: "A name of an object group." 53 | }, 54 | 55 | { 56 | name: "objectno", 57 | label: "Object No", 58 | optional: true, 59 | hint: "Identifying number of an object. Unique within an account, case-sensitive. Can be used alternatively to Object UID" 60 | }, 61 | 62 | { 63 | name: "objectuid", 64 | label: "Object UID", 65 | optional: true, 66 | hint: "Identifying number of an object. Unique within an account, case-sensitive. Can be used alternatively to Object No" 67 | }, 68 | ] 69 | }, 70 | 71 | execute: ->(connection, input) { 72 | reply = get("https://csv.business.tomtom.com/extern?action=showObjectReportExtern", input) 73 | { 74 | reports: (reply[0] ? reply : []) # reply is an array when successful otherwise an object 75 | } 76 | }, 77 | 78 | output_fields: ->(object_definitions) { 79 | [ 80 | { 81 | name: 'reports', 82 | type: 'array', 83 | of: 'object', 84 | properties: [ 85 | { name: "objectno" }, 86 | { name: "objectname" }, 87 | { name: "objectclassname" }, 88 | { name: "objecttype" }, 89 | { name: "description" }, 90 | { name: "lastmsgid", type: "integer" }, 91 | { name: "deleted", type: "boolean" }, 92 | { name: "msgtime", type: "date_time" }, 93 | { name: "longitude" }, 94 | { name: "latitude" }, 95 | { name: "postext" }, 96 | { name: "postext_short" }, 97 | { name: "status" }, 98 | { name: "driver" }, 99 | { name: "driver_currentworkstate", type: "integer" }, 100 | { name: "codriver_currentworkstate", type: "integer" }, 101 | { name: "odometer", type: "integer" }, 102 | { name: "ignition", type: "integer" }, 103 | { name: "dest_distance", type: "integer" }, 104 | { name: "tripmode", type: "integer" }, 105 | { name: "standstill", type: "integer" }, 106 | { name: "pndconn", type: "integer" }, 107 | { name: "ignition_time", type: "date_time" }, 108 | { name: "drivername" }, 109 | { name: "pos_time", type: "date_time" }, 110 | { name: "longitude_mdeg", type: "integer" }, 111 | { name: "latitude_mdeg", type: "integer" }, 112 | { name: "objectuid" }, 113 | { name: "driveruid" } 114 | ] 115 | } 116 | ] 117 | } 118 | } 119 | }, 120 | } 121 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/unbounce_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Unbounce', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'api_key', 8 | optional: false, 9 | control_type: 'password', 10 | hint: 'Profile (top right) > Manage Account > API Keys' 11 | }, 12 | { 13 | name: 'page_id', 14 | optional: false, 15 | hint: "ID of page to connect, found at the end of respective page's URL" 16 | } 17 | ], 18 | 19 | authorization: { 20 | type: 'basic_auth', 21 | # unbounce uses api key only for authentication. treats apikey as username and password left blank 22 | # curl -u "{your api_key}:" "https://api.unbounce.com" 23 | credentials: ->(connection) { 24 | user(connection['api_key']) 25 | password("") 26 | } 27 | } 28 | }, 29 | 30 | test: ->(connection) { 31 | get("https://api.unbounce.com/pages/#{connection['page_id']}") 32 | }, 33 | 34 | object_definitions: { 35 | 36 | form: { 37 | fields: ->(connection) { 38 | get("https://api.unbounce.com/pages/#{connection['page_id']}/form_fields")['formFields']. 39 | map { |field| { name: field['id'] } } 40 | } 41 | }, 42 | 43 | page: { 44 | fields: ->(connection) { 45 | [ 46 | { name:'subAccountId', type: :integer }, 47 | { name:'integrationsCount', type: :integer }, 48 | { name:'integrationsErrorsCount', type: :integer }, 49 | { name:'id' }, 50 | { name:'createdAt', type: :datetime }, 51 | { name:'lastPublishedAt', type: :datetime }, 52 | { name:'name' }, 53 | { name:'state' }, 54 | { name:'url', control_type: 'url' }, 55 | { name:'variantsCount', type: :integer }, 56 | { name:'domain', control_type: 'url' }, 57 | { name:'fullUrl', control_type: 'url' }, 58 | { name: 'metaData', type: :object, properties: [ 59 | { name: 'documentation', control_type: 'url' }, 60 | { name: 'location', control_type: 'url' }, 61 | { name: 'related', type: :object, properties: [ 62 | { name: 'leads', control_type: 'url' }, 63 | { name: 'subAccount', control_type: 'url' } 64 | ]} 65 | ]} 66 | ] 67 | } 68 | } 69 | }, 70 | 71 | actions: { 72 | 73 | get_page_details: { 74 | input_fields: ->() {}, 75 | execute: ->(connection,input) { 76 | get("https://api.unbounce.com/pages/#{connection['page_id']}") 77 | }, 78 | output_fields: ->(object_definitions) { 79 | object_definitions['page'] 80 | } 81 | } 82 | }, 83 | 84 | triggers: { 85 | 86 | new_submission: { 87 | 88 | type: :paging_desc, 89 | 90 | input_fields: ->() { 91 | [ 92 | { name: 'since', type: :timestamp, 93 | hint: 'Defaults to submissions after the recipe is first started' } 94 | ] 95 | }, 96 | 97 | poll: ->(connection, input, last_created_since) { 98 | since = last_created_since || input['since'] || Time.now 99 | 100 | leads = get("https://api.unbounce.com/pages/#{connection['page_id']}/leads"). 101 | params(from: since.to_time.iso8601, 102 | sort_order: :desc, # API sorts by creation date 103 | limit: 10)['leads'] 104 | 105 | leads.map do |lead| 106 | lead['formData'] = lead['formData'].map do |k,v| 107 | { k => v.first } 108 | end. 109 | inject(:merge) 110 | lead 111 | end 112 | 113 | next_updated_since = leads.first['createdAt'] unless leads.length == 0 114 | 115 | { 116 | events: leads, 117 | next_page: next_updated_since 118 | } 119 | }, 120 | 121 | output_fields: ->(object_definitions) { 122 | [ 123 | { name: 'id', type: :integer }, 124 | { name: 'formData', type: :object, properties: object_definitions['form'] } 125 | ] 126 | } 127 | } 128 | }, 129 | } 130 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/safetyculture_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'SafetyCulture', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'access_token', 8 | control_type: 'password' 9 | } 10 | ], 11 | 12 | authorization: { 13 | type: 'custom_auth', 14 | 15 | # Safety Culture uses non standard OAuth 2.0 type authentication. Workaround is to use access token generates in the UI 16 | credentials: ->(connection) { 17 | headers('Authorization': "Bearer #{connection['access_token']}") 18 | } 19 | } 20 | }, 21 | 22 | test: ->(connection) { 23 | get("https://api.safetyculture.io/audits/search") 24 | }, 25 | 26 | object_definitions: { 27 | audit: { 28 | fields: ->() { 29 | [ 30 | { name: 'template_id' }, 31 | { name: 'audit_id' }, 32 | { name: 'created_at', type: :datetime }, 33 | { name: 'modified_at', type: :datetime }, 34 | { name: 'audit_data', type: :object, properties: [ 35 | { name: 'name' }, 36 | { name: 'score', type: :integer }, 37 | { name: 'total_score', type: :integer }, 38 | { name: 'score_percentage', type: :integer }, 39 | { name: 'duration', type: :integer }, 40 | { name: 'date_completed', type: :datetime }, 41 | { name: 'date_modified', type: :datetime }, 42 | { name: 'date_started', type: :datetime }, 43 | { name: 'authorship', type: :object, properties: [ 44 | { name: 'devise_id' }, 45 | { name: 'owner' }, 46 | { name: 'author' }, 47 | ]} 48 | ]}, 49 | { name: 'template_data', type: :object, properties: [ 50 | { name: 'authorship', type: :object, properties: [ 51 | { name: 'devise_id' }, 52 | { name: 'owner' }, 53 | { name: 'author' }, 54 | ]}, 55 | { name: 'metadata', type: :object, properties: [ 56 | { name: 'description' }, 57 | { name: 'name' } 58 | ]} 59 | ]}, 60 | { name: 'header_items',type: :array, of: :object, properties: [ 61 | { name: 'item_id' }, 62 | { name: 'label' }, 63 | { name: 'type' } 64 | ]}, 65 | { name: 'items',type: :array, of: :object, properties: [ 66 | { name: 'item_id' }, 67 | { name: 'parent_id' }, 68 | { name: 'label' }, 69 | { name: 'type' }, 70 | { name: 'scoring', type: :object, properties: [ 71 | { name: 'combined_score', type: :integer }, 72 | { name: 'combined_max_score', type: :integer }, 73 | { name: 'combined_score_percentage', type: :integer }, 74 | ]} 75 | ]} 76 | ] 77 | } 78 | } 79 | }, 80 | 81 | actions: { 82 | get_audit_data: { 83 | input_fields: ->() { 84 | [ 85 | { name: 'audit_id' } 86 | ] 87 | }, 88 | execute: ->(connection,input) { 89 | get("https://api.safetyculture.io/audits/#{input['audit_id']}") 90 | }, 91 | output_fields: ->(object_definitions) { 92 | object_definitions['audit'] 93 | } 94 | } 95 | }, 96 | 97 | triggers: { 98 | 99 | new_or_updated_audit: { 100 | 101 | type: :paging_desc, 102 | 103 | input_fields: ->() { 104 | [ 105 | { name: 'since', type: :timestamp, optional: false, 106 | hint: 'Defaults to audits modified after the recipe is first started' }, 107 | { name: 'completed', control_type: 'select', optional: false, 108 | pick_list: [ 109 | ["Yes", "true"], 110 | ["No", "false"], 111 | ["Both", "both"]] 112 | } 113 | ] 114 | }, 115 | 116 | poll: ->(connection, input, last_updated_since) { 117 | updated_since = last_updated_since || input['since'] || Time.now 118 | 119 | response = get("https://api.safetyculture.io/audits/search"). 120 | params(modified_after: updated_since.to_time.utc.iso8601, 121 | order: :desc, 122 | limit: 2, 123 | completed: input['completed']) 124 | 125 | audits = response['audits'].map do |audit| 126 | get("https://api.safetyculture.io/audits/#{audit['audit_id']}") 127 | end 128 | 129 | next_updated_since = response['audits'].last['modified_at'] unless response['count'] == 0 130 | 131 | { 132 | events: audits, 133 | next_page: next_updated_since, 134 | } 135 | }, 136 | 137 | dedup: ->(audit) { 138 | audit['audit_id'] 139 | }, 140 | 141 | output_fields: ->(object_definitions) { 142 | object_definitions['audit'] 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/convert_api_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "ConvertAPI", 3 | 4 | connection: { 5 | fields: [{ 6 | name: "secret", 7 | hint: "Secret can be found in ConvertAPI " \ 8 | "Control Panel.", 9 | optional: false 10 | }], 11 | 12 | base_uri: ->(_connection) { "https://v2.convertapi.com" }, 13 | 14 | authorization: { 15 | type: "custom_auth", 16 | 17 | acquire: lambda do |connection| 18 | { 19 | token: post("/token/create"). 20 | params("Secret" => connection["secret"], 21 | "RequestCount" => 100, 22 | "Lifetime" => 1000). 23 | dig("Tokens", 0, "Id") 24 | } 25 | end, 26 | 27 | refresh_on: [401, /"Code"\s*\:\s*4011/], 28 | 29 | apply: ->(connection) { params("Token" => connection["token"]) } 30 | } 31 | }, 32 | 33 | test: ->(connection) { get("/user", "Secret" => connection["secret"]) }, 34 | 35 | object_definitions: { 36 | file_input: { 37 | fields: lambda do |_connection, _config_fields| 38 | [ 39 | { 40 | name: "File", 41 | label: "File URL", 42 | control_type: "url" 43 | }, 44 | { name: "StoreFile", control_type: "checkbox", type: "boolean" }, 45 | { 46 | name: "FileName", 47 | hint: "Converted output file name without extension. " \ 48 | "The extension will be added automatically." 49 | }, 50 | { 51 | name: "Timeout", 52 | hint: "Default: 300. Valid value ranges from 10 to 1200", 53 | type: "integer" 54 | }, 55 | { 56 | name: "PdfResolution", 57 | label: "PDF Resolution", 58 | hint: "Default: 300. Valid value ranges from 10 to 2400", 59 | type: "integer" 60 | } 61 | ] 62 | end 63 | }, 64 | 65 | file_output: { 66 | fields: lambda do |_connection, _config_fields| 67 | [ 68 | { name: "FileName" }, 69 | { name: "FileSize" }, 70 | { name: "FileData" }, 71 | { name: "Url", label: "URL" } 72 | ] 73 | end 74 | } 75 | }, 76 | 77 | actions: { 78 | split_pdf_file: { 79 | title: "Split PDF file", 80 | description: "Split PDF file in " \ 81 | "ConvertAPI", 82 | help: "Splits each page into a PDF file.", 83 | 84 | execute: lambda do |_connection, input| 85 | post("/pdf/to/split", input).request_format_www_form_urlencoded 86 | end, 87 | 88 | input_fields: lambda do |object_definitions| 89 | object_definitions["file_input"].required("File") 90 | end, 91 | 92 | output_fields: lambda do |object_definitions| 93 | [{ 94 | name: "Files", 95 | type: "array", 96 | of: "object", 97 | properties: object_definitions["file_output"] 98 | }] 99 | end, 100 | 101 | sample_output: lambda do |_connection, _input| 102 | { 103 | "Files" => [{ 104 | "FileName" => "Workato.pdf", 105 | "FileSize" => 1234, 106 | "Url" => "https://v2.convertapi.com/d/ABCD/Workato.pdf" 107 | }] 108 | } 109 | end 110 | }, 111 | 112 | merge_pdf_file: { 113 | title: "Merge PDF file", 114 | description: "Merge PDF files in " \ 115 | "ConvertAPI", 116 | 117 | execute: lambda do |_connection, input| 118 | files_list = [] 119 | index = 0 120 | input_files = input["Files"]&.pluck("File") 121 | input_files_size = input_files.size 122 | while index < input_files_size 123 | files_list.concat([{ index => input_files[index] }]) 124 | index = index + 1 125 | end 126 | input["Files"] = files_list.inject(:merge) 127 | 128 | post("/pdf/to/merge", input).request_format_www_form_urlencoded 129 | end, 130 | 131 | input_fields: lambda do |object_definitions| 132 | [{ 133 | name: "Files", 134 | optional: false, 135 | type: "array", 136 | of: "object", 137 | properties: object_definitions["file_input"]. 138 | only("File"). 139 | required("File") 140 | }] + object_definitions["file_input"].ignored("File") 141 | end, 142 | 143 | output_fields: lambda do |object_definitions| 144 | [{ 145 | name: "Files", 146 | type: "array", 147 | of: "object", 148 | properties: object_definitions["file_output"] 149 | }] 150 | end, 151 | 152 | sample_output: lambda do |_connection, _input| 153 | { 154 | "Files" => [{ 155 | "FileName" => "Workato.pdf", 156 | "FileSize" => 1234, 157 | "Url" => "https://v2.convertapi.com/d/ABCD/Workato.pdf" 158 | }] 159 | } 160 | end 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/podio_connector.rb: -------------------------------------------------------------------------------- 1 | # Substitute YOUR_PODIO_CLIENT_ID for your OAuth2 client id from Podio 2 | # Substitute YOUR_PODIO_CLIENT_SECRET for your OAuth2 client secret from Podio 3 | { 4 | title: 'Podio', 5 | 6 | connection: { 7 | authorization: { 8 | type: 'oauth2', 9 | 10 | authorization_url: ->() { 11 | 'https://podio.com/oauth/authorize' 12 | }, 13 | 14 | token_url: ->() { 15 | 'https://podio.com/oauth/token' 16 | }, 17 | 18 | client_id: 'YOUR_PODIO_CLIENT_ID', 19 | 20 | client_secret: 'YOUR_PODIO_CLIENT_SECRET', 21 | 22 | credentials: ->(connection, access_token) { 23 | headers('Authorization': "OAuth2 #{access_token}") 24 | } 25 | } 26 | }, 27 | 28 | object_definitions: { 29 | contact: { 30 | fields: ->(connection) { 31 | [ 32 | { name: 'profile_id'}, 33 | { name: 'name'}, 34 | { name: 'organization'}, 35 | { name: 'department'}, 36 | { name: 'skype'}, 37 | { name: 'about'}, 38 | { name: 'address'}, 39 | { name: 'zip'}, 40 | { name: 'city'}, 41 | { name: 'state'}, 42 | { name: 'country'}, 43 | { name: 'mail'}, 44 | { name: 'phone'}, 45 | { name: 'title'}, 46 | { name: 'linkedin'}, 47 | { name: 'url'}, 48 | { name: 'twitter'} 49 | ] 50 | }, 51 | }, 52 | 53 | application: { 54 | fields: ->(connection) { 55 | [ 56 | { name: 'app_id'}, 57 | { name: 'original'}, 58 | { name: 'original_revision'}, 59 | { name: 'status'}, 60 | { name: 'space_id'}, 61 | { name: 'owner', type: :object, properties: [{name: 'user_id', type: :integer}, {name: 'avatar'}, {name: 'name'}]}, 62 | { name: 'link'}, 63 | { name: 'link_add'}, 64 | { name: 'config', type: :object, 65 | properties: 66 | [ 67 | { name: 'type', control_type: 'select', picklist: [['standard'], ['meeting'], ['contact']]}, 68 | { name: 'name'}, 69 | { name: 'item_name'}, 70 | { name: 'description'}, 71 | { name: 'usage'}, 72 | { name: 'external_id'}, 73 | { name: 'icon'}, 74 | { name: 'allow_edit', type: :boolean}, 75 | { name: 'default view'}, 76 | { name: 'allow_attachments', type: :boolean}, 77 | { name: 'allow_comments', type: :boolean}, 78 | { name: 'fivestar', type: :boolean}, 79 | { name: 'fivestar_label'}, 80 | { name: 'approved', type: :boolean}, 81 | { name: 'thumbs', type: :boolean}, 82 | { name: 'thumbs_label', type: :boolean}, 83 | { name: 'rsvp', type: :boolean}, 84 | { name: 'yesno', type: :boolean}, 85 | { name: 'yesno_label'}, 86 | { name: 'tasks'} 87 | ] 88 | } 89 | ] 90 | } 91 | }, 92 | 93 | tag: { 94 | fields: ->(connection){ 95 | [{ name: 'count'},{ name: 'text'}] 96 | } 97 | } 98 | }, 99 | 100 | actions: { 101 | 102 | get_apps: { 103 | input_fields: ->(object_definitions){ 104 | [ 105 | { name: 'Workspace ID', optional: 'false'} 106 | ] 107 | }, 108 | 109 | execute: ->(connection, input) { 110 | { applications: get("https://api.podio.com/app/space/#{input['Workspace ID']}")} 111 | }, 112 | 113 | output_fields: ->(object_definitions) { 114 | { name: 'applications', type: 'array', of: 'object', properties: object_definitions['application']} 115 | } 116 | }, 117 | 118 | get_app_detail: { 119 | input_fields: ->(object_definitions){ 120 | [ 121 | { name: 'Application ID', optional: 'false'} 122 | ] 123 | }, 124 | 125 | execute: ->(connection, input) { 126 | { app_detail: get("https://api.podio.com/app/#{input['Application ID']}")} 127 | }, 128 | 129 | output_fields: ->(object_definitions){ 130 | object_definitions['application'] 131 | } 132 | }, 133 | 134 | get_app_tags: { 135 | input_fields: ->(object_definitions){ 136 | [ 137 | { name: 'Application ID', optional: 'false'} 138 | ] 139 | }, 140 | 141 | execute: ->(connection, input) { 142 | { tags: get("https://api.podio.com/tag/app/#{input['Application ID']}")} 143 | }, 144 | 145 | output_fields: ->(object_definitions){ 146 | { name: 'tags', type = 'array', of: 'object', properties: object_definitions['tag']} 147 | } 148 | }, 149 | 150 | get_contacts: { 151 | input_fields: ->(object_definitions) {[]}, 152 | 153 | execute: ->(connection, input) { 154 | { contacts: get("https://api.podio.com/contact/?limit=5")} 155 | }, 156 | 157 | output_fields: ->(object_definitions) { 158 | [ 159 | { name: 'contacts', type: 'array', of: 'object', properties: object_definitions['contact']} 160 | ] 161 | } 162 | } 163 | 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/toggl_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Toggl', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'api_token', 8 | control_type: 'password', 9 | label: 'Toggl API token', 10 | hint: 'Available in "My Profile" page' 11 | } 12 | ], 13 | 14 | authorization: { 15 | type: 'basic_auth', 16 | 17 | # Toggl API expect the token to be sent as user name and the string 'api_token' as the password 18 | # curl -u "{your api_token}:api_token" "https://www.toggl.com/api/v8/me" 19 | credentials: ->(connection) { 20 | user(connection['api_token']) 21 | password('api_token') 22 | } 23 | } 24 | }, 25 | 26 | test: ->(connection) { 27 | get("https://www.toggl.com/api/v8/me") 28 | }, 29 | 30 | object_definitions: { 31 | time_entry: { 32 | fields: ->() { 33 | [ 34 | { name:'id', type: :integer }, 35 | { name:'wid', type: :integer }, 36 | { name:'pid' }, 37 | { name:'project_name' }, 38 | { name:'billable', type: :boolean }, 39 | { name:'start', type: :timestamp }, 40 | { name:'stop', type: :timestamp }, 41 | { name:'duration', type: :integer }, 42 | { name:'description' }, 43 | { name:'at', type: :timestamp } 44 | ] 45 | } 46 | } 47 | }, 48 | 49 | actions: { 50 | get_time_entries: { 51 | input_fields: ->(object_definitions) { 52 | [ 53 | { name: 'date', type: :date, optional: false } 54 | ] 55 | }, 56 | 57 | execute: ->(connection, input) { 58 | start_time = input['date'].to_time.beginning_of_day.utc.iso8601 59 | end_time = (input['date'].to_time + 1.days).beginning_of_day.utc.iso8601 60 | 61 | entries = get("https://www.toggl.com/api/v8/time_entries"). 62 | params(start_date: start_time, 63 | end_date: end_time) 64 | 65 | { 66 | 'entries': entries 67 | } 68 | }, 69 | 70 | output_fields: ->(object_definitions) { 71 | [ 72 | { name: 'entries', type: :array, of: :object, properties: object_definitions['time_entry'] } 73 | ] 74 | } 75 | }, 76 | get_project_name: { 77 | input_fields: ->(object_definitions) { 78 | [ 79 | { name: 'id', optional: false } 80 | ] 81 | }, 82 | 83 | execute: ->(connection, input) { 84 | project = get("https://www.toggl.com/api/v8/projects/#{input['id']}")['data'] 85 | { 86 | 'project_name': project['name'], 87 | 'client_id': project['cid'] 88 | } 89 | }, 90 | 91 | output_fields: ->(object_definitions) { 92 | [ 93 | { name: 'project_name' }, 94 | { name: 'client_id' } 95 | ] 96 | } 97 | }, 98 | get_client_name: { 99 | input_fields: ->(object_definitions) { 100 | [ 101 | { name: 'id', optional: false } 102 | ] 103 | }, 104 | 105 | execute: ->(connection, input) { 106 | { 107 | 'name': get("https://www.toggl.com/api/v8/clients/#{input['id']}")['data']['name'] 108 | } 109 | }, 110 | 111 | output_fields: ->(object_definitions) { 112 | [ 113 | { name: 'name' } 114 | ] 115 | } 116 | }, 117 | get_daily_report: { 118 | input_fields: ->(object_definitions) { 119 | [ 120 | { name: 'date', optional: false }, 121 | { name: 'workspace_id', optional: false }, 122 | { name: 'user', optional: false, hint: 'Name of agent to generate report for. Input "me" for current user' } 123 | ] 124 | }, 125 | 126 | execute: ->(connection, input) { 127 | # Toggl API expects ISO8601 format 128 | start_time = input['date'].to_time.beginning_of_day.utc.iso8601 129 | end_time = (input['date'].to_time + 1.days).beginning_of_day.utc.iso8601 130 | 131 | user = input['user'] == 'me' ? get("https://www.toggl.com/api/v8/me")['data']['fullname'] : input['user'] 132 | 133 | report = get("https://toggl.com/reports/api/v2/summary"). 134 | params(since: start_time, 135 | until: end_time, 136 | user_agent: user, 137 | workspace_id: input['workspace_id'])['data'] 138 | 139 | { 140 | 'report': report 141 | } 142 | }, 143 | 144 | output_fields: ->(object_definitions) { 145 | [ 146 | { name: 'report', type: :array, of: :object, properties: [ 147 | { name: 'id' }, 148 | { name: 'title', type: :object, properties: [ 149 | { name: 'project' }, 150 | { name: 'client' } 151 | ]}, 152 | { name: 'time', type: :integer }, 153 | { name: 'items', type: :array, of: :object, properties: [ 154 | { name: 'title', type: :object, properties: [ 155 | { name: 'time_entry' } 156 | ]} 157 | ]} 158 | ]} 159 | ] 160 | } 161 | }, 162 | }, 163 | 164 | triggers: {} 165 | } 166 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/amcards_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'AMcards.com', 3 | connection: { 4 | authorization: { 5 | type: 'oauth2', 6 | 7 | authorization_url: ->() { 8 | 'https://amcards.com/oauth2/authorize/?response_type=code&scope=read+write' 9 | }, 10 | 11 | token_url: ->() { 12 | 'https://amcards.com/oauth2/access_token/' 13 | }, 14 | 15 | client_id: 'doesThisNeedToBeInRepo?', 16 | 17 | client_secret: 'doesThisNeedToBeInRepo?', 18 | 19 | credentials: ->(connection, access_token) { 20 | headers('Authorization': "Bearer #{access_token}") 21 | } 22 | } 23 | }, 24 | 25 | object_definitions: { 26 | card: { 27 | fields: ->() { 28 | [ 29 | { 30 | name: 'template_id', 31 | type: :integer, 32 | hint: "You must own the template or it must be public in order to use it.", 33 | control_type: 'select', 34 | pick_list: 'templates', 35 | optional: false, 36 | }, 37 | { 38 | name: 'message', 39 | label: 'Message for inside of card', 40 | control_type: 'text-area', 41 | hint: "Maximum of 700 characters please! Message will be added to the inside right (vertical) or inside bottom (horizontal) panel of the card. It will be added to the card and not replace an existing message!", 42 | }, 43 | { 44 | name: 'send_date', 45 | type: :date, 46 | hint: "The card will be scheduled to mail out the next business day if you leave this out.", 47 | }, 48 | { 49 | name: 'initiator', 50 | hint: "This is something that will help you to track who or what triggered this card.", 51 | optional: false, 52 | }, 53 | { 54 | name: 'first_name', 55 | label: "TO: First Name", 56 | optional: false, 57 | }, 58 | { 59 | name: 'last_name', 60 | label: "TO: Last Name", 61 | optional: false, 62 | }, 63 | { 64 | name: 'organization', 65 | hint: "Only use this if the mailing address is for this place. This will be included on envelope.", 66 | label: "TO: Organization Name", 67 | }, 68 | { 69 | name: 'address_line_1', 70 | label: "TO: Address Line 1", 71 | optional: false, 72 | }, 73 | { 74 | name: 'address_line_2', 75 | label: "TO: Address Line 2", 76 | }, 77 | { 78 | name: 'city', 79 | label: "TO: City", 80 | optional: false, 81 | }, 82 | { 83 | name: 'state', 84 | label: "TO: State", 85 | hint: "Try to use 2 character state codes! ('UT' is better than 'Utah')", 86 | optional: false, 87 | }, 88 | { 89 | name: 'postal_code', 90 | label: "TO: Postal/Zip Code", 91 | optional: false, 92 | }, 93 | { 94 | name: 'country', 95 | label: "TO: Country", 96 | hint: "Try to use 2 character country codes! ('US' is better than 'United States')", 97 | }, 98 | { 99 | name: 'return_first_name', 100 | label: "FROM: First Name", 101 | optional: false, 102 | }, 103 | { 104 | name: 'return_last_name', 105 | label: "FROM: Last Name", 106 | optional: false, 107 | }, 108 | { 109 | name: 'return_address_line_1', 110 | label: "FROM: Address Line 1", 111 | optional: false, 112 | }, 113 | { 114 | name: 'return_address_line_2', 115 | label: "FROM: Address Line 2", 116 | }, 117 | { 118 | name: 'return_city', 119 | label: "FROM: City", 120 | optional: false, 121 | }, 122 | { 123 | name: 'return_state', 124 | label: "FROM: State", 125 | hint: "Use 2 character state codes! ('UT' is better than 'Utah')", 126 | optional: false, 127 | }, 128 | { 129 | name: 'return_postal_code', 130 | label: "FROM: Postal/Zip Code", 131 | optional: false, 132 | }, 133 | { 134 | name: 'return_country', 135 | label: "FROM: Country", 136 | default: "US", 137 | hint: "Use 2 character country codes! ('US' is better than 'United States')", 138 | }, 139 | ] 140 | }, 141 | }, 142 | }, 143 | 144 | actions: { 145 | create_card: { 146 | input_fields: ->(object_definitions) { 147 | object_definitions['card'] 148 | }, 149 | 150 | execute: ->(connection, input) { 151 | post("https://amcards.com/cards/open-card-form-oa/", input) 152 | }, 153 | 154 | output_fields: ->(object_definitions) { 155 | object_definitions['card'] 156 | }, 157 | }, 158 | }, 159 | 160 | triggers: { 161 | # None so far... 162 | }, 163 | 164 | pick_lists: { 165 | templates: ->(connection){ 166 | get("https://amcards.com/.api/v1/template/")['objects']. 167 | map { |template| [template['name'], template['id']] } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/bill_connector.rb: -------------------------------------------------------------------------------- 1 | # Adds operations missing from the standard adapter. 2 | { 3 | title: "Bill.com (custom)", 4 | 5 | connection: { 6 | fields: [ 7 | { 8 | name: "user_name", 9 | hint: "Bill.com app login username", 10 | optional: false 11 | }, 12 | { 13 | name: "password", 14 | hint: "Bill.com app login password", 15 | optional: false, 16 | control_type: "password" 17 | }, 18 | { 19 | name: "org_id", 20 | label: "Organisation ID", 21 | hint: "Log in to your Bill.com account, click on gear icon, click " \ 22 | "on settings then click on profiles under your company. The " \ 23 | "Organization ID is at the end of the URL, after " \ 24 | "https://[app/app-stage].bill.com/Organization?Id=", 25 | optional: false 26 | }, 27 | { 28 | name: "dev_key", 29 | label: "Developer API key", 30 | hint: "Sign up for the developer program to get API key. " \ 31 | "You may find more info here", 33 | optional: false, 34 | control_type: "password" 35 | }, 36 | { 37 | name: "environment", 38 | hint: "Find more info here", 41 | control_type: "select", 42 | pick_list: [ 43 | %w[Production api], 44 | %w[Sandbox/Stage api-stage] 45 | ], 46 | optional: false 47 | } 48 | ], 49 | 50 | authorization: { 51 | type: "custom_auth", 52 | 53 | acquire: lambda { |connection| 54 | { 55 | session_id: post("https://#{connection['environment']}.bill.com" \ 56 | "/api/v2/Login.json"). 57 | payload(userName: connection["user_name"], 58 | password: connection["password"], 59 | orgId: connection["org_id"], 60 | devKey: connection["dev_key"]). 61 | request_format_www_form_urlencoded. 62 | dig("response_data", "sessionId") 63 | } 64 | }, 65 | 66 | refresh_on: [/"error_message"\s*\:\s*"Session is invalid/], 67 | 68 | detect_on: [/"response_message"\s*\:\s*"Error"/], 69 | 70 | apply: lambda { |connection| 71 | payload(sessionId: connection["session_id"], 72 | devKey: connection["dev_key"]) 73 | request_format_www_form_urlencoded 74 | } 75 | }, 76 | 77 | base_uri: lambda { |connection| 78 | "https://#{connection['environment']}.bill.com" 79 | } 80 | }, 81 | 82 | test: lambda { |_connection| 83 | post("/api/v2/GetSessionInfo.json") 84 | }, 85 | 86 | object_definitions: { 87 | vendor: { 88 | fields: lambda { |_connection, _config_fields| 89 | post("/api/v2/GetEntityMetadata.json"). 90 | dig("response_data", "Vendor", "fields"). 91 | map { |key, _value| { name: key } } || [] 92 | } 93 | } 94 | }, 95 | 96 | triggers: { 97 | new_or_updated_vendor: { 98 | subtitle: "New or updated vendor", 99 | description: "New or updated vendor in " \ 100 | "Bill.com", 101 | type: "paging_desc", 102 | 103 | input_fields: lambda { |_connection| 104 | [ 105 | { 106 | name: "since", 107 | label: "From", 108 | type: "timestamp", 109 | optional: true, 110 | sticky: true, 111 | hint: "Get vendors created or updated since given date/time. " \ 112 | "Leave empty to get vendors created or updated one hour ago" 113 | } 114 | ] 115 | }, 116 | 117 | poll: lambda { |_connection, input, page| 118 | page ||= 0 119 | page_size = 50 120 | query = { 121 | start: page, 122 | max: page_size, 123 | filters: [ 124 | { 125 | field: "updatedTime", 126 | op: ">=", 127 | value: (input["since"].presence || 1.hour.ago). 128 | utc. 129 | strftime("%Y-%m-%dT%H:%M:%S.%L%z") 130 | } 131 | ], 132 | sort: [{ field: "updatedTime", asc: 0 }] 133 | } 134 | 135 | vendors = post("/api/v2/List/Vendor.json"). 136 | payload(data: query.to_json). 137 | dig("response_data") || [] 138 | 139 | { 140 | events: vendors, 141 | next_page: (vendors.size >= page_size ? page + page_size : nil) 142 | } 143 | }, 144 | 145 | document_id: lambda { |vendor| 146 | vendor["id"] 147 | }, 148 | 149 | sort_by: lambda { |vendor| 150 | vendor["updatedTime"] 151 | }, 152 | 153 | output_fields: lambda { |object_definitions| 154 | object_definitions["vendor"] 155 | }, 156 | 157 | sample_output: lambda { |_connection| 158 | post("/api/v2/List/Vendor.json"). 159 | payload(data: { start: 0, 160 | max: 1, 161 | sort: [{ field: "updatedTime", asc: 0 }] }.to_json). 162 | dig("response_data", 0) || {} 163 | } 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/cisco_spark_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Cisco Spark', 3 | 4 | connection: { 5 | authorization: { 6 | type: 'oauth2', 7 | 8 | authorization_url: ->() { 9 | scope = [ "spark:messages_write", 10 | "spark:rooms_read", 11 | "spark:memberships_read", 12 | "spark:messages_read", 13 | "spark:rooms_write", 14 | "spark:people_read", 15 | "spark:memberships_write"].join(" ") 16 | 17 | "https://api.ciscospark.com/v1/authorize?response_type=code&scope=#{scope}" 18 | }, 19 | 20 | token_url: ->() { 21 | "https://api.ciscospark.com/v1/access_token" 22 | }, 23 | 24 | client_id: "YOUR_CISCO_SPARK_CLIENT_ID", 25 | 26 | client_secret: "YOUR_CISCO_SPARK_CLIENT_SECRET", 27 | 28 | credentials: ->(connection, access_token) { 29 | headers("Authorization": "Bearer #{access_token}") 30 | } 31 | } 32 | }, 33 | 34 | object_definitions: { 35 | message: { 36 | fields: ->() { 37 | [ 38 | { name: "id" }, 39 | { name: "roomId" }, 40 | { name: "personId" }, 41 | { name: "personEmail" }, 42 | { name: "created", type: :timestamp } 43 | ] 44 | } 45 | }, 46 | 47 | message_detail: { 48 | fields: ->() { 49 | [ 50 | { name: "id", optional: false }, 51 | { name: "personId", hint: "The ID of the recipient when sending a private 1 to 1 message" }, 52 | { name: "personEmail", hint: "The email address of the recipient when sendinga private 1:1 message" }, 53 | { name: "roomId", control_type: "select", pick_list: "rooms" }, 54 | { name: "text" }, 55 | { name: "toPersonId" }, 56 | { name: "toPersonEmail" }, 57 | { name: "created", type: :timestamp } 58 | ] 59 | } 60 | }, 61 | 62 | room: { 63 | fields: ->() { 64 | [ 65 | { name: "id", control_type: 'select', pick_list: 'rooms', label: 'Room', optional: false }, 66 | { name: "title" }, 67 | { name: "type" }, 68 | { name: "isLocked" }, 69 | { name: "lastActivity", type: :timestamp }, 70 | { name: "created", type: :timestamp } 71 | ] 72 | } 73 | }, 74 | 75 | person: { 76 | fields: ->() { 77 | [ 78 | { name: "id", label: "Person ID", optional: false }, 79 | { name: "displayName" } 80 | ] 81 | } 82 | } 83 | }, 84 | 85 | actions: { 86 | get_message_details: { 87 | input_fields: ->(object_definitions) { 88 | object_definitions['message_detail'].only('id') 89 | }, 90 | 91 | execute: ->(connection,input) { 92 | get("https://api.ciscospark.com/v1/messages/" + input['id']) 93 | }, 94 | 95 | output_fields: ->(object_definitions) { 96 | object_definitions['message_detail'] 97 | } 98 | }, 99 | 100 | get_room_details: { 101 | input_fields: ->(object_definitions) { 102 | object_definitions['room'].only('id') 103 | }, 104 | 105 | execute: ->(connection,input) { 106 | get("https://api.ciscospark.com/v1/rooms/" + input['id']) 107 | }, 108 | 109 | output_fields: ->(object_definitions) { 110 | object_definitions['room'] 111 | } 112 | }, 113 | 114 | get_person_details: { 115 | input_fields: ->(object_definitions) { 116 | object_definitions['person'].only('id') 117 | }, 118 | 119 | execute: ->(connection,input) { 120 | get("https://api.ciscospark.com/v1/people/" + input['id']) 121 | }, 122 | 123 | output_fields: ->(object_definitions) { 124 | object_definitions['person'] 125 | } 126 | }, 127 | 128 | post_message: { 129 | input_fields: ->(object_definitions) { 130 | object_definitions['message_detail'].only('roomId', 'text', 'toPersonEmail', 'toPersonId') 131 | }, 132 | 133 | execute: ->(connection,input) { 134 | post("https://api.ciscospark.com/v1/messages", input) 135 | }, 136 | 137 | output_fields: ->(object_definitions) { 138 | object_definitions['message_detail'] 139 | } 140 | } 141 | }, 142 | 143 | triggers: { 144 | new_message: { 145 | type: "paging_desc", 146 | 147 | input_fields: ->(object_definitions) { 148 | object_definitions['room'].only('id') 149 | }, 150 | 151 | webhook_subscribe: ->(webhook_url, connection, input, flow_id) { 152 | post('https://api.ciscospark.com/v1/webhooks', 153 | name: "Workato recipe #{flow_id}", 154 | targetUrl: webhook_url, 155 | resource: 'messages', 156 | event: 'created', 157 | filter: "roomId=#{input['id']}") 158 | }, 159 | 160 | webhook_notification: ->(input, payload) { 161 | payload['data'] 162 | }, 163 | 164 | webhook_unsubscribe: ->(webhook) { 165 | delete("https://api.ciscospark.com/v1/webhooks/#{webhook['id']}") 166 | }, 167 | 168 | dedup: ->(message) { 169 | message['id'] 170 | }, 171 | 172 | output_fields: ->(object_definitions) { 173 | object_definitions['message'] 174 | } 175 | } 176 | }, 177 | 178 | pick_lists: { 179 | rooms: ->(connection) { 180 | get("https://api.ciscospark.com/v1/rooms")['items'].map { |r| [r['title'], r['id']] } 181 | } 182 | }, 183 | } 184 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/typeform_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Typeform", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "client_id", 8 | label: "Client ID", 9 | hint: "You can find your client ID in the settings page.", 10 | optional: false 11 | }, 12 | { 13 | name: "client_secret", 14 | label: "Client Secret", 15 | control_type: "password", 16 | hint: "You can find your client ID in the settings page.", 17 | optional: false 18 | } 19 | ], 20 | 21 | authorization: { 22 | type: "oauth2", 23 | 24 | authorization_url: lambda do |connection| 25 | scopes = ["forms:read", "workspaces:read"].join(" ") 26 | 27 | "https://api.typeform.com/oauth/authorize?client_id=" \ 28 | "#{connection['client_id']}&scope=#{scopes}" \ 29 | "&redirect_uri=https%3A%2F%2Fwww.workato.com%2Foauth%2Fcallback" 30 | end, 31 | 32 | acquire: lambda do |connection, auth_code, _redirect_uri| 33 | response = post("https://api.typeform.com/oauth/token"). 34 | payload(client_id: connection["client_id"], 35 | client_secret: connection["client_secret"], 36 | code: auth_code, 37 | redirect_uri: "https://www.workato.com/oauth/" \ 38 | "callback"). 39 | request_format_www_form_urlencoded 40 | 41 | [response, nil, nil] 42 | end, 43 | 44 | apply: lambda { |_connection, access_token| 45 | headers("Authorization": "Bearer #{access_token}") 46 | } 47 | }, 48 | 49 | base_uri: lambda do 50 | "https://api.typeform.com" 51 | end 52 | }, 53 | 54 | object_definitions: { 55 | get_forms: { 56 | fields: lambda do |_object_definitions| 57 | [ 58 | { 59 | name: "workspace_id", 60 | label: "Workspace", 61 | hint: "Workspace that form belongs to", 62 | type: "string", 63 | control_type: "select", 64 | pick_list: "workspaces" 65 | }, 66 | { 67 | name: "search", 68 | label: "Form Name", 69 | hint: "Whole or partial form name to search for", 70 | type: "string" 71 | } 72 | ] 73 | end 74 | }, 75 | 76 | forms: { 77 | fields: lambda do |_object_definitions| 78 | [ 79 | { name: "id" }, 80 | { name: "title" }, 81 | { name: "last_updated_at", type: "timestamp" }, 82 | { 83 | name: "self", 84 | type: "object", 85 | properties: [ 86 | { name: "href" } 87 | ] 88 | }, 89 | { 90 | name: "theme", 91 | type: "object", 92 | properties: [ 93 | { name: "href" } 94 | ] 95 | }, 96 | { 97 | name: "_links", 98 | type: "object", 99 | properties: [ 100 | { name: "display" } 101 | ] 102 | } 103 | ] 104 | end 105 | } 106 | }, 107 | 108 | actions: { 109 | search_forms: { 110 | subtitle: "Search forms", 111 | description: "Search forms in " \ 112 | "Typeform", 113 | help: "Search will return a list of forms that matches " \ 114 | "the search criteria.", 115 | 116 | input_fields: lambda do |object_definitions| 117 | object_definitions["get_forms"] 118 | end, 119 | 120 | execute: lambda do |_connection, input| 121 | { 122 | forms: get("/forms"). 123 | params(search: input["search"], 124 | page_size: 200, 125 | workspace_id: input["workspace_id"])["items"] 126 | } 127 | end, 128 | 129 | output_fields: lambda do |object_definitions| 130 | [ 131 | { 132 | name: "forms", 133 | type: "array", 134 | of: "object", 135 | properties: object_definitions["forms"] 136 | } 137 | ] 138 | end, 139 | 140 | sample_output: lambda do |_connection, _input| 141 | { 142 | forms: get("/forms"). 143 | params(page_size: 1)["items"] || [] 144 | } 145 | end 146 | } 147 | }, 148 | 149 | triggers: { 150 | new_form: { 151 | # Results are listed in descending order based on the modified date. 152 | type: :paging_desc, 153 | 154 | description: "New Form " \ 155 | "in Typeform", 156 | subtitle: "New form in Typeform", 157 | 158 | poll: lambda do |_connection, _input, closure| 159 | closure ||= 1 160 | per_page = 100 161 | 162 | forms = get("/forms"). 163 | params(page_size: per_page, 164 | page: closure) 165 | 166 | { 167 | events: forms["items"] || [], 168 | next_page: forms.length >= per_page ? closure + 1 : nil 169 | } 170 | end, 171 | 172 | dedup: lambda do |forms| 173 | forms["id"] 174 | end, 175 | 176 | output_fields: lambda do |object_definitions| 177 | object_definitions["forms"] 178 | end, 179 | 180 | sample_output: lambda do |_connection, _input| 181 | get("/forms").params(page_size: 1).dig("items", 0) || {} 182 | end 183 | } 184 | }, 185 | 186 | pick_lists: { 187 | workspaces: lambda do |_connection| 188 | get("/workspaces")["items"]. 189 | pluck("name", "id") 190 | end 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/wrike_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Wrike', 3 | 4 | connection: { 5 | authorization: { 6 | type: 'oauth2', 7 | 8 | authorization_url: ->() { 9 | 'https://www.wrike.com/oauth2/authorize?response_type=code' 10 | }, 11 | 12 | token_url: ->() { 13 | 'https://www.wrike.com/oauth2/token' 14 | }, 15 | 16 | client_id: 'YOUR_WRIKE_CLIENT_ID', 17 | 18 | client_secret: 'YOUR_WRIKE_CLIENT_SECRET', 19 | 20 | credentials: ->(connection, access_token) { 21 | headers('Authorization': "Bearer #{access_token}") 22 | } 23 | } 24 | }, 25 | 26 | object_definitions: { 27 | 28 | task: { 29 | fields: ->() { 30 | [ 31 | { name: 'id' }, 32 | { name: 'accountId' }, 33 | { name: 'title', hint: 'Searches for exact match' }, 34 | { name: 'description' }, 35 | { name: 'briefDescription' }, 36 | { name: 'status', hint: 'Accepted values are Active, Completed, Deferred and Cancelled' }, 37 | { name: 'importance', hint: 'Accepted values are High, Normal and Low' }, 38 | { name: 'createdDate', type: :timestamp }, 39 | { name: 'updatedDate', type: :timestamp }, 40 | { name: 'dates', type: :object, properties: [ 41 | { name: 'type' }, 42 | { name: 'duration', type: :integer}, 43 | { name: 'start', type: :timestamp }, 44 | { name: 'due', type: :timestamp } 45 | ]}, 46 | { name: 'scope' }, 47 | { name: 'customStatusId' }, 48 | { name: 'hasAttachments', type: :boolean }, 49 | { name: 'attachmentCount', type: :integer }, 50 | { name: 'permalink', control_type: 'url' }, 51 | { name: 'priority' }, 52 | { name: 'responsibles', control_type: 'select', pick_list: 'user' } 53 | ] 54 | }, 55 | } 56 | }, 57 | 58 | actions: { 59 | 60 | get_task_by_id: { 61 | input_fields: ->(object_definitions) { 62 | [{ name: 'id', optional: false }] 63 | }, 64 | 65 | execute: ->(connection,input) { 66 | get("https://www.wrike.com/api/v3/tasks/#{input['id']}")['data'].first 67 | }, 68 | 69 | output_fields: ->(object_definitions) { 70 | object_definitions['task'] 71 | } 72 | }, 73 | 74 | search_task: { 75 | input_fields: ->(object_definitions) { 76 | [{ name: 'created_after' }].concat(object_definitions['task'].only('title', 'status', 'importance')) 77 | }, 78 | 79 | execute: ->(connection,input) { 80 | if input['created_after'].present? 81 | created_date_query = '?createdDate={"start":"' + input['created_after'].to_time.utc.iso8601 + '"}' 82 | input = input.reject { |k,v| k == 'created_after' } 83 | end 84 | 85 | get("https://www.wrike.com/api/v3/tasks" + (created_date_query || ""), input)['data'] 86 | }, 87 | 88 | output_fields: ->(object_definitions) { 89 | [ 90 | { name: 'tasks', type: :array, of: :object, properties: object_definitions['task'] } 91 | ] 92 | } 93 | }, 94 | 95 | create_task: { 96 | input_fields: ->(object_definitions) { 97 | [{ name: 'folder_id', control_type: 'select', pick_list: 'folder' }]. 98 | concat(object_definitions['task']. 99 | only('title', 'description', 'status', 'importance', 'responsibles')). 100 | required('folder_id', 'title') 101 | }, 102 | 103 | execute: ->(connection,input) { 104 | updated_input = input.reject { |k,v| k == 'folder_id' } 105 | 106 | updated_input['responsibles'] = "[" + updated_input['responsibles'] + "]" 107 | 108 | post("https://www.wrike.com/api/v3/folders/#{input['folder_id']}/tasks").params(updated_input)['data'].first 109 | }, 110 | 111 | output_fields: ->(object_definitions) { 112 | object_definitions['task'] 113 | } 114 | }, 115 | 116 | update_task: { 117 | input_fields: ->(object_definitions) { 118 | [{ name: 'id', optional: false }]. 119 | concat(object_definitions['task']. 120 | only('title', 'description', 'status', 'importance')) 121 | }, 122 | 123 | execute: ->(connection,input) { 124 | updated_input = input.reject { |k,v| k == 'id' } 125 | 126 | put("https://www.wrike.com/api/v3/tasks/#{input['id']}").params(updated_input)['data'].first 127 | }, 128 | 129 | output_fields: ->(object_definitions) { 130 | object_definitions['task'] 131 | } 132 | } 133 | }, 134 | 135 | triggers: { 136 | 137 | new_or_updated_task: { 138 | 139 | type: :paging_desc, 140 | 141 | input_fields: ->(object_definition) { 142 | [{ name: 'since', type: :timestamp, optional: false, hint: 'Select earliest updated date' }] 143 | }, 144 | 145 | poll: ->(connection,input,next_page) { 146 | params = { 147 | 'updatedDate' => '{"start":"' + input['since'].to_time.utc.iso8601 + '"}', 148 | 'sortField' => 'UpdatedDate', 149 | 'sortOrder' => 'Desc', 150 | 'pageSize' => '10', 151 | 'nextPageToken' => next_page 152 | } 153 | 154 | response = get("https://www.wrike.com/api/v3/tasks", params) 155 | 156 | { 157 | events: response['data'], 158 | next_page: response['nextPageToken'], 159 | } 160 | }, 161 | 162 | output_fields: ->(object_definitions) { 163 | object_definitions['task'] 164 | } 165 | } 166 | }, 167 | 168 | pick_lists: { 169 | folder: ->(connection) { 170 | get("https://www.wrike.com/api/v3/folders")['data']. 171 | map { |folder| [folder['title'], folder['id']] } 172 | }, 173 | 174 | user: ->(connection) { 175 | get("https://www.wrike.com/api/v3/contacts")['data']. 176 | map { |contact| [contact['firstName'] + " " + contact['lastName'], contact['id']] } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/freshdesk_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Freshdesk', 3 | 4 | # HTTP basic auth example. 5 | connection: { 6 | fields: [ 7 | { 8 | name: 'helpdesk', 9 | control_type: 'subdomain', 10 | url: '.freshdesk.com', 11 | hint: 'Your helpdesk name as found in your Freshdesk URL' 12 | }, 13 | { 14 | name: 'username', 15 | optional: true, 16 | hint: 'Your username; leave empty if using API key below' 17 | }, 18 | { 19 | name: 'password', 20 | control_type: 'password', 21 | label: 'Password or personal API key' 22 | } 23 | ], 24 | 25 | authorization: { 26 | type: 'basic_auth', 27 | 28 | # Basic auth credentials are just the username and password; framework handles adding 29 | # them to the HTTP requests. 30 | credentials: ->(connection) { 31 | # Freshdesk-specific quirk: If only using API key to authenticate, API expects it as username, 32 | # but we prefer to store it in 'password' to keep it obscured (control_type: 'password' above). 33 | if connection['username'].blank? 34 | user(connection['password']) 35 | else 36 | user(connection['username']) 37 | password(connection['password']) 38 | end 39 | } 40 | } 41 | }, 42 | 43 | object_definitions: { 44 | 45 | user: { 46 | 47 | # Provide a preview user to display in the recipe data tree. 48 | preview: ->(connection) { 49 | get("https://#{connection['helpdesk']}.freshdesk.com/api/users.json?page_size=1&wf_order=created_at&wf_order_type=desc")['results'].first 50 | }, 51 | 52 | fields: ->() { 53 | [ 54 | { 55 | name: 'id', 56 | type: :integer 57 | }, 58 | { 59 | name: 'name', 60 | }, 61 | { 62 | name: 'email' 63 | # type defaults to string 64 | }, 65 | { 66 | name: 'created_at', 67 | type: :timestamp 68 | } 69 | ] 70 | }, 71 | }, 72 | 73 | ticket: { 74 | 75 | fields: ->() { 76 | [ 77 | { 78 | name: 'id', 79 | type: :integer 80 | }, 81 | { 82 | name: 'email', 83 | control_type: 'email' 84 | }, 85 | { 86 | name: 'subject' 87 | }, 88 | { 89 | name: 'description' 90 | } 91 | ] 92 | } 93 | } 94 | }, 95 | 96 | test: ->(connection) { 97 | get("https://#{connection['helpdesk']}.freshdesk.com/agents.json") 98 | }, 99 | 100 | actions: { 101 | create_user: { 102 | input_fields: ->(object_definitions) { 103 | object_definitions['user'].ignored('id', 'created_at').required('email') 104 | }, 105 | 106 | execute: ->(connection, input) { 107 | # Freshdesk API uses a 'user' envelope around this endpoint's input, 108 | # and responds in a similar envelope. 109 | post("https://#{connection['helpdesk']}.freshdesk.com/contacts.json", { user: input } )['user'] 110 | }, 111 | 112 | # Output schema. Same as input above. 113 | output_fields: ->(object_definitions) { 114 | # API endpoint just returns the whole object, so we don't need to adapt the object 115 | # fields definition any further here. 116 | object_definitions['user'] 117 | } 118 | }, 119 | 120 | search_users: { 121 | input_fields: ->(object_definitions) { 122 | # Assuming here that the API only allows searching by these terms. 123 | object_definitions['user'].only('id', 'email') 124 | }, 125 | 126 | execute: ->(connection, input) { 127 | { 128 | 'users': get("https://#{connection['helpdesk']}.freshdesk.com/api/users.json", input)['results'] 129 | } 130 | }, 131 | 132 | output_fields: ->(object_definitions) { 133 | [ 134 | { 135 | name: 'users', 136 | type: :array, 137 | of: :object, 138 | properties: object_definitions['user'] 139 | } 140 | ] 141 | } 142 | } 143 | }, 144 | 145 | triggers: { 146 | 147 | new_ticket: { 148 | 149 | input_fields: ->() { 150 | [ 151 | { 152 | name: 'since', 153 | type: :timestamp, 154 | hint: 'Defaults to tickets created after the recipe is first started' 155 | } 156 | ] 157 | }, 158 | 159 | poll: ->(connection, input, last_updated_since) { 160 | updated_since = last_updated_since || input['since'] || Time.now 161 | 162 | tickets = get("https://#{connection['helpdesk']}.freshdesk.com/api/v2/tickets.json"). 163 | params(order_by: 'updated_at', # Because we can only query by updated_since in this API. 164 | order_type: 'asc', # We expect events in ascending order. 165 | per_page: 2, # Small page size to help with testing. 166 | updated_since: updated_since.to_time.utc.iso8601) 167 | 168 | next_updated_since = tickets.last['updated_at'] unless tickets.blank? 169 | 170 | # Return three items: 171 | # - The polled objects/events (default: empty/nil if nothing found) 172 | # - Any data needed for the next poll (default: nil, uses one from previous poll if available) 173 | # - Flag on whether more objects/events may be immediately available (default: false) 174 | { 175 | events: tickets, 176 | next_poll: next_updated_since, 177 | # common heuristic when no explicit next_page available in response: full page means maybe more. 178 | can_poll_more: tickets.length >= 2 179 | } 180 | }, 181 | 182 | dedup: ->(ticket) { 183 | ticket['id'] 184 | }, 185 | 186 | output_fields: ->(object_definitions) { 187 | object_definitions['ticket'] 188 | } 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/blacklinereports.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'BlackLine Reports', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'environment', 8 | control_type: 'select', 9 | pick_list: [%w[Production us], %w[Sandbox sbus]], 10 | optional: false 11 | }, 12 | { 13 | name: 'data_center', 14 | hint: 'Ex: 2, if the app URL is ' \ 15 | 'https://subdomain.us2.blackline.com/', 16 | optional: false 17 | }, 18 | { name: 'username', optional: false }, 19 | { 20 | name: 'api_key', 21 | label: 'API key', 22 | hint: 'Login to app as System Admin and generate an API key by ' \ 23 | 'navigating to - System > Users Admin Grid > User > User Information', 24 | control_type: 'password', 25 | optional: false 26 | }, 27 | { 28 | name: 'client_id', 29 | hint: 'Ex: subdomain, if the app URL is ' \ 30 | 'https://subdomain.us2.blackline.com', 31 | optional: false 32 | }, 33 | { name: 'client_secret', control_type: 'password', optional: false }, 34 | { 35 | name: 'user_scope', 36 | hint: 'Ex: ReportsAPI instance_ABCD-1234-5AC6-7D89-1C0A2345B6AB', 37 | optional: false 38 | } 39 | ], 40 | 41 | base_uri: lambda do |connection| 42 | "https://#{connection['environment']}#{connection['data_center']}" \ 43 | '.api.blackline.com' 44 | end, 45 | 46 | authorization: { 47 | type: 'custom', 48 | 49 | acquire: lambda do |connection| 50 | auth_header = 51 | "#{connection['client_id']}:#{connection['client_secret']}". 52 | encode_base64 53 | response = post('/authorize/connect/token'). 54 | headers('Content-Type' => 55 | 'application/x-www-form-urlencoded', 56 | 'Authorization' => "Basic #{auth_header}"). 57 | payload(grant_type: 'password', 58 | password: connection['api_key'], 59 | scope: connection['user_scope'], 60 | username: connection['username']). 61 | request_format_www_form_urlencoded 62 | 63 | { access_token: response['access_token'] } 64 | end, 65 | 66 | refresh_on: [401], 67 | 68 | apply: lambda do |connection| 69 | headers('Authorization' => "Bearer #{connection['access_token']}") 70 | end 71 | } 72 | }, 73 | 74 | object_definitions: { 75 | report: { 76 | fields: lambda do |_connection, _config_fields| 77 | [ 78 | { 79 | control_type: 'text', 80 | label: 'End time', 81 | render_input: 'date_time_conversion', 82 | parse_output: 'date_time_conversion', 83 | type: 'date_time', 84 | name: 'endTime' 85 | }, 86 | { 87 | name: 'exportUrls', 88 | type: 'array', 89 | of: 'object', 90 | label: 'Export URLs', 91 | properties: [{ name: 'type' }, { name: 'url', label: 'URL' }] 92 | }, 93 | { 94 | control_type: 'number', 95 | label: 'ID', 96 | parse_output: 'float_conversion', 97 | type: 'number', 98 | name: 'id' 99 | }, 100 | { name: 'message' }, 101 | { name: 'name' }, 102 | { name: 'notes' }, 103 | { 104 | control_type: 'text', 105 | label: 'Start time', 106 | render_input: 'date_time_conversion', 107 | parse_output: 'date_time_conversion', 108 | type: 'date_time', 109 | name: 'startTime' 110 | }, 111 | { name: 'status' } 112 | ] 113 | end 114 | } 115 | }, 116 | 117 | test: ->(_connection) { get('/api/queryruns') }, 118 | 119 | actions: { 120 | get_report_content: { 121 | title: 'Get report content in CSV', 122 | description: "Get report content (CSV) " \ 123 | "in BlackLine Reports", 124 | 125 | execute: lambda do |_connection, input| 126 | { 127 | content: get("/api/completedqueryrun/#{input['report_id']}/CSV"). 128 | response_format_raw. 129 | encode.to_s.gsub(/�/, '') 130 | } 131 | end, 132 | 133 | input_fields: lambda do |_connection| 134 | [{ name: 'report_id', 135 | type: 'integer', 136 | optional: false, 137 | hint: 'Only reports with CSV ExportURLs can be used' }] 138 | end, 139 | 140 | output_fields: ->(_object_definitions) { [{ name: 'content' }] }, 141 | 142 | sample_output: lambda do |_connection, _input| 143 | { content: 'EUR 0.99 01/01/2000 M' } 144 | end 145 | }, 146 | 147 | list_reports: { 148 | description: "List reports " \ 149 | "in BlackLine Reports", 150 | 151 | execute: lambda do |_connection, _input| 152 | { 153 | reports: get('/api/queryruns'). 154 | after_error_response(/.*/) do |_code, body, _header, message| 155 | error("#{message}: #{body}") 156 | end 157 | } 158 | end, 159 | 160 | output_fields: lambda do |object_definitions| 161 | [{ 162 | name: 'reports', 163 | type: 'array', 164 | of: 'object', 165 | properties: object_definitions['report'] 166 | }] 167 | end, 168 | 169 | sample_output: lambda do |_connection, _input| 170 | { 171 | 'endTime' => '2019-01-01T23:16:05.543Z', 172 | 'exportUrls' => [], 173 | 'id' => 123_456, 174 | 'message' => '', 175 | 'name' => 'User Access', 176 | 'notes' => 'User Access - List of users showing their current ' \ 177 | 'authorized Roles', 178 | 'startTime' => '2019-01-01T23:16:03.433Z', 179 | 'status' => 'Complete' 180 | } 181 | end 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/salesforceiq_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "SalesforceIQ", 3 | 4 | connection: { 5 | fields: [ 6 | { name: "api_key", 7 | label: "API key", 8 | optional: false, 9 | hint: "Get more info from here" }, 11 | { name: "api_secret", 12 | label: "API secret", 13 | optional: false, 14 | control_type: "password", 15 | hint: "Get more info from here" } 17 | ], 18 | 19 | authorization: { 20 | type: "basic_auth", 21 | 22 | credentials: lambda do |connection| 23 | user(connection["api_key"]) 24 | password(connection["api_secret"]) 25 | end 26 | }, 27 | 28 | base_uri: lambda do 29 | "https://api.salesforceiq.com" 30 | end 31 | }, 32 | 33 | test: lambda do |_connection| 34 | get("/v2/accounts?_limit=1") 35 | end, 36 | 37 | object_definitions: { 38 | account: { 39 | fields: lambda do |_connection| 40 | [ 41 | { name: "id" }, 42 | { name: "name", optional: false }, 43 | { name: "modifiedDate", label: "Modified date", type: :integer, 44 | hint: "Stores a particular Date & Time in UTC milliseconds past" \ 45 | " the epoch." }, # milliseconds since epoch 46 | ].concat( 47 | get("/v2/accounts/fields")["fields"]. 48 | map do |field| 49 | if field["dataType"] == "List" 50 | pick_list = field["listOptions"].pluck("display", "id") 51 | end 52 | 53 | { 54 | name: field["id"], 55 | label: field["name"], 56 | control_type: field["dataType"] == "List" ? "select" : "text", 57 | pick_list: pick_list 58 | } 59 | end 60 | ) 61 | end 62 | }, 63 | }, 64 | 65 | actions: { 66 | create_account: { 67 | description: "Create Account in " \ 68 | "SalesforceIQ", 69 | 70 | input_fields: lambda do |object_definitions| 71 | object_definitions["account"]. 72 | ignored("id", "modifiedDate", "address_city", "address_state", 73 | "address_postal_code", "address_country") 74 | end, 75 | 76 | execute: lambda do |_connection, input| 77 | fields = input.reject { |key, _| key == "name" }. 78 | inject({}) do |hash, (key, value)| 79 | hash.merge(key => [{ raw: value }]) 80 | end 81 | post("/v2/accounts"). 82 | payload(name: input["name"], fieldValues: fields) 83 | end, 84 | 85 | output_fields: lambda do |object_definitions| 86 | object_definitions["account"] 87 | end, 88 | 89 | sample_output: lambda do 90 | get("/v2/accounts"). 91 | params(_limit: 1).dig("objects", 0) || {} 92 | end 93 | }, 94 | 95 | search_account: { 96 | description: "Search Account in " \ 97 | "SalesforceIQ", 98 | help: "Returns accounts matching the IDs. Returns all" \ 99 | " accounts, if blank.", 100 | 101 | input_fields: lambda do 102 | [ 103 | { name: "_ids", label: "Account identifiers", 104 | hint: "Comma separated list of account identifiers" } 105 | ] 106 | end, 107 | 108 | execute: lambda do |_connection, input| 109 | accounts = get("/v2/accounts", 110 | input)["objects"].each do |account| 111 | (account["fieldValues"] || {}).map do |k, v| 112 | account[k] = v.dig(0, "raw") 113 | end 114 | end 115 | 116 | { 117 | "accounts": accounts 118 | } 119 | end, 120 | 121 | output_fields: lambda do |object_definitions| 122 | [ 123 | { 124 | name: "accounts", type: :array, of: :object, 125 | properties: object_definitions["account"] 126 | } 127 | ] 128 | end, 129 | 130 | sample_output: lambda do 131 | get("/v2/accounts"). 132 | params(_limit: 1).dig("objects", 0) || {} 133 | end 134 | } 135 | }, 136 | 137 | triggers: { 138 | new_or_updated_accounts: { 139 | description: "New/Updated Account in " \ 140 | "SalesforceIQ", 141 | help: "Checks for new or updated accounts.", 142 | 143 | input_fields: lambda do 144 | [ 145 | { 146 | name: "since", type: :timestamp, 147 | sticky: true, label: "From", 148 | hint: "Fetch trigger events from specified time. If left blank," \ 149 | " accounts are processed from recipe start time" 150 | } 151 | ] 152 | end, 153 | 154 | poll: lambda do |_connection, input, _| 155 | limit = 50 156 | modified_date ||= ((input["since"].presence || Time.now.utc). 157 | to_time.to_f * 1000).to_i 158 | # result returns in ascending order 159 | result = get("/v2/accounts"). 160 | params(_limit: limit, _start: 0, 161 | modifiedDate: modified_date)["objects"] 162 | accounts = result.each do |account| 163 | (account["fieldValues"] || {}).map do |k, v| 164 | account[k] = v.dig(0, "raw") 165 | end 166 | end 167 | modified_date_since = accounts.dig(-1, "modifiedDate") || 168 | (Time.now.to_f * 1000).to_i 169 | 170 | { 171 | events: accounts, 172 | next_poll: modified_date_since, 173 | can_poll_more: accounts.size >= limit 174 | } 175 | end, 176 | 177 | dedup: lambda do |account| 178 | [account["id"], account["modifiedDate"]].join("_") 179 | end, 180 | 181 | output_fields: lambda do |object_definitions| 182 | object_definitions["account"] 183 | end, 184 | 185 | sample_output: lambda do 186 | get("/v2/accounts"). 187 | params(_limit: 1).dig("objects", 0) || {} 188 | end 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/mandrill_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Mandrill", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "api_key", 8 | label: "API key", 9 | control_type: "password", 10 | hint: "You may find the API key here", 12 | optional: false 13 | } 14 | ], 15 | 16 | authorization: { 17 | type: "api_key", 18 | 19 | acquire: ->(_connection) {}, 20 | 21 | refresh_on: [/"name"\:\s*"Invalid_Key"/], 22 | 23 | detect_on: [ 24 | /"status"\:\s*"error"/, 25 | /"reject_reason"\:"*"/, 26 | /"status"\:\s*"invalid"/ 27 | ], 28 | 29 | apply: ->(connection) { payload(key: connection["api_key"]) } 30 | }, 31 | 32 | base_uri: ->(_connection) { "https://mandrillapp.com" } 33 | }, 34 | 35 | test: ->(_connection) { post("/api/1.0/users/ping.json") }, 36 | 37 | object_definitions: { 38 | send_template: { 39 | fields: lambda { |_connection, config_fields| 40 | template_variables = if config_fields.blank? 41 | [] 42 | else 43 | post("/api/1.0/templates/info.json"). 44 | payload(name: config_fields["template_name"]). 45 | dig("code"). 46 | scan(/mc:edit=\"([^\"]*)\"/). 47 | map do |var| 48 | { 49 | name: var.first, 50 | hint: "Include html tags for better " \ 51 | "formatting" 52 | } 53 | end 54 | end 55 | 56 | if template_variables.blank? 57 | [] 58 | else 59 | [{ 60 | name: "template_content", 61 | type: "object", 62 | properties: template_variables 63 | }] 64 | end.concat([ 65 | { 66 | name: "message", 67 | optional: false, 68 | type: "object", 69 | properties: [ 70 | { 71 | name: "from_email", 72 | hint: "The sender email address", 73 | optional: false 74 | }, 75 | { 76 | name: "from_name", 77 | hint: "The sender name" 78 | }, 79 | { 80 | name: "to", 81 | hint: "List of email recipients, one per line.", 82 | optional: false 83 | }, 84 | { 85 | name: "important", 86 | hint: "Whether or not this message is important, " \ 87 | "and should be delivered ahead of non-important " \ 88 | "messages.", 89 | control_type: "checkbox", 90 | type: "boolean" 91 | }, 92 | { 93 | name: "track_opens", 94 | hint: "Whether or not to turn on open tracking " \ 95 | "for the message", 96 | control_type: "checkbox", 97 | type: "boolean" 98 | }, 99 | { 100 | name: "track_clicks", 101 | hint: "Whether or not to turn on click tracking " \ 102 | "for the message", 103 | control_type: "checkbox", 104 | type: "boolean" 105 | } 106 | ] 107 | }, 108 | { 109 | name: "send_at", 110 | hint: "When this message should be sent. If you " \ 111 | "specify a time in the past, the message will be " \ 112 | "sent immediately.", 113 | type: "timestamp" 114 | } 115 | ]) 116 | } 117 | } 118 | }, 119 | 120 | actions: { 121 | send_message: { 122 | description: "Send message using " \ 123 | "template in Mandrill", 124 | 125 | config_fields: [ 126 | { 127 | name: "template_name", 128 | control_type: "select", 129 | pick_list: "templates", 130 | optional: false 131 | } 132 | ], 133 | 134 | input_fields: lambda { |object_definitions| 135 | object_definitions["send_template"] 136 | }, 137 | 138 | execute: lambda { |_connection, input| 139 | input["template_content"] = (input["template_content"] || []). 140 | map do |key, val| 141 | { name: key, content: val } 142 | end 143 | input["message"]["to"] = (input["message"]["to"] || ""). 144 | split("\n"). 145 | map { |to| { email: to.strip } } 146 | if input["send_at"].present? 147 | input["send_at"] = input["send_at"]. 148 | to_time. 149 | utc. 150 | strftime("%Y-%m-%d %H:%M:%S.%6N") 151 | end 152 | 153 | post("/api/1.0/messages/send-template.json", input).dig(0) || {} 154 | }, 155 | 156 | output_fields: lambda { |_object_definitions| 157 | [{ name: "email" }, 158 | { name: "status" }, 159 | { name: "_id" }] 160 | }, 161 | 162 | sample_output: lambda { 163 | { 164 | email: "mail@workato.com", 165 | status: "send", 166 | _id: "abc123abc123abc123abc123abc123" 167 | } 168 | } 169 | } 170 | }, 171 | 172 | pick_lists: { 173 | templates: lambda { |_connection| 174 | post("/api/1.0/templates/list.json").pluck("name", "slug") 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/splunk_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Splunk", 3 | secure_tunnel: true, 4 | 5 | connection: { 6 | fields: [ 7 | { 8 | name: "server_url", 9 | label: "Server URL", 10 | control_type: "text", 11 | hint: "The URL of the Splunk management port " \ 12 | "(e.g. https://yourdomain:8089). You MUST install the " \ 13 | "" \ 14 | "Workato Add-on for Splunk first." 15 | }, 16 | { 17 | name: "username", 18 | hint: "The Splunk username (e.g. admin)" 19 | }, 20 | { 21 | name: "password", 22 | control_type: "password", 23 | hint: "The password for the Splunk username" 24 | } 25 | ], 26 | 27 | authorization: { 28 | type: "basic_auth", 29 | credentials: lambda do |connection| 30 | user(connection["username"]) 31 | password(connection["password"]) 32 | end 33 | } 34 | }, 35 | 36 | test: lambda do |connection| 37 | get("#{connection['server_url']}/services/workato/version") 38 | end, 39 | 40 | object_definitions: { 41 | generic_alert: { 42 | fields: lambda do |_connection, config_fields| 43 | config_fields["fields"].split(",").map do |name| 44 | { 45 | name: name.strip 46 | } 47 | end 48 | end 49 | }, 50 | 51 | service_alert: { 52 | fields: lambda do |_connection, _config_fields| 53 | [ 54 | { name: "event_id", type: :string, optional: false }, 55 | { name: "severity", type: :string }, 56 | { name: "title", type: :string }, 57 | { name: "_time", type: :integer, optional: false }, 58 | { name: "severity_label", type: :string }, 59 | { name: "description", type: :string }, 60 | ] 61 | end 62 | }, 63 | }, 64 | 65 | triggers: { 66 | new_generic_alert: { 67 | input_fields: lambda do |_object_definitions| 68 | [{ 69 | name: "search_name", 70 | label: "Splunk alert", 71 | type: :string, 72 | control_type: :select, 73 | pick_list: "saved_searches", 74 | optional: false, 75 | hint: "Select one of the alerts saved in Splunk that " \ 76 | "have the Workato alert action assigned.", 77 | }] 78 | end, 79 | 80 | config_fields: [ 81 | { 82 | name: "fields", 83 | label: "Alert fields", 84 | type: :string, 85 | optional: false, 86 | hint: "Comma-separated field names to be taken over from the Splunk" \ 87 | "data (e.g. host, count)", 88 | } 89 | ], 90 | 91 | webhook_subscribe: lambda do |callback_url, connection, input, _flow_id| 92 | data = post( 93 | "#{connection['server_url']}/services/workato/alerts", 94 | callback_url: callback_url, 95 | search_name: input["search_name"] 96 | ) 97 | { 98 | server_url: connection["server_url"], 99 | search_name: data["search_name"], 100 | callback_url: data["callback_url"] 101 | } 102 | end, 103 | 104 | webhook_unsubscribe: lambda do |subscription| 105 | delete( 106 | "#{subscription['server_url']}/services/workato/alerts", 107 | search_name: subscription["search_name"], 108 | callback_url: subscription["callback_url"] 109 | ) 110 | end, 111 | 112 | webhook_notification: lambda do |_input, payload| 113 | payload 114 | end, 115 | 116 | dedup: lambda do |_event| 117 | rand() 118 | end, 119 | 120 | output_fields: lambda do |object_definitions| 121 | object_definitions["generic_alert"] 122 | end 123 | }, 124 | new_service_alert: { 125 | webhook_subscribe: lambda do |callback_url, connection, _input, _flow_id| 126 | data = post( 127 | "#{connection['server_url']}/services/workato/servicealerts", 128 | callback_url: callback_url 129 | ) 130 | { 131 | server_url: connection["server_url"], 132 | search_name: data["search_name"], 133 | callback_url: data["callback_url"] 134 | } 135 | end, 136 | 137 | webhook_unsubscribe: lambda do |subscription| 138 | delete( 139 | "#{subscription['server_url']}/services/workato/servicealerts", 140 | search_name: subscription["search_name"], 141 | callback_url: subscription["callback_url"] 142 | ) 143 | end, 144 | 145 | webhook_notification: lambda do |_input, payload| 146 | payload 147 | end, 148 | 149 | dedup: lambda do |event| 150 | event["event_id"] 151 | end, 152 | 153 | output_fields: lambda do |object_definitions| 154 | object_definitions["service_alert"] 155 | end 156 | }, 157 | }, 158 | 159 | pick_lists: { 160 | saved_searches: lambda do |connection| 161 | get("#{connection['server_url']}/services/workato/alerts"). 162 | map { |name| [name,name] } 163 | end 164 | }, 165 | 166 | actions: { 167 | send_event_to_splunk: { 168 | input_fields: lambda do 169 | [ 170 | { 171 | name: "payload", 172 | optional: false, 173 | }, 174 | { 175 | name: "index", 176 | hint: "The name of the repository for Splunk to store the event in." 177 | }, 178 | { 179 | name: "source", 180 | hint: "The source value to assign to the event data. For example," \ 181 | " if you're sending data from an app you're developing, " \ 182 | "you could set this key to the name of the app." 183 | }, 184 | { 185 | name: "sourcetype", 186 | hint: "The sourcetype value to assign to the event data. " \ 187 | "It identifies the data structure of an event. " \ 188 | "A source type determines how Splunk formats the " \ 189 | "data during the indexing and also parses the data " \ 190 | "during searching process." 191 | }, 192 | { 193 | name: "host", 194 | hint: "The host value to assign to the event data. " \ 195 | "This is typically the hostname of the " \ 196 | "client/server/service from which the data came from." 197 | }, 198 | ] 199 | end, 200 | 201 | execute: lambda do |connection, input| 202 | post( 203 | "#{connection['server_url']}/services/workato/events", 204 | payload: input["payload"], 205 | index: input["index"], 206 | source: input["source"], 207 | sourcetype: input["sourcetype"], 208 | host: input["host"] 209 | ) 210 | end, 211 | 212 | output_fields: lambda do |_object_definitions| 213 | [] 214 | end 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /custom_connectors/basic_auth/clearbit_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Clearbit', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'api_key', 8 | control_type: 'password', 9 | hint: 'Can be found here: https://dashboard.clearbit.com/keys' 10 | } 11 | ], 12 | 13 | authorization: { 14 | type: 'basic_auth', 15 | 16 | # clearbit uses api key only for authentication. treats api key as username and password left blank 17 | # curl -u "{your api_key}:" "https://person.clearbit.com/v1/people/email/eeshansim@gmail.com" 18 | credentials: ->(connection) { 19 | user(connection['api_key']) 20 | password("") 21 | } 22 | } 23 | }, 24 | 25 | test: ->(connection) { 26 | get("https://person.clearbit.com/v1/people/email/eeshansim@gmail.com") 27 | }, 28 | 29 | object_definitions: { 30 | person: { 31 | fields: ->() { 32 | [ 33 | { name: 'id' }, 34 | { name: 'name', type: :object, properties: [ 35 | { name: 'fullName' }, 36 | { name: 'givenName' }, 37 | { name: 'familyName' } 38 | ]}, 39 | { name: 'email' }, 40 | { name: 'gender' }, 41 | { name: 'timeZone' }, 42 | { name: 'avatar' }, 43 | { name: 'geo', type: :object, properties: [ 44 | { name: 'city' }, 45 | { name: 'stateCode' }, 46 | { name: 'countryCode' } 47 | ]}, 48 | { name: 'bio' }, 49 | { name: 'fuzzy', type: :boolean }, 50 | { name: 'site' }, 51 | { name: 'employment', type: :object, properties: [ 52 | { name: 'domain' }, 53 | { name: 'name' }, 54 | { name: 'title' }, 55 | { name: 'role' }, 56 | { name: 'seniority' } 57 | ]}, 58 | { name: 'facebook', type: :object, properties: [ 59 | { name: 'handle' } 60 | ]}, 61 | { name: 'linkedin', type: :object, properties: [ 62 | { name: 'handle' } 63 | ]}, 64 | { name: 'googleplus', type: :object, properties: [ 65 | { name: 'handle' } 66 | ]}, 67 | { name: 'twitter', type: :object, properties: [ 68 | { name: 'handle' }, 69 | { name: 'id' }, 70 | { name: 'bio' }, 71 | { name: 'followers', type: :integer }, 72 | { name: 'following', type: :integer }, 73 | { name: 'site' } 74 | ]}, 75 | { name: 'github', type: :object, properties: [ 76 | { name: 'handle' }, 77 | { name: 'company' }, 78 | { name: 'blog' }, 79 | { name: 'followers', type: :integer }, 80 | { name: 'following', type: :integer } 81 | ]}, 82 | { name: 'angellist', type: :object, properties: [ 83 | { name: 'handle' }, 84 | { name: 'bio' }, 85 | { name: 'blog' }, 86 | { name: 'site' }, 87 | { name: 'followers', type: :integer } 88 | ]}, 89 | { name: 'aboutme', type: :object, properties: [ 90 | { name: 'handle' }, 91 | { name: 'bio' } 92 | ]}, 93 | { name: 'gravatar', type: :object, properties: [ 94 | { name: 'handle' }, 95 | { name: 'urls', type: :object, properties: [ 96 | { name: 'value', type: :url }, 97 | { name: 'title' } 98 | ]} 99 | ]} 100 | ] 101 | } 102 | }, 103 | company: { 104 | fields: ->() { 105 | [ 106 | { name: 'id' }, 107 | { name: 'name' }, 108 | { name: 'legalName' }, 109 | { name: 'domain' }, 110 | { name: 'site', type: :object, properties: [ 111 | { name: 'url' }, 112 | { name: 'title' }, 113 | { name: 'metaDescription' }, 114 | { name: 'metaAuthor' }, 115 | ]}, 116 | { name: 'location' }, 117 | { name: 'geo', type: :object, properties: [ 118 | { name: 'streetNumber' }, 119 | { name: 'streetName' }, 120 | { name: 'subPremise' }, 121 | { name: 'city' }, 122 | { name: 'state' }, 123 | { name: 'stateCode' }, 124 | { name: 'postalCode' }, 125 | { name: 'country' }, 126 | { name: 'countryCode' } 127 | ]}, 128 | { name: 'timeZone' }, 129 | { name: 'description' }, 130 | { name: 'foundedDate', type: :date }, 131 | { name: 'metrics', type: :object, properties: [ 132 | { name: 'raised', type: :integer }, 133 | { name: 'employees', type: :integer }, 134 | { name: 'googleRank' }, 135 | { name: 'annualRevenue', type: :integer } 136 | ]}, 137 | { name: 'logo' }, 138 | { name: 'facebook', type: :object, properties: [ 139 | { name: 'handle' } 140 | ]}, 141 | { name: 'linkedin', type: :object, properties: [ 142 | { name: 'handle' } 143 | ]}, 144 | { name: 'twitter', type: :object, properties: [ 145 | { name: 'handle' }, 146 | { name: 'id' }, 147 | { name: 'bio' }, 148 | { name: 'followers', type: :integer }, 149 | { name: 'following', type: :integer }, 150 | { name: 'site' } 151 | ]}, 152 | { name: 'angellist', type: :object, properties: [ 153 | { name: 'id' }, 154 | { name: 'handle' }, 155 | { name: 'description' }, 156 | { name: 'blogUrl' }, 157 | { name: 'followers', type: :integer }, 158 | ]}, 159 | { name: 'crunchbase', type: :object, properties: [ 160 | { name: 'handle' }, 161 | ]}, 162 | { name: 'phone' }, 163 | { name: 'emailProvider', type: :boolean }, 164 | ] 165 | } 166 | } 167 | }, 168 | 169 | actions: { 170 | 171 | email_lookup: { 172 | input_fields: ->() { 173 | [ 174 | { name: 'email', optional: false } 175 | ] 176 | }, 177 | execute: ->(connection, input) { 178 | get("https://person.clearbit.com/v2/combined/find?email=#{input['email']}") 179 | }, 180 | output_fields: ->(object_definitions) { 181 | [ 182 | { name: 'person', type: :object, properties: object_definitions['person'] }, 183 | { name: 'company', type: :object, properties: object_definitions['company'] } 184 | ] 185 | } 186 | }, 187 | 188 | company_lookup: { 189 | input_fields: ->() { 190 | [ 191 | { name: 'company_name', optional: false } 192 | ] 193 | }, 194 | execute: ->(connection, input) { 195 | query = "?page_size=1&limit=1&query=name:#{input['company_name']}" 196 | { 197 | 'companies': get("https://company.clearbit.com/v1/companies/search" + query)['results'] 198 | } 199 | }, 200 | output_fields: ->(object_definitions) { 201 | [ 202 | { name: 'companies', type: :array, of: :object, properties: object_definitions['company'] } 203 | ] 204 | } 205 | } 206 | }, 207 | 208 | triggers: {} 209 | } 210 | -------------------------------------------------------------------------------- /custom_connectors/none/statuspage.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | { 4 | title: 'Statuspage', 5 | 6 | connection: { 7 | fields: [ 8 | { 9 | name: 'status_page_url', 10 | label: 'Statuspage URL', 11 | optional: false, 12 | hint: 'For example: https://status.workato.com/' 13 | } 14 | ], 15 | 16 | base_uri: lambda { |connection| 17 | connection['status_page_url'] 18 | }, 19 | 20 | authorization: { 21 | type: 'no_auth' 22 | } 23 | }, 24 | 25 | object_definitions: { 26 | incident_update: { 27 | fields: lambda do |_connection, _config_fields| 28 | [ 29 | { 30 | control_type: 'text', 31 | label: 'Status', 32 | type: 'string', 33 | name: 'status' 34 | }, 35 | { 36 | control_type: 'text', 37 | label: 'Body', 38 | type: 'string', 39 | name: 'body' 40 | }, 41 | { 42 | control_type: 'text', 43 | label: 'Created at', 44 | type: 'date_time', 45 | name: 'created_at' 46 | }, 47 | { 48 | control_type: 'text', 49 | label: 'Wants twitter update', 50 | toggle_hint: 'Select from option list', 51 | toggle_field: { 52 | label: 'Wants twitter update', 53 | control_type: 'text', 54 | toggle_hint: 'Use custom value', 55 | type: 'boolean', 56 | name: 'wants_twitter_update' 57 | }, 58 | type: 'boolean', 59 | name: 'wants_twitter_update' 60 | }, 61 | { 62 | control_type: 'text', 63 | label: 'Twitter updated at', 64 | type: 'date_time', 65 | name: 'twitter_updated_at' 66 | }, 67 | { 68 | control_type: 'text', 69 | label: 'Updated at', 70 | type: 'date_time', 71 | name: 'updated_at' 72 | }, 73 | { 74 | control_type: 'text', 75 | label: 'Display at', 76 | type: 'date_time', 77 | name: 'display_at' 78 | }, 79 | { 80 | control_type: 'text', 81 | label: 'Affected components', 82 | type: 'string', 83 | name: 'affected_components' 84 | }, 85 | { 86 | control_type: 'text', 87 | label: 'Custom tweet', 88 | type: 'string', 89 | name: 'custom_tweet' 90 | }, 91 | { 92 | control_type: 'text', 93 | label: 'Deliver notifications', 94 | toggle_hint: 'Select from option list', 95 | toggle_field: { 96 | label: 'Deliver notifications', 97 | control_type: 'text', 98 | toggle_hint: 'Use custom value', 99 | type: 'boolean', 100 | name: 'deliver_notifications' 101 | }, 102 | type: 'boolean', 103 | name: 'deliver_notifications' 104 | }, 105 | { 106 | control_type: 'text', 107 | label: 'Tweet ID', 108 | type: 'string', 109 | name: 'tweet_id' 110 | }, 111 | { 112 | control_type: 'text', 113 | label: 'ID', 114 | type: 'string', 115 | name: 'id' 116 | }, 117 | { 118 | control_type: 'text', 119 | label: 'Incident ID', 120 | type: 'string', 121 | name: 'incident_id' 122 | } 123 | ] 124 | end 125 | } 126 | }, 127 | 128 | test: lambda do |_connection| 129 | get('history.json') 130 | end, 131 | 132 | actions: { 133 | list_incident_updates: { 134 | description: "List incedent updates " \ 135 | "in Statuspage", 136 | 137 | execute: lambda do |_connection, _input| 138 | incedents = get('history.json')['months']&.pluck('incidents') 139 | incedent_codes = [] 140 | incedents.select { |incedent| incedent&.pluck('code').present? } 141 | .each do |incedent| 142 | incedent_codes.concat(incedent&.pluck('code').presence) 143 | end 144 | incident_updates = [] 145 | incedent_codes.each do |incedent| 146 | incident_updates.concat(get("incidents/#{incedent}.json") 147 | .[]('incident_updates')) 148 | end 149 | 150 | { incident_updates: incident_updates } 151 | end, 152 | 153 | output_fields: lambda do |object_definitions| 154 | [{ 155 | name: 'incident_updates', 156 | type: 'array', 157 | of: 'object', 158 | properties: object_definitions['incident_update'] 159 | }] 160 | end, 161 | 162 | sample_output: lambda do |_connection, _input| 163 | incedent_code = get('history.json')['months'] 164 | &.pluck('incidents') 165 | &.select { |incedent| incedent&.pluck('code').present? } 166 | &.[](0)&.pluck('code')&.[](0).presence 167 | 168 | incident_updates = if incedent_code 169 | get("incidents/#{incedent_code}.json") 170 | .[]('incident_updates') 171 | end || [] 172 | 173 | { incident_updates: incident_updates } 174 | end 175 | } 176 | }, 177 | 178 | triggers: { 179 | new_updated_incident_update: { 180 | title: 'New/updated incident update', 181 | description: "New or updated incident update" \ 182 | " in Statuspage", 183 | type: 'paging_desc', 184 | 185 | poll: lambda do |_connection, _input, _last_updated_since| 186 | incedents = get('history.json')['months']&.pluck('incidents') 187 | incedent_codes = [] 188 | incedents.select { |incedent| incedent&.pluck('code').present? } 189 | .each do |incedent| 190 | incedent_codes.concat(incedent&.pluck('code').presence) 191 | end 192 | incident_updates = [] 193 | incedent_codes.each do |incedent| 194 | incident_updates.concat(get("incidents/#{incedent}.json") 195 | .[]('incident_updates')) 196 | end 197 | 198 | { events: incident_updates, next_page: nil } 199 | end, 200 | 201 | document_id: ->(incident_update) { incident_update['id'] }, 202 | 203 | sort_by: ->(incident_update) { incident_update['updated_at'] }, 204 | 205 | output_fields: lambda do |object_definitions| 206 | object_definitions['incident_update'] 207 | end, 208 | 209 | sample_output: lambda do |_connection, _input| 210 | incedent_code = get('history.json')['months'] 211 | &.pluck('incidents') 212 | &.select { |incedent| incedent&.pluck('code').present? } 213 | &.[](0)&.pluck('code')&.[](0).presence 214 | 215 | incident_updates = if incedent_code 216 | get("incidents/#{incedent_code}.json") 217 | .[]('incident_updates') 218 | end || [] 219 | 220 | { incident_updates: incident_updates } 221 | end 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/formassembly.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "FormAssembly", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "client_id", 8 | optional: false, 9 | hint: "Get your API key here" 12 | }, 13 | { 14 | name: "client_secret", 15 | optional: false, 16 | control_type: :password 17 | }, 18 | { 19 | name: "endpoint", 20 | label: "Edition", 21 | control_type: "select", 22 | pick_list: [ 23 | %w[FormAssembly\ Developer\ Sandbox https://developer.formassembly.com], 24 | %w[FormAssembly.com https://app.formassembly.com], 25 | %w[FormAssembly\ Enterprise\ Cloud https://base.tfaforms.net], 26 | %w[FormAssembly\ Enterprise\ On-Site https://base/formassembly] 27 | ], 28 | optional: false 29 | }, 30 | { 31 | name: "base_url", 32 | ngIf: 'input.endpoint == "https://base/formassembly" || 33 | input.endpoint == "https://base.tfaforms.net"', 34 | label: "Base URL", 35 | hint: "Use 'workato_formassembly' if that is your instance name or " \ 36 | "'workato.formassembly.com' if your server is " \ 37 | "'https://workato.formassembly.com/'" 38 | }, 39 | ], 40 | 41 | base_uri: lambda do |connection| 42 | connection['endpoint'].gsub("base", connection['base_url'] || '') 43 | end, 44 | 45 | authorization: { 46 | type: 'oauth2', 47 | 48 | authorization_url: ->(connection) { 49 | base_url = connection['endpoint'].gsub("base", connection['base_url'] || '') 50 | "#{base_url}/oauth/login?type=web&client_id=#{connection['client_id']}" + 51 | "&response_type=code" 52 | }, 53 | 54 | acquire: lambda do |connection, auth_code, redirect_uri| 55 | base_url = connection['endpoint'].gsub("base", connection['base_url'] || '') 56 | response = post("#{base_url}/oauth/access_token?type=web_server"). 57 | payload( 58 | client_id: connection['client_id'] || '', 59 | client_secret: connection['client_secret'] || '', 60 | grant_type: 'authorization_code', 61 | code: auth_code, 62 | redirect_uri: redirect_uri 63 | ).request_format_www_form_urlencoded 64 | [ 65 | { 66 | access_token: response['access_token'], 67 | refresh_token: response['refresh_token'] 68 | }, 69 | nil, 70 | nil 71 | ] 72 | end, 73 | 74 | refresh: lambda do |connection, refresh_token| 75 | base_url = connection['endpoint']. 76 | gsub("base", connection['base_url'] || "") 77 | post("#{base_url}/oauth/access_token?type=web_server"). 78 | payload(client_id: connection['client_id'], 79 | client_secret: connection['client_secret'], 80 | grant_type: 'refresh_token', 81 | refresh_token: refresh_token). 82 | request_format_www_form_urlencoded 83 | end, 84 | 85 | refresh_on: [401, 403], 86 | 87 | apply: ->(_connection, access_token) { 88 | params(access_token: access_token) 89 | }, 90 | }, 91 | }, 92 | 93 | object_definitions: { 94 | form: { 95 | fields: lambda do |_connection, _config_fields| 96 | [ 97 | { name: "id", type: "integer" }, 98 | { name: "version_id", type: "integer" }, 99 | { name: "name" }, 100 | { name: "category" }, 101 | { name: "subcategory" }, 102 | { name: "is_template", type: "integer", 103 | hint: "0: Not a template
0: Is a template" }, 104 | { name: "display_status", type: "integer", 105 | hint: "0: Archived
2: Active" }, 106 | { name: "moderation_status", type: "integer", 107 | hint: "0: Not checked
2: " \ 108 | "Reviewed and approved
3: Reviewed and denied" }, 109 | { name: "expired" }, 110 | { name: "use_ssl" }, 111 | { name: "user_id" }, 112 | { name: "created", type: "timestamp" }, 113 | { name: "modified", type: "timestamp" }, 114 | { 115 | name: "Aggregate_metadata", 116 | type: "object", 117 | properties: [ 118 | { name: "id", type: "integer" }, 119 | { name: "response_count", type: "integer" }, 120 | { name: "submitted_count", type: "integer" }, 121 | { name: "saved_count", type: "integer" }, 122 | { name: "unread_count", type: "integer" }, 123 | { name: "dropout_rate" }, 124 | { name: "average_completion_time" }, 125 | { name: "is_uptodate", type: "boolean" } 126 | ] 127 | } 128 | ] 129 | end 130 | }, 131 | 132 | connector: { 133 | fields: lambda do |_connection, _config_fields| 134 | [ 135 | { name: "id", type: "integer" }, 136 | { name: "form_id", type: "integer" }, 137 | { name: "name" }, 138 | { name: "status", type: "integer", 139 | hint: "0: Disabled
1: Enabled" }, 140 | { name: "event" } 141 | ] 142 | end 143 | } 144 | }, 145 | 146 | actions: { 147 | list_forms: { 148 | description: "List forms in " \ 149 | " FormAssembly ", 150 | 151 | execute: ->(_connection, _input) { 152 | get("/api_v1/forms/index.json") 153 | }, 154 | 155 | output_fields: lambda do |object_definitions| 156 | [ 157 | { 158 | name: "Forms", 159 | type: "array", 160 | of: "object", 161 | properties: [ 162 | { 163 | name: "Form", 164 | type: "object", 165 | properties: object_definitions['form'] 166 | } 167 | ] 168 | } 169 | ] 170 | end, 171 | 172 | sample_output: lambda do |_connection| 173 | get("/api_v1/forms/index.json"). 174 | dig("Forms", "Form", 0) || {} 175 | end 176 | }, 177 | 178 | list_connectors: { 179 | description: "List connectors in" \ 180 | " FormAssembly ", 181 | 182 | config_fields: [ 183 | { 184 | name: "form", 185 | control_type: "select", 186 | pick_list: "forms", 187 | optional: false 188 | } 189 | ], 190 | 191 | execute: ->(_connection, input) { 192 | get("/api_v1/connectors/index/#{input['form']}.json") 193 | }, 194 | 195 | output_fields: lambda do |object_definitions| 196 | [ 197 | { 198 | name: "Connectors", 199 | type: "array", 200 | of: "object", 201 | properties: [ 202 | { 203 | name: "Connector", 204 | type: "object", 205 | properties: object_definitions['connector'] 206 | } 207 | ] 208 | } 209 | ] 210 | end, 211 | 212 | sample_output: lambda do |_connection, input| 213 | get("/api_v1/connectors/index/#{input['form']}.json"). 214 | dig("Connectors", "Connector", 0) || {} 215 | end 216 | }, 217 | 218 | get_connector_by_id: { 219 | description: "Get connector details " \ 220 | "in FormAssembly ", 221 | 222 | input_fields: lambda do 223 | [ 224 | { name: "connector_id", optional: false } 225 | ] 226 | end, 227 | 228 | execute: ->(_connection, input) { 229 | get("/api_v1/connectors/view/#{input['connector_id']}.json") 230 | }, 231 | 232 | output_fields: lambda do |object_definitions| 233 | [ 234 | { 235 | name: "Form", 236 | type: "object", 237 | properties: [ 238 | { name: "id", type: "integer" }, 239 | { name: "version_id", type: "integer" }, 240 | { name: "name" } 241 | ] 242 | }, 243 | { 244 | name: "Connector", 245 | type: "object", 246 | properties: object_definitions['connector'] 247 | } 248 | ] 249 | end, 250 | 251 | sample_output: lambda do |_connection, input| 252 | get("/api_v1/connectors/view/#{input['connector_id']}.json"). 253 | dig("Connectors", "Connector", 0) || {} 254 | end 255 | } 256 | }, 257 | 258 | pick_lists: { 259 | forms: lambda do |_connection| 260 | get("/api_v1/forms/index.json")['Forms']. 261 | map do |form| 262 | form_data = form['Form'] 263 | [form_data['name'], form_data['id']] 264 | end 265 | end 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/neto_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Neto', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'domain', 8 | control_type: 'subdomain', 9 | url: '.neto.com.au', 10 | optional: false 11 | }, 12 | { 13 | name: 'api_key', 14 | control_type: 'password', optional: false 15 | } 16 | ], 17 | 18 | authorization: { 19 | type: 'custom_auth', 20 | 21 | credentials: ->(connection) { 22 | headers('NETOAPI_KEY': connection['api_key']) 23 | } 24 | } 25 | }, 26 | 27 | object_definitions: { 28 | 29 | customer: { 30 | 31 | fields: ->() { 32 | [ 33 | { name: 'ID' }, 34 | { name: 'EmailAddress', control_type: 'email' }, 35 | { name: 'Username' }, 36 | { name: 'DateUpdated', type: :date_time }, 37 | { name: 'DateAdded', type: :date_time } 38 | ] 39 | } 40 | }, 41 | 42 | order: { 43 | 44 | fields: ->() { 45 | [ 46 | { name: "OrderID" }, 47 | { name: "ShippingOption" }, 48 | { name: "DeliveryInstruction" }, 49 | { name: "Username" }, 50 | { name: "Email", control_type: 'email' }, 51 | { name: "ShipAddress" }, 52 | { name: "BillAddress" }, 53 | { name: "PurchaseOrderNumber" }, 54 | { name: "CustomerRef1" }, 55 | { name: "CustomerRef2" }, 56 | { name: "CustomerRef3" }, 57 | { name: "CustomerRef4" }, 58 | { name: "SalesChannel" }, 59 | { name: "GrandTotal" }, 60 | { name: "TaxInclusive" }, 61 | { name: "OrderTax" }, 62 | { name: "SurchargeTotal" }, 63 | { name: "SurchargeTaxable" }, 64 | { name: "ProductSubtotal" }, 65 | { name: "ShippingTotal" }, 66 | { name: "ShippingTax" }, 67 | { name: "ClientIPAddress" }, 68 | { name: "CouponCode" }, 69 | { name: "CouponDiscount" }, 70 | { name: "ShippingDiscount" }, 71 | { name: "OrderType" }, 72 | { name: "OrderStatus" }, 73 | { name: "OrderPayment", type: :array, of: :object, parse_output: :item_array_wrap, properties: [ 74 | { name: "OrderPaymentId" }, 75 | { name: "OrderPaymentAmount" }, 76 | { name: "PaymentType" } 77 | ]}, 78 | { name: "DateUpdated", type: :date_time }, 79 | { name: "DatePlaced", type: :date_time }, 80 | { name: "DateRequired", type: :date_time }, 81 | { name: "DateInvoiced", type: :date_time }, 82 | { name: "DatePaid", type: :date_time }, 83 | { name: "DateCompleted", type: :date_time }, 84 | { name: "OrderLine", type: :array, of: :object, parse_output: :item_array_wrap, properties: [ 85 | { name: "ProductName" }, 86 | { name: "ItemNotes" }, 87 | { name: "PickQuantity" }, 88 | { name: "BackorderQuantity" }, 89 | { name: "UnitPrice" }, 90 | { name: "Tax" }, 91 | { name: "TaxCode" }, 92 | { name: "WarehouseID", type: :integer }, 93 | { name: "WarehouseName" }, 94 | { name: "WarehouseReference" }, 95 | { name: "Quantity", type: :integer }, 96 | { name: "PercentDiscount" }, 97 | { name: "ProductDiscount" }, 98 | { name: "CouponDiscount" }, 99 | { name: "CostPrice" }, 100 | { name: "ShippingMethod" }, 101 | { name: "ShippingTracking" }, 102 | { name: "Weight" }, 103 | { name: "Cubic" }, 104 | { name: "Extra" }, 105 | { name: "eBay", type: :object, properties: [ 106 | { name: "eBayUsername" }, 107 | { name: "eBayStoreName" }, 108 | { name: "eBayTransactionID" }, 109 | { name: "eBayAuctionID" }, 110 | { name: "ListingType" }, 111 | { name: "DateCreated", type: :date_time }, 112 | { name: "DatePaid", type: :date_time }, 113 | ]} 114 | ]}, 115 | { name: "ShippingSignature" }, 116 | { name: "RealtimeConfirmation" }, 117 | { name: "InternalOrderNotes" }, 118 | { name: "CompleteStatus" }, 119 | { name: "UserGroup" }, 120 | { name: "StickyNotes", type: :array, of: :object, parse_output: :item_array_wrap, properties: [ 121 | { name: "Title" }, 122 | { name: "Description" } 123 | ]} 124 | ] 125 | } 126 | } 127 | }, 128 | 129 | test: ->(connection) { 130 | post("https://#{connection['domain']}.neto.com.au/do/WS/NetoAPI").headers('NETOAPI_ACTION': 'GetCustomer') 131 | }, 132 | 133 | triggers: { 134 | 135 | new_updated_customer: { 136 | description: 'New or Updated customer in Neto', 137 | 138 | input_fields: ->() { 139 | [ 140 | { 141 | name: 'since', 142 | type: :timestamp, 143 | hint: 'Defaults to customer updated after the recipe is first started' 144 | } 145 | ] 146 | }, 147 | 148 | poll: ->(connection, input, page) { 149 | page ||= 0 150 | limit = 50 151 | updated_since = input['since'] || Time.now 152 | 153 | payload = { 154 | "filter" => { 155 | "DateUpdatedFrom" => (updated_since).utc.strftime("%F %T"), 156 | "Page" => page, 157 | "Limit" => limit, 158 | "OutputSelector" => ["Username", "ID", "EmailAddress", "DateUpdated", "DateAdded"] 159 | }, 160 | } 161 | 162 | customers = post("https://#{connection['domain']}.neto.com.au/do/WS/NetoAPI", payload). 163 | headers('NETOAPI_ACTION': 'GetCustomer')['Customer'] 164 | 165 | { 166 | events: customers, 167 | next_poll: (page + 1), 168 | can_poll_more: customers.length == limit 169 | } 170 | }, 171 | 172 | dedup: ->(customer) { 173 | customer['ID'] + "@" + customer['DateUpdated'] 174 | }, 175 | 176 | output_fields: ->(object_definitions) { 177 | object_definitions['customer'] 178 | } 179 | }, 180 | 181 | new_updated_order: { 182 | description: 'New or Updated order in Neto', 183 | 184 | input_fields: ->() { 185 | [ 186 | { 187 | name: 'since', 188 | type: :timestamp, 189 | hint: 'Defaults to customer updated after the recipe is first started' 190 | } 191 | ] 192 | }, 193 | 194 | poll: ->(connection, input, page) { 195 | page ||= 0 196 | limit = 50 197 | updated_since = input['since'] || Time.now 198 | 199 | payload = { 200 | "filter" => { 201 | "DateUpdatedFrom" => (updated_since).utc.strftime("%F %T"), 202 | "Page" => page, 203 | "Limit" => limit, 204 | "OutputSelector" => [ 205 | "ID","ShippingOption","DeliveryInstruction","Username", 206 | "Email","ShipAddress","BillAddress","PurchaseOrderNumber", 207 | "CustomerRef1","CustomerRef2","CustomerRef3","CustomerRef4", 208 | "SalesChannel","GrandTotal","TaxInclusive","OrderTax", 209 | "SurchargeTotal","SurchargeTaxable","ProductSubtotal", 210 | "ShippingTotal","ShippingTax","ClientIPAddress","CouponCode", 211 | "CouponDiscount","ShippingDiscount","OrderType","OrderStatus", 212 | "OrderPayment","OrderPayment.PaymentType","OrderPayment.DatePaid", 213 | "DateUpdated","DatePlaced","DateRequired","DateInvoiced", 214 | "DatePaid","DateCompleted","OrderLine","OrderLine.ProductName", 215 | "OrderLine.ItemNotes","OrderLine.PickQuantity","OrderLine.BackorderQuantity", 216 | "OrderLine.UnitPrice","OrderLine.Tax","OrderLine.TaxCode", 217 | "OrderLine.WarehouseID","OrderLine.WarehouseName", 218 | "OrderLine.WarehouseReference","OrderLine.Quantity","OrderLine.PercentDiscount", 219 | "OrderLine.ProductDiscount","OrderLine.CouponDiscount","OrderLine.CostPrice", 220 | "OrderLine.ShippingMethod","OrderLine.ShippingTracking","OrderLine.Weight", 221 | "OrderLine.Cubic","OrderLine.Extra","ShippingSignature","RealtimeConfirmation", 222 | "InternalOrderNotes","OrderLine.eBay.eBayUsername","OrderLine.eBay.eBayStoreName", 223 | "OrderLine.eBay.eBayTransactionID","OrderLine.eBay.eBayAuctionID", 224 | "OrderLine.eBay.ListingType","OrderLine.eBay.DateCreated","CompleteStatus", 225 | "OrderLine.eBay.DatePaid","UserGroup","StickyNotes" 226 | ] 227 | }, 228 | } 229 | 230 | orders = post("https://#{connection['domain']}.neto.com.au/do/WS/NetoAPI", payload). 231 | headers('NETOAPI_ACTION': 'GetOrder')['Order'] 232 | 233 | { 234 | events: orders, 235 | next_poll: (page + 1), 236 | can_poll_more: orders.length == limit 237 | } 238 | }, 239 | 240 | dedup: ->(order) { 241 | order['ID'] + "@" + order['DateUpdated'] 242 | }, 243 | 244 | output_fields: ->(object_definitions) { 245 | object_definitions['order'] 246 | } 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /custom_connectors/custom_auth/domo.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Domo', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'client_id', 8 | optional: false, 9 | hint: 'To create client ID click " \ 11 | 'here' 12 | }, 13 | { 14 | name: 'client_secret', 15 | control_type: 'password', 16 | optional: false, 17 | hint: 'To create client secret click " \ 19 | 'here' 20 | } 21 | ], 22 | authorization: { 23 | type: 'custom_auth', 24 | 25 | acquire: lambda do |connection| 26 | { 27 | access_token: get('https://api.domo.com/oauth/token?' \ 28 | 'grant_type=client_credentials&scope=data') 29 | .user(connection['client_id']) 30 | .password(connection['client_secret'])['access_token'] 31 | } 32 | end, 33 | 34 | refresh_on: [401], 35 | 36 | apply: lambda do |connection| 37 | headers(Authorization: "bearer #{connection['access_token']}") 38 | end 39 | }, 40 | 41 | base_uri: lambda do |_connection| 42 | 'https://api.domo.com' 43 | end 44 | }, 45 | 46 | test: lambda do |_connection| 47 | get('/v1/datasets?limit=1') 48 | end, 49 | 50 | object_definitions: { 51 | dataset: { 52 | fields: lambda do |_connection, config_fields| 53 | column_headers = 54 | if config_fields.blank? 55 | [] 56 | else 57 | get("/v1/datasets/#{config_fields['dataset_id']}") 58 | .dig('schema', 'columns') 59 | end&.map do |col| 60 | { 61 | name: col['name'], 62 | sticky: true, 63 | type: case col['type'] 64 | when 'DATE' 65 | 'date' 66 | when 'DATETIME' 67 | 'date_time' 68 | when 'LONG' 69 | 'integer' 70 | when 'DECIMAL' 71 | 'number' 72 | else 73 | 'string' 74 | end 75 | } 76 | end 77 | { 78 | name: 'data', 79 | type: 'array', 80 | of: 'object', 81 | properties: column_headers, 82 | optional: false, 83 | hint: 'Map the Data source list and Data fields.' 84 | } 85 | end 86 | }, 87 | 88 | dataset_definition: { 89 | fields: lambda do |_connection, _config_fields| 90 | [ 91 | { name: 'id' }, 92 | { name: 'name', optional: false }, 93 | { name: 'description', optional: false }, 94 | { 95 | name: 'schema', 96 | optional: false, 97 | type: 'object', properties: [ 98 | { name: 'columns', 99 | optional: false, 100 | type: 'array', of: 'object', 101 | properties: [ 102 | { name: 'name', optional: false }, 103 | { name: 'type', 104 | optional: false, 105 | control_type: 'select', 106 | pick_list: [ 107 | %w[String STRING], 108 | %w[Decimal DECIMAL], 109 | %w[Long LONG], 110 | %w[Double DOUBLE], 111 | %w[Date DATE], 112 | %w[Date\ time DATETIME] 113 | ], 114 | toggle_hint: 'Select from list', 115 | toggle_field: { 116 | name: 'type', type: 'string', 117 | control_type: 'text', 118 | label: 'Type', 119 | toggle_hint: 'Enter custom value', 120 | hint: 'Allowed values are: STRING, DECIMAL, LONG,' \ 121 | 'DOUBLE, DATE, DATETIME' 122 | } } 123 | ] } 124 | ] 125 | }, 126 | { name: 'rows', type: 'integer' }, 127 | { name: 'columns', type: 'integer' }, 128 | { name: 'owner', type: 'object', properties: [ 129 | { name: 'id' }, 130 | { name: 'name' } 131 | ] }, 132 | { name: 'createdAt', type: 'date_time' }, 133 | { name: 'updatedAt', type: 'date_time' } 134 | ] 135 | end 136 | } 137 | }, 138 | 139 | actions: { 140 | import_data: { 141 | description: 'Import data into ' \ 142 | 'dataset in ' \ 143 | 'Domo', 144 | help: 'Import data action replaces the data in the dataset', 145 | 146 | config_fields: [ 147 | { 148 | name: 'dataset_id', 149 | control_type: 'select', 150 | pick_list: 'datasets', 151 | label: 'Dataset name', 152 | optional: false, 153 | help: 'Select the Dataset to import data.', 154 | toggle_hint: 'Select from list', 155 | toggle_field: 156 | { name: 'dataset_id', 157 | label: 'Dataset ID', 158 | type: :string, 159 | control_type: 'text', 160 | optional: false, 161 | toggle_hint: 'Use custom value' } 162 | } 163 | ], 164 | 165 | input_fields: lambda do |object_definitions| 166 | object_definitions['dataset'] 167 | end, 168 | 169 | execute: lambda do |_connection, input| 170 | payload = input['data'].map do |row| 171 | row.map do |_key, val| 172 | val 173 | end.join(',') 174 | end.join("\n") 175 | { 176 | status: put("/v1/datasets/#{input['dataset_id']}/data") 177 | .headers("Content-Type": 'text/csv') 178 | .request_body(payload) 179 | .request_format_www_form_urlencoded 180 | .after_error_response(/40*/) do |code, _body, _header, message| 181 | error("#{code}: #{message}") 182 | end&.presence || 'success' 183 | } 184 | end, 185 | 186 | output_fields: lambda do |_object_definitions| 187 | [{ name: 'status' }] 188 | end, 189 | 190 | sample_output: lambda do |_connection, _input| 191 | { status: 'success' } 192 | end 193 | }, 194 | 195 | export_data: { 196 | description: 'Export data from ' \ 197 | 'dataset in ' \ 198 | 'Domo', 199 | help: 'Known Limitation: Data types will be exported as they are' \ 200 | ' currently stored in the dataset. In addition, the only supported' \ 201 | ' export type is CSV.', 202 | 203 | config_fields: [ 204 | { 205 | name: 'dataset_id', 206 | control_type: 'select', 207 | label: 'Dataset name', 208 | pick_list: 'datasets', 209 | optional: false, 210 | toggle_hint: 'Select from list', 211 | toggle_field: 212 | { name: 'dataset_id', 213 | label: 'Dataset ID', 214 | type: :string, 215 | control_type: 'text', 216 | optional: false, 217 | toggle_hint: 'Use custom value' } 218 | } 219 | ], 220 | 221 | input_fields: lambda do |_object_definitions| 222 | [ 223 | { 224 | name: 'includeHeader', 225 | type: 'boolean', 226 | label: 'Include header', 227 | control_type: 'checkbox', 228 | sticky: true, 229 | toggle_hint: 'Select from list', 230 | toggle_field: 231 | { name: 'includeHeader', 232 | label: 'Include header', 233 | type: :string, 234 | control_type: 'text', 235 | optional: true, 236 | toggle_hint: 'Use custom value', 237 | hint: 'Allowed values are: true, false' } 238 | }, 239 | { 240 | name: 'fileName', 241 | hint: 'The filename of the exported csv', 242 | sticky: true 243 | } 244 | ] 245 | end, 246 | 247 | execute: lambda do |_connection, input| 248 | { 249 | data: get("/v1/datasets/#{input.delete('dataset_id')}/data", input) 250 | .headers("Content-Type": 'text/csv', 251 | 'Accept': 'text/csv') 252 | .request_format_www_form_urlencoded 253 | .response_format_raw 254 | } 255 | end, 256 | 257 | output_fields: lambda do |_object_definitions| 258 | [{ name: 'data' }] 259 | end, 260 | 261 | sample_output: lambda do |_connection, _input| 262 | { data: 'name,id sam,123 xavier,124' } 263 | end 264 | }, 265 | 266 | create_dataset: { 267 | description: 'Create dataset ' \ 268 | 'in Domo', 269 | help: "Click " \ 271 | 'here for supported data types', 272 | 273 | input_fields: lambda do |object_definitions| 274 | object_definitions['dataset_definition'] 275 | .ignored('id', 'rows', 'columns', 'owner', 'createdAt', 'updatedAt') 276 | end, 277 | 278 | execute: lambda do |_connection, input| 279 | post('/v1/datasets/', input) 280 | .after_error_response(/40*/) do |code, _body, _header, message| 281 | error("#{code}: #{message}") 282 | end 283 | end, 284 | 285 | output_fields: lambda do |object_definitions| 286 | object_definitions['dataset_definition'] 287 | end, 288 | 289 | sample_output: lambda do |_connection, _input| 290 | dataset_id = get('/v1/datasets?limit=1')&.dig(0, 'id') 291 | dataset_id.present? ? get("/v1/datasets/#{dataset_id}") : {} 292 | end 293 | } 294 | }, 295 | 296 | pick_lists: { 297 | datasets: lambda do |_connection| 298 | get('/v1/datasets')&.pluck('name', 'id') 299 | end 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /custom_connectors/oauth2/ephesoft_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'Ephesoft Semantik for Invoices', 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: 'client_id', 8 | optional: false 9 | }, 10 | { 11 | name: 'client_secret', 12 | optional: false, 13 | control_type: 'password' 14 | } 15 | ], 16 | authorization: { 17 | type: "oauth2", 18 | authorization_url: lambda do |connection| 19 | params = { 20 | client_id: connection["client_id"], 21 | response_type: "code", 22 | redirect_uri: "https://www.workato.com/oauth/callback", 23 | scope: "admin" 24 | }.to_param 25 | 26 | "https://api.us.ephesoft.io/v1/auth/oauth2/authorize?" + params 27 | end, 28 | 29 | acquire: lambda do |connection, auth_code| 30 | response = post("https://api.us.ephesoft.io/v1/auth/oauth2/token"). 31 | payload( 32 | code: auth_code, 33 | grant_type: "authorization_code", 34 | client_id: connection["client_id"], 35 | client_secret: connection["client_secret"], 36 | redirect_uri: "https://www.workato.com/oauth/callback" 37 | ). 38 | request_format_www_form_urlencoded 39 | [ 40 | { 41 | access_token: response["access_token"], 42 | refresh_token: response["refresh_token"] 43 | } 44 | ] 45 | end, 46 | 47 | refresh_on: [401, 403], 48 | 49 | refresh: lambda do |connection, refresh_token| 50 | response = post("https://api.us.ephesoft.io/v1/auth/oauth2/refresh"). 51 | payload( 52 | grant_type: "refresh_token", 53 | refresh_token: refresh_token, 54 | client_id: connection["client_id"], 55 | client_secret: connection["client_secret"], 56 | redirect_uri: "https://www.workato.com/oauth/callback" 57 | ). 58 | request_format_www_form_urlencoded 59 | [ 60 | { # Tokens hash 61 | access_token: response["access_token"], 62 | refresh_token: response["refresh_token"] 63 | } 64 | ] 65 | end, 66 | 67 | apply: lambda do |connection, access_token| 68 | headers("Authorization": "Bearer #{access_token}") 69 | end 70 | } 71 | }, 72 | test: 73 | lambda do |connection| 74 | get("https://api.us.ephesoft.io/v1/settings/integrations/configurations") 75 | end, 76 | actions: { 77 | # Future action code here 78 | }, 79 | triggers: { 80 | new_message: { 81 | title: 'New complete Semantik invoice', 82 | description: 'Triggers when a Semantik invoice is completed', 83 | webhook_subscribe: lambda do |webhook_url, connection, input, recipe_id| 84 | payload = { 85 | "AmountDue": "$AmountDue", 86 | "DocumentId": "$DocumentId", 87 | "DueDate": "$DueDate", 88 | "FileName": "$FileName", 89 | "InvoiceDate": "$InvoiceDate", 90 | "InvoiceNumber": "$InvoiceNumber", 91 | "OrderDate": "$OrderDate", 92 | "PdfUrl": "$PdfUrl", 93 | "PONumber": "$PONumber", 94 | "ShipDate": "$ShipDate", 95 | "ShipFreight": "$ShipFreight", 96 | "SubTotal": "$SubTotal", 97 | "TableUrl": "$TableUrl", 98 | "TaxAmount": "$TaxAmount", 99 | "TaxId": "$TaxId", 100 | "TaxRate": "$TaxRate", 101 | "TenantId": "$TenantId", 102 | "Terms": "$Terms", 103 | "TotalAmount": "$TotalAmount", 104 | "Vendor": { 105 | "VendorAddress": { 106 | "VendorStreetAddress": "$Vendor:StreetAddress", 107 | "VendorPOBox": "$Vendor:POBox", 108 | "VendorLocality": "$Vendor:Locality", 109 | "VendorRegion": "$Vendor:Region", 110 | "VendorPostalCode": "$Vendor:PostalCode", 111 | "VedorCountry": "$Vendor:Country" 112 | }, 113 | "VendorCustom1": "$Vendor:Custom1", 114 | "VendorCustom2": "$Vendor:Custom2", 115 | "VendorCustom3": "$Vendor:Custom3", 116 | "VendorCustom4": "$Vendor:Custom4", 117 | "VendorCustom5": "$Vendor:Custom5", 118 | "VendorCustomerID": "$Vendor:CustomerID", 119 | "VendorIBAN": "$Vendor:IBAN", 120 | "VendorID": "$Vendor:VendorID", 121 | "VendorMatched": "$Vendor:Matched", 122 | "VendorName": "$Vendor:Name", 123 | "VendorStatus": "$Vendor:Status", 124 | "VendorSWIFT": "$Vendor:SWIFT", 125 | "VendorTelephone": "$Vendor:Telephone" 126 | } 127 | } 128 | post("https://api.us.ephesoft.io/v1/settings/integrations/configurations", 129 | integrationName: "Workato Recipe #{recipe_id}", 130 | integrationType: "webhook", 131 | enabled: true, 132 | settings: { 133 | targetUrl: webhook_url, 134 | encoding: "application/json", 135 | payload: payload.to_json.to_s 136 | } 137 | ) 138 | end, 139 | 140 | webhook_notification: lambda do |input, payload| 141 | payload 142 | end, 143 | 144 | webhook_unsubscribe: lambda do |webhook| 145 | 146 | # Get the webhook ID to be removed 147 | json_output = webhook['data'] 148 | configurationId = (json_output.scan(/configurationId"=>"([\S\s]*)", "integrationId/).dig(0))[0] 149 | 150 | # Delete the webhook 151 | delete("https://api.us.ephesoft.io/v1/settings/integrations/configurations/#{configurationId}") 152 | end, 153 | 154 | output_fields: lambda do |object_definitions| 155 | object_definitions["webhook_output"] 156 | end, 157 | sample_output: lambda do || 158 | { 159 | AmountDue: '3693.80', 160 | DocumentId: '00000000-0000-0000-0000-000000000000', 161 | DueDate: '2020-04-15', 162 | FileName: 'Kord-Invoice9291873611.pdf', 163 | InvoiceDate: '2020-03-29', 164 | InvoiceNumber: '9291873611', 165 | OrderDate: '2020-03-29', 166 | PdfUrl: 'https://semantik.us.ephesoft.io/assets/samples/webhookTestInvoice.pdf', 167 | PONumber: '8762943181', 168 | ShipDate: '2020-04-10', 169 | ShipFreight: '123.45', 170 | SubTotal: '3358', 171 | TableUrl: 'https://semantik.us.ephesoft.io/assets/samples/webhookTestInvoiceTable.json', 172 | TaxAmount: '335.80', 173 | TaxId: '012-34-56789', 174 | TaxRate: '10', 175 | TenantId: '555e4d7e-0115-4ec4-adac-892367844222', 176 | Terms: 'Net 30 days', 177 | TotalAmount: '3693.8', 178 | Vendor: { 179 | VendorAddress: { 180 | VendorStreetAddress: '2775 S. 900 W.', 181 | VendorPOBox: '123', 182 | VendorLocality: 'Salt Lake City', 183 | VendorRegion: 'UT', 184 | VendorPostalCode: '84128-3227', 185 | VendorCountry: 'US' 186 | }, 187 | VendorCustom1: 'Custom1', 188 | VendorCustom2: 'Custom2', 189 | VendorCustom3: 'Custom3', 190 | VendorCustom4: 'Custom4', 191 | VendorCustom5: 'Custom5', 192 | VendorIBAN: 'aa5d5efa-fcaf-4d2b-a118-a093f184a876', 193 | VendorCustomerID: '95bba4ab-1897-4e9d-aff1-f42ae7e5cb86', 194 | VendorID: '1', 195 | VendorMatched: 'true', 196 | VendorName: 'Kord Industries', 197 | VendorStatus: 'active', 198 | VendorSWIFT: '1b0f74e1-ce62-4d1d-9153-f07e88bc0a29', 199 | VendorTelephone: '513-101-4074' 200 | } 201 | } 202 | end, 203 | dedup: lambda do |messages| 204 | Time.now.to_f 205 | end 206 | } 207 | }, 208 | object_definitions: { 209 | webhook_output: { 210 | fields: lambda do 211 | [ 212 | { name: "AmountDue", type: "string"}, 213 | { name: "DocumentId", type: "string"}, 214 | { name: "DueDate", type: "string"}, 215 | { name: "FileName", type: "string"}, 216 | { name: "InvoiceDate", type: "string"}, 217 | { name: "InvoiceNumber", type: "string"}, 218 | { name: "OrderDate", type: "string"}, 219 | { name: "PdfUrl", type: "string"}, 220 | { name: "PONumber", type: "string"}, 221 | { name: "ShipDate", type: "string"}, 222 | { name: "ShipFreight", type: "string"}, 223 | { name: "SubTotal", type: "string"}, 224 | { name: "TableUrl", type: "string"}, 225 | { name: "TaxAmount", type: "string"}, 226 | { name: "TaxId", type: "string"}, 227 | { name: "TaxRate", type: "string"}, 228 | { name: "TenantId", type: "string"}, 229 | { name: "Terms", type: "string"}, 230 | { name: "TotalAmount", type: "string"}, 231 | { 232 | name: "Vendor", 233 | type: :object, 234 | properties: [ 235 | { 236 | name: "VendorAddress", 237 | type: :object, 238 | properties: [ 239 | { name: "VendorStreetAddress", type: "string"}, 240 | { name: "VendorPOBox", type: "string"}, 241 | { name: "VendorLocality", type: "string"}, 242 | { name: "VendorRegion", type: "string"}, 243 | { name: "VendorPostalCode", type: "string"}, 244 | { name: "VendorCountry", type: "string"} 245 | ] 246 | }, 247 | { name: "VendorCustom1", type: "string"}, 248 | { name: "VendorCustom2", type: "string"}, 249 | { name: "VendorCustom3", type: "string"}, 250 | { name: "VendorCustom4", type: "string"}, 251 | { name: "VendorCustom5", type: "string"}, 252 | { name: "VendorIBAN", type: "string"}, 253 | { name: "VendorCustomerID", type: "string"}, 254 | { name: "VendorID", type: "string"}, 255 | { name: "VendorMatched", type: "string"}, 256 | { name: "VendorName", type: "string"}, 257 | { name: "VendorStatus", type: "string"}, 258 | { name: "VendorSWIFT", type: "string"}, 259 | { name: "VendorTelephone", type: "string"} 260 | ] 261 | } 262 | ] 263 | end 264 | } 265 | }, 266 | picklists: {}, 267 | methods: {}, 268 | } -------------------------------------------------------------------------------- /custom_connectors/none/rssfeedreader.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: 'RSS Feed Reader 2.0', 3 | 4 | methods: { 5 | parse_xml_to_hash: lambda do |xml_obj| 6 | xml_obj['xml']&. 7 | inject({}) do |hash, (key, value)| 8 | if value.is_a?(Array) 9 | hash.merge(if (array_fields = xml_obj['array_fields'])&.include?(key) 10 | { 11 | key => value.map do |inner_hash| 12 | call('parse_xml_to_hash', 13 | 'xml' => inner_hash, 14 | 'array_fields' => array_fields) 15 | end 16 | } 17 | else 18 | { 19 | key => call('parse_xml_to_hash', 20 | 'xml' => value[0], 21 | 'array_fields' => array_fields) 22 | } 23 | end) 24 | else 25 | value 26 | end 27 | end&.presence 28 | end, 29 | 30 | format_api_output_field_names: lambda do |input| 31 | if input.is_a?(Array) 32 | input.map do |array_value| 33 | call('format_api_output_field_names', array_value) 34 | end 35 | elsif input.is_a?(Hash) 36 | input.map do |key, value| 37 | value = call('format_api_output_field_names', value) 38 | { key.gsub(/[!@#$%^&*(),.?":{}|<>]/, '_') => value } 39 | end.inject(:merge) 40 | else 41 | input 42 | end 43 | end, 44 | 45 | format_schema_field_names: lambda do |input| 46 | input.map do |field| 47 | if field[:properties].present? 48 | field[:properties] = call('format_schema_field_names', 49 | field[:properties]) 50 | elsif field['properties'].present? 51 | field['properties'] = call('format_schema_field_names', 52 | field['properties']) 53 | end 54 | if field[:name].present? 55 | field[:name] = field[:name].gsub(/[!@#$%^&*(),.?":{}|<>]/, '_') 56 | elsif field['name'].present? 57 | field['name'] = field['name'].gsub(/[!@#$%^&*(),.?":{}|<>]/, '_') 58 | end 59 | field 60 | end 61 | end 62 | }, 63 | 64 | connection: { authorization: { type: 'no_auth' } }, 65 | 66 | test: ->(_connection) { true }, 67 | 68 | object_definitions: { 69 | item: { 70 | fields: lambda do |_connection, _config_fields| 71 | item_fields = [ 72 | { name: 'author' }, 73 | { name: 'category' }, 74 | { name: 'comments' }, 75 | { name: 'description' }, 76 | { 77 | type: 'object', 78 | name: 'enclosure', 79 | properties: [ 80 | { name: '@length', label: 'Length' }, 81 | { name: '@type', label: 'Type' }, 82 | { name: '@url', label: 'URL' } 83 | ] 84 | }, 85 | { name: 'guid', label: 'GUID' }, 86 | { name: 'link' }, 87 | { 88 | control_type: 'text', 89 | render_input: 'date_time_conversion', 90 | parse_output: 'date_time_conversion', 91 | type: 'date_time', 92 | name: 'pubDate' 93 | }, 94 | { name: 'source' }, 95 | { name: 'title' } 96 | ] 97 | 98 | call('format_schema_field_names', item_fields.compact) 99 | end 100 | }, 101 | 102 | channel: { 103 | fields: lambda do |_connection, _config_fields| 104 | channel_fields = [ 105 | { name: 'title' }, 106 | { name: 'link' }, 107 | { name: 'description' }, 108 | { name: 'language' }, 109 | { name: 'copyright' }, 110 | { name: 'managingEditor' }, 111 | { 112 | control_type: 'text', 113 | render_input: 'date_time_conversion', 114 | parse_output: 'date_time_conversion', 115 | type: 'date_time', 116 | name: 'pubDate' 117 | }, 118 | { 119 | control_type: 'text', 120 | render_input: 'date_time_conversion', 121 | parse_output: 'date_time_conversion', 122 | type: 'date_time', 123 | name: 'lastBuildDate' 124 | }, 125 | { name: 'category' }, 126 | { name: 'generator' }, 127 | { name: 'docs' }, 128 | { 129 | type: 'object', 130 | name: 'cloud', 131 | properties: [ 132 | { name: '@domain', label: 'Domain' }, 133 | { name: '@path', label: 'Path' }, 134 | { name: '@url', label: 'URL' }, 135 | { name: '@port', label: 'Port' }, 136 | { name: '@protocol', label: 'Protocol' }, 137 | { name: '@registerProcedure', label: 'Register procedure' } 138 | ] 139 | }, 140 | { 141 | control_type: 'text', 142 | label: 'Time to live', 143 | type: 'string', 144 | name: 'ttl' 145 | }, 146 | { 147 | name: 'image', 148 | type: 'object', 149 | properties: [ 150 | { name: 'link' }, 151 | { name: 'title' }, 152 | { name: 'url' }, 153 | { name: 'description' }, 154 | { name: 'height' }, 155 | { name: 'width' } 156 | ] 157 | }, 158 | { name: 'language' }, 159 | { name: 'rating' }, 160 | { 161 | type: 'object', 162 | name: 'textInput', 163 | properties: [ 164 | { name: 'description' }, 165 | { name: 'link' }, 166 | { name: 'name' }, 167 | { name: 'title' } 168 | ] 169 | }, 170 | { name: 'webMaster' }, 171 | { 172 | name: 'item', 173 | type: 'array', 174 | of: 'object', 175 | properties: [ 176 | { name: 'author' }, 177 | { name: 'category' }, 178 | { name: 'comments' }, 179 | { name: 'description' }, 180 | { 181 | type: 'object', 182 | name: 'enclosure', 183 | properties: [ 184 | { name: '@length', label: 'Length' }, 185 | { name: '@type', label: 'Type' }, 186 | { name: '@url', label: 'URL' } 187 | ] 188 | }, 189 | { name: 'guid', label: 'GUID' }, 190 | { name: 'link' }, 191 | { 192 | control_type: 'text', 193 | render_input: 'date_time_conversion', 194 | parse_output: 'date_time_conversion', 195 | type: 'date_time', 196 | name: 'pubDate' 197 | }, 198 | { name: 'source' }, 199 | { name: 'title' } 200 | ] 201 | } 202 | ] 203 | 204 | call('format_schema_field_names', channel_fields.compact) 205 | end 206 | } 207 | }, 208 | 209 | actions: { 210 | get_feed: { 211 | description: "Get items from feed in " \ 212 | "RSS Feed Reader 2.0", 213 | 214 | input_fields: lambda do |_object_definitions| 215 | [{ 216 | name: 'feed_url', 217 | label: 'Feed URL', 218 | type: 'string', 219 | control_type: 'url', 220 | optional: false, 221 | hint: 'E.g. https://status.workato.com/history.rss' 222 | }] 223 | end, 224 | 225 | execute: lambda do |_connection, input| 226 | { 227 | channel: call('format_api_output_field_names', 228 | call('parse_xml_to_hash', 229 | 'xml' => get(input['feed_url']) 230 | .response_format_xml 231 | .dig('rss', 0, 'channel', 0), 232 | 'array_fields' => ['item'])&.compact) 233 | } 234 | end, 235 | 236 | output_fields: lambda do |object_definitions| 237 | [{ 238 | name: 'channel', 239 | type: 'object', 240 | properties: object_definitions['channel'] 241 | }] 242 | end, 243 | 244 | sample_output: lambda do |_connection, input| 245 | { 246 | channel: [call('format_api_output_field_names', 247 | call('parse_xml_to_hash', 248 | 'xml' => get(input['feed_url']) 249 | .response_format_xml 250 | .dig('rss', 0, 'channel', 0), 251 | 'array_fields' => ['item'])&.compact)] || [] 252 | } 253 | end 254 | } 255 | }, 256 | 257 | triggers: { 258 | new_item_in_feed: { 259 | description: "New item in" \ 260 | "RSS Feed Reader 2.0", 261 | type: 'paging_desc', 262 | 263 | input_fields: lambda do |_object_definitions| 264 | [{ 265 | name: 'feed_url', 266 | label: 'Feed URL', 267 | type: 'string', 268 | control_type: 'url', 269 | optional: false, 270 | hint: 'E.g. https://status.workato.com/history.rss' 271 | }] 272 | end, 273 | 274 | poll: lambda do |_connection, input, _page| 275 | items = call('format_api_output_field_names', 276 | call('parse_xml_to_hash', 277 | 'xml' => get(input['feed_url']) 278 | .response_format_xml 279 | .dig('rss', 0, 'channel', 0), 280 | 'array_fields' => ['item'])&.compact)&.[]('item') 281 | 282 | { events: items || [], next_page: nil } 283 | end, 284 | 285 | document_id: lambda do |item| 286 | "#{item['guid']}@#{item['title']}@#{item['description']}" 287 | end, 288 | 289 | sort_by: ->(item) { item['pubDate'] }, 290 | 291 | output_fields: ->(object_definitions) { object_definitions['item'] }, 292 | 293 | sample_output: lambda do |_connection, input| 294 | call('format_api_output_field_names', 295 | call('parse_xml_to_hash', 296 | 'xml' => get(input['feed_url']) 297 | .response_format_xml 298 | .dig('rss', 0, 'channel', 0), 299 | 'array_fields' => ['item'])&.dig('item', 0)) || {} 300 | end 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /custom_connectors/api_key_auth/aftership_connector.rb: -------------------------------------------------------------------------------- 1 | { 2 | title: "Aftership", 3 | 4 | connection: { 5 | fields: [ 6 | { 7 | name: "api_key", 8 | control_type: "password", 9 | optional: false, 10 | label: "API key", 11 | hint: "Get your API key here" 13 | } 14 | ], 15 | 16 | authorization: { 17 | type: "api_key", 18 | 19 | credentials: lambda do |connection| 20 | headers("aftership-api-key": connection["api_key"]) 21 | end 22 | }, 23 | 24 | base_uri: lambda do 25 | "https://api.aftership.com" 26 | end 27 | }, 28 | 29 | object_definitions: { 30 | tracking_input: { 31 | fields: lambda do |_connection| 32 | [ 33 | { name: "tracking_number", type: :string, optional: false, 34 | hint: "Duplicate tracking numbers, or tracking number with " \ 35 | "invalid tracking number format will not be accepted." }, 36 | { name: "slug", type: :string, 37 | hint: "If you do not specify a slug, Aftership will " \ 38 | "automatically detect the courier based on the tracking number " \ 39 | "format and your selected couriers." }, 40 | { name: "tracking_postal_code", type: :string, 41 | hint: "The postal code of receiver's address. " \ 42 | "Required by some couriers, such as deutsch-post" }, 43 | { name: "tracking_ship_date", type: :string, 44 | hint: "Shipping date in YYYYMMDD format. Required by some " \ 45 | "couriers, such as deutsch-post" }, 46 | { name: "tracking_account_number", type: :string, 47 | hint: "Account number of the shipper for a specific courier. " \ 48 | "Required by some couriers, such as dynamic-logistics" }, 49 | { name: "tracking_key", type: :string, 50 | hint: "Key of the shipment for a specific courier. " \ 51 | "Required by some couriers, such assic-teliway" }, 52 | { name: "tracking_origin_country", type: :string, 53 | hint: "Origin Country of the shipment for a specific courier. " \ 54 | "Required by some couriers, such as dhl" }, 55 | { name: "tracking_destination_country", type: :string, 56 | hint: "Destination Country of the shipment for a specific courier" \ 57 | ". Required by some couriers, such as postnl-3s" }, 58 | { name: "tracking_state", type: :string, 59 | hint: "Located state of the shipment for a specific courier. " \ 60 | "Required by some couriers, such asstar-track-courier" }, 61 | { name: "android", type: :string, 62 | hint: "Google cloud message comma separated registration IDs to " \ 63 | "receive the push notifications" }, 64 | { name: "ios", type: :string, 65 | hint: "Apple iOS comma separated device IDs to receive the " \ 66 | "push notifications" }, 67 | { name: "emails", type: :string, label: "E-mails", 68 | hint: "Comma separated email address(es) to receive email " \ 69 | "notifications" }, 70 | { name: "smses", type: :string, label: "SMSes", 71 | hint: "Comma separated phone number(s) to receive SMS " \ 72 | "notifications. Note: Enter + and area code before number." }, 73 | { name: "title", type: :string, 74 | hint: "Title of the tracking. Default value astracking_number" }, 75 | { name: "customer_name", type: :string }, 76 | { name: "origin_country_iso3", type: :string, 77 | label: "Origin Country ISO3", 78 | hint: "Enter ISO Alpha-3 " \ 80 | "country code to specify the origin of the shipment" }, 81 | { name: "destination_country_iso3", type: :string, 82 | label: "Destination Country ISO3", 83 | hint: "Enter ISO Alpha-3 " \ 85 | "country code to specify the destination of the shipment" \ 86 | "If you use postal service to send international shipments, " \ 87 | "AfterShip will automatically get tracking results at " \ 88 | "destination courier as well." }, 89 | { name: "order_id", type: :string, label: "Order ID" }, 90 | { name: "order_id_path", type: :string, label: "Order ID Path" }, 91 | { name: "note", type: :string }, 92 | { name: "language", type: :string, 93 | hint: "Enter ISO 639-1 Language Code to specify the " \ 96 | "store, customer or order language" } 97 | ] 98 | end 99 | }, 100 | 101 | tracking_output: { 102 | fields: lambda do |_connection| 103 | [ 104 | { name: "id", type: :string, control_type: :text }, 105 | { name: "created_at", type: :date_time, control_type: :date_time }, 106 | { name: "updated_at", type: :date_time, control_type: :date_time }, 107 | { name: "tracking_number", type: :string, control_type: :text }, 108 | { name: "tracking_account_number", type: :string, 109 | control_type: :text }, 110 | { name: "tracking_postal_code", type: :string, control_type: :text }, 111 | { name: "tracking_ship_date", type: :date_time, 112 | control_type: :date_time }, 113 | { name: "tracking_origin_country", type: :string }, 114 | { name: "tracking_destination_country", type: :string }, 115 | { name: "tracking_state", type: :string }, 116 | { name: "tracking_key", type: :string }, 117 | { name: "slug", type: :string, control_type: :text }, 118 | { name: "active", type: :boolean }, 119 | { name: "android", type: :string }, 120 | { name: "custom_fields", type: :object }, 121 | { name: "customer_name", type: :string, control_type: :text }, 122 | { name: "delivery_time", type: :string }, 123 | { name: "destination_country_iso3", type: :string, 124 | control_type: :text }, 125 | { name: "courier_destination_country_iso3", type: :string, 126 | control_type: :text }, 127 | { name: "emails", type: :array, of: :string }, 128 | { name: "expected_delivery", type: :date_time, 129 | control_type: :date_time }, 130 | { name: "ios", type: :string }, 131 | { name: "note", type: :string, control_type: :text }, 132 | { name: "order_id", type: :string, control_type: :text }, 133 | { name: "order_id_path", type: :string, control_type: :url }, 134 | { name: "origin_country_iso3", type: :string, control_type: :text }, 135 | { name: "unique_token", type: :string, control_type: :password }, 136 | { name: "shipment_package_count", type: :integer, 137 | control_type: :number }, 138 | { name: "shipment_type", type: :string, control_type: :text }, 139 | { name: "shipment_weight", type: :string, control_type: :text }, 140 | { name: "shipment_weight_unit", type: :string, control_type: :text }, 141 | { name: "signed_by", type: :string, control_type: :text }, 142 | { name: "smses", type: :array, of: :string }, 143 | { name: "source", type: :string, control_type: :text }, 144 | { name: "tag", type: :string, control_type: :text }, 145 | { name: "title", type: :string, control_type: :text }, 146 | { name: "tracked_count", type: :integer, control_type: :number }, 147 | { name: "last_mile_tracking_supported", type: :boolean, 148 | control_type: :checkbox }, 149 | { name: "language", type: :string, control_type: :text }, 150 | { name: "return_to_sender", type: :boolean, control_type: :checkbox }, 151 | { name: "checkpoints", type: :array, of: :object, properties: [ 152 | { name: "slug", type: :string, control_type: :text }, 153 | { name: "location", type: :string, control_type: :text }, 154 | { name: "city", type: :string, control_type: :text }, 155 | { name: "created_at", type: :date_time, 156 | control_type: :date_time }, 157 | { name: "country_name", type: :string, control_type: :text }, 158 | { name: "message", type: :string, control_type: :text }, 159 | { name: "country_iso3", type: :string, control_type: :text }, 160 | { name: "tag", type: :string, control_type: :text }, 161 | { name: "checkpoint_time", type: :date_time, 162 | control_type: :date_time }, 163 | { name: "coordinates", type: :array, of: :integer, 164 | control_type: :number }, 165 | { name: "state", type: :string, control_type: :text }, 166 | { name: "zip", type: :string, control_type: :text } 167 | ] } 168 | ] 169 | end 170 | } 171 | }, 172 | 173 | test: lambda do |_connection| 174 | get("/v4/trackings") 175 | end, 176 | 177 | actions: { 178 | create_tracking: { 179 | description: "Create a tracking in " \ 180 | "Aftership", 181 | 182 | input_fields: lambda do |object_definitions| 183 | object_definitions["tracking_input"].required("tracking_number") 184 | end, 185 | 186 | execute: lambda do |_connection, input| 187 | post("/v4/trackings", tracking: input).dig("data", "tracking") 188 | end, 189 | 190 | output_fields: lambda do |object_definitions| 191 | object_definitions["tracking_output"] 192 | end, 193 | 194 | sample_output: lambda do |_connection| 195 | get("/v4/trackings", limit: 1).dig("data", "trackings", 0) 196 | end 197 | }, 198 | 199 | search_tracking: { 200 | description: "Search tracking in " \ 201 | "Aftership", 202 | 203 | input_fields: lambda do |object_definitions| 204 | object_definitions["tracking_input"]. 205 | only("tracking_number"). 206 | required("tracking_number") 207 | end, 208 | 209 | execute: lambda do |_connection, input| 210 | { 211 | trackings: get("/v4/trackings", keyword: input["tracking_number"]). 212 | []("data") || [] 213 | } 214 | end, 215 | 216 | output_fields: lambda do |object_definitions| 217 | object_definitions["tracking_output"] 218 | end, 219 | 220 | sample_output: lambda do |_connection| 221 | get("/v4/trackings", limit: 1).dig("data", "trackings", 0) 222 | end 223 | } 224 | } 225 | } 226 | --------------------------------------------------------------------------------