diff --git a/custom_connectors/oauth2/plangrid.rb b/custom_connectors/oauth2/plangrid.rb new file mode 100644 index 00000000..c7f67eb1 --- /dev/null +++ b/custom_connectors/oauth2/plangrid.rb @@ -0,0 +1,2775 @@ +{ + title: 'PlanGrid', + connection: { + fields: [ + { + name: 'client_id', + label: 'Client ID', + optional: false, + hint: 'To create client id, you need to register an application' \ + ' under Admin Console => Project => Oauth => Create Oauth app' + }, + { + name: 'client_secret', + label: 'Client secret', + control_type: 'password', + optional: false, + hint: 'To create client id, you need to register an application' \ + ' under Admin Console => Project => Oauth => Create Oauth app' + } + ], + authorization: { + type: 'oauth2', + authorization_url: lambda do |connection| + 'https://io.plangrid.com/oauth/authorize?response_type=' \ + "code&client_id=#{connection['client_id']}&" \ + 'scope=write:projects%20read:profile' + end, + acquire: lambda do |connection, auth_code, redirect_uri| + response = post('https://io.plangrid.com/oauth/token'). + payload(client_id: connection['client_id'], + client_secret: connection['client_secret'], + grant_type: 'authorization_code', + code: auth_code, + redirect_uri: redirect_uri). + request_format_www_form_urlencoded + [response, nil, nil] + end, + refresh_on: [401, 403], + refresh: lambda do |_connection, refresh_token| + post('https://io.plangrid.com/oauth/token'). + payload(grant_type: 'refresh_token', + refresh_token: refresh_token). + request_format_www_form_urlencoded + end, + apply: lambda do |_connection, access_token| + if current_url.include?('https://io.plangrid.com') + headers(Authorization: "Bearer #{access_token}", + Accept: 'application/vnd.plangrid+json; version=1') + end + end + }, + base_uri: lambda do |_connection| + 'https://io.plangrid.com' + end + }, + + test: ->(_connection) { get('/me') }, + + object_definitions: { + get_input_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'rfi_status' + [ + { name: 'limit', type: 'integer', control_type: 'integer', + hint: 'Number of RFI statuses to retrieve. Maximum value of 50.' }, + { name: 'skip', type: 'integer', control_type: 'integer', + hint: 'Number of RFI statuses to skip in the set of results.' } + ] + when 'field_report' + [ + { name: 'template_uid', label: 'Field Report Template ID', + hint: 'Only retrieve field reports for the specified template ID.'}, + { name: 'report_date_min', type: 'date', + label: 'Report Start Date', + hint: 'Only retrieve field reports between a date range starting with this date in UTC format.' }, + { name: 'report_date_max', type: 'date', + label: 'Report End Date', + hint: 'Only retrieve field reports between a date range ending with this date in UTC format.' }, + { name: 'updated_after', label: 'Updated After', type: 'date_time', + hint: 'Only retrieve field reports created/updated after specified UTC date and time.' }, + { name: 'skip', type: 'integer', + hint: 'Number of records to skip.' }, + { name: 'limit', type: 'integer', + hint: 'Number of records to retrieve.' }, + { + name: 'sort_by', label: 'Sort by column', control_type: 'select', + pick_list: + %w[report_date updated_at]&.map { |e| [e.labelize, e] }, + toggle_hint: 'Select column', + toggle_field: { + name: 'sort_by', label: 'Sort by column', type: 'string', control_type: 'text', + toggle_hint: 'Enter column', + hint: 'Allowed values report_date or updated_at.' + } + }, + { + name: 'sort_order', label: 'Sort by order', control_type: 'select', + pick_list: [%w[Ascending asc], %w[Descending desc]], + toggle_hint: 'Select order', + toggle_field: { + name: 'sort_by', label: 'Sort by order', type: 'string', control_type: 'text', + toggle_hint: 'Enter order', + hint: 'Allowed values Ascending or Descending.' + } + }, + { + name: 'output_schema', + control_type: 'schema-designer', + extends_schema: true, + label: 'PDF field values', + hint: 'Manually define the values expected of your PDF field values in the field report.', + optional: true + } + ] + when 'field_report_template' + [ + { name: 'updated_after', label: 'Updated After', type: 'date_time', + hint: 'Only retrieve field reports created/updated after specified UTC date and time.' }, + { name: 'updated_before', label: 'Updated Before', type: 'date_time', + hint: 'Only retrieve field reports created/updated before specified UTC date and time.' }, + { name: 'skip', type: 'integer', + hint: 'Number of records to skip.' }, + { name: 'limit', type: 'integer', + hint: 'Number of records to retrieve.' } + ] + when 'field_reports/export' + [{ name: 'uid', label: 'Export ID', optional: false }] + when 'role', 'project' + [] + when 'submittals/item' + [{ name: 'uids', label: 'Submittal Item ID', optional: false }] + when 'submittals/package' + [{ name: 'uid', label: 'Submittal Package ID', optional: false, + hint: 'ID can be found at the end of the url.' }] + when 'submittals_file_group' + [ + { name: 'uid', label: 'Submittal Package ID', optional: false, + hint: 'ID can be found at the end of the url.' }, + { name: 'created_after', type: 'date_time', + hint: 'Only return file groups created after the specified date.' } + ] + else + [{ name: "uid", label: "#{config_fields['object'].labelize} ID", optional: false, + hint: 'ID can be found at the end of the url.' }] + end + end + }, + + get_output_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'project' + [ + { + name: 'uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project ID', + sticky: true, + toggle_hint: 'Select project', + toggle_field: { + name: 'uid', + type: 'string', + control_type: 'text', + sticky: true, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + }, + { name: 'name', label: 'Project Name', sticky: true }, + { name: 'custom_id', label: 'Project Code', sticky: true }, + { name: 'organization_id', label: 'Organization ID' }, + { + name: 'type', control_type: 'select', + label: 'Project Type', sticky: true, + pick_list: 'project_types', + toggle_hint: 'Select project type', + toggle_field: { + name: 'type', + label: 'Project type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Project type with possible values of general,' \ + ' manufacturing, power, water-sewer-waste, industrial-' \ + 'petroleum, transportation, hazardous-waste, telecom, ' \ + 'education-k-12, education-higher, gov-federal, ' \ + 'gov-state-local, or other' + } + }, + { name: 'status', label: 'Project Status', sticky: true }, + { name: 'owner', sticky: true, label: 'Project Owner' }, + { name: 'start_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project Start Date', + hint: 'Project start date. ISO-8601 date format (YYYY-MM-DD).' }, + { name: 'end_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project End Date', + hint: 'Project end date. ISO-8601 date format (YYYY-MM-DD).' }, + { name: 'street_1', sticky: true, + label: 'Street Line 1' }, + { name: 'street_2', sticky: true, label: 'Street line 2' }, + { name: 'city', sticky: true, label: 'Town or City' }, + { name: 'region', sticky: true, label: 'State, Province, or Region' }, + { name: 'postal_code', sticky: true, label: 'Zip or Postal Code' }, + { name: 'country', + sticky: true, + hint: 'Project address country in 2-letter ISO 3166 code.' }, + { name: 'latitude' }, + { name: 'longitude' }, + { name: 'updated_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + label: 'Updated at' } + ] + when 'attachment' + [ + { name: 'uid', label: 'Document ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'name', label: 'Document Name' }, + { name: 'folder' }, + { name: 'url' }, + { name: 'created_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'deleted', type: 'boolean', control_type: 'checkbox' } + ] + when 'issue' + [ + { name: 'uid', label: 'Task ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'number', type: 'integer' }, + { name: 'title' }, + { + name: 'status', control_type: 'select', pick_list: + %w[open in_review pending closed].select { |op| [op.labelize, op] }, + toggle_hint: 'Select status', + toggle_field: { + name: 'status', + label: 'Status', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are : "open", "in_review", "pending", "closed".' + } + }, + { + name: 'type', control_type: 'select', + pick_list: [ + %w[issue issue], + %w[Planned\ work planned_work], + %w[other other] + ], + toggle_hint: 'Select type', + toggle_field: { + name: 'type', + label: 'Type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: "issue", "planned_work", "other".' + } + }, + { + name: 'assignees', type: 'array', of: 'object', properties: [ + { name: 'uid', label: 'Assignee ID' }, + { name: 'type' } + ] + }, + { + name: 'followers', label: 'Watchers', type: 'array', of: 'object', properties: [ + { name: 'uid', label: 'Follower ID' }, + { name: 'type' } + ] + }, + { name: 'room', label: 'Location' }, + { name: 'start_date', label: 'Start Date', + type: 'date_time', + render_input: 'date_conversion', + parse_output: 'date_conversion' }, + { name: 'closed_at', type: 'date_time', + render_input: 'date_time_conversion', + parse_output: 'date_time_conversion' }, + { name: 'due_at', type: 'date_time', + render_input: 'date_time_conversion', + parse_output: 'date_time_conversion' }, + { name: 'string', label: 'Stamp', + hint: 'One to two character stamp associated with task.' }, + { + name: 'issue_list', label: 'Task List', type: 'object', properties: [ + { name: 'uid', label: 'Task List ID' }, + { name: 'url' } + ] + }, + { name: 'description' }, + { name: 'cost_impact', label: 'Cost Impact', type: 'integer' }, + { + name: 'has_cost_impact', label: 'Has Cost Impact?', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'currency_code', label: 'Currency Code', + hint: 'The ISO-4217 currency code of the cost_impact,' \ + ' Currently only supports USD. maybe null if cost_impact is ' \ + 'not specified' }, + { name: 'schedule_impact', label: 'Schedule Impact', type: 'integer' }, + { + name: 'has_schedule_impact', label: 'Has Schedule Impact?', type: 'boolean', + control_type: 'checkbox' + }, + { + name: 'current_annotation', type: 'object', properties: [ + { name: 'uid', label: 'Annotation ID' }, + { name: 'color' }, + { name: 'stamp' }, + { name: 'visibility' }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { + name: 'sheet', type: 'object', properties: [ + { name: 'uid', label: 'Sheet ID' }, + { name: 'url' } + ] + } + ] + }, + { + name: 'comments', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'photos', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'created_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'updated_at', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + type: 'date_time' }, + { + name: 'updated_by', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' }, + { name: 'email' } + ] + } + ] + when 'file_upload' + [ + { name: 'uid' }, + { + name: 'aws_post_form_arguments', type: 'object', + properties: [ + { name: 'action' }, + { name: 'fields', type: 'array', of: 'object', properties: [ + { name: 'name' }, + { name: 'value' } + ] } + ] + }, + { name: "project_uid", label: "Project ID" }, + { name: 'webhook_url' } + ] + when 'annotation' + [ + { name: 'uid', label: 'Annotation ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'color' }, + { name: 'stamp' }, + { name: 'visibility' }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { + name: 'sheet', type: 'object', properties: [ + { name: 'uid', label: 'Sheet ID' }, + { name: 'url' } + ] + } + ] + when 'snapshot' + [ + { name: 'uid', label: 'Snapshot ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'title' }, + { name: 'url' }, + { name: 'created_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { + name: 'sheet', type: 'object', properties: [ + { name: 'uid', label: 'Sheet ID' }, + { name: 'url' } + ] + }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + } + ] + when 'photo' + [ + { name: 'uid', label: 'Photo ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'title' }, + { name: 'url', label: 'URL' }, + { name: 'created_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'deleted', type: 'boolean' } + ] + when 'field_report' + [ + { name: 'uid', label: 'Field Report ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'title' }, + { name: 'description' }, + { name: 'report_date', label: 'Report Date', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { name: 'status' }, + { + name: 'field_report_type', label: 'Field Report Type', type: 'object', properties: [ + { name: 'name' }, + { name: 'status' }, + { name: 'uid', label: 'Field Report ID' }, + { name: 'project_uid' }, + { name: 'template_type' } + ] + }, + { name: 'pdf_url', label: 'PDF URL' }, + { + name: 'pdf_form_values', label: 'PDF Form Values', type: 'array', of: 'object', + properties: [ + { name: 'name' }, + { name: 'value' } + ] + }, + { + name: 'pdf_form_fields', label: 'PDF Field Values', type: 'object' + }, + { + name: 'pg_form_values', type: 'object', properties: [ + { + name: 'pg_worklog_entries', label: 'Work Log Entries', type: 'array', of: 'object', + properties: [ + { name: 'trade' }, + { name: 'timespan' }, + { name: 'headcount', type: 'integer' }, + { name: 'description' }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'uid' } + ] + }, + { + name: 'pg_materials_entries', label: 'Material Entries', type: 'array', of: 'object', + properties: [ + { name: 'unit', type: 'integer' }, + { name: 'quantity', type: 'integer' }, + { name: 'item' }, + { name: 'description' }, + { name: 'deleted', type: "boolean", control_type: 'checkbox' }, + { name: 'uid' } + ] + }, + { + name: 'pg_equipment_entries', label: 'Equipment Entries', type: 'array', of: 'object', + properties: [ + { name: 'timespan' }, + { name: 'quantity', type: 'integer' }, + { name: 'item' }, + { name: 'description' }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'uid' } + ] + } + ] + }, + { + name: 'custom_items', label: 'Custom Form Items', type: 'array', + of: 'object', properties: [ + { name: 'section_label', label: 'Section Name' }, + { name: 'item_label', label: 'Item Label' }, + { name: 'value_name', label: 'Value Name' }, + { name: 'notes' }, + { name: 'text_val', label: 'Text Value' }, + { name: 'choice_val', label: 'Choice Value' }, + { name: 'number_val', label: 'Number Value', type: 'number' }, + { name: 'date_val', label: 'Date Value', type: 'date_time' }, + { name: 'array_val', label: 'Array Value', type: 'array', + of: 'string' }, + { name: 'toggle_val', label: 'Toggle Value', type: 'integer' } + ] + }, + { + name: 'attachments', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'photos', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'snapshots', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'email' }, + { name: 'uid', label: 'UID' }, + { name: 'url' } + ] + }, + { name: 'updated_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'weather', type: 'object', properties: [ + { name: 'humidity', type: 'number' }, + { name: 'precipitation_accumulation', type: 'number' }, + { name: 'precipitation_accumulation_unit' }, + { name: 'speed_unit' }, + { name: 'summary_key' }, + { name: 'temperature_max', type: 'integer' }, + { name: 'temperature_min' }, + { name: 'temperature_unit' }, + { name: 'wind_bearing', type: 'integer' }, + { name: 'wind_gust', type: 'number' }, + { name: 'wind_speed', type: 'number' } + ] + } + ] + when 'field_report_template' + [ + { name: 'uid', label: 'Field Report Template ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'name' }, + { name: 'field_reports', label: 'Field Reports', + type: 'object', properties: [ + { name: 'url' } + ] + }, + { name: 'cadence' }, + { name: 'is_pdf', label: 'Is PDF', type: 'boolean' }, + { name: 'pdf_url', label: 'PDF URL' }, + { name: 'template_type', label: 'Template Type' }, + { name: 'status' }, + { name: 'group_permissions', label: 'Group Permissions', + type: 'array', of: 'object', properties: [ + { name: 'permissions', type: 'array', of: 'string' }, + { name: 'role_name', label: 'Role Name' }, + { name: 'role_uid', label: 'Role UID' } + ] + }, + { name: 'user_permissions', label: 'User Permissions', + type: 'array', of: 'object', properties: [ + { name: 'permissions', type: 'array', of:' string' }, + { name: 'user_id', label: 'User UID'} + ] + }, + { name: 'created_by', label: 'Created By', type: 'object', + properties: [ + { name: 'email' }, + { name: 'uid' }, + { name: 'url' } + ] + }, + { name: 'updated_at', label: 'Updated At', type: 'date_time' } + ] + when 'field_reports/export' + [ + { name: 'uid', label: 'Export ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'status' }, + { name: 'file_url' }, + { name: 'resource_url' } + ] + when 'rfi' + [ + { name: 'uid', label: 'RFI ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'number', type: 'integer' }, + { + name: 'status', type: 'object', properties: [ + { name: 'uid', label: 'Status ID' }, + { name: 'label' }, + { name: 'color' } + ] + }, + { + name: 'locked', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'title' }, + { name: 'question' }, + { name: 'answer' }, + { name: 'sent_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + hint: 'Date when the RFI was sent. See ' \ + "Timestamps and " \ + 'Timezones for accepted date formats' }, + { name: 'due_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'assigned_to', type: 'array', of: 'object', + properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'updated_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + hint: 'Date when the RFI was sent. See ' \ + "Timestamps and " \ + 'Timezones for accepted date formats' }, + { + name: 'updated_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'created_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { + name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { + name: 'photos', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'attachments', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'snapshots', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + }, + { + name: 'comments', type: 'object', properties: [ + { name: 'total_count', type: 'integer' }, + { name: 'url' } + ] + } + ] + when 'rfi_status' + [ + { name: 'uid', label: 'RFI status ID' }, + { name: 'label' }, + { name: 'color' }, + { name: "project_uid", label: "Project ID" } + ] + when 'user', 'user_invite' + [ + { name: 'uid', label: 'User ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'email' }, + { name: 'first_name', label: 'First Name' }, + { name: 'last_name', label: 'Last Name' }, + { name: 'language' }, + { + name: 'role', type: 'object', properties: [ + { name: 'uid', label: 'role ID' }, + { name: 'url' } + ] + }, + { name: 'removed', label: 'Removed?', type: 'boolean' } + ] + when 'sheet' + [ + { name: 'uid', label: 'Sheet ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'name' }, + { name: 'version_name', label: 'Version Name' }, + { name: 'description' }, + { name: 'tags', type: 'array', of: 'string', hint: 'An array of strings representing the' \ + ' tags added to this sheet.' }, + { + name: 'published_by', type: 'object', properties: [ + { name: 'uid', label: 'UID' }, + { name: 'url' }, + { name: 'email' } + ] + }, + { name: 'published_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + hint: 'UTC date and time in ISO-8601 format.' }, + { + name: 'deleted', type: 'boolean', + control_type: 'checkbox' + }, + { name: 'uploaded_file_name', label: 'Uploaded file name' }, + { name: 'history_set_uid', label: 'History set ID' } + ] + when 'sheet_packet' + [ + { name: 'uid', label: 'Sheet Packet ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'status' }, + { name: 'file_url', label: 'File URL' }, + { + name: 'resource', type: 'object', properties: [ + { name: 'uid', label: 'Resource ID' }, + { name: 'url' } + ] + } + ] + when 'issue_list' + [ + { name: "uid", label: "Task List ID" }, + { name: "project_uid", label: "Project ID" }, + { name: "name", label: "Name" }, + { name: "deleted", label: "Deleted", type: "boolean", control_type: 'checkbox' } + ] + when 'role' + [ + { name: 'uid', label: 'Role ID' }, + { name: 'label', label: 'Role' }, + { name: "project_uid", label: "Project ID" } + ] + when 'sheet_upload' + [ + { name: 'uid', label: 'Sheet Version Upload ID' }, + { name: 'complete_url', label: 'Upload Completion URL' }, + { name: 'status' }, + { name: "project_uid", label: "Project ID" }, + { + name: 'file_upload_requests', label: 'File Upload Requests', + type: 'array', of: 'object', properties: [ + { name: 'uid', label: 'File Upload ID' }, + { name: 'upload_status', label: 'File Upload Status' }, + { name: 'url', label: 'File Upload URL' } + ] + } + ] + when 'version_upload' + [ + { name: 'uid', label: 'Sheet Version ID' }, + { name: "project_uid", label: "Project ID" }, + { name: 'status', label: 'Status' } + ] + when 'submittals/package' + [ + { name: 'uid', label: 'Submittal Package ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'name' }, + { name: 'spec_section' }, + { name: 'spec_section_name' }, + { name: 'custom_id' }, + { name: 'version' }, + { name: 'ball_in_court_status' }, + { name: 'submittals_due_date', type: 'date_time' }, + { name: 'required_on_job_date', type: 'date_time' }, + { name: 'transmission_status' }, + { name: 'is_voided', type: 'boolean' }, + { name: 'items', type: 'object', properties: [ + { name: 'uids', type: 'array', of: 'string' }, + { name: 'url' }, + { name: 'total_count', type: 'integer'} + ]}, + { name: 'visible_file_group_uid' }, + { name: 'design_review_due_date', type: 'date_time', label: 'Reviewer due date' }, + { name: 'general_contractor_review_due_date', type: 'date_time', label: 'Manager due date' }, + { name: 'latest_review', type: 'object', properties: [ + { name: 'uid', label: 'Review ID' }, + { name: 'created_at', type: 'date_time' }, + { name: 'created_by', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'file_group_uid' }, + { name: 'is_official_review' }, + { name: 'package_version' }, + { name: 'review_response_uid' }, + { name: 'reviewed_by', type: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]} + ]}, + { name: 'latest_review_response_uid' }, + { name: 'received_from_design_at', type: 'date_time', label: 'Received from reviewer at' }, + { name: 'sent_to_design_at', type: 'date_time', label: 'Sent to reviewer at' }, + { name: 'received_from_sub_at', type: 'date_time' }, + { name: 'returned_to_sub_at', type: 'date_time' }, + { name: 'managers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'reviewers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'submitters', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'unioned_watchers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'watchers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'created_at', type: 'date_time' }, + { name: 'created_by', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'published_at', type: 'date_time' }, + { name: 'updated_at', type: 'date_time' }, + { name: 'updated_by', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]} + ] + when 'submittals/item' + [ + { name: 'uid', label: 'Submittal Item ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'name' }, + { name: 'description' }, + { name: 'spec_bullet' }, + { name: 'spec_doc' }, + { name: 'spec_heading' }, + { name: 'spec_page' }, + { name: 'spec_section' }, + { name: 'spec_section_name' }, + { name: 'spec_subsection_name' }, + { name: 'lead_time_days', type:'integer' }, + { name: 'required_on_job_date', type: 'date_time' }, + { name: 'submittal_due_date', type: 'date_time' }, + { name: 'submittals_type' }, + { name: 'package', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'design_review_due_date', type: 'date_time', label: 'Reviewer due date' }, + { name: 'general_contractor_review_due_date', type: 'date_time', label: 'Manager due date' }, + { name: 'reviewers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'managers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'submitters', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'watchers', type: 'array', of: 'object', properties: [ + { name: 'type' }, + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'created_at' }, + { name: 'created_by', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]} + ] + when 'submittals_file_group' + [ + { name: 'package_uid', label: 'Submittal Package ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'data', label: 'File Groups', + type: 'array', of: 'object', properties: [ + { name: 'uid', label: 'File Group ID' }, + { name: 'files', type: 'array', of: 'object', properties: [ + { name: 'document_uid', label: 'Document ID' }, + { name: 'name' }, + { name: 'url' }, + { name: 'created_at', type: 'date_time' }, + { name: 'file_group_uid' } + ]}, + { name: 'package', type: 'object', properties: [ + { name: 'uid' }, + { name: 'url' } + ]}, + { name: 'upload_completed', type: 'boolean'} + ] + }, + { name: 'total_count' }, + { name: 'next_page_url' } + ] + when 'submittals_review_status' + [ + { name: 'package_uid', label: 'Submittal Package ID' }, + { name: 'project_uid', label: 'Project ID' }, + { name: 'data', label: 'Review Status', + type: 'array', of: 'object', properties: [ + { name: 'uid', label: 'Review ID' }, + { name: 'review_response_uid', label: 'Review Response ID' }, + { name: 'is_official_review', type: 'boolean' }, + { name: 'package_version', type: 'integer' }, + { name: 'file_group_uid', label: 'File Group ID' }, + { name: 'created_at' }, + { name: 'created_by', type: 'object', properties: [ + { name: 'uid', label: 'User ID' }, + { name: 'url' } + ]} + ] + }, + { name: 'total_count' }, + { name: 'next_page_url' } + ] + end + end + }, + + create_input_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'project' + [ + { name: 'name', label: 'Project Name', sticky: true, optional: false }, + { name: 'custom_id', label: 'Project Code', sticky: true }, + { + name: 'type', control_type: 'select', + label: 'Project Type', sticky: true, + pick_list: 'project_types', + toggle_hint: 'Select project type', + toggle_field: { + name: 'type', + label: 'Project type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Project type with possible values of general,' \ + ' manufacturing, power, water-sewer-waste, industrial-' \ + 'petroleum, transportation, hazardous-waste, telecom, ' \ + 'education-k-12, education-higher, gov-federal, ' \ + 'gov-state-local, or other' + } + }, + { name: 'status', label: 'Project Status', sticky: true }, + { name: 'owner', sticky: true, label: 'Project Owner' }, + { name: 'start_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project Start Date', + hint: 'Project start date. ISO-8601 date format (YYYY-MM-DD).' }, + { name: 'end_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project End Date', + hint: 'Project end date. ISO-8601 date format (YYYY-MM-DD).' }, + { name: 'street_1', sticky: true, + label: 'Street Line 1' }, + { name: 'street_2', sticky: true, label: 'Street line 2' }, + { name: 'city', sticky: true, label: 'Town or City' }, + { name: 'region', sticky: true, label: 'State, Province, or Region' }, + { name: 'postal_code', sticky: true, label: 'Zip or Postal Code' }, + { name: 'country', + sticky: true, + hint: 'Project address country in 2-letter ISO 3166 code.' }, + { + name: 'add_to_organization', type: 'boolean', sticky: true, + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'add_to_organization', + label: 'Add to Organization', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + } + ] + when 'rfi' + [ + { + name: 'locked', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'locked', + label: 'Locked', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'title', optional: false }, + { name: 'question' }, + { name: 'answer' }, + { name: 'sent_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + hint: 'Date when the RFI was sent. See ' \ + "Timestamps and " \ + 'Timezones for accepted date formats' }, + { name: 'due_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { name: 'assigned_to_uids', hint: "A comma separated list of user IDs." }, + { name: 'status', label: 'Status', + hint: "Uid's of the RFI's initial status. Defaults to the first RFI status in the project" } + ] + when 'issue' + [ + { name: 'title' }, + { + name: 'status', control_type: 'select', pick_list: + %w[open in_review pending closed].select { |op| [op.labelize, op] }, + toggle_hint: 'Select status', + toggle_field: { + name: 'status', + label: 'Status', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are : "open", "in_review", "pending", "closed".' + } + }, + { + name: 'type', control_type: 'select', + pick_list: [ + %w[issue issue], + %w[Planned\ work planned_work], + %w[other other] + ], + toggle_hint: 'Select type', + toggle_field: { + name: 'type', + label: 'Type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: "issue", "planned_work", "other".' + } + }, + { name: 'room', label: 'Location' }, + { name: 'start_date', label: 'Start Date', + type: 'date_time', + render_input: 'date_conversion', + parse_output: 'date_conversion' }, + { name: 'due_at', type: 'date_time', + render_input: 'date_time_conversion', + parse_output: 'date_time_conversion' }, + { name: 'description' }, + { name: 'cost_impact', label: 'Cost Impact', type: 'number' }, + { + name: 'has_cost_impact', label: 'Has Cost Impact?', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'has_cost_impact', + label: 'Has cost impact?', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'schedule_impact', label: 'Schedule Impact', type: 'integer' }, + { + name: 'has_schedule_impact', label: 'Has Schedule Impact?', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'has_schedule_impact', + label: 'Has schedule impact', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'assigned_to_uids', hint: "A comma separated list of user IDs." }, + { name: 'issue_list_uid', label: 'Task List ID', sticky: true } + ] + when 'sheet_packet' + [ + { name: 'sheet_uids', label: 'Sheet IDs', optional: false, + type: 'string', + hint: 'A comma separated list of sheet IDs.' }, + { + name: 'include_annotations', label: 'Include annotations?', type: 'boolean', sticky: true, + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'include_annotations', + label: 'Include annotations?', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + ] + when 'issue_list' + [ + { name: 'name', optional: false } + ] + when 'sheet_upload' + [ + { name: 'num_files', label: 'Number of PDFs', type: 'integer', optional: false }, + { name: 'version_name', label: 'Version Name', optional: false } + ] + when 'user_invite' + [ + { name: 'email', optional: false }, + { name: 'role_uid', label: 'Role ID', sticky: true, + hint: 'Unique identifier of role to assign user on project team' }, + { name: 'first_name' }, + { name: 'last_name' }, + { name: 'language' } + ] + when 'submittals/package' + [ + { name: 'name', optional: false, + hint: 'The name of the submittal package.' }, + { name: 'custom_id', optional: false, + hint: 'A custom ID for the submittal package.' }, + { name: 'item_uids', optional: false, label: 'Submittal Item IDs', + hint: 'The IDs of the specific submittal items to include in the submittal package. ' \ + 'Separate each item with a comma.' }, + { name: 'ball_in_court_status', + hint: "Reference to the role of the user from whom the next step is expected." \ + " Can be 'manager', 'submitter', or 'reviewer'." }, + { name: 'design_review_due_date', type: 'date', label: 'Reviewer due date', + hint: 'The date when the review from reviewers\' is due.' }, + { name: 'file_group_uid', + hint: 'UID of the file group to associate with this submittal package.' }, + { name: 'general_contractor_review_due_date', type: 'date', label: 'Manager due date', + hint: 'The date when the review from manager\'s is due.' }, + { name: 'notes' }, + { name: 'required_on_job_date', type: 'date', + hint: 'The date when the submitted material is required on the job.' }, + { name: 'submittal_due_date', type: 'date', + hint: 'Due date for this submittal package.' }, + { name: 'managers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of manager.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'reviewers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of reviewer.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'watchers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of watcher.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + } + ] + when 'submittals/item' + [ + { name: 'name', optional: false, + hint: 'Name of the submittal item.' }, + { name: 'description', + hint: 'Description of the submittal package.' }, + { name: 'package_uid', + hint: 'ID of the submittal package to which this item should be associated.' }, + { name: 'submittal_due_date', type: 'date', + hint: 'Date when the submittal is due.' }, + { name: 'required_on_job_date', type: 'date', + hint: 'The date when the submitted material is required on the job.' }, + { name: 'design_review_due_date', type: 'date', label: 'Reviewer due date', + hint: 'The date when the review from reviewers\' is due.' }, + { name: 'general_contractor_review_due_date', type: 'date', label: 'Manager due date', + hint: 'The date when the review from manager\'s is due.' }, + { name: 'managers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of manager.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'reviewers', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of reviewer.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'submitters', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of submitter.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'watchers', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of watcher.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + } + ] + when 'field_reports/export' + [ + { name: 'field_report_uids', label: 'Field Report IDs', optional: false, + hint: 'A comma separated list of field report IDs.' }, + { + name: 'file_type', + control_type: 'select', + pick_list: 'field_report_export_file_type', + sticky: true, + hint: 'Select the file type of the export', + }, + { + name: 'timezone', + control_type: 'select', + pick_list: 'timezones', + sticky: true, + toggle_hint: 'Select timezone', + toggle_field: { + name: 'timezone', + type: 'string', + label: 'Timezone', + control_type: 'text', + optional: false, + toggle_hint: 'Enter timezone', + hint: 'A valid timezone. See fill list ' \ + 'here.' + } + } + ] + else + [] + end.concat( + if config_fields['object'] != 'project' + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + hint: 'If your project is not in top 50, use project ID toggle', + toggle_hint: 'Select project', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + } + ] + else + [] + end + ) + end + }, + + update_input_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'project' + [ + { name: 'name', label: 'Project Name', sticky: true }, + { name: 'custom_id', label: 'Project Code', sticky: true }, + { + name: 'type', control_type: 'select', + label: 'Project Type', sticky: true, + pick_list: 'project_types', + toggle_hint: 'Select project type', + toggle_field: { + name: 'type', + label: 'Project type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Project type with possible values of general,' \ + ' manufacturing, power, water-sewer-waste, industrial-' \ + 'petroleum, transportation, hazardous-waste, telecom, ' \ + 'education-k-12, education-higher, gov-federal, ' \ + 'gov-state-local, or other' + } + }, + { name: 'status', label: 'Project Status', sticky: true }, + { name: 'owner', sticky: true, label: 'Project Owner' }, + { name: 'start_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project Start Date', + hint: 'Project start date. ISO-8601 date format (DD-MM-YYYY).' }, + { name: 'end_date', type: 'date', + sticky: true, + render_input: 'date_conversion', + parse_output: 'date_conversion', + label: 'Project End Date', + hint: 'Project end date. ISO-8601 date format (DD-MM-YYYY).' }, + { name: 'street_1', sticky: true, + label: 'Street Line 1' }, + { name: 'street_2', sticky: true, label: 'Street line 2' }, + { name: 'city', sticky: true, label: 'Town or City' }, + { name: 'region', sticky: true, label: 'State, Province, or Region' }, + { name: 'postal_code', sticky: true, label: 'Zip or Postal Code' }, + { name: 'country', + sticky: true, + hint: 'Project address country in 2-letter ISO 3166 code.' } + ] + when 'rfi' + [ + { + name: 'locked', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'locked', + label: 'Locked', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'title', optional: false }, + { name: 'question' }, + { name: 'answer' }, + { name: 'sent_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp', + hint: 'Date when the RFI was sent. See ' \ + "Timestamps and " \ + 'Timezones for accepted date formats' }, + { name: 'due_at', type: 'date_time', + render_input: 'render_iso8601_timestamp', + parse_output: 'parse_iso8601_timestamp' }, + { name: 'assigned_to_uids', hint: "A comma separated list of user IDs." }, + { name: 'status', label: 'Status', + hint: "Uid's of the RFI's initial status. Defaults to the first RFI status in the project" } + ] + when 'issue' + [ + { name: 'title' }, + { + name: 'status', control_type: 'select', pick_list: + %w[open in_review pending closed].select { |op| [op.labelize, op] }, + toggle_hint: 'Select status', + toggle_field: { + name: 'status', + label: 'Status', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are : "open", "in_review", "pending", "closed".' + } + }, + { + name: 'type', control_type: 'select', + pick_list: [ + %w[issue issue], + %w[Planned\ work planned_work], + %w[other other] + ], + toggle_hint: 'Select type', + toggle_field: { + name: 'type', + label: 'Type', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: "issue", "planned_work", "other".' + } + }, + { name: 'room', label: 'Location' }, + { name: 'start_date', label: 'Start Date', + type: 'date_time', + render_input: 'date_conversion', + parse_output: 'date_conversion' }, + { name: 'due_at', type: 'date_time', + render_input: 'date_time_conversion', + parse_output: 'date_time_conversion' }, + { name: 'description' }, + { name: 'cost_impact', label: 'Cost Impact', type: 'number' }, + { + name: 'has_cost_impact', label: 'Has Cost Impact?', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'has_cost_impact', + label: 'Has cost impact', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'schedule_impact', label: 'Schedule Impact', type: 'integer', + hint: "The task's schedule impact in seconds, if it has one." }, + { + name: 'has_schedule_impact', label: 'Has Schedule Impact?', type: 'boolean', + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'has_schedule_impact', + label: 'Has schedule impact', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + { name: 'assigned_to_uids', hint: "A comma separated list of user IDs." }, + { name: 'issue_list_uid', label: 'Task List ID', sticky: true } + ] + when 'sheet_packet' + [ + { name: 'sheet_uids', label: 'Sheet IDs', optional: false, + type: 'string', + hint: 'A comma separated list of sheet IDs.' }, + { + name: 'include_annotations', label: 'Include annotations?', type: 'boolean', sticky: true, + control_type: 'checkbox', toggle_hint: 'Select from options list', + toggle_field: { + name: 'include_annotations', + label: 'Include annotations?', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + }, + ] + when 'issue_list' + [ + { name: 'name', optional: false } + ] + when 'sheet_upload' + [ + { name: 'num_files', label: 'Number of PDFs', type: 'integer', optional: false }, + { name: 'version_name', label: 'Version Name', optional: false } + ] + when 'photo' + [ + { name: 'title', label: 'Photo title', + hint: 'New title of the photo', sticky: true } + ] + when 'submittals/package' + [ + { name: 'name', + hint: 'The name of the submittal package.' }, + { name: 'custom_id', + hint: 'A custom ID for the submittal package.' }, + { name: 'item_uids', label: 'Submittal Item IDs', type: 'array', + hint: 'The UIDs of the specific items to include in the submittal package.' }, + { name: 'ball_in_court_status', + hint: 'Reference to the role of the user from whom the next step is expected.' \ + ' Can be \'manager\', \'submitter\', or \'reviewer\'.' }, + { name: 'design_review_due_date', type: 'date', label: 'Reviewer due date', + hint: 'The date when the review from reviewers\' is due.' }, + { name: 'file_group_uid', + hint: 'UID of the file group to associate with this submittal package.' }, + { name: 'general_contractor_review_due_date', type: 'date', label: 'Manager review due date', + hint: 'The date when the review from manager\'s is due.' }, + { name: 'notes' }, + { name: 'required_on_job_date', type: 'date', + hint: 'The date when the submitted material is required on the job.' }, + { name: 'submittal_due_date', type: 'date', + hint: 'Due date for this submittal package.' }, + { name: 'managers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of manager.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'reviewers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of reviewer.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'watchers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of watcher.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + } + ] + when 'submittals/item' + [ + { name: 'name', + hint: 'Name of the submittal item.' }, + { name: 'description', + hint: 'Description of the submittal package.' }, + { name: 'package_uid', label: 'Submittal Package ID', + hint: 'ID of the submittal package to which this item should be associated.' }, + { name: 'submittal_due_date', type: 'date', + hint: 'Date when the submittal is due.' }, + { name: 'required_on_job_date', type: 'date', + hint: 'The date when the submitted material is required on the job.' }, + { name: 'design_review_due_date', type: 'date', label: 'Reviewer due date', + hint: 'The date when the review from reviewers is due.' }, + { name: 'general_contractor_review_due_date', type: 'date', label: 'Manager review due date', + hint: 'The date when the review from manager\'s is due.' }, + { name: 'managers', type: 'array', of: 'object', + hint: 'An array of objects describing users assigned the role of manager.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'reviewers', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of reviewer.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'submitters', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of submitter.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + }, + { name: 'watchers', type: 'array', of: 'object', + hint: 'A list describing users assigned the role of watcher.', + properties: [ + { name: 'type', hint: 'Can be `user` or `group`.' }, + { name: 'uid', hint: 'ID of either the user or group' } + ] + } + ] + else + [] + end.concat( + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_hint: 'Select project', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + } + ] + ).concat( + case config_fields['object'] + when 'submittals/package' + [{ name: 'uid', label: 'Submittal Package ID', optional: false }] + when 'submittals/item' + [{ name: 'uid', label: 'Submittal Item ID', optional: false }] + else + [{ name: 'uid', label: "#{config_fields['object'].labelize} ID", optional: false }] + end + ) + end + }, + + upload_input_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'attachment' + [ + { name: 'content_type', + hint: 'Content type of the document\'s file. Example for pdf application/pdf', + optional: false }, + { name: 'file_content', optional: false }, + { name: 'name', optional: false, + label: 'Document name', + hint: 'Name of the document.' }, + { + name: 'folder', label: 'Folder', sticky: true, + control_type: 'select', + pick_list: 'project_folders', + pick_list_params: { project_uid: 'project_uid' }, + toggle_hint: 'Select folder', + hint: 'Folder shows in select options only if at least one file exist in the folder', + toggle_field: { + name: 'folder', + label: 'Project folder', + type: 'string', + control_type: 'text', + toggle_hint: 'Use custom value', + hint: 'Folder in project to place the document ' \ + '(case-sensitive). Leave blank to select root folder' + } + }, + { + name: 'auto_version', type: 'boolean', sticky: true, + control_type: 'checkbox', + toggle_hint: 'Select from options list', + toggle_field: { + name: 'auto_version', + type: 'boolean', + control_type: 'text', + label: 'Auto version', + toggle_hint: 'Use custom value', + hint: 'Allowed values are: true, false' + } + } + ] + when 'photo' + [ + { name: 'content_type', optional: false, + hint: "Content type of the photo's file" }, + { name: 'file_content', optional: false }, + { name: 'title', optional: false, label: 'Photo title' } + ] + when 'file_upload' + [ + { name: 'ver_upload_uid', label: 'Sheet Version Upload ID', optional: false }, + { name: 'file_upload_request_uid', label: 'File Upload ID', optional: false }, + { name: 'file_name', label: 'File Name', optional: false }, + { name: 'file_content', label: 'File Content', optional: false } + ] + when 'version_upload' + [ + { name: 'ver_upload_uid', label: 'Sheet Version Upload ID', optional: false } + ] + else + [] + end.concat( + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_hint: 'Select project', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + } + ] + ) + end + }, + + download_input_schema: { + fields: lambda do |_connection, config_fields| + case config_fields['object'] + when 'field_reports/export' + [{ name: 'uid', label: 'Export ID', optional: false }] + when 'sheets/packet' + [{ name: 'uid', label: "Sheet Packet ID", optional: false, + hint: 'Ensure the status of the sheet packet export is `complete` before downloading.' }] + else + [{ name: 'uid', label: "#{config_fields['object'].labelize} ID", optional: false }] + end + end + }, + + trigger_input: { + fields: lambda do |_connection, config_fields| + if config_fields['object'] != 'project' + [ + { + name: 'project_uid', optional: false, + label: 'Project', + control_type: 'select', pick_list: 'project_list', + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_hint: 'Select project', + toggle_field: { + name: 'project_uid', + label: 'Project ID', + type: 'string', + control_type: 'text', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + }, + { + name: 'output_schema', + control_type: 'schema-designer', + ngIf: 'input.object == "field_report"', + extends_schema: true, + label: 'PDF field values', + hint: 'Manually define the values expected of your PDF field values in the field report.', + optional: true + } + ] + else + [] + end + end + }, + + custom_action_input: { + fields: lambda do |_connection, config_fields| + input_schema = parse_json(config_fields.dig('input', 'schema') || '[]') + + [ + { + name: 'path', + optional: false, + hint: 'Base URI is https://io.plangrid.com' \ + ' - path will be appended to this URI. ' \ + 'Use absolute URI to override this base URI.' + }, + ( + if %w[get delete].include?(config_fields['verb']) + { + name: 'input', + type: 'object', + control_type: 'form-schema-builder', + sticky: input_schema.blank?, + label: 'URL parameters', + add_field_label: 'Add URL parameter', + properties: [ + { + name: 'schema', + extends_schema: true, + sticky: input_schema.blank? + }, + ( + if input_schema.present? + { + name: 'data', + type: 'object', + properties: call('make_schema_builder_fields_sticky', + input_schema) + } + end + ) + ].compact + } + else + { + name: 'input', + type: 'object', + properties: [ + { + name: 'schema', + extends_schema: true, + schema_neutral: true, + control_type: 'schema-designer', + sample_data_type: 'json_input', + sticky: input_schema.blank?, + label: 'Request body parameters', + add_field_label: 'Add request body parameter' + }, + ( + if input_schema.present? + { + name: 'data', + type: 'object', + properties: call('make_schema_builder_fields_sticky', + input_schema) + } + end + ) + ].compact + } + end + ), + { + name: 'output', + control_type: 'schema-designer', + sample_data_type: 'json_http', + extends_schema: true, + schema_neutral: true, + sticky: true + } + ] + end + }, + + custom_action_output: { + fields: lambda do |_connection, config_fields| + parse_json(config_fields['output'] || '[]') + end + } + }, + + actions: { + custom_action: { + description: "Custom action in Plangrid", + help: { + body: 'Build your own Plangrid action for any Plangrid REST endpoint.', + learn_more_url: 'https://developer.plangrid.com/docs/', + learn_more_text: 'The Plangrid API documentation' + }, + + config_fields: [{ + name: 'verb', + label: 'Request type', + hint: 'Select HTTP method of the request', + optional: false, + control_type: 'select', + pick_list: %w[get post patch delete].map { |verb| [verb.upcase, verb] } + }], + + input_fields: lambda do |object_definitions| + object_definitions['custom_action_input'] + end, + + execute: lambda do |_connection, input| + error("#{input['verb']} not supported") if %w[get post patch delete].exclude?(input['verb']) + data = input.dig('input', 'data').presence || {} + case input['verb'] + when 'get' + response = get(input['path'], data). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end.compact + if response.is_a?(Array) + array_name = parse_json(input['output'] || '[]'). + dig(0, 'name') || 'array' + { array_name.to_s => response } + elsif response.is_a?(Hash) + response + else + error('API response is not a JSON') + end + when 'post' + post(input['path'], data). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end.compact + when 'patch' + patch(input['path'], data). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end.compact + when 'delete' + delete(input['path'], data). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end.compact + end + end, + + output_fields: lambda do |object_definitions| + object_definitions['custom_action_output'] + end + }, + + create_object: { + title: 'Create object', + description: lambda do |_, objects| + "Create #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Create object in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'create_object_list', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + object_definitions['create_input_schema'] + end, + + execute: lambda do |_connection, input| + if input['object'] == 'submittals/package' + input['item_uids'] = input['item_uids'].split(',') + end + + if input['object'] == 'field_reports/export' + input['field_report_uids'] = input['field_report_uids'].split(',') + end + + payload = + input.except('project_uid', 'object').each_with_object({}) do |(key, val), hash| + hash[key] = if %w[due_at sent_at].include?(key) + val.to_time.utc.iso8601 + elsif %w[sheet_uids assigned_to_uids].include?(key) + val.split(",") + else + val + end + end + case input['object'] + when 'project' + post('/projects').payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + when 'sheet_packet', 'sheet_upload', 'user_invite' + path = input['object'].split("_") + post("/projects/#{input['project_uid']}/#{path.first.pluralize}/#{path.last.pluralize}").payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + + when 'field_reports/export' + post("/projects/#{input['project_uid']}/field_reports/export").payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + else + post("/projects/#{input['project_uid']}/#{input['object'].pluralize}").payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + end.merge('project_uid' => input['project_uid']) + end, + + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + + update_object: { + title: 'Update object', + description: lambda do |_, objects| + "Update #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Update object in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'update_object_list', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + object_definitions['update_input_schema'] + end, + + execute: lambda do |_connection, input| + payload = + input.except('project_uid', 'uid', 'object').each_with_object({}) do |(key, val), hash| + hash[key] = %w[due_at sent_at].include?(key) ? val.to_time.utc.iso8601 : val + end + if input['object'] == 'project' + patch("/projects/#{input['project_uid']}").payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + else + patch("/projects/#{input['project_uid']}/#{input['object'].pluralize}/#{input['uid']}").payload(payload). + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + end.merge('project_uid' => input['project_uid']) + end, + + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + + get_object: { + title: 'Get object by ID', + description: lambda do |_, objects| + "Get #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Get object by ID in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'get_objects', + toggle_hint: 'Select object', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + toggle_hint: 'Select project', + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + }, + ].concat(object_definitions['get_input_schema']) + end, + + execute: lambda do |_connection, input| + params = + input.except('project_uid', 'object').each_with_object({}) do |(key, val), hash| + hash[key] = %w[updated_after].include?(key) ? val.to_time.utc.iso8601 : val + end + case input['object'] + when 'project' + get("/projects/#{input['project_uid']}") + when 'field_reports/export' + get("/projects/#{input['project_uid']}/field_reports/export/#{input['uid']}") + when 'sheet_packet' + get("/projects/#{input['project_uid']}/sheets/packets/#{input['uid']}") + when 'submittals/item' + get("/projects/#{input['project_uid']}/submittals/items?uids=#{input['uids']}").dig('data',0) + when 'submittals_file_group' + get("/projects/#{input['project_uid']}/submittals/packages/#{input['uid']}/file_groups") + .merge('package_uid' => input['uid']) + when 'submittals_history' + get("/projects/#{input['project_uid']}/submittals/packages/#{input['uid']}/history") + .merge('package_uid' => input['uid']) + when 'submittals_review_status' + get("/projects/#{input['project_uid']}/submittals/packages/#{input['uid']}/reviews") + .merge('package_uid' => input['uid']) + else + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}/#{input['uid']}") + end.merge('project_uid' => input['project_uid']) + end, + + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + + search_objects: { + title: 'Search objects', + description: lambda do |_, objects| + "Search #{objects['object']&.downcase&.pluralize || 'objects'} in PlanGrid" + end, + help: "Search objects in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'search_objects', + toggle_hint: 'Select object', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + toggle_hint: 'Select project', + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + } + ].concat(object_definitions['get_input_schema']) + end, + + execute: lambda do |_connection, input| + params = + input.except('project_uid', 'object', 'output_schema').each_with_object({}) do |(key, val), hash| + hash[key] = %w[updated_after].include?(key) ? val.to_time.utc.iso8601 : val + end + case input['object'] + when 'rfi_status' + response = get("/projects/#{input['project_uid']}/rfis/statuses", params) + { data: response['data'], total_count: response['total_count'], next_page_url: response['next_page_url'] } + when 'field_report' + response = get("/projects/#{input['project_uid']}/#{input['object'].pluralize}", params) + results = response['data'] + results.map do |field_report| + field_report.merge('pdf_form_fields' => field_report['pdf_form_values']&.map { |a| { a['name'] => a['value'] } }&.inject(:merge)) + end + { data: results, total_count: response['total_count'], next_page_url: response['next_page_url'] } + else + response = get("/projects/#{input['project_uid']}/#{input['object'].pluralize}", params) + { data: response['data'], total_count: response['total_count'], next_page_url: response['next_page_url'] } + end.merge('project_uid' => input['project_uid']) + end, + + output_fields: lambda do |object_definitions| + [ + { + name: 'data', label: 'Results', type: 'array', of: 'object', properties: object_definitions['get_output_schema'] + }, + { name: 'total_count', label: 'Total Count', type: 'integer' }, + { name: 'next_page_url', label: 'Next Page URL' } + ] + end, + + sample_output: lambda do |_connection, input| + { "data" => Array.wrap(call(:get_sample_output, input['object'], input['project_uid'] || "")) } + end + }, + + upload_object: { + title: 'Upload object', + description: lambda do |_, objects| + "Upload #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Upload object in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'upload_object_list', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + object_definitions['upload_input_schema'] + end, + + execute: lambda do |_connection, input| + case input['object'] + when 'file_upload' + file_upload_info = post("/projects/#{input['project_uid']}/sheets/" \ + "uploads/#{input.delete('ver_upload_uid')}/" \ + "files/#{input.delete('file_upload_request_uid')}"). + headers('Content-Type': 'application/json'). + payload(file_name: input.delete('file_name')) + call(:get_upload_object, file_upload_info, input) + when 'photo', 'attachment' + file_upload_info = post("/projects/#{input['project_uid']}/#{input['object'].pluralize}/uploads"). + headers('Content-type': 'application/json'). + payload(input.except('project_uid', 'file_content', 'object')) + call(:get_upload_object, file_upload_info, input) + else + post("/projects/#{input['project_uid']}/sheets/uploads/#{input['ver_upload_uid']}/completions") + end + end, + + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + + download_object: { + title: 'Download object', + description: lambda do |_, objects| + "Download #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Download object in PlanGrid", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'download_object_list', + toggle_hint: 'Select object', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + [ + { + name: 'project_uid', + control_type: 'select', + pick_list: 'project_list', + label: 'Project', + optional: false, + toggle_hint: 'Select project', + hint: 'If your project is not in top 50, use project ID toggle.', + toggle_field: { + name: 'project_uid', + type: 'string', + control_type: 'text', + optional: false, + label: 'Project ID', + toggle_hint: 'Enter project ID', + hint: 'Provide project ID. For example, 0bbb5bdb-3f87-4b46-9975-90e797ee9ff9' + } + }, + ].concat(object_definitions['download_input_schema']) + end, + + execute: lambda do |_connection, input| + case input['object'] + when 'field_reports/export' + record = get("/projects/#{input['project_uid']}/field_reports/export/#{input['uid']}") + file_content = get(record['url'] || record['file_url']). + response_format_raw. + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + { content: file_content } + else + record = get("/projects/#{input['project_uid']}/#{input['object'].pluralize}/#{input['uid']}") + file_content = get(record['url'] || record['file_url']). + response_format_raw. + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + { content: file_content } + end + end, + + output_fields: lambda do |object_definitions| + [ + { name: 'content', label: 'File Contents' } + ] + end + } + }, + + triggers: { + new_object: { + title: 'New object in PlanGrid', + description: lambda do |_, objects| + "New #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Triggers when an object is created", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'object_list', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + object_definitions['trigger_input'].concat( + [ + { + name: 'since', + label: 'When first started, this recipe should pick up events from', + hint: 'When you start recipe for the first time, ' \ + 'it picks up trigger events from this specified date and time. ' \ + 'Leave empty to get records created or updated one hour ago', + sticky: true, + type: 'timestamp' + } + ] + ) + end, + poll: lambda do |_connection, input, closure| + updated_after = closure&.[]('updated_after') || + (input['since'] || 1.hour.ago).to_time.utc.iso8601 + limit = 10 + skip = closure&.[]('skip') || 0 + response = if (next_page_url = closure&.[]('next_page_url')).present? + get(next_page_url) + elsif input['object'] == 'project' + get("/projects").params(limit: limit, skip: skip, updated_after: updated_after) + elsif input['object'] == 'issue' + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after, include_annotationless: true) + elsif input['object'] == 'field_report' + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after) + else + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after) + end + closure = if (next_page_url = response['next_page_url']).present? + { 'skip' => skip + limit, + 'next_page_url' => next_page_url } + else + { 'skip' => 0, + 'updated_after' => response['data']&. + last&.[]('created_at') || response['data']&.last&.[]('published_at') } + end + objects = if input['object'] == 'field_report' + response['data']&.map do |field_report| + field_report.merge('pdf_form_fields' => field_report['pdf_form_values']&.map { |a| { a['name'] => a['value'] } }&.inject(:merge), + 'project_uid' => input['project_uid']) + end + else + response['data']&.map { |o| o.merge('project_uid' => input['project_uid']) } + end + { + events: objects || [], + next_poll: closure, + can_poll_more: response['next_page_url'].present? + } + end, + dedup: lambda do |object| + object['uid'] + end, + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + + new_updated_object: { + title: 'New or updated object in PlanGrid', + description: lambda do |_, objects| + "New or updated #{objects['object']&.downcase || 'object'} in PlanGrid" + end, + help: "Triggers when an object is created or updated", + + config_fields: [ + { + name: 'object', + optional: false, + label: 'Object', + control_type: 'select', + pick_list: 'object_list_new', + hint: 'Select the object from picklist.' + } + ], + + input_fields: lambda do |object_definitions| + object_definitions['trigger_input'].concat( + [ + { + name: 'since', + label: 'When first started, this recipe should pick up events from', + hint: 'When you start recipe for the first time, ' \ + 'it picks up trigger events from this specified date and time. ' \ + 'Leave empty to get records created or updated one hour ago', + sticky: true, + type: 'timestamp' + } + ] + ) + end, + poll: lambda do |_connection, input, closure| + updated_after = closure&.[]('updated_after') || + (input['since'] || 1.hour.ago).to_time.utc.iso8601 + limit = 10 + skip = closure&.[]('skip') || 0 + update_time = Time.now.utc.iso8601 + response = if (next_page_url = closure&.[]('next_page_url')).present? + get(next_page_url) + elsif input['object'] == 'project' + get("/projects").params(limit: limit, skip: skip, updated_after: updated_after) + elsif input['object'] == 'issue' + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after, include_annotationless: true) + elsif input['object'] == 'field_report' + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after) + elsif input['object'] == 'attachment' + get("/projects/#{input['project_uid']}/documents"). + params(limit: limit, skip: skip, updated_after: updated_after) + else + get("/projects/#{input['project_uid']}/#{input['object'].pluralize}"). + params(limit: limit, skip: skip, updated_after: updated_after) + end + object_update_time = ['annotation', 'sheet', 'attachment'].include? input['object'] + closure = if (next_page_url = response['next_page_url']).present? + { 'skip' => skip + limit, + 'next_page_url' => next_page_url } + else + { 'skip' => 0, + 'updated_after' => object_update_time ? update_time : response['data']&.last&.[]('updated_at') } + end + project_hash = { 'project_uid' => input['project_uid'] } + project_hash["updated_after"] = (closure["updated_after"] || updated_after) if ['annotation', 'sheet', 'attachment'].include? input['object'] + objects = if input['object'] == 'field_report' + response['data']&.map do |field_report| + field_report.merge('pdf_form_fields' => field_report['pdf_form_values']&.map { |a| { a['name'] => a['value'] } }&.inject(:merge), + 'project_uid' => input['project_uid']) + end + elsif input['object'] == 'attachment' + response['data'].select { |o| o['type'] == 'file' }&.map do |document| + get("/projects/#{input['project_uid']}/attachments/#{document['uid']}") + end&.map { |o| o.merge('project_uid' => input['project_uid']) } + else + response['data']&.map { |o| o.merge('project_uid' => input['project_uid']) } + end + { + events: objects || [], + next_poll: closure, + can_poll_more: response['next_page_url'].present? + } + end, + dedup: lambda do |object| + "#{object['uid']}@#{(object['updated_after'] || object['updated_at'])}" + end, + output_fields: lambda do |object_definitions| + object_definitions['get_output_schema'] + end, + sample_output: lambda do |_connection, input| + call(:get_sample_output, input['object'], input['project_uid'] || "") + end + }, + }, + + methods: { + get_sample_output: lambda do |object, project_uid| + case object + when 'project' + get('/projects')&.dig('data', 0) + when 'field_report' + results = get("/projects/#{project_uid}/field_reports").dig('data', 0)&.merge('project_uid' => project_uid) + when 'rfi_status' + get("/projects/#{project_uid}/rfis/statuses")&.dig('data', 0)&.merge('project_uid' => project_uid) + when 'user_invite' + get("/projects/#{project_uid}/users")&.dig('data', 0)&.merge('project_uid' => project_uid) + when 'sheet_packet' + { + "data" => [ + { + uid: "92cf7193-af0c-42fc-a3ab-7ef5149da720", + file_url: "https://packet-assets.plangrid.com/92cf7193-af0c-42fc-a3ab-7ef5149da720.pdf", + resource: { + uid: "92cf7193-af0c-42fc-a3ab-7ef5149da720", + url: "https://io.plangrid.com/projects/da48fcc3-7af1-4fd6-a083-70195468718a/sheets/" \ + "packets/92cf7193-af0c-42fc-a3ab-7ef5149da720" + }, + status: "incomplete" + } + ] + } + when 'sheet_upload' + { + "data" => [ + { + uid: "d050ed4d-425b-4cd2-a46a-1113be6ed37c", + complete_url: "https://io.plangrid.com/projects/5a77d1aa-44ef-4d0f-904a-839015d82dbb/sheets/uploads/d050ed4d-425b-4cd2-a46a-1113be6ed37c/completions", + status: "incomplete", + file_upload_requests: [ + { + uid: "cba96343-bfaf-4f95-805a-dd49169944c0", + upload_status: "issued", + url: "https://io.plangrid.com/projects/5a77d1aa-44ef-4d0f-904a-839015d82dbb/sheets/uploads/d050ed4d-425b-4cd2-a46a-1113be6ed37c/files/cba96343-bfaf-4f95-805a-dd49169944c0" + } + ] + } + ] + } + when 'version_upload' + { + "data" => [ + { + uid: "f23f1677-cb77-425f-bdd5-626a9fba8e83", + complete_url: "https://io.plangrid.com/projects/5a77d1aa-44ef-4d0f-904a-839015d82dbb/sheets/uploads/f23f1677-cb77-425f-bdd5-626a9fba8e83/completions", + status: "complete" + } + ] + } + when 'submittals/items' + get("/projects/#{project_uid}/submittals/items")&.dig('data', 0)&.merge('project_uid' => project_uid) + else + get("/projects/#{project_uid}/#{object.pluralize}")&.dig('data', 0)&.merge('project_uid' => project_uid) + end + end, + + get_upload_object: lambda do |file_upload_info, input| + headers = file_upload_info&.dig('aws_post_form_arguments', 'fields')&. + each_with_object({}) do |obj, hash| + hash[obj['name']] = obj['value'] + end + post(file_upload_info&.dig('aws_post_form_arguments', 'action')). + payload(key: headers['key'], + policy: headers['policy'], + signature: headers['signature'], + AWSAccessKeyId: headers['AWSAccessKeyId'], + 'content-type': headers['Content-Type'], + 'success_action_redirect': headers['success_action_redirect'], + 'x-amz-server-side-encryption': + headers['x-amz-server-side-encryption'], + 'x-amz-storage-class': headers['x-amz-storage-class'], + file: input['file_content']). + request_format_multipart_form. + after_error_response(/.*/) do |_code, body, _header, message| + error("#{message}: #{body}") + end + end, + + make_schema_builder_fields_sticky: lambda do |input| + input.map do |field| + if field[:properties].present? + field[:properties] = call("make_schema_builder_fields_sticky", + field[:properties]) + elsif field["properties"].present? + field["properties"] = call("make_schema_builder_fields_sticky", + field["properties"]) + end + field[:sticky] = true + field + end + end + }, + + pick_lists: { + create_object_list: lambda do |_connection| + [ + ['Project', 'project'], + ['Field Report Export', 'field_reports/export'], + ['Invite User', 'user_invite'], + ['RFI', 'rfi'], + ['Task List', 'issue_list'], + ['Task', 'issue'], + ['Sheet Packet', 'sheet_packet'], + ['Sheet Version', 'sheet_upload'], + ['Submittal Item', 'submittals/item'], + ['Submittal Package', 'submittals/package'] + ] + end, + + update_object_list: lambda do |_connection| + [ + ['Project', 'project'], + ['Photo', 'photo'], + ['RFI', 'rfi'], + ['Task List', 'issue_list'], + ['Task', 'issue'], + ['Submittal Package', 'submittals/package'], + ['Submittal Item', 'submittals/item'] + ] + end, + + upload_object_list: lambda do |_connection| + [ + ['Document', 'attachment'], + ['Photo', 'photo'], + ['File to Sheet Version', 'file_upload'], + ['Complete Sheet Version', 'version_upload'] + ] + end, + + get_objects: lambda do |_connection| + [ + ['Project', 'project'], + ['Document', 'attachment'], + ['Field Report Export', 'field_reports/export'], + ['Photo', 'photo'], + ['RFI', 'rfi'], + ['Sheet', 'sheet'], + ['Sheet Packet', 'sheet_packet'], + ['Snapshot', 'snapshot'], + ['Submittal Package', 'submittals/package'], + ['Submittal Item', 'submittals/item'], + ['Submittal Package File Group', 'submittals_file_group'], + ['Submittal Package Review Status', 'submittals_review_status'], + ['Task', 'issue'], + ['Task List', 'issue_list'], + ['User', 'user'] + ] + end, + + search_objects: lambda do |_connection| + [ + ['Field Report', 'field_report'], + ['Field Report Template', 'field_report_template'], + ['RFI Status', 'rfi_status'], + ['Role', 'role'] + ] + end, + + download_object_list: lambda do |_connection| + [ + ['Document', 'attachment'], + ['Field Report Export', 'field_reports/export'], + ['Sheet Packet', 'sheets/packet'] + ] + end, + + object_list: lambda do |_connection| + [ + ['Project', 'project'], + ['Annotation', 'annotation'], + ['Document', 'attachment'], + ['Field Report', 'field_report'], + ['Photo', 'photo'], + ['RFI', 'rfi'], + ['Sheet', 'sheet'], + ['Snapshot', 'snapshot'], + ['Task', 'issue'] + ] + end, + + object_list_new: lambda do |_connection| + [ + ['Project', 'project'], + ['Annotation', 'annotation'], + ['Document', 'attachment'], + ['Field Report', 'field_report'], + ['Field Report Template', 'field_report_template'], + ['RFI', 'rfi'], + ['Sheet', 'sheet'], + ['Submittal Package', 'submittals/package'], + ['Submittal Item', 'submittals/item'], + ['Task', 'issue'] + ] + end, + + project_list: lambda do |_connection| + get('/projects')&.[]('data')&.pluck('name', 'uid') + end, + + project_types: lambda do |_connection| + ['general', 'manufacturing', 'power', 'water-sewer-waste', + 'industrial-petroleum', 'transportation', 'hazardous-waste', + 'telecom', 'education-k-12', 'education-higher', 'gov-federal', + 'gov-state-local', 'other'].map { |type| [type.labelize, type] } + end, + + download_object_list: lambda do |_connection| + [ + ['Document', 'attachment'], + ['Field Report Export', 'field_reports/export'], + ['Sheet Packet', 'sheets/packet'] + ] + end, + + project_folders: lambda do |_connection, project_uid:| + if project_uid.length == 36 + folders = get("/projects/#{project_uid}/attachments")['data']&. + pluck('folder')&.uniq + folders.size > 0 ? folders&.map { |folder| [folder || 'Root', folder || ''] } : [['Root', '']] + end + end, + + field_report_export_file_type: lambda do |_connection| + [ + [ 'PDF', 'pdf' ], + [ 'XLSL', 'xlsx' ] + ] + end, + + timezones: lambda do |_connection| + [ + ['America/Los_Angeles','America/Los_Angeles'], + ['America/Denver','America/Denver'], + ['America/Phoenix','America/Phoenix'], + ['America/Chicago','America/Chicago'], + ['America/Mexico_City','America/Mexico_City'], + ['America/New_York','America/New_York'] + ] + end + } +}