diff --git a/custom_connectors/oauth2/assemble.rb b/custom_connectors/oauth2/assemble.rb
new file mode 100644
index 00000000..40908594
--- /dev/null
+++ b/custom_connectors/oauth2/assemble.rb
@@ -0,0 +1,704 @@
+{
+ title: 'Assemble',
+
+ connection: {
+ fields: [
+ {
+ name: 'client_id',
+ optional: false
+ },
+ {
+ name: 'client_secret',
+ optional: false,
+ type: 'password'
+ },
+ {
+ name: 'subdomain',
+ optional: false,
+ control_type: 'subdomain',
+ url: '.tryassemble.com'
+ }
+ ],
+
+ authorization: {
+ type: 'oauth2',
+
+ authorization_url: lambda do |connection|
+ 'https://auth.tryassemble.com/connect/authorize?' +
+ 'acr_values=idp:forge&scope=tenant%20openid%20core_api.all%20offline_access&response_type=code' +
+ "&client_id=#{connection['client_id']}&redirect_uri=https%3A%2F%2Fwww.workato.com%2Foauth%2Fcallback"
+ end,
+
+ acquire: lambda do |connection, auth_code, redirect_uri|
+ post('https://auth.tryassemble.com/connect/token').
+ payload(client_id: connection['client_id'],
+ client_secret: connection['client_secret'],
+ code: auth_code,
+ grant_type: 'authorization_code',
+ redirect_uri: redirect_uri).
+ request_format_www_form_urlencoded
+ end,
+
+ refresh_on: [400, 401, 403, 500],
+
+ refresh: lambda do |connection, refresh_token|
+ post('https://auth.tryassemble.com/connect/token').
+ payload(
+ client_id: connection['client_id'],
+ client_secret: connection['client_secret'],
+ grant_type: 'refresh_token',
+ refresh_token: refresh_token
+ ).
+ request_format_www_form_urlencoded
+
+ end,
+
+ apply: lambda do |_connection, access_token|
+ headers('Authorization' => "Bearer #{access_token}", 'Content-Type' => 'application/json')
+ end
+
+ },
+
+ base_uri: lambda do |connection|
+ "https://#{connection['subdomain']}.tryassemble.com"
+ end
+
+ },
+
+ test: lambda do |_connection|
+ get('/api/v1/powerbi/projects')
+ end,
+
+ object_definitions: {
+ project: {
+ fields: lambda do |_connection|
+ [
+ { name: 'id', type: 'number'},
+ { name: 'name' },
+ { name: 'description' },
+ { name: 'jobCode' },
+ { name: 'cardColorCode' },
+ { name: 'isArchived', type: 'boolean' },
+ { name: 'unitType' },
+ { name: 'userPublishAlignment' },
+ { name: 'isValidation', type: 'boolean' },
+ { name: 'isApproved', type: 'boolean' },
+ { name: 'modelCount', type: 'number' },
+ { name: 'viewCount', type: 'number' },
+ { name: 'lastActivityTime', type: 'date_time' },
+ { name: 'imageAttachmentId' },
+ { name: 'imgUrl' },
+ { name: 'organizationId', type: 'number' },
+ { name: 'approvedByName' },
+ { name: 'approvedByDate', type: 'date_time' },
+ { name: 'referencedBIM360ProjectData' }
+ ]
+ end
+ },
+ model: {
+ fields: lambda do |_connection|
+ [
+ { name: 'id', type: 'number'},
+ { name: 'name' },
+ { name: 'createdDate', type: 'date_time' },
+ { name: 'createdBy' },
+ { name: 'description' },
+ { name: 'datasource' },
+ { name: 'datasourceTitle' },
+ { name: 'projectId', type: 'number' },
+ { name: 'projectName' },
+ { name: 'hasVersions', type: 'boolean' },
+ { name: 'activeVersion', type: 'object', properties: [
+ { name: 'id', type: 'number' },
+ { name: 'name' },
+ { name: 'guid' },
+ { name: 'createdDate', type: 'date_time' },
+ { name: 'createdBy' },
+ { name: 'versionNumber', type: 'number' },
+ { name: 'comments' },
+ { name: 'projectId', type: 'number' },
+ { name: 'projectName' },
+ { name: 'modelId', type: 'number' },
+ { name: 'modelName' },
+ { name: 'datasource' },
+ { name: 'source' },
+ { name: 'instanceCount', type: 'number' },
+ { name: 'hasExpandedEdit', type: 'boolean' },
+ { name: 'isMerged', type: 'boolean' },
+ { name: 'geometryResourceId' },
+ { name: 'sheetMappingResourceId' },
+ { name: 'hasGeometry', type: 'boolean' },
+ { name: 'canUseWebWorkers', type: 'boolean' },
+ { name: 'geometryFilteredCategories' },
+ { name: 'estimatedOn' },
+ { name: 'estimatedBy' },
+ { name: 'formattedEstimatedOn' },
+ { name: 'canEstimate' },
+ { name: 'audits' },
+ { name: 'origins', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'rotation', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'userAlignment' },
+ { name: 'data' }
+ ]},
+ { name: 'versions', type: 'array', of: 'object', properties: [
+ { name: 'id', type: 'number' },
+ { name: 'name' },
+ { name: 'guid' },
+ { name: 'createdDate', type: 'date_time' },
+ { name: 'createdBy' },
+ { name: 'versionNumber', type: 'number' },
+ { name: 'comments' },
+ { name: 'projectId', type: 'number' },
+ { name: 'projectName' },
+ { name: 'modelId', type: 'number' },
+ { name: 'modelName' },
+ { name: 'datasource' },
+ { name: 'source' },
+ { name: 'instanceCount', type: 'number' },
+ { name: 'hasExpandedEdit', type: 'boolean' },
+ { name: 'isMerged', type: 'boolean' },
+ { name: 'geometryResourceId' },
+ { name: 'sheetMappingResourceId' },
+ { name: 'hasGeometry', type: 'boolean' },
+ { name: 'canUseWebWorkers', type: 'boolean' },
+ { name: 'geometryFilteredCategories' },
+ { name: 'estimatedOn' },
+ { name: 'estimatedBy' },
+ { name: 'formattedEstimatedOn' },
+ { name: 'canEstimate' },
+ { name: 'audits' },
+ { name: 'origins', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'rotation', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'userAlignment' },
+ { name: 'data' }
+ ]}
+ ]
+ end
+ },
+ model_version: {
+ fields: lambda do |_connection|
+ [
+ { name: 'id', type: 'number' },
+ { name: 'name' },
+ { name: 'guid' },
+ { name: 'createdDate', type: 'date_time' },
+ { name: 'createdBy' },
+ { name: 'versionNumber', type: 'number' },
+ { name: 'comments' },
+ { name: 'projectId', type: 'number' },
+ { name: 'projectName' },
+ { name: 'modelId', type: 'number' },
+ { name: 'modelName' },
+ { name: 'datasource' },
+ { name: 'source' },
+ { name: 'instanceCount', type: 'number' },
+ { name: 'hasExpandedEdit', type: 'boolean' },
+ { name: 'isMerged', type: 'boolean' },
+ { name: 'geometryResourceId' },
+ { name: 'sheetMappingResourceId' },
+ { name: 'hasGeometry', type: 'boolean' },
+ { name: 'canUseWebWorkers', type: 'boolean' },
+ { name: 'geometryFilteredCategories' },
+ { name: 'estimatedOn' },
+ { name: 'estimatedBy' },
+ { name: 'formattedEstimatedOn' },
+ { name: 'canEstimate' },
+ { name: 'audits' },
+ { name: 'origins', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'rotation', type: 'object', properties: [
+ { name: 'x', type: 'number' },
+ { name: 'y', type: 'number' },
+ { name: 'z', type: 'number' }
+ ]},
+ { name: 'userAlignment' },
+ { name: 'data' }
+ ]
+ end
+ },
+ properties: {
+ fields: lambda do |_connection|
+ [
+ { name: 'modelVersionId', type: 'number' },
+ { name: 'properties', type: 'array', of: 'object', properties: [
+ { name: 'id' },
+ { name: 'name' },
+ { name: 'unit' },
+ { name: 'dataType' },
+ { name: 'type' },
+ { name: 'source' }
+ ]}
+ ]
+ end
+ },
+ grid_data: {
+ fields: lambda do |_connection|
+ [
+ { name: 'Id' },
+ { name: 'RowType' },
+ { name: 'AssembleName' },
+ { name: 'AssembleModelName' },
+ { name: 'ModelVersionDataSource' },
+ { name: 'TakeoffUnitAbbreviation' },
+ { name: 'TakeoffQuantity' },
+ { name: 'QuantityProperty' },
+ { name: 'IsActiveTakeoffQuantity' },
+ { name: 'TypeProperties' },
+ { name: 'ChildSourceIds' },
+ { name: 'ModelVersionId' },
+ { name: 'ColorOverrides' },
+ { name: 'InstanceCount' },
+ { name: 'CategoryName:::string', label: 'CategoryName' },
+ { name: 'FamilyName:::string', label: 'FamilyName' },
+ { name: 'TypeName:::string', label: 'TypeName' },
+ { name: 'UnitCost:::numeric', label: 'UnitCost' },
+ { name: 'AssembleTotalCost:::numeric', label: 'AssembleTotalCost' },
+ { name: 'BidPackage_AssembleProperty:::string', label: 'BidPackage_AssembleProperty'},
+ { name: 'AssemblyCode:::string', label: 'AssemblyCode' },
+ { name: 'SourceId:::numeric', label: 'SourceId' }
+ ]
+ 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://demo.tryassemble.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: input_schema.
+ each { |field| field[:sticky] = true }
+ }
+ 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 Assemble",
+ help: {
+ body: 'Build your own Assemble action with an HTTP request'
+ },
+ 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|
+ verb = input['verb']
+ if %w[get post patch delete].exclude?(verb)
+ error("#{verb} not supported")
+ end
+ data = input.dig('input', 'data').presence || {}
+ case verb
+ when 'get'
+ response =
+ get(input['path'], data).
+ headers('Content-Type': 'application/vnd.api+json').
+ 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).
+ headers('Content-Type': 'application/vnd.api+json').
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.compact
+ when 'patch'
+ patch(input['path'], data).
+ headers('Content-Type': 'application/vnd.api+json').
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.compact
+ when 'delete'
+ delete(input['path'], data).
+ headers('Content-Type': 'application/vnd.api+json').
+ 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
+ },
+ get_projects: {
+ title: 'Get projects',
+ description: 'Get projects in Assemble.',
+
+ execute: lambda do |_connection, input|
+ { projects: get('/api/v1/powerbi/projects') }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'projects', label: 'Projects',
+ type: 'array', of: 'object', properties: object_definitions['project'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ { projects: get("/api/v1/powerbi/projects")&.dig(0) || {} }
+ end
+
+ },
+ get_models: {
+ title: 'Get models for a project',
+ description: 'Get models in a project in Assemble.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'projects_list',
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Use project ID',
+ hint: 'Provide a project ID'
+ }
+ }
+ ]
+ end,
+ execute: lambda do |_connection, input|
+ { models: get("/api/v1/powerbi/projects/#{input['project_id']}/models") }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'models', label: 'Models',
+ type: 'array', of: 'object', properties: object_definitions['model'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ project = input['project_id'] || get("/api/v1/powerbi/projects")&.dig(0, 'id') || {}
+ { models: get("/api/v1/powerbi/projects/#{project}/models")&.dig(0) || {} }
+ end
+
+ },
+ get_model_versions: {
+ title: 'Get model versions for a project',
+ description: 'Get model versions in a project in Assemble.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'projects_list',
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Use project ID',
+ hint: 'Provide a project ID'
+ }
+ }
+ ]
+ end,
+ execute: lambda do |_connection, input|
+ { versions: get("/api/v1/powerbi/projects/#{input['project_id']}/versions") }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'versions', label: 'Versions',
+ type: 'array', of: 'object', properties: object_definitions['model_version'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ project = input['project_id'] || get("/api/v1/powerbi/projects")&.dig(0, 'id') || {}
+ { versions: get("/api/v1/powerbi/projects/#{project}/versions")&.dig(0) || {} }
+ end
+
+ },
+ get_properties: {
+ title: 'Get properties for a project',
+ description: 'Get properties in a project in Assemble.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'projects_list',
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Use project ID',
+ hint: 'Provide a project ID'
+ }
+ }
+ ]
+ end,
+ execute: lambda do |_connection, input|
+ { properties: get("/api/v1/powerbi/projects/#{input['project_id']}/properties") }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'properties', label: 'Properties',
+ type: 'array', of: 'object', properties: object_definitions['properties'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ project = input['project_id'] || get("/api/v1/powerbi/projects")&.dig(0, 'id') || {}
+ { properties: get("/api/v1/powerbi/projects/#{project}/properties")&.dig(0) || {} }
+ end
+
+ },
+ get_model_version_data: {
+ title: 'Get model version data for a project',
+ description: 'Get model version data in a project in Assemble.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'projects_list',
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Use project ID',
+ hint: 'Provide a project ID'
+ }
+ },
+ {
+ name: 'model_id',
+ label: 'Model version',
+ control_type: 'select',
+ pick_list: 'model_versions_list',
+ pick_list_params: { project_id: 'project_id' },
+ optional: false,
+ toggle_hint: 'Select model version',
+ toggle_field: {
+ name: 'model_id',
+ label: 'Model ID',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Use model ID',
+ hint: 'Provide a model ID'
+ }
+ },
+ {
+ name: 'properties_select',
+ label: 'Select properties',
+ control_type: 'multiselect',
+ pick_list: 'project_properties_list',
+ pick_list_params: { project_id: 'project_id', model_id: 'model_id' },
+ delimiter: ',',
+ optional: true,
+ sticky: true
+ },
+ {
+ name: 'properties_define',
+ label: 'Define properties',
+ optional: true,
+ sticky: true,
+ type: 'array',
+ of: 'object',
+ properties: [
+ { name: 'id' },
+ { name: 'name' },
+ { name: 'unit' },
+ { name: 'dataType' },
+ { name: 'type' },
+ { name: 'source' }
+ ]
+ }
+ ]
+ end,
+ execute: lambda do |_connection, input|
+ if input['properties_select'].present?
+ properties = input['properties_select'].split(',').map do |property|
+ {
+ 'id': property.split('_')[0],
+ 'name': property.split('_')[1],
+ 'unit': property.split('_')[2],
+ 'dataType': property.split('_')[3],
+ 'type': property.split('_')[4],
+ 'source': property.split('_')[5]
+ }
+ end
+ else
+ properties = input['properties_define']
+ end
+
+ post("/api/v1/powerbi/projects/#{input['project_id']}/versiongriddata/#{input['model_id']}")
+ .request_body(properties.to_json)
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'records' },
+ { name: 'rows', type: 'array', of: 'object',
+ properties: object_definitions['grid_data']
+ }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ project = input['project_id'] || get("/api/v1/powerbi/projects")&.dig(0, 'id') || {}
+ { properties: get("/api/v1/powerbi/projects/#{project}/properties")&.dig(0) || {} }
+ end
+ },
+ },
+
+ pick_lists: {
+ projects_list: lambda do |_connection|
+ get('/api/v1/powerbi/projects')&.map do |project|
+ [ project.dig('name'), project.dig('id') ]
+ end
+ end,
+ model_versions_list: lambda do |_connection, project_id:|
+ if project_id.present? && project_id.slice(0,1) != '#'
+ get("/api/v1/powerbi/projects/#{project_id}/versions")&.map do |version|
+ [version.dig('name'), version['id']]
+ end
+ end
+ end,
+ project_properties_list: lambda do |_connection, project_id:, model_id:|
+ if project_id.present? && project_id.slice(0,1) != '#'
+ if model_id.present? && model_id.slice(0,1) != '#'
+ properties = get("/api/v1/powerbi/projects/#{project_id}/properties")
+ properties.where(['modelVersionId = ?', model_id]).first['properties'].map do |property|
+ [ property.dig('name'),
+ "#{property['id']}_#{property['name']}_#{property['unit']}_#{property['dataType']}_#{property['type']}_#{property['source']}"
+ ]
+ end
+ end
+ end
+ end,
+ }
+}
diff --git a/custom_connectors/oauth2/bim_360.rb b/custom_connectors/oauth2/bim_360.rb
new file mode 100644
index 00000000..1c30be1d
--- /dev/null
+++ b/custom_connectors/oauth2/bim_360.rb
@@ -0,0 +1,6255 @@
+{
+ title: 'BIM 360',
+
+ connection: {
+ authorization: {
+ type: 'oauth2',
+
+ authorization_url: lambda do |connection|
+ 'https://developer.api.autodesk.com/authentication/v1/authorize?response_type=' \
+ "code&scope=user:read account:read data:write data:write data:read data:create account:write"
+ end,
+
+ acquire: lambda do |connection, auth_code, redirect_uri|
+ post('https://developer.api.autodesk.com/authentication/v1/gettoken').
+ payload(client_id: "#{'client_id'}",
+ client_secret: "#{'client_secret'}",
+ grant_type: 'authorization_code',
+ code: auth_code,
+ redirect_uri: redirect_uri).request_format_www_form_urlencoded
+ end,
+
+ refresh_on: [401, 403],
+
+ refresh: lambda do |connection, refresh_token|
+ post('https://developer.api.autodesk.com/authentication/v1/refreshtoken').
+ payload(client_id: "#{'client_id'}",
+ client_secret: "#{'client_secret'}",
+ grant_type: 'refresh_token',
+ refresh_token: refresh_token,
+ scope: 'user:read account:read data:read data:write account:write').request_format_www_form_urlencoded
+ end,
+
+ apply: lambda do |_connection, access_token|
+ if current_url.include?('https://developer.api.autodesk.com/cost/')
+ headers('Authorization': "Bearer #{access_token}", 'Content-Type' => 'application/json')
+ else
+ headers('Authorization': "Bearer #{access_token}", 'Content-Type' => 'application/vnd.api+json')
+ end
+ end
+ },
+
+ base_uri: lambda do |_connection|
+ 'https://developer.api.autodesk.com'
+ end
+ },
+
+ test: lambda do |_connection|
+ get('/userprofile/v1/users/@me')
+ end,
+
+ methods: {
+
+ format_output_response: lambda do |res, keys|
+ keys.each do |key|
+ res[key] = res[key]&.map { |value| { 'value' => value } } if res&.has_key?(key)
+ end
+ end,
+
+ format_search: lambda do |input|
+ if input.is_a?(Hash)
+ input.each_with_object({}) do |(key, value), hash|
+ value = call('format_search', value)
+ if %w[limit offset].include?(key)
+ hash["page[#{key}]"] = value
+ elsif %w[include_voided assigned_to target_urn due_date synced_after
+ created_at created_by search
+ ng_issue_type_id ng_issue_subtype_id status].include?(key)
+ hash["filter[#{key}]"] = value
+ elsif key == "rfis"
+ hash["fields[#{key}]"] = value
+ else
+ hash[key] = value
+ end
+ end
+ else
+ input
+ end
+ end,
+
+ format_cost_search: lambda do |input|
+ if input.is_a?(Hash)
+ input.each_with_object({}) do |(key, value), hash|
+ value = call('format_search', value)
+ if %w[rootId externalSystem externalId code
+ contractId mainContractId budgetStatus costStatus
+ changeOrderId budgetId associationId associationType
+ latest signed lastModifiedSince].include?(key)
+ hash["filter[#{key}]"] = value
+ else
+ hash[key] = value
+ end
+ end
+ else
+ input
+ 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,
+
+ sample_data_export_job: lambda do
+ {
+ 'id': '433d07ec-32a2-44eb-a5eb-b41090bfe932',
+ 'status': 'completed',
+ 'data': {
+ 'versionUrn': 'dXJuOmFkc2sud2lwcWE6ZnMuZmlsZTp2Zi5wZjNmclUwZ1RaYU9vdEtYZzJoMUZ3P3ZlcnNpb249MQ',
+ 'resourceId': "urn:adsk.viewing:fs.file:dXJuOmFkc2sud2lwcWE6ZnMuZmlsZTp2Zi5wZjNmclUwZ1RaYU9vdEtYZzJoMUZ3P3ZlcnNpb249MQ/output' \
+ '/qXP_ZA5_3EqJoq5zqvnLHA/h8YAl4KMcEe9-L5SaEXY6A.pdf",
+ 'link': "https://developer.api.autodesk.com/modelderivative/v2/designdata/dXJuOmFkc2sud2lwcWE6ZnMuZmlsZTp2Zi5wZjNmclUwZ1RaYU9v' \
+ 'dEtYZzJoMUZ3P3ZlcnNpb249MQ/manifest/urn%3Aadsk.viewing%3Afs.file%3AdXJuOmFkc2sud2lwcWE6ZnMuZmlsZTp2Zi5wZjNmclUwZ1RaYU9vdEtYZzJo' \
+ 'MUZ3P3ZlcnNpb249MQ%2Foutput%2FqXP_ZA5_3EqJoq5zqvnLHA%2Fh8YAl4KMcEe9-L5SaEXY6A.pdf"
+ }
+ }
+ end
+ },
+
+ object_definitions: {
+ project: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id', label: 'Project ID' },
+ { name: 'type' },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'name' },
+ { name: 'scopes', type: 'array', of: 'object', properties:[
+ { name: "value" }
+ ] },
+ { name: 'extension', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'version' },
+ { name: 'schema', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties:[
+ { name: 'self', type: 'object', properties:[
+ { name: 'href' }
+ ] }
+ ] },
+ { name: 'relationships', type: 'object', properties: [
+ { name: 'hub', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'id', label: 'Hub ID' }
+ ] },
+ { name: 'links', type: 'object', properties:[
+ { name: 'self', type: 'object', properties:[
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'rootFolder', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'id', label: 'Root folder ID' },
+ { name: 'type' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'topFolders', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'issues', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Issues container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'submittals', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Submittals container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'rfis', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'RFIs container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'markups', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Markups container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'checklists', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Checklists container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'cost', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Cost container ID' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'location', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Locations container ID' }
+ ] }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] }
+ ]
+ end
+ },
+
+ issue: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id', label: 'Issue ID' },
+ { name: 'type' },
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' }
+ ] },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'created_at', type: 'date_time',
+ hint: 'The timestamp of the date and time the issue was created, in the following format: YYYY-MM-DDThh:mm:ss.sz',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'synced_at', type: 'date_time',
+ hint: 'The date and time the issue was synced with BIM 360, in the following format: YYYY-MM-DDThh:mm:ss.sz',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'updated_at', type: 'date_time',
+ hint: 'The last time the issue’s attributes were updated, in the following format: YYYY-MM-DDThh:mm:ss.sz',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'close_version', type: :integer, control_type: :integer,
+ hint: 'The version of the issue when it was closed.' },
+ { name: 'closed_at', type: 'date_time',
+ hint: 'The timestamp of the data and time the issue was closed, in the following format: YYYY-MM-DDThh:mm:ss.sz',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'closed_by',
+ hint: 'The Autodesk ID of the user who closed the issue.' },
+ { name: 'created_by',
+ hint: 'The Autodesk ID of the user who created the issue.' },
+ { name: 'opened_at' },
+ { name: 'opened_by' },
+ { name: 'updated_by' },
+ { name: 'starting_version', type: 'integer',
+ hint: 'The first version of the issue' },
+ { name: 'title', label: 'Issue title' },
+ { name: 'description',
+ hint: 'The description of the purpose of the issue.' },
+ { name: 'location_description',
+ hint: 'The location of the issue.' },
+ { name: 'markup_metadata' },
+ { name: 'tags' },
+ { name: 'resource_urns' },
+ { name: 'target_urn',
+ hint: 'The item ID of the document associated with the pushpin issue.' },
+ { name: 'target_urn_page' },
+ { name: 'collection_urn' },
+ { name: 'snapshot_urn' },
+ { name: 'due_date', type: 'date_time',
+ hint: 'The timestamp of the issue’s specified due date, in the following format: YYYY-MM-DDThh:mm:ss.sz',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'identifier', type: 'integer',
+ hint: 'The identifier of the issue.' },
+ { name: 'status', control_type: 'select',
+ pick_list: 'issue_status_list',
+ toggle_hint: 'Select status',
+ toggle_field: {
+ name: 'status', label: 'Status', type: 'string',
+ control_type: 'text', toggle_hint: 'Enter status value',
+ hint: 'Allowed values are :open, work_complete, ready_to_inspect, not_approved, close in_dispute, void.'
+ } },
+ { name: 'assigned_to', hint: 'The Autodesk ID of the user' },
+ { name: 'assigned_to_type',
+ hint: 'The type of subject this issue is assigned to. Possible values: user, company, role' },
+ { name: 'answer' },
+ { name: 'answered_at', type: 'date_time',
+ hint: 'The date and time the issue was answered, in the following format: YYYY-MM-DDThh:mm:ss.sz.',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'answered_by',
+ hint: 'The user who suggested an answer for the issue.' },
+ { name: 'pushpin_attributes' },
+ { name: 'owner',
+ hint: 'The Autodesk ID of the user who owns this issue.' },
+ { name: 'issue_type_id' },
+ { name: 'issue_type' },
+ { name: 'issue_sub_type' },
+ { name: 'root_cause_id' },
+ { name: 'root_cause' },
+ { name: 'quality_urns' },
+ { name: 'permitted_statuses', type: 'array', of: 'object', properties: [
+ { name: "value" }],
+ hint: 'A list of statuses accessible to the current user.' },
+ { name: 'permitted_attributes', type: 'array', of: 'object', properties: [
+ { name: "value" }],
+ hint: 'A list of attributes accessible to the current user.' },
+ { name: 'comment_count', type: 'integer',
+ hint: 'The number of comments added to this issue.' },
+ { name: 'attachment_count', type: 'integer', control_type: 'integer',
+ hint: 'The number of attachments added to this issue.' },
+ { name: 'permitted_actions', type: 'array', of: 'object', properties: [
+ { name: "value" }],
+ hint: 'The actions that are permitted for the issue in this state.' },
+ { name: 'lbs_location',
+ hint: 'The ID of the location that relates to the issue.' },
+ { name: 'sheet_metadata' },
+ { name: 'ng_issue_type_id', label: 'Issue type ID',
+ hint: 'The ID of the issue type.' },
+ { name: 'ng_issue_subtype_id', label: 'Issue subtype ID',
+ hint: 'The ID of the issue subtype' },
+ { name: 'issue_template_id' },
+ { name: 'trades' },
+ { name: 'comments_attributes' },
+ { name: 'attachments_attributes' }
+ ] },
+ { name: 'relationships', type: 'object', properties:[
+ { name: 'container', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'attachments', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'activity_batches', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'comments', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'root_cause_obj', label: "Root cause object", type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'changesets', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] },
+ { name: 'issue_type_obj', label: "Issue type object", type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'self' },
+ { name: 'related' }
+ ] }
+ ] }
+ ] },
+ # To Do
+ { name: 'custom_attributes', type: 'array', of: 'object', properties: [
+ { name: 'value' }
+ ] },
+ { name: 'trades', type: 'array', of: 'object', properties: [
+ { name: 'value' }
+ ] },
+ { name: 'comments_attributes' },
+ { name: 'attachments_attributes' }
+ ]
+ end
+ },
+
+ create_issue: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'title', label: 'Issue title' },
+ { name: 'description',
+ hint: 'The description of the purpose of the issue.' },
+ { name: 'status', control_type: 'select',
+ pick_list: %w[draft open].map { |option| [option.labelize, option] },
+ toggle_hint: 'Select status',
+ toggle_field: {
+ name: 'status', label: 'Status', type: 'string',
+ control_type: 'text', toggle_hint: 'Enter status value',
+ hint: 'The status of the issue. Possible values:draft, open. The default is draft'
+ } },
+ { name: 'starting_version', type: 'integer',
+ hint: 'The first version of the issue' },
+ { name: 'due_date', type: 'date_time',
+ hint: 'The timestamp of the date and time the issue created.',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'location_description' },
+ { name: 'created_at', type: 'date_time',
+ hint: 'The timestamp of the date and time the issue created.',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'assigned_to',
+ hint: 'The Autodesk ID (uid) of the user you want to assign to this issue. If you specify this attribute you need to also specify assigned_to_type.' },
+ { name: 'assigned_to_type',
+ hint: 'The type of subject this issue is assigned to. Possible values: user. If you specify this attribute you need to also specify assigned_to.' },
+ { name: 'owner',
+ hint: 'The BIM 360 ID of the user who owns this issue.' },
+ { name: 'root_cause_id',
+ hint: 'The ID of the type of root cause for this issue.' }
+ ]
+ end
+ },
+
+ update_issue: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id', label: 'Issue ID',
+ optional: 'false' },
+ { name: 'title', label: 'Issue title',
+ optional: 'false' },
+ { name: 'description',
+ hint: 'The description of the purpose of the issue.' },
+ { name: 'status', control_type: 'select',
+ pick_list: 'issue_status_list',
+ toggle_hint: 'Select status',
+ toggle_field: {
+ name: 'status', label: 'Status', type: 'string',
+ control_type: 'text', toggle_hint: 'Enter status value',
+ hint: 'The status of the issue. Possible values:draft, open. The default is draft'
+ } },
+ { name: 'due_date', type: 'date_time',
+ hint: 'The timestamp of the date and time the issue updated.',
+ render_input: 'render_iso8601_timestamp',
+ parse_output: 'parse_iso8601_timestamp' },
+ { name: 'location_description' },
+ { name: 'assigned_to',
+ hint: 'The Autodesk ID (uid) of the user you want to assign to this issue. If you specify this attribute you need to also specify assigned_to_type.' },
+ { name: 'assigned_to_type',
+ hint: 'The type of subject this issue is assigned to. Possible values: user. If you specify this attribute you need to also specify assigned_to.' },
+ { name: 'owner',
+ hint: 'The BIM 360 ID of the user who owns this issue.' },
+ { name: 'ng_issue_type_id', label: 'Issue type ID',
+ hint: 'The ID of the issue type. You can only configure this attribute when the issue is in draft state' },
+ { name: 'ng_issue_subtype_id', label: 'Issue subtype ID',
+ hint: 'The ID of the issue subtype. You can configure this attribute when the issue is in draft or open state' },
+ { name: 'root_cause_id',
+ hint: 'The ID of the type of root cause for this issue.' },
+ { name: 'close_version' }
+ ]
+ end
+ },
+
+ search_criteria: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of c8b0c73d-3ae9 translates '\
+ 'to a hub ID of b.c8b0c73d-3ae9. Get account ID from admin page.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'target_urn', sticky: true },
+ { name: 'due_date', type: 'date_time',
+ sticky: true,
+ hint: 'Retrieves issues due by the specified due date.' },
+ { name: 'synced_after', type: 'date_time',
+ sticky: true,
+ hint: 'Retrieves issues updated after the specified date' },
+ { name: 'created_at', type: 'date_time',
+ sticky: true,
+ hint: 'Retrieves issues created after the specified date.' },
+ { name: 'created_by',
+ sticky: true,
+ hint: 'Retrieves issues created by the user. matchValue is the unique identifier of the user who created the issue.' },
+ { name: 'ng_issue_type_id', label: 'Issue type ID',
+ sticky: true,
+ hint: 'Retrieves issues associated with the specified issue type' },
+ { name: 'ng_issue_subtype_id', label: 'Issue subtype ID',
+ sticky: true,
+ hint: 'Retrieves issues associated with the specified issue subtype.' },
+ { name: 'limit', type: 'integer',
+ sticky: true,
+ hint: 'Number of issues to return in the response. Acceptable values: 1-100. Default value: 10' },
+ { name: 'offset',
+ sticky: true,
+ hint: 'The page number that you want to begin issue results from.' },
+ { name: 'sort',
+ sticky: true,
+ hint: 'Sort the issues by status, created_at, and updated_a. To sort in descending order add a - before the sort criteria' },
+ {
+ name: 'include',
+ sticky: true,
+ label: 'Include additional data',
+ control_type: 'multiselect',
+ pick_list: 'issue_child_objects',
+ pick_list_params: {},
+ delimiter: ',',
+ toggle_hint: 'Select from list',
+ toggle_field: {
+ name: 'include',
+ label: 'Include additinal data',
+ type: :string,
+ change_on_blur: true,
+ control_type: 'text',
+ optional: true,
+ hint: 'Multiple values separated by comma.',
+ toggle_hint: 'Comma separated list of values. Allowed values are: attachments, comments, container.'
+ }
+ }
+ ]
+ end
+ },
+
+ hub_container_ids: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter hub ID',
+ hint: 'Provide hub id for example, b.baf-0871-4aca-82e8-3dd6db00.'
+ }
+ },
+ {
+ name: 'container_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'issue_container_lists',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select container',
+ toggle_field: {
+ name: 'container_id',
+ label: 'Container ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter container ID',
+ hint: 'Provide container id e.g. b.baf-0871-4aca-82e8-3dd6db00.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ tree_options: { selectable_folder: true },
+ pick_list: :folders,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'Folder ID',
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from folder page.'
+ } }
+ ]
+ end
+ },
+
+ folder_file: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id', label: 'Item ID' },
+ { name: 'type', label: 'Item type' },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'displayName', label: 'Name' },
+ { name: 'createTime', label: 'Created at', type: 'date_time' },
+ { name: 'createUserId', label: 'Created by (User ID)' },
+ { name: 'createUserName', label: 'Created by (User name)' },
+ { name: 'lastModifiedTime',
+ label: 'Last modified at', type: 'date_time' },
+ { name: 'lastModifiedUserId', label: 'Last modified by (User ID)' },
+ { name: 'lastModifiedUserName',
+ label: 'Last modified by (User Name)' },
+ { name: 'lastModifiedTimeRollup', type: 'date_time' },
+ { name: 'objectCount', type: 'integer' },
+ { name: 'hidden', type: 'boolean', control_type: 'checkbox' },
+ { name: 'reserved', type: 'boolean', control_type: 'checkbox' },
+ { name: 'extension', type: 'object', properties: [
+ { name: 'version' },
+ { name: 'type' },
+ { name: 'schema', type: 'object', properties:[
+ { name: 'href' }
+ ] },
+ { name: 'data', type: 'object', properties: [
+ { name: 'sourceFileName' },
+ { name: 'visibleTypes', type: 'array', of: 'object', properties: [{ name: "value" }] },
+ { name: 'actions', type: 'array', of: 'object', properties: [{ name: "value" }] },
+ { name: 'allowedTypes', type: 'array', of: 'object', properties: [{ name: "value" }] }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties:[
+ { name: 'self', type: 'object', properties:[
+ { name: 'href' }
+ ] }
+ ] },
+ { name: 'relationships', type: 'object', properties:[
+ { name: 'tip', type: 'object', properties: [
+ { name: 'data', type: 'object', properties:[
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'links', type: 'object', properties:[
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'versions', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'parent', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'refs', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] },
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'contents', type: 'object', properties:[
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ]}
+ ] }
+ ] }
+ ] }
+ ]
+ end
+ },
+
+ item: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'jsonapi', type: 'object', properties: [
+ { name: 'version' }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] },
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id', label: 'Item ID' },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'displayName', label: 'Name' },
+ { name: 'createTime', label: 'Created at', type: 'date_time' },
+ { name: 'createUserId', label: 'Created by (User ID)' },
+ { name: 'createUserName', label: 'Created by (User name)' },
+ { name: 'lastModifiedTime',
+ label: 'Last modified at', type: 'date_time' },
+ { name: 'lastModifiedUserId',
+ label: 'Last modified by (User ID)' },
+ { name: 'lastModifiedUserName',
+ label: 'Last modified by (User Name)' },
+ { name: 'hidden', type: 'boolean', control_type: 'checkbox' },
+ { name: 'reserved', type: 'boolean', control_type: 'checkbox' },
+ { name: 'extension', type: 'object', properties: [
+ { name: 'version' },
+ { name: 'type' },
+ { name: 'schema', type: 'object', properties: [
+ { name: 'href' }
+ ] },
+ { name: 'data', type: 'object', properties: [
+ { name: 'sourceFileName' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] },
+ { name: 'relationships', type: 'object', properties: [
+ { name: 'tip', type: 'object', properties: [
+ { name: 'data', type: 'object', properties:[
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'links', type: 'object', properties:[
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'versions', type: 'object', properties:[
+ { name: 'links', type: 'object', properties:[
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'parent', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'refs', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] },
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] }
+ ] }
+ ] },
+ { name: 'included', type: 'array', of: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'name' },
+ { name: 'displayName' },
+ { name: 'createTime', type: 'date_time' },
+ { name: 'createUserId' },
+ { name: 'createUserName' },
+ { name: 'lastModifiedTime', type: 'date_time' },
+ { name: 'lastModifiedUserId' },
+ { name: 'lastModifiedUserName' },
+ { name: 'versionNumber', type: 'integer' },
+ { name: 'storageSize', type: 'integer' },
+ { name: 'fileType' },
+ { name: 'extension', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'version' },
+ { name: 'schema', type: 'object', properties: [
+ { name: 'href' }
+ ] },
+ { name: 'data', type: 'object', properties: [
+ { name: 'processState' },
+ { name: 'extractionState' },
+ { name: 'splittingState' },
+ { name: 'reviewState' },
+ { name: 'revisionDisplayLabel' },
+ { name: 'sourceFileName' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] },
+ { name: 'relationships', type: 'object', properties: [
+ { name: 'item', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'links', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'refs', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'self', type: 'object', properties: [
+ { name: 'href' }
+ ] },
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'downloadFormats', type: 'object', properties: [
+ { name: 'links', type: 'object', properties: [
+ { name: 'related', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'derivatives', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'thumbnails', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] },
+ { name: 'storage', type: 'object', properties: [
+ { name: 'data', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'id' }
+ ] },
+ { name: 'meta', type: 'object', properties: [
+ { name: 'link', type: 'object', properties: [
+ { name: 'href' }
+ ] }
+ ] }
+ ] }
+ ] }
+ ] }
+ ]
+ end
+ },
+
+ version: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'type' },
+ { name: 'id' },
+ { name: 'attributes', type: 'object', properties: [
+ { name: 'name' },
+ { name: 'displayName' },
+ { name: 'createTime', type: 'date_time' },
+ { name: 'createUserId' },
+ { name: 'createUserName' },
+ { name: 'lastModifiedTime' },
+ { name: 'lastModifiedUserId' },
+ { name: 'lastModifiedUserName' },
+ { name: 'versionNumber', type: 'integer' },
+ { name: 'storageSize' },
+ { name: 'fileType' },
+ { name: 'extension', type: 'object', properties: [
+ { name: 'type' },
+ { name: 'version' },
+ { name: 'data', type: 'object', properties: [
+ { name: 'processState' },
+ { name: 'extractionState' },
+ { name: 'splittingState' },
+ { name: 'reviewState' },
+ { name: 'revisionDisplayLabel' },
+ { name: 'sourceFileName' }
+ ] }
+ ] }
+ ] }
+ ]
+ end
+ },
+
+ export_status: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id', label: 'Export job ID' },
+ { name: 'status' },
+ { name: 'data', type: 'object', properties: [
+ { name: 'versionUrn', label: 'Version URN' },
+ { name: 'resourceId' },
+ { name: 'link' }
+ ] }
+ ]
+ end
+ },
+
+ budget: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'parentId' },
+ { name: 'code' },
+ { name: 'name' },
+ { name: 'description' },
+ { name: 'quantity', type: 'number' },
+ { name: 'unitPrice' },
+ { name: 'unit' },
+ { name: 'originalAmount', type: 'number' },
+ { name: 'internalAdjustment', type: 'number' },
+ { name: 'approvedOwnerChanges', type: 'number' },
+ { name: 'pendingOwnerChanges' , type: 'number'},
+ { name: 'originalCommitment', type: 'number' },
+ { name: 'approvedChangeOrders', type: 'number' },
+ { name: 'approvedInScopeChangeOrders', type: 'number' },
+ { name: 'pendingChangeOrders', type: 'number' },
+ { name: 'reserves', type: 'number' },
+ { name: 'actualCost', type: 'number' },
+ { name: 'mainContractId' },
+ { name: 'adjustments', type: 'object', properties: [
+ { name: 'total', type: 'number'},
+ { name: 'details', type: 'array', of: 'objects', properties: [
+ { name: 'quantity', type: 'number' },
+ { name: 'unitPrice', type: 'number' },
+ { name: 'unit' }
+ ]},
+ { name: 'updatedAt', type: 'date_time'}
+ ]},
+ { name: 'uncommited', type: 'number' },
+ { name: 'revised', type: 'number' },
+ { name: 'projectedCost', type: 'number' },
+ { name: 'projectedBudget', type: 'number' },
+ { name: 'forecastFinalCost', type: 'number' },
+ { name: 'forecastVariance', type: 'number' },
+ { name: 'forecastCostComplete', type: 'number' },
+ { name: 'varianceTotal', type: 'number' },
+ { name: 'externalId' },
+ { name: 'externalSystem' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ end
+ },
+
+ contract: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'code' },
+ { name: 'name' },
+ { name: 'description' },
+ { name: 'companyId' },
+ { name: 'type' },
+ { name: 'contactId' },
+ { name: 'signedBy' },
+ { name: 'ownerId' },
+ { name: 'statusId' },
+ { name: 'status' },
+ { name: 'changedBy' },
+ { name: 'creatorId' },
+ { name: 'awarded', type: 'number' },
+ { name: 'changes', type: 'number' },
+ { name: 'total', type: 'number' },
+ { name: 'originalBudget', type: 'number' },
+ { name: 'internalAdjustment', type: 'number' },
+ { name: 'approvedOwnerChanges', type: 'number' },
+ { name: 'pendingOwnerChanges', type: 'number' },
+ { name: 'approvedChangeOrders', type: 'number' },
+ { name: 'approvedInScopeChangeOrders', type: 'number' },
+ { name: 'pendingChangeOrders', type: 'number' },
+ { name: 'reserves', type: 'number' },
+ { name: 'actualCost', type: 'number' },
+ { name: 'uncommitted', type: 'number' },
+ { name: 'revised', type: 'number' },
+ { name: 'projectedCost', type: 'number' },
+ { name: 'projectedBudget', type: 'number' },
+ { name: 'forecastFinalCost', type: 'number' },
+ { name: 'forecastVariance', type: 'number' },
+ { name: 'forecastCostComplete', type: 'number' },
+ { name: 'varianceTotal', type: 'number' },
+ { name: 'awardedAt', type: 'date_time' },
+ { name: 'statusChangedAt', type: 'date_time' },
+ { name: 'documentGeneratedAt', type: 'date_time' },
+ { name: 'sentAt', type: 'date_time' },
+ { name: 'respondedAt', type: 'date_time' },
+ { name: 'returnedAt', type: 'date_time' },
+ { name: 'onsiteAt', type: 'date_time' },
+ { name: 'offsiteAt', type: 'date_time' },
+ { name: 'procuredAt', type: 'date_time' },
+ { name: 'approvedAt', type: 'date_time' },
+ { name: 'scopeOfWork' },
+ { name: 'note' },
+ { name: 'budgets', type: 'array', of: 'object', properties: [
+ { name: 'id' },
+ { name: 'mainContractId' }
+ ]},
+ { name: 'adjustments', type: 'array', of: 'object', properties: [
+ { name: 'total', type: 'number' },
+ { name: 'details', type: 'array', of: 'object', properties: [
+ { name: 'quantity', type: 'number' },
+ { name: 'unitPrice', type: 'number' },
+ { name: 'unit' }
+ ]},
+ { name: 'updatedAt', type: 'date_time' }
+ ]},
+ { name: 'externalId' },
+ { name: 'externalSystem' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ end
+ },
+
+ change_order: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'number' },
+ { name: 'name' },
+ { name: 'description' },
+ { name: 'scope' },
+ { name: 'creatorId' },
+ { name: 'ownerId' },
+ { name: 'changedBy' },
+ { name: 'budgetStatus' },
+ { name: 'costStatus' },
+ { name: 'estimated', type: 'number' },
+ { name: 'proposed', type: 'number' },
+ { name: 'submitted', type: 'number' },
+ { name: 'approved', type: 'number' },
+ { name: 'committed', type: 'number' },
+ { name: 'scopeOfWork' },
+ { name: 'note' },
+ { name: 'externalId' },
+ { name: 'externalSystem' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' },
+ { name: 'properties', type: 'array', of: 'object', properties: [
+ { name: 'name' },
+ { name: 'builtIn', type: 'boolean' },
+ { name: 'position', type: 'number' },
+ { name: 'propertyDefinitionId'},
+ { name: 'type'},
+ { name: 'value'}
+ ]},
+ { name: 'costItems', type: 'array', of: 'object', properties: [
+ { name: 'id' }
+ ]}
+ ]
+ end
+ },
+
+ cost_item: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'number' },
+ { name: 'name' },
+ { name: 'description' },
+ { name: 'budgetStatus' },
+ { name: 'costStatus' },
+ { name: 'scope' },
+ { name: 'type' },
+ { name: 'isMarkup', type: 'boolean' },
+ { name: 'estimated', type: 'number' },
+ { name: 'proposed', type: 'number' },
+ { name: 'submitted', type: 'number' },
+ { name: 'approved', type: 'number' },
+ { name: 'committed', type: 'number' },
+ { name: 'scopeOfWork' },
+ { name: 'note' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ end
+ },
+
+ cost_attachment: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'folderId' },
+ { name: 'urn' },
+ { name: 'type' },
+ { name: 'name' },
+ { name: 'associationId', label: 'Object ID' },
+ { name: 'associationType', label: 'Object Type' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ end
+ },
+
+ cost_document: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'templateId' },
+ { name: 'recipientId' },
+ { name: 'signedBy' },
+ { name: 'urn' },
+ { name: 'signedUrn' },
+ { name: 'status' },
+ { name: 'jobId' },
+ { name: 'errorInfo', type: 'object', properties: [
+ { name: 'code' },
+ { name: 'message' },
+ { name: 'detail' }
+ ]},
+ { name: 'associationId', label: 'Object ID' },
+ { name: 'associationType', label: 'Object Type' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ end
+ },
+
+ cost_file_packages: {
+ fields: lambda do |_connection, _config_fields|
+ [
+ { name: 'id' },
+ { name: 'recipient' },
+ { name: 'urn' },
+ { name: 'errorInfo', type: 'object', properties: [
+ { name: 'code' },
+ { name: 'message' },
+ { name: 'detail' }
+ ]},
+ { name: 'items', type: 'array', of: 'object', properties: [
+ { name: 'id' },
+ { name: 'urn' },
+ { name: 'name' },
+ { name: 'type' },
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]},
+ { name: 'createdAt', type: 'date_time' },
+ { name: 'updatedAt', type: 'date_time' }
+ ]
+ 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://developer.api.autodesk.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: input_schema.
+ each { |field| field[:sticky] = true }
+ }
+ 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 BIM 360",
+
+ help: {
+ body: 'Build your own BIM 360 action with an HTTP request',
+ learn_more_url: 'https://forge.autodesk.com/en/docs/bim360/v1/reference/http/',
+ learn_more_text: 'BIM 360 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|
+ if %w[get post patch delete].exclude?(input['verb'])
+ error("#{input['verb']} not supported")
+ end
+ 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
+ },
+
+ search_issues_in_project: {
+ title: 'Search issues in a project',
+
+ description: 'Search issues in a project in BIM 360',
+
+ help: {
+ body: 'Retrieves information about all the BIM 360 issues in a project, including details about their associated comments and attachments.'
+ },
+
+ input_fields: lambda do |object_definitions|
+ object_definitions['search_criteria']
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_search', input.except('hub_id', 'project_id'))
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id') || {}
+ issues = if container_id.present?
+ get("/issues/v1/containers/#{container_id}/quality-issues", filter_criteria)['data']&.each do |issue|
+ call(:format_output_response, issue['attributes'], %w(permitted_statuses permitted_actions permitted_attributes custom_attributes trades))
+ issue
+ end
+ end
+ { issues: issues }&.merge({ hub_id: input['hub_id'], container_id: container_id, project_id: input['project_id'] })
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'container_id' },
+ { name: 'issues', type: 'array', of: 'object',
+ properties: object_definitions['issue'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id') || {}
+ { issues: get("/issues/v1/containers/#{container_id}/quality-issues?page[limit]=1")&.dig('data', 0) || {} }
+ end
+
+ },
+
+ create_issue_in_project: {
+ title: 'Create issue in a project',
+
+ description: 'Create issue in a project in BIM 360',
+
+ help: {
+ body: 'Adds a BIM 360 issue to a project. You can create both document-related (pushpin) issues, and project-related issues.'
+ },
+
+ input_fields: lambda do |object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'ng_issue_type_id',
+ label: 'Issue type',
+ control_type: 'select',
+ pick_list: 'issue_type',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ optional: false,
+ toggle_hint: 'Select issue type',
+ toggle_field: {
+ name: 'ng_issue_type_id',
+ label: 'Issue type ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter issue type ID',
+ hint: 'The ID of the issue type for example, 2e310a74-90e1-484a-aa87-9e9205ec2372.'
+ }
+ },
+ { name: 'ng_issue_subtype_id',
+ label: 'Issue subtype',
+ control_type: 'select',
+ pick_list: 'issue_sub_type',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id', ng_issue_type_id: 'ng_issue_type_id' },
+ optional: false,
+ toggle_hint: 'Select issue subtype',
+ toggle_field: {
+ name: 'ng_issue_subtype_id',
+ label: 'Issue subtype ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter issue subtype ID',
+ hint: 'The ID of the issue subtype for example, ac0c58ec-cac0-4fea-a555-ecc97fa1bc1a.'
+ }
+ }
+ ].concat(object_definitions['create_issue'].required('title'))
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id')
+ response = if container_id.present?
+ post("/issues/v1/containers/#{container_id}/quality-issues").
+ payload(data: { type: 'quality_issues', attributes: input.except('hub_id', 'project_id') }).
+ headers('Content-Type': 'application/vnd.api+json').after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end['data']&.merge({ container_id: container_id, hub_id: input['hub_id'] })
+ end
+ call("format_output_response", response&.[]('attributes'), %w(permitted_statuses permitted_actions permitted_attributes custom_attributes trades))
+ response
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ].concat(object_definitions['issue'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects")&.dig('data', 0, 'relationships', 'issues', 'data', 'id') || {}
+ get("/issues/v1/containers/#{container_id}/quality-issues?page[limit]=1")&.dig('data', 0) || {}
+ end
+ },
+
+ update_issue_in_project: {
+ title: 'Update issue in a project',
+
+ description: 'Update issue in a project in BIM 360',
+
+ help: {
+ body: 'BIM 360 issues are managed either in the BIM 360 Document Management module or the BIM 360 Field Management module.The following users can update issues:' \
+ 'Project members who are assigned either create, view and create, or full control Field Management permissions.'
+ },
+
+ input_fields: lambda do |object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'id', label: 'Issue ID',
+ optional: false
+ },
+ { name: 'ng_issue_type_id',
+ label: 'Issue type',
+ control_type: 'select',
+ pick_list: 'issue_type',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ optional: true,
+ hint: "Within BIM 360, once an Issue has been created, its Issue type cannot be updated." \
+ " Only issues in draft mode can have its type updated.",
+ toggle_hint: 'Select issue type',
+ toggle_field: {
+ name: 'ng_issue_type_id',
+ label: 'Issue type ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter issue type ID',
+ hint: "The ID of the issue type for example, 2e310a74-90e1-484a-aa87-9e9205ec2372. Within BIM 360, " \
+ "once an Issue has been created, its Issue type cannot be updated." \
+ " Only issues in draft mode can have its type updated."
+ }
+ },
+ { name: 'ng_issue_subtype_id',
+ label: 'Issue subtype',
+ control_type: 'select',
+ pick_list: 'issue_sub_type',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id', ng_issue_type_id: 'ng_issue_type_id' },
+ optional: false,
+ toggle_hint: 'Select issue subtype',
+ toggle_field: {
+ name: 'ng_issue_subtype_id',
+ label: 'Issue subtype ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter issue subtype ID',
+ hint: 'The ID of the issue subtype for example, ac0c58ec-cac0-4fea-a555-ecc97fa1bc1a.'
+ }
+ }
+ ].concat(object_definitions['update_issue'].ignored('id', 'ng_issue_type_id', 'ng_issue_subtype_id'))
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id')
+ response = if container_id.present?
+ patch("/issues/v1/containers/#{container_id}/quality-issues/#{input['id']}").
+ payload(data: { id: input['id'], type: 'quality_issues', attributes: input.except('hub_id', 'project_id', 'id') }).
+ headers('Content-Type': 'application/vnd.api+json').after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end['data']&.merge({ hub_id: input['hub_id'], container_id: container_id })
+ end
+ call("format_output_response", response&.[]('attributes'), %w(permitted_statuses permitted_actions permitted_attributes custom_attributes trades))
+ response
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ].concat(object_definitions['issue'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects")&.dig('data', 0, 'relationships', 'issues', 'data', 'id') || {}
+ get("/issues/v1/containers/#{container_id}/quality-issues?page[limit]=1")&.dig('data', 0) || {}
+ end
+ },
+
+ get_issue_in_project: {
+ title: 'Get issue in a project',
+
+ description: 'Get issue in a project in BIM 360',
+
+ help: 'Retrieves detailed information about a single BIM 360 issue.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'issue_id',
+ optional: false,
+ hint: 'Get ID from url of the issue page.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id')
+ response = if container_id.present?
+ get("/issues/v1/containers/#{container_id}/quality-issues/#{input['issue_id']}").after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end['data'].merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ call("format_output_response", response&.[]('attributes'), %w(permitted_statuses permitted_actions permitted_attributes custom_attributes trades))
+ response
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ].concat(object_definitions['issue'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects")&.dig('data', 0, 'relationships', 'issues', 'data', 'id') || {}
+ get("/issues/v1/containers/#{container_id}/quality-issues?page[limit]=1")&.dig('data', 0) || {}
+ end
+ },
+
+ get_project_details: {
+ title: 'Get project details',
+
+ description: 'Get project details in BIM 360',
+ help: 'Retrieves detailed information about a project.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ response = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}").after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end['data']
+ call("format_output_response", response['attributes'], %w(scopes))
+ response
+ end,
+
+ output_fields: lambda do |object_definitions|
+ object_definitions['project']
+ end,
+
+ sample_output: lambda do |_connection, _input|
+ id = get('/project/v1/hubs')&.dig('data', 0, 'id')
+ id.present? ? get("/project/v1/hubs/#{id}/projects")&.dig('data', 0) : {}
+ end
+ },
+
+ download_drawing_export: {
+ title: 'Download drawing export in a project',
+
+ description: 'Download drawing export in a project in BIM 360',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ { name: 'export_link', label: 'Export link', optional: false }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ file_content = get(input['export_link']).headers('Accept-Encoding': 'Accept-Encoding:gzip').response_format_raw.
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ { content: file_content }
+ end,
+
+ output_fields: lambda do |_object_definitions|
+ [{ name: 'content' }]
+ end,
+
+ sample_output: lambda do |_connection, _input|
+ {
+ "content": ""
+ }
+ end
+ },
+
+ export_project_plan: {
+ title: 'Export drawing in a project',
+
+ description: 'Export drawing in a project in BIM 360',
+
+ help: {
+ body: 'Note that you can only export a page from a PDF file that was uploaded to the Plans folder or to a folder nested under the ' \
+ 'Plans folder. BIM 360 Document Management splits these files into separate pages (sheets) when they are uploaded, and assigns a separate ID to each page.'
+ },
+
+ config_fields:
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder name',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'Folder ID',
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from url of the folder page.'
+ } },
+ {
+ name: 'item_id',
+ label: 'File name',
+ control_type: 'select',
+ pick_list: 'folder_items',
+ pick_list_params: { project_id: 'project_id', folder_id: 'folder_id' },
+ optional: false,
+ toggle_hint: 'Select file',
+ toggle_field: {
+ name: 'item_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'File ID',
+ toggle_hint: 'Enter file ID',
+ hint: 'Use file/item ID.'
+ }
+ },
+ {
+ name: 'version_number',
+ control_type: 'select',
+ pick_list: 'item_versions',
+ sticky: true,
+ pick_list_params: { project_id: 'project_id', item_id: 'item_id' },
+ hint: "Latest version will be used if no value is selected.",
+ optional: true,
+ toggle_hint: 'Select version',
+ toggle_field: {
+ name: 'version_number',
+ label: 'Version number',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ optional: true,
+ toggle_hint: 'Enter version number',
+ hint: 'Latest version will be used if no value is specified.'
+ }
+ },
+ {
+ name: 'includeMarkups', control_type: 'checkbox',
+ type: 'boolean', label: 'Include markups',
+ sticky: true,
+ hint: 'Include markups in the export',
+ toggle_hint: 'Select from options list',
+ toggle_field: {
+ name: 'includeMarkups',
+ label: 'Include markups',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter value to include markups',
+ hint: 'Allowed values are true, false.'
+ }
+ },
+ {
+ name: 'includeHyperlinks', control_type: 'checkbox',
+ type: 'boolean', label: 'Include hyperlinks',
+ sticky: true,
+ hint: 'Include hyperlinks in the export',
+ toggle_hint: 'Select from options list',
+ toggle_field: {
+ name: 'includeHyperlinks',
+ label: 'Include hyperlinks',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter value to include hyperlinks',
+ hint: 'Allowed values are true, false.'
+ }
+ }
+ ],
+
+ execute: lambda do |_connection, input|
+ # Step 1 find the version id of the file to export
+ version_number = input['version_number'] || get("/data/v1/projects/#{input['project_id']}/items/#{input['item_id']}/versions")&.dig('data', 0, 'id')
+ version_url = version_number.encode_url.gsub("+", "%20")
+ # Step 2 upload the file
+ # create payload with `true`/`false` booleansversion_number
+ input_payload = { 'version_number' => false }
+ input.except('hub_id', 'folder_id', 'project_id', 'item_id', 'version_number')&.map do |key, value|
+ input_payload[key] = value.is_true?
+ end
+ post("/bim360/docs/v1/projects/#{input['project_id'].gsub('b.', '')}/versions/#{version_url}/exports").payload(input_payload).
+ headers('content-type': 'application/json').after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end&.merge({ hub_id: input['hub_id'], project_id: input['project_id'], item_id: input['item_id'] })
+ end,
+
+ output_fields: lambda do |_object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'item_id' },
+ { name: 'id', label: 'Export ID' },
+ { name: 'status' }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, _input|
+ {
+ "id": '345eb2fb-d5b0-44c9-a50a-2c792d833f3f',
+ "status": 'committed'
+ }
+ end
+ },
+
+ get_folder_details: {
+ title: 'Get folder info in a project',
+
+ description: 'Get folder info in a project in BIM 360',
+
+ help: {
+ body: 'Returns the folder by ID for any folder within a given project. All folders or sub-folders within a project are associated' \
+ ' with their own unique ID, including the root folder.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ control_type: 'text',
+ label: 'Folder ID',
+ change_on_blur: true,
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from url of the folder page.'
+ } }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ get("/data/v1/projects/#{input['project_id']}/folders/#{input['folder_id']}")['data']&.
+ merge({ hub_id: input['hub_id'], project_id: input['project_id'], folder_id: input['folder_id'] })
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'folder_id' }
+ ].concat(object_definitions['folder_file'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ get("project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}/topFolders?filter[type]=folders")&.dig('data', 0) || {}
+ end
+ },
+
+ get_folder_contents: {
+ description: 'Get folder contents in BIM 360',
+
+ help: {
+ body: 'Returns a collection of items and folders within a folder. Items represent word documents, fusion design files, drawings, spreadsheets, etc.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select Folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'Folder ID',
+ toggle_hint: 'Enter Folder ID',
+ hint: 'Get ID from url of the folder page.'
+ } },
+ { name: 'filters',
+ label: 'Filters',
+ control_type: 'text',
+ sticky: true,
+ hint: 'Enter filters for the search. A list of filters can be' \
+ ' found here.' }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ get("/data/v1/projects/#{input['project_id']}/folders/#{input['folder_id']}/contents?#{input['filters']}")&.
+ merge({ hub_id: input['hub_id'], project_id: input['project_id'], folder_id: input['folder_id'] })
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'folder_id' },
+ { name: 'data', type: 'array', of: 'object',
+ properties: object_definitions['folder_file'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ folder_id = get("project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}/topFolders?filter[type]=folders")&.dig('data', 0, 'id') || {}
+ folder_id.present? ? get("/data/v1/projects/#{input['project_id']}/folders/#{folder_id}/contents") : {}
+ end
+ },
+
+ get_document_in_project: {
+ title: 'Get document in a project',
+
+ description: 'Get document in a project in BIM 360',
+
+ help: {
+ body: 'Retrieves metadata for a specified item. Items represent word documents, fusion design files, drawings, spreadsheets, etc.'
+ },
+
+ config_fields:
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ control_type: 'text',
+ label: 'Folder ID',
+ change_on_blur: true,
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from url of the folder page.'
+ } },
+ { name: 'item_id',
+ label: 'File name',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select Item',
+ pick_list: :folder_items,
+ pick_list_params: { project_id: 'project_id', folder_id: 'folder_id' },
+ optional: false,
+ toggle_field: {
+ name: 'item_id',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ label: 'File ID',
+ toggle_hint: 'Enter file ID',
+ hint: 'Provide file ID.'
+ } }
+ ],
+
+ execute: lambda do |_connection, input|
+ get("/data/v1/projects/#{input['project_id']}/items/#{input['item_id']}")&.merge({ project_id: input['project_id'], hub_id: input['hub_id'] })
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['item'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ get("/data/v1/projects/#{input['project_id']}/items/#{input['item_id']}")
+ end
+ },
+
+ get_drawing_export_status: {
+ title: 'Get drawing export status in a project',
+
+ description: 'Get drawing export status in a project in BIM 360',
+
+ help: {
+ body: 'This action returns the status of a PDF export job, as well as data you need to download the exported file when the export is complete.'
+ },
+
+ config_fields:
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'folder_id',
+ label: 'Folder name',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'Folder ID',
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from url of the folder page.'
+ }
+ },
+ {
+ name: 'item_id',
+ label: 'File name',
+ control_type: 'select',
+ pick_list: 'folder_items',
+ pick_list_params: { project_id: 'project_id', folder_id: 'folder_id' },
+ optional: false,
+ toggle_hint: 'Select file',
+ toggle_field: {
+ name: 'item_id',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ label: 'File ID',
+ toggle_hint: 'Enter file ID',
+ hint: 'Use file or item ID.'
+ }
+ },
+ {
+ name: 'version_number',
+ control_type: 'select',
+ pick_list: 'item_versions',
+ sticky: true,
+ pick_list_params: { project_id: 'project_id', item_id: 'item_id' },
+ hint: "Latest version will be used if no value is selected.",
+ optional: true,
+ toggle_hint: 'Select version',
+ toggle_field: {
+ name: 'version_number',
+ label: 'Version number',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ optional: true,
+ toggle_hint: 'Enter version number',
+ hint: 'Latest version will be used if no value is specified.'
+ }
+ }
+ ],
+
+ input_fields: lambda do |_object_definitions|
+ [
+ { name: 'export_id', optional: false }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ version_number = input['version_number'] || get("/data/v1/projects/#{input['project_id']}/items/#{input['item_id']}/versions")&.dig('data', 0, 'id')
+ version_url = version_number.split('?').first.encode_url.gsub("+", "%20")
+ # version_url = version_number.encode_url
+ project_id = input['project_id'].split('.').last
+ get("/bim360/docs/v1/projects/#{project_id}/versions/#{version_url}/exports/#{input['export_id']}").
+ merge(hub_id: input['hub_id'], project_id: input['project_id'], item_id: input['item_id'])
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'item_id' }
+ ].concat(object_definitions['export_status'])
+ end,
+
+ sample_output: lambda do |_connection, _input|
+ call('sample_data_export_job')
+ end
+ },
+
+ download_document: {
+ title: 'Download document in a project',
+
+ description: 'Download document in a project in BIM 360',
+
+ help: 'Retrieve the content of a specific document in the project folder. This content can be used to upload the attachment into another application in subsequent recipe steps.',
+
+ config_fields:
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder name',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ optional: false,
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ label: 'Folder ID',
+ toggle_hint: 'Enter folder ID',
+ hint: 'Get ID from url of the folder page.'
+ } },
+ { name: 'item_id',
+ label: 'File',
+ control_type: 'tree',
+ hint: 'Select file',
+ toggle_hint: 'Select file',
+ pick_list: :folder_items,
+ pick_list_params: { project_id: 'project_id', folder_id: 'folder_id' },
+ optional: false,
+ toggle_field: {
+ name: 'item_id',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ label: 'File ID',
+ toggle_hint: 'Enter file ID',
+ hint: 'Get file ID from url of the file page.'
+ } }
+ ],
+
+ execute: lambda do |_connection, input|
+ # 1 find the storage location of the item
+ file_url = get("/data/v1/projects/#{input['project_id']}/items/#{input['item_id']}")&.
+ dig('included', 0, 'relationships', 'storage', 'meta', 'link', 'href')
+ if file_url.present?
+ { content: get(file_url).headers('Accept-Encoding': 'Accept-Encoding:gzip').
+ response_format_raw.
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end }
+ else
+ error('File does not exist')
+ end
+ end,
+
+ output_fields: lambda do |_object_definitions|
+ [{ name: 'content' }]
+ end,
+
+ sample_output: lambda do |_connection, _input|
+ {
+ "content": ""
+ }
+ end
+ },
+
+ upload_document_to_project: {
+ title: 'Upload document to a project',
+
+ description: 'Upload document to a project in BIM 360',
+
+ help: {
+ body: 'Note that you cannot upload documents to the root folder in BIM 360 Docs; you can only upload documents to the Project Files ' \
+ 'folder or to a folder nested under the Project Files folder'
+ },
+
+ config_fields: [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ type: 'string',
+ control_type: 'text',
+ change_on_blur: true,
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ { name: 'folder_id',
+ label: 'Folder name',
+ control_type: 'tree',
+ hint: 'Select folder',
+ toggle_hint: 'Select folder',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false,
+ toggle_field: {
+ name: 'folder_id',
+ type: 'string',
+ control_type: 'text',
+ label: 'Folder ID',
+ change_on_blur: true,
+ toggle_hint: 'Enter folder ID',
+ hint: 'Use folder ID.'
+ } }
+ ],
+
+ input_fields: lambda do |_object_definitions|
+ [
+ { name: 'file_name', optional: false, label: 'File name',
+ hint: 'File name should include extension of the file. e.g. my_file.jpg. The name of the file (1-255 characters). ' \
+ 'Reserved characters: <, >, :, ", /, \, |, ?, *, `, \n, \r, \t, \0, \f, ¢, ™, $, ®' },
+ { name: 'file_content', optional: false },
+ { name: 'file_extension', control_type: 'select',
+ pick_list: 'file_types',
+ optional: false,
+ toggle_hint: 'Select file extension',
+ toggle_field: {
+ name: 'file_extension',
+ type: 'string',
+ control_type: 'text',
+ label: 'File extension',
+ toggle_hint: 'Enter file extension value',
+ hint: 'Only relevant for creating files - the type of file extension.
. For BIM 360 Docs files, use ' \
+ 'items:autodesk.bim360:File.
For all other services, use items:autodesk.core:File.'
+ } },
+ { name: 'version_type', label: 'Type of version',
+ hint: 'Only relevant for creating files - the type of version.',
+ control_type: 'select',
+ pick_list: 'version_types',
+ optional: false,
+ toggle_hint: 'Select version type',
+ toggle_field: {
+ name: 'version_type',
+ label: 'Type of version',
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter version type value',
+ hint: 'Only relevant for creating files - the type of version.
For BIM 360 Docs files, use versions:autodesk.bim360:File' \
+ '
For A360 composite design files, use versions:autodesk.a360:CompositeDesign
' \
+ 'For A360 Personal, Fusion Team, or BIM 360 Team files, use versions:autodesk.core:File.'
+ } },
+ { name: 'extension_type_version',
+ hint: 'The version of the version extension type. The current version is 1.0 ' }
+
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ # 1 create storage location
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id').gsub('b:', '')
+ response_storage = post("https://developer.api.autodesk.com/data/v1/projects/#{project_id}/storage").
+ headers('Content-Type' => 'application/vnd.api+json', 'Accept' => 'application/vnd.api+json').
+ payload('jsonapi' => { 'version' => '1.0' },
+ 'data' => {
+ 'type' => 'objects',
+ 'attributes' => { 'name' => input['file_name'] },
+ 'relationships' => {
+ 'target' => {
+ 'data' => { 'type' => 'folders', 'id' => input['folder_id'] }
+ }
+ }
+ }).
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ object_id = response_storage&.dig('data', 'id')
+
+ # 2 Upload file to storage location
+ bucket_key = object_id.split('/').first.split('object:').last
+ object_name = object_id.split('/').last
+ response = put("https://developer.api.autodesk.com/oss/v2/buckets/#{bucket_key}/objects/#{object_name}").
+ request_body(input['file_content']).
+ headers('Content-Type' => 'application/octet-stream').
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ # 3 create a first version of the File
+ # folder_urn = get("/data/v1/projects/#{input['project_id']}/folders" \
+ # "/#{input['folder_id']}")['data']
+
+ post("/data/v1/projects/#{project_id}/items").
+ payload('jsonapi' => { 'version' => '1.0' },
+ 'data' => {
+ # type of the resource
+ 'type' => 'items',
+ # The attributes of the data object.
+ 'attributes' => {
+ 'displayName' => input['file_name'],
+ # Extended information on the resource.
+ 'extension' =>
+ {
+ # relevant for creating files
+ 'type' => input['file_extension'],
+ 'version' => '1.0'
+ }
+ },
+ 'relationships' => {
+ # The information on the tip version of this resource.
+ 'tip' => {
+ 'data' => {
+ 'type' => 'versions',
+ 'id' => '1'
+ }
+ },
+ # Information on the parent resource of this resource.
+ 'parent' => {
+ 'data' => {
+ 'type' => 'folders',
+ # The URN of the parent folder in which you want to create
+ # a version of a file or to copy a file to.
+ 'id' => input['folder_id']
+ }
+ }
+ }
+ },
+ 'included' => [
+ {
+ 'type' => 'versions',
+ 'id' => '1',
+ 'attributes' => {
+ 'name' => input['file_name'],
+ 'extension' => {
+ 'type' => input['version_type'],
+ 'version' => input['extension_type_version'] || '1.0'
+ }
+ },
+ 'relationships' => {
+ 'storage' => {
+ 'data' => {
+ 'type' => 'objects',
+ 'id' => response['objectId']
+ }
+ }
+ }
+ }
+ ]).
+ headers('Content-Type' => 'application/vnd.api+json', 'Accept' => 'application/vnd.api+json').
+ after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end&.dig('data')&.merge({ hub_id: hub_id, project_id: project_id })
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['folder_file'])
+ end
+ },
+
+ search_budgets_in_project: {
+ title: 'Search budgets in a project',
+
+ description: 'Search budgets in a project in' \
+ ' BIM 360',
+
+ help: {
+ body: 'This action searches for budgets in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'rootId',
+ hint: 'Filter by related subitems for the given root item ID.' \
+ ' Separate multiple IDs with commas.'
+ },
+ {
+ name: 'externalSystem',
+ hint: 'Filter by name of source if used in system integration.'
+ },
+ {
+ name: 'externalId',
+ hint: 'Filter by item ID in the external system. ' \
+ 'Separate multiple IDs with commas.'
+ },
+ {
+ name: 'code',
+ hint: 'Filter by item code.'
+ },
+ {
+ name: 'offset',
+ type: 'number',
+ hint: 'Number of items to skip before starting to collect the result set.'
+ },
+ {
+ name: 'limit',
+ type: 'number',
+ hint: 'Number of items to return.'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'sort',
+ hint: 'Order of items to sort. Each item can be followed by a direction modifier' \
+ ' of either asc or desc. If no direction is specified then asc is assumed.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id'))
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ &.dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/budgets", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat([
+ { name: 'results', type: 'array', of: 'object', properties: object_definitions['budget'] },
+ { name: 'pagination', type: 'object', properties: [
+ { name: 'totalResults', type: 'number' },
+ { name: 'limit', type: 'number' },
+ { name: 'offset', type: 'number' }
+ ]}
+ ])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/budgets?limit=1") || {})
+ .merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ get_budget_in_project: {
+ title: 'Get budget in a project',
+
+ description: 'Get budget in a project' \
+ ' in BIM 360',
+
+ help: {
+ body: 'This action returns a budget in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'budget_id',
+ label: 'Budget ID',
+ optional: false,
+ sticky: true
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/budgets/#{input['budget_id']}")
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['budget'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/budgets?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ create_budget_in_project: {
+ title: 'Create budget in a project',
+
+ description: 'Create budget in a ' \
+ 'project in BIM 360',
+
+ help: {
+ body: 'This action creates a budget in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'code',
+ optional: false,
+ sticky: true,
+ hint: 'Unique code that is compliant with the budget code template' \
+ ' defined by the project admin. Max length of 255 characters.'
+ },
+ {
+ name: 'name',
+ optional: false,
+ sticky: true,
+ hint: 'Name of the budget. Max length of 1024 characters.'
+ },
+ {
+ name: 'parentId',
+ sticky: true,
+ hint: 'ID of the parent budget. Used only when creating sub-budgets.'
+ },
+ {
+ name: 'quantity',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Quantity of labor, material, etc, that is planned for a budget.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the budget.'
+ },
+ {
+ name: 'unitPrice',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Unit price of a budget.'
+ },
+ {
+ name: 'unit',
+ sticky: true,
+ hint: 'Unit of measure used in the budget.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ post("/cost/v1/containers/#{container_id}/budgets")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['budget'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/budgets?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ update_budget_in_project: {
+ title: 'Update budget in a project',
+
+ description: 'Update budget in a project' \
+ ' in BIM 360',
+
+ help: {
+ body: 'This action updates a budget in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'budgetId',
+ optional: false,
+ sticky: true,
+ hint: 'ID of the budget to update.'
+ },
+ {
+ name: 'name',
+ sticky: true,
+ hint: 'Name of the budget. Max length of 1024 characters.'
+ },
+ {
+ name: 'code',
+ sticky: true,
+ hint: 'Unique code that is compliant with the budget code template ' \
+ 'defined by the project admin. Max length of 255 characters.'
+ },
+ {
+ name: 'parentId',
+ sticky: true,
+ hint: 'ID of the parent budget. Used only when creating sub-budgets.'
+ },
+ {
+ name: 'quantity',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Quantity of labor, material, etc, that is planned for a budget.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the budget.'
+ },
+ {
+ name: 'unitPrice',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Unit price of a budget.'
+ },
+ {
+ name: 'unit',
+ sticky: true,
+ hint: 'Unit of measure used in the budget.'
+ },
+ {
+ name: 'actualCost',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Total amount of actual cost of the budget.'
+ },
+ {
+ name: 'adjustments',
+ sticky: true,
+ hint: 'Items to make and explain adjustments to actual cost ' \
+ 'amounts to make forecast more accurate.',
+ type: 'object', properties: [
+ { name: 'details', type: 'array', of: 'object', properties: [
+ {
+ name: 'quantity',
+ sticky: true,
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Quantity of actual adjustment.'
+ },
+ {
+ name: 'unitPrice',
+ sticky: true,
+ type: 'number',
+ type: 'integer',
+ render_input: 'integer_conversion',
+ parse_output: 'integer_conversion',
+ hint: 'Unit price of the actual adjustment.'
+ },
+ {
+ name: 'unit',
+ sticky: true,
+ hint: 'Unit of measurement of the actual adjustment.'
+ }
+ ]}
+ ]
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ budget_id = input.delete('budgetId')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ patch("/cost/v1/containers/#{container_id}/budgets/#{budget_id}")
+ .headers("Content-Type": "application/json")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['budget'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/budgets?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ search_contracts_in_project: {
+ title: 'Search contracts in a project',
+
+ description: 'Search contracts in a ' \
+ 'project in BIM 360',
+
+ help: {
+ body: 'This action searches for contracts in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'externalSystem',
+ hint: 'Filter by name of source if used in system integration.'
+ },
+ {
+ name: 'externalId',
+ hint: 'Filter by item ID in the external system.' \
+ ' Separate multiple IDs with commas.'
+ },
+ {
+ name: 'code',
+ hint: 'Filter by item code.'
+ },
+ {
+ name: 'include',
+ hint: 'Resources to include in the response.',
+ control_type: 'multiselect',
+ pick_list: [
+ ['Budgets', 'budgets'],
+ ['Attributes', 'attributes']
+ ],
+ delimiter: ','
+ },
+ {
+ name: 'offset',
+ type: 'number',
+ hint: 'Number of items to skip before starting to ' \
+ 'collect the result set.'
+ },
+ {
+ name: 'limit',
+ type: 'number',
+ hint: 'Number of items to return.'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'sort',
+ hint: 'Order of items to sort. Each item can be followed by a ' \
+ 'direction modifier of either asc or desc. If no direction is ' \
+ 'specified then asc is assumed.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id'))
+
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/contracts", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat([
+ { name: 'results', type: 'array', of: 'object', properties: object_definitions['contract'] },
+ { name: 'pagination', type: 'object', properties: [
+ { name: 'totalResults', type: 'number' },
+ { name: 'limit', type: 'number' },
+ { name: 'offset', type: 'number' },
+ { name: 'nextUrl' }
+ ]}
+ ])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/contracts?limit=1") || {})
+ .merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ get_contract_in_project: {
+ title: 'Get contract in a project',
+
+ description: 'Get contract in a ' \
+ 'project in BIM 360',
+
+ help: {
+ body: 'This action returns a contract in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'contract_id',
+ label: 'Contract ID',
+ optional: false,
+ sticky: true
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/contracts/#{input['contract_id']}")
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['contract'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/contracts?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ create_contract_in_project: {
+ title: 'Create contract in a project',
+
+ description: 'Create contract in a ' \
+ 'project in BIM 360',
+
+ help: {
+ body: 'This action creates a contract in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'code',
+ optional: false,
+ sticky: true,
+ hint: 'Code of the contract. Max lenght of 255 characters.'
+ },
+ {
+ name: 'name',
+ optional: false,
+ sticky: true,
+ hint: 'Name of the contract. Max length of 1024 characters.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the contract. Max length of 2048 characters.'
+ },
+ {
+ name: 'companyId',
+ sticky: true,
+ hint: 'ID of the supplier company managed by the BIM 360 admin.'
+ },
+ {
+ name: 'type',
+ sticky: true,
+ hint: 'Type of the contract as specified by the project admin.'
+ },
+ {
+ name: 'contactId',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 for the contact of the supplier.'
+ },
+ {
+ name: 'signedBy',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 who signed the contract.'
+ },
+ {
+ name: 'ownerId',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 who is responsible for the purchase.'
+ },
+ {
+ name: 'status',
+ sticky: true,
+ control_type: 'select',
+ pick_list: 'contract_status',
+ toggle_hint: 'Select status',
+ toggle_field: {
+ name: 'status',
+ label: 'Status',
+ change_on_blur: true,
+ control_type: 'text',
+ type: 'string',
+ toggle_hint: 'Enter status',
+ hint: 'Status of the contract. Possible values include:' \
+ ' draft, open, sent, executed, closed.'
+ }
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ post("/cost/v1/containers/#{container_id}/contracts")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['contract'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/contracts?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ update_contract_in_project: {
+ title: 'Update contract in a project',
+
+ description: 'Update contract in a ' \
+ 'project in BIM 360',
+
+ help: {
+ body: 'This action updates a contract in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'contractId',
+ optional: false,
+ sticky: true,
+ hint: 'ID of the budget to update.'
+ },
+ {
+ name: 'code',
+ sticky: true,
+ hint: 'Code of the contract. Max lenght of 255 characters.'
+ },
+ {
+ name: 'name',
+ sticky: true,
+ hint: 'Name of the contract. Max length of 1024 characters.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the contract. Max length of 2048 characters.'
+ },
+ {
+ name: 'companyId',
+ sticky: true,
+ hint: 'ID of the supplier company managed by the BIM 360 admin.'
+ },
+ {
+ name: 'type',
+ sticky: true,
+ hint: 'Type of the contract as specified by the project admin.'
+ },
+ {
+ name: 'contactId',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 for the contact of the supplier.'
+ },
+ {
+ name: 'signedBy',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 who signed the contract.'
+ },
+ {
+ name: 'ownerId',
+ sticky: true,
+ hint: 'ID of the user in BIM 360 who is responsible for the purchase.'
+ },
+ {
+ name: 'status',
+ sticky: true,
+ control_type: 'select',
+ pick_list: 'contract_status',
+ toggle_hint: 'Select status',
+ toggle_field: {
+ name: 'status',
+ label: 'Status',
+ change_on_blur: true,
+ control_type: 'text',
+ type: 'string',
+ toggle_hint: 'Enter status',
+ hint: 'Status of the contract. Possible values include:' \
+ ' draft, open, sent, executed, closed.'
+ }
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ contract_id = input.delete('contractId')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ patch("/cost/v1/containers/#{container_id}/contracts/#{contract_id}")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['contract'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/contracts?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ search_change_orders_in_project: {
+ title: 'Search change orders in a project',
+
+ description: 'Search change orders' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action searches for change orders in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'type',
+ label: 'Change Order Type',
+ control_type: 'select',
+ pick_list: 'change_order_types',
+ optional: false,
+ sticky: true,
+ toggle_hint: 'Select type',
+ toggle_field: {
+ name: 'type',
+ label: 'Change Order Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type',
+ hint: 'Enter the change order type name. ' \
+ 'Possible values are: pco, rfq, rco, oco, sco.'
+ }
+ },
+ {
+ name: 'externalSystem',
+ label: 'External System',
+ hint: 'Filter by name of source if used in system integration.'
+ },
+ {
+ name: 'externalId',
+ label: 'External ID',
+ hint: 'Filter by item ID in the external system. ' \
+ 'Separate multiple IDs with commas.'
+ },
+ {
+ name: 'contractId',
+ label: 'Contract ID',
+ hint: 'Filter by contract ID. ' \
+ 'Separate multiple IDs with commas.'
+ },
+ {
+ name: 'mainContractId',
+ label: 'Main Contract ID',
+ hint: 'Filter by main contract ID. Separate multiple IDs with commas.'
+ },
+ {
+ name: 'budgetStatus',
+ label: 'Budget Status',
+ hint: 'Filter by budget status code. Separate multiple codes with commas.'
+ },
+ {
+ name: 'costStatus',
+ label: 'Cost Status',
+ hint: 'Filter by cost status code. Separate multiple codes with commas.'
+ },
+ {
+ name: 'include',
+ hint: 'Resources to include in the response.',
+ control_type: 'multiselect',
+ pick_list: [
+ ['Cost Items', 'costItems'],
+ ['Attributes', 'attributes']
+ ],
+ delimiter: ','
+ },
+ {
+ name: 'offset',
+ type: 'number',
+ hint: 'Number of items to skip before starting to collect the result set.'
+ },
+ {
+ name: 'limit',
+ type: 'number',
+ hint: 'Number of items to return.'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ },
+ {
+ name: 'sort',
+ hint: 'Order of items to sort. Each item can be followed by a direction modifier' \
+ ' of either asc or desc. If no direction is specified then asc is assumed.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id'))
+
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/change-orders/#{input['type']}", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat([
+ { name: 'results', type: 'array', of: 'object', properties: object_definitions['change_order'] },
+ { name: 'pagination', type: 'object', properties: [
+ { name: 'totalResults', type: 'number' },
+ { name: 'limit', type: 'number' },
+ { name: 'offset', type: 'number' },
+ { name: 'nextUrl' }
+ ]}
+ ])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'cost', 'data', 'id') || {}
+ (get("/cost/v1/containers/#{container_id}/change-orders/pco?limit=1") || {})&.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ get_change_order_in_project: {
+ title: 'Get change order in a project',
+
+ description: 'Get change order' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action returns a change order in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'type',
+ label: 'Change Order Type',
+ control_type: 'select',
+ pick_list: 'change_order_types',
+ optional: false,
+ sticky: true,
+ toggle_hint: 'Select type',
+ toggle_field: {
+ name: 'type',
+ label: 'Change Order Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name',
+ hint: 'Enter the change order type name. ' \
+ 'Possible values are: pco, rfq, rco, oco, sco.'
+ }
+ },
+ {
+ name: 'id',
+ label: 'Change Order ID',
+ optional: false,
+ sticky: true
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/change-orders/#{input['type']}/#{input['id']}")
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['change_order'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+ (get("/cost/v1/containers/#{container_id}/change-orders/pco?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ create_change_order_in_project: {
+ title: 'Create change order in a project',
+
+ description: 'Create change order' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action creates a change order in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'type',
+ label: 'Change Order Type',
+ control_type: 'select',
+ pick_list: 'change_order_types',
+ optional: false,
+ sticky: true,
+ toggle_hint: 'Select type',
+ toggle_field: {
+ name: 'type',
+ label: 'Change Order Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name',
+ hint: 'Enter the change order type name. ' \
+ 'Possible values are: pco, rfq, rco, oco, sco.'
+ }
+ },
+ {
+ name: 'name',
+ sticky: true,
+ hint: 'Name of the change order. Max length of 1024 characters.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the change order. Max length of 2048 characters.'
+ },
+ {
+ name: 'scope',
+ sticky: true,
+ label: 'Scope',
+ hint: 'Scope of the change order.',
+ control_type: 'select',
+ pick_list: 'change_order_scope',
+ toggle_hint: 'Select scope',
+ toggle_field: {
+ name: 'scope',
+ label: 'Scope',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name',
+ hint: 'Enter scope of change order. ' \
+ 'Possible values are: out, in, tbd, contigency.'
+ }
+ },
+ {
+ name: 'scopeOfWork',
+ optional: true,
+ sticky: true,
+ hint: 'Scope of work of the change order.'
+ },
+ {
+ name: 'note',
+ optional: true,
+ sticky: true,
+ hint: 'Additional notes to the change order.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ type = input.delete('type')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ post("/cost/v1/containers/#{container_id}/change-orders/#{type}")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['change_order'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+ (get("/cost/v1/containers/#{container_id}/change-orders/pco?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ update_change_order_in_project: {
+ title: 'Update change order in a project',
+
+ description: 'Update change order' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action updates a change order in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'type',
+ label: 'Change Order Type',
+ control_type: 'select',
+ pick_list: 'change_order_types',
+ optional: false,
+ sticky: true,
+ toggle_hint: 'Select type',
+ toggle_field: {
+ name: 'type',
+ label: 'Change Order Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name',
+ hint: 'Enter the change order type name. ' \
+ 'Possible values are: pco, rfq, rco, oco, sco.'
+ }
+ },
+ {
+ name: 'id',
+ label: 'Change Order ID',
+ optional: false,
+ sticky: true,
+ hint: 'ID of the change order. Max length of 1024 characters.'
+ },
+ {
+ name: 'name',
+ sticky: true,
+ hint: 'Name of the change order. Max length of 1024 characters.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the change order. Max length of 2048 characters.'
+ },
+ {
+ name: 'scope',
+ sticky: true,
+ label: 'Scope',
+ hint: 'Scope of the change order.',
+ control_type: 'select',
+ pick_list: 'change_order_scope',
+ toggle_hint: 'Select scope',
+ toggle_field: {
+ name: 'scope',
+ label: 'Scope',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name',
+ hint: 'Enter scope of change order. ' \
+ 'Possible values are: out, in, tbd, contigency.'
+ }
+ },
+ {
+ name: 'scopeOfWork',
+ sticky: true,
+ hint: 'Scope of work of the change order.'
+ },
+ {
+ name: 'note',
+ sticky: true,
+ hint: 'Additional notes to the change order.'
+ },
+ {
+ name: 'recipients',
+ sticky: true,
+ hint: 'Users who will receive the generated documents.',
+ type: 'array', of: 'object', properties: [
+ { name: 'id', hint: 'ID of the user in BIM 360.' },
+ { name: 'isDefault', type: 'boolean', hint: 'Indicate whether the user is the default recipient.'}
+ ]
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ type = input.delete('type')
+ id = input.delete('id')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ patch("/cost/v1/containers/#{container_id}/change-orders/#{type}/#{id}")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['change_order'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/change-orders/pco?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ search_cost_items_in_project: {
+ title: 'Search cost items in a project',
+
+ description: 'Search cost items' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action searches for cost items in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'changeOrderId',
+ label: 'Change Order',
+ hint: 'Filter by change order ID. Separate multiple IDs with commas.'
+ },
+ {
+ name: 'budgetId',
+ label: 'Budget',
+ hint: 'Filter by budget ID. Separate multiple IDs with commas.'
+ },
+ {
+ name: 'externalSystem',
+ label: 'External System',
+ hint: 'Filter by name of source if used in system integration.'
+ },
+ {
+ name: 'externalId',
+ label: 'External ID',
+ hint: 'Filter by item ID in the external system. ' \
+ 'Separate multiple IDs with commas.'
+ },
+ {
+ name: 'include',
+ hint: 'Resources to include in the response.',
+ control_type: 'multiselect',
+ pick_list: [
+ ['Budgets', 'budgets'],
+ ['Change Orders', 'changeOrders'],
+ ['Attributes', 'attributes']
+ ],
+ delimiter: ','
+ },
+ {
+ name: 'offset',
+ type: 'number',
+ hint: 'Number of items to skip before starting to collect the result set.'
+ },
+ {
+ name: 'limit',
+ type: 'number',
+ hint: 'Number of items to return.'
+ },
+ {
+ name: 'sort',
+ hint: 'Order of items to sort. Each item can be followed by ' \
+ 'a direction modifier of either asc or desc. ' \
+ 'If no direction is specified then asc is assumed.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id', 'type'))
+
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/cost-items", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat([
+ { name: 'results', type: 'array', of: 'object', properties: object_definitions['cost_item'] },
+ { name: 'pagination', type: 'object', properties: [
+ { name: 'totalResults', type: 'number' },
+ { name: 'limit', type: 'number' },
+ { name: 'offset', type: 'number' },
+ { name: 'nextUrl' }
+ ]}
+ ])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/cost-items?limit=1") || {})
+ .merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ get_cost_item_in_project: {
+ title: 'Get cost item in a project',
+
+ description: 'Get cost item' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action returns a cost item in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'id',
+ label: 'Cost Item ID',
+ optional: false,
+ sticky: true
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/cost-items/#{input['id']}")
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['cost_item'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+ (get("/cost/v1/containers/#{container_id}/cost-items?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ create_cost_item_in_project: {
+ title: 'Create cost item in a project',
+
+ description: 'Create cost item' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action creates a cost item in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'name',
+ optional: false,
+ sticky: true,
+ hint: 'Name of the cost item. Max length of 1024 characters.'
+ },
+ {
+ name: 'changeOrderId',
+ sticky: true,
+ hint: 'ID of the change order that the cost item is created in.'
+ },
+ {
+ name: 'budgetId',
+ sticky: true,
+ hint: 'ID of the budget that the cost item is linked to.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the cost item. Max length of 2048 characters.'
+ },
+ {
+ name: 'estimated',
+ sticky: true,
+ type: 'number',
+ hint: 'Rough estimation of this item without a quotation.'
+ },
+ {
+ name: 'proposed',
+ sticky: true,
+ type: 'number',
+ hint: 'Quoted cost of the cost item.'
+ },
+ {
+ name: 'submitted',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount submitted to the owner for approval.'
+ },
+ {
+ name: 'approved',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount approved by the owner.'
+ },
+ {
+ name: 'committed',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount committed to the supplier.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+ response = if container_id.present?
+ post("/cost/v1/containers/#{container_id}/cost-items")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['cost_item'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/cost-items?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ update_cost_item_in_project: {
+ title: 'Update cost item in a project',
+
+ description: 'Update cost item' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action updates a cost item in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'id',
+ label: 'Cost Item ID',
+ optional: false,
+ sticky: true
+ },
+ {
+ name: 'name',
+ sticky: true,
+ hint: 'Name of the cost item. Max length of 1024 characters.'
+ },
+ {
+ name: 'changeOrderId',
+ sticky: true,
+ hint: 'ID of the change order that the cost item is created in.'
+ },
+ {
+ name: 'budgetId',
+ sticky: true,
+ hint: 'ID of the budget that the cost item is linked to.'
+ },
+ {
+ name: 'description',
+ sticky: true,
+ hint: 'Description of the cost item. Max length of 2048 characters.'
+ },
+ {
+ name: 'estimated',
+ sticky: true,
+ type: 'number',
+ hint: 'Rough estimation of this item without a quotation.'
+ },
+ {
+ name: 'proposed',
+ sticky: true,
+ type: 'number',
+ hint: 'Quoted cost of the cost item.'
+ },
+ {
+ name: 'submitted',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount submitted to the owner for approval.'
+ },
+ {
+ name: 'approved',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount approved by the owner.'
+ },
+ {
+ name: 'committed',
+ sticky: true,
+ type: 'number',
+ hint: 'Amount committed to the supplier.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ cost_item_id = input.delete('id')
+
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ patch("/cost/v1/containers/#{container_id}/cost-items/#{cost_item_id}")
+ .payload(input)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['cost_item'])
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+ (get("/cost/v1/containers/#{container_id}/cost-items?limit=1") || {})
+ .dig('results', 0).merge(hub_id: input['hub_id'], container_id: container_id)
+ end
+ },
+
+ get_cost_attachments_in_project: {
+ title: 'Get cost attachments in a project',
+
+ description: 'Get cost attachments' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action returns all attachments associated with a cost in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'associationId',
+ label: 'Object ID',
+ optional: false,
+ sticky: true,
+ hint: 'Object ID of the budget, contract, or cost item.'
+ },
+ {
+ name: 'associationType',
+ label: 'Object Type',
+ optional: false,
+ sticky: true,
+ control_type: 'select',
+ pick_list: 'cost_association_types',
+ toggle_hint: 'Select object type',
+ toggle_field: {
+ name: 'associationType',
+ label: 'Object Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter object type',
+ hint: 'Possible values are: Budget, Contract, CostItem, ' \
+ 'FormInstance, Payment, BudgetPayment'
+ }
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id', 'associationId', 'associationType'))
+ container_id = get("/project/v1/hubs/#{input.delete('hub_id')}/projects/#{input.delete('project_id')}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ filter_criteria['associationType'] = input['associationType']
+ filter_criteria['associationId'] = input['associationId']
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/attachments", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ end
+ { results: response }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'results', type: 'array',
+ of: 'object', properties: object_definitions['cost_attachment'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection|
+ {
+ results: [
+ {
+ "id": "F2D2ED17-C763-465B-8FAB-251C5A35D42F",
+ "folderId": "8E34872D-A56F-4096-B675-476F50F4EF51",
+ "urn": "urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1",
+ "type": "Upload",
+ "name": "Architecture",
+ "associationId": "EDC42DF6-277A-436A-A50D-EF57F35E1248",
+ "associationType": "Budget",
+ "createdAt": "2019-01-06T01:24:22.678Z",
+ "updatedAt": "2019-09-05T01:00:12.989Z"
+ }
+ ]
+ }
+ end
+ },
+
+ create_cost_attachment_in_project: {
+ title: 'Create cost attachment in a project',
+
+ description: 'Create cost attachment' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action creates an attachment to a cost object in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'name',
+ optional: false,
+ sticky: true,
+ hint: 'Name of the attachment. Max length of 255 characters.'
+ },
+ {
+ name: 'urn',
+ optional: false,
+ sticky: true,
+ hint: 'Version URN of the BIM 360 Docs file.'
+ },
+ {
+ name: 'associationId',
+ optional: false,
+ sticky: true,
+ hint: 'Object ID of the budget, contract, or cost item.'
+ },
+ {
+ name: 'associationType',
+ label: 'Object Type',
+ optional: false,
+ sticky: true,
+ control_type: 'select',
+ pick_list: 'cost_association_types',
+ toggle_hint: 'Select object type',
+ toggle_field: {
+ name: 'associationType',
+ label: 'Object Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter object type',
+ hint: 'Possible values are: Budget, Contract, CostItem,' \
+ ' FormInstance, Payment, BudgetPayment'
+ }
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ hub_id = input.delete('hub_id')
+ project_id = input.delete('project_id')
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ attachment_folder = post("/cost/v1/containers/#{container_id}/attachment-folders")
+ .payload(
+ 'associationId' => input['associationId'],
+ 'associationType' => input['associationType']
+ )
+ .dig('id')
+
+ response = if container_id.present?
+ post("/cost/v1/containers/#{container_id}/attachments")
+ .payload(
+ 'name': input['name'],
+ 'folderId': attachment_folder,
+ 'urn': input['urn'],
+ 'associationId': input['associationId'],
+ 'associationType': input['associationType']
+ )
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end.merge(hub_id: hub_id, container_id: container_id)
+ end
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'container_id' }
+ ]
+ .concat(object_definitions['cost_attachment'])
+ end,
+
+ sample_output: lambda do |_connection|
+ {
+ 'id': 'F2D2ED17-C763-465B-8FAB-251C5A35D42F',
+ 'folderId': '8E34872D-A56F-4096-B675-476F50F4EF51',
+ 'urn': 'urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1',
+ 'type': 'Upload',
+ 'name': 'Architecture',
+ 'associationId': 'EDC42DF6-277A-436A-A50D-EF57F35E1248',
+ 'associationType': 'Budget',
+ 'createdAt': '2019-01-06T01:24:22.678Z',
+ 'updatedAt': '2019-09-05T01:00:12.989Z'
+ }
+ end
+ },
+
+ get_cost_documents_in_project: {
+ title: 'Get cost documents in a project',
+
+ description: 'Get generated cost documents' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action returns generated documents associated with cost in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'associationId',
+ label: 'Object ID',
+ optional: false,
+ sticky: true,
+ hint: 'Object ID of the item, such as budget, contract, or cost item.'
+ },
+ {
+ name: 'associationType',
+ label: 'Object Type',
+ optional: false,
+ sticky: true,
+ control_type: 'select',
+ pick_list: 'cost_association_types',
+ toggle_hint: 'Select object type',
+ toggle_field: {
+ name: 'associationType',
+ label: 'Object Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter object type',
+ hint: 'Possible values are: Budget, Contract, CostItem, ' \
+ 'FormInstance, Payment, BudgetPayment'
+ }
+ },
+ {
+ name: 'latest',
+ label: 'Latest Version',
+ sticky: true,
+ type: 'boolean',
+ control_type: 'checkbox',
+ hint: 'Return only the latest version of a document if it has ' \
+ 'been generated multiple times.'
+ },
+ {
+ name: 'signed',
+ label: 'Signed Documents',
+ sticky: true,
+ type: 'boolean',
+ control_type: 'checkbox',
+ hint: 'Return only documents that have been signed.'
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id', 'associationId', 'associationType'))
+ container_id = get("/project/v1/hubs/#{input.delete('hub_id')}/projects/#{input.delete('project_id')}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ filter_criteria['associationType'] = input['associationType']
+ filter_criteria['associationId'] = input['associationId']
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/documents", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ end
+ { results: response }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'results', type: 'array', of: 'object',
+ properties: object_definitions['cost_document'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection|
+ {
+ "results": [
+ {
+ "id": "1df59db0-9484-11e8-a7ec-7ddae203e404",
+ "templateId": "1df59db0-9484-11e8-a7ec-7ddae203e404",
+ "recipientId": "GF8XKPKWM38E",
+ "signedBy": "CED9LVTLHNXV",
+ "urn": "urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1",
+ "signedUrn": "urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1",
+ "status": "Completed",
+ "jobId": 1,
+ "errorInfo": {
+ "code": "missingTemplate",
+ "message": "Couldn't generate the document because the template is invalid.",
+ "detail": "Got timeout for POST upload URL."
+ },
+ "associationId": "EDC42DF6-277A-436A-A50D-EF57F35E1248",
+ "associationType": "Budget",
+ "createdAt": "2019-01-06T01:24:22.678Z",
+ "updatedAt": "2019-09-05T01:00:12.989Z"
+ }
+ ]
+ }
+ end
+ },
+
+ download_cost_document_in_project: {
+ title: 'Download cost document in a project',
+
+ description: 'Download generated cost document' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action downloads generated document associated with cost in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'urn',
+ optional: false,
+ sticky: true,
+ hint: 'The URN ID of the generated cost document.'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ file_url = get("/data/v1/projects/#{input['project_id']}/versions/#{input['urn'].encode_url}")
+ .dig('data', 'relationships', 'storage', 'meta', 'link', 'href')
+
+ if file_url.present?
+ { content: get(file_url).headers('Accept-Encoding': 'Accept-Encoding:gzip')
+ .response_format_raw
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end }
+ else
+ error('File does not exist')
+ end
+ end,
+
+ output_fields: lambda do |_object_definitions|
+ [{ name: 'content' }]
+ end,
+
+ sample_output: lambda do |_connection|
+ {
+ "content": ""
+ }
+ end
+ },
+
+ search_cost_file_packages_in_project: {
+ title: 'Search cost file packages in a project',
+
+ description: 'Search cost file packages' \
+ ' in a project in BIM 360',
+
+ help: {
+ body: 'This action returns file packages of cost objects in a project.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false,
+ toggle_hint: 'Select hub',
+ toggle_field: {
+ name: 'hub_id',
+ label: 'Hub ID',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter hub ID',
+ hint: 'Get account ID from admin page. To convert an account ID into a hub ID you need to add a “b.” prefix. For example, an account ID of '\
+ 'c8b0c73d-3ae9 translates to a hub ID of b.c8b0c73d-3ae9.'
+ }
+ },
+ {
+ name: 'project_id',
+ label: 'Project',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false,
+ toggle_hint: 'Select project',
+ toggle_field: {
+ name: 'project_id',
+ label: 'Project ID',
+ change_on_blur: true,
+ type: 'string',
+ control_type: 'text',
+ toggle_hint: 'Enter project ID',
+ hint: 'Get ID from url of the project page. For example, a project ID is b.baf-0871-4aca-82e8-3dd6db.'
+ }
+ },
+ {
+ name: 'associationId',
+ label: 'Object ID',
+ sticky: true,
+ optional: false,
+ hint: 'The ID associated with a specific a budget, contract, ' \
+ 'cost item, etc.'
+ },
+ {
+ name: 'associationType',
+ label: 'Object Type',
+ sticky: true,
+ optional: false,
+ control_type: 'select',
+ pick_list: 'cost_association_types',
+ toggle_hint: 'Select object type',
+ toggle_field: {
+ name: 'associationType',
+ label: 'Object Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter object type',
+ hint: 'Possible values are: Budget, Contract, CostItem, ' \
+ 'FormInstance, Payment, BudgetPayment'
+ }
+ },
+ {
+ name: 'lastModifiedSince',
+ hint: 'Filter to include only items updated since this datetime.',
+ type: 'date_time'
+ }
+ ]
+ end,
+
+ execute: lambda do |_connection, input|
+ filter_criteria = call('format_cost_search', input.except('hub_id', 'project_id'))
+ container_id = get("/project/v1/hubs/#{input.delete('hub_id')}/projects/#{input.delete('project_id')}")
+ .dig('data', 'relationships', 'cost', 'data', 'id')
+
+ response = if container_id.present?
+ get("/cost/v1/containers/#{container_id}/file-packages", filter_criteria)
+ .after_error_response(/.*/) do |_code, body, _header, message|
+ error("#{message}: #{body}")
+ end
+ end
+ { results: response }
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'results', type: 'array', of: 'object',
+ properties: object_definitions['cost_file_packages'] }
+ ]
+ end,
+
+ sample_output: lambda do |_connection, input|
+ {
+ "results": [
+ {
+ "id": "a2e16076-d5bb-44b3-b451-fb1fb390e4fc",
+ "recipient": "GF8XKPKWM38E",
+ "urn": "urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1",
+ "errorInfo": {
+ "code": "missingTemplate",
+ "message": "Couldn't generate the document because the template is invalid.",
+ "detail": "Got timeout for POST upload URL."
+ },
+ "items": [
+ {
+ "id": "a2e16076-d5bb-44b3-b451-fb1fb390e4fc",
+ "urn": "urn:adsk.wipprod:fs.file:vf.PMbRnoPZR2mKDhau2uw4SQ?version=1",
+ "name": "GC_001.John-Smith.docx",
+ "type": "Document",
+ "createdAt": "2019-01-06T01:24:22.678Z",
+ "updatedAt": "2019-09-05T01:00:12.989Z"
+ }
+ ],
+ "createdAt": "2019-01-06T01:24:22.678Z",
+ "updatedAt": "2019-09-05T01:00:12.989Z"
+ }
+ ]
+ }
+ end
+ }
+
+ },
+
+ triggers: {
+ new_updated_issue_in_project: {
+ title: 'New or updated issue in a project',
+
+ description: 'New or updated issue in a project in BIM 360',
+
+ help: {
+ body: 'Triggers when an issue in a project is created or updated.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ {
+ 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|
+ closure ||= {}
+ updated_after = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ closure['container_id'] ||= get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.
+ dig('data', 'relationships', 'issues', 'data', 'id')
+ get("/issues/v1/containers/#{closure['container_id']}/quality-issues").
+ params(page: { limit: limit, offset: closure['offset'] || 0 },
+ filter: { synced_after: updated_after },
+ sort: 'updated_at',
+ included: input['include'])
+ end
+
+ if (next_page_url = response.dig('links', 'next')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['offset'] = 0
+ closure['updated_after'] = Array.wrap(response['data']).last.dig('attributes', 'updated_at')
+ end
+
+ issues = response['data']&.map do |out|
+ call(:format_output_response,
+ out['attributes'],
+ %w(permitted_statuses permitted_actions permitted_attributes custom_attributes trades))
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'], container_id: closure['container_id'])
+ end
+ {
+ events: issues || [],
+ next_poll: closure,
+ can_poll_more: response.dig('links', 'next').present?
+ }
+
+ end,
+
+ dedup: lambda do |issue|
+ "#{issue['id']}@#{issue.dig('attributes', 'updated_at')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' },
+ { name: 'container_id' }
+ ].concat(object_definitions['issue']).compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.dig('data', 'relationships', 'issues', 'data', 'id') || {}
+ get("/issues/v1/containers/#{container_id}/quality-issues?page[limit]=1")&.dig('data', 0) || {}
+ end
+ },
+
+ new_updated_document_in_project: {
+ title: 'New or updated document in a project folder',
+
+ description: 'New or updated document in a project folder in BIM 360',
+
+ help: 'Triggers when a document in a project folder is created or updated.',
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ { name: 'folder_id',
+ label: 'Folder',
+ control_type: 'tree',
+ hint: 'Select the folder to monitor for documents. Sub-folders will also be monitored.',
+ pick_list_params: { hub_id: 'hub_id', project_id: 'project_id' },
+ tree_options: { selectable_folder: true },
+ pick_list: :folders_list,
+ optional: false
+ },
+ {
+ 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|
+ closure ||= {}
+ last_modified_time = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ get("/data/v1/projects/#{input['project_id']}/folders/#{input['folder_id']}/contents",
+ "filter[type]=items&filter[lastModifiedTimeRollup]-ge=#{last_modified_time}&page[limit]=#{limit}&page[number]=#{closure['number'] || 0}")
+ end
+
+ items = response['data']&.map do |out|
+ call(:format_output_response, out.dig('attributes', 'extension', 'data'),
+ %w(visibleTypes actions allowedTypes))
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'], folder_id: input['folder_id'])
+ end&.sort_by { |res| res.dig('attributes', 'lastModifiedTime') }
+ if (next_page_url = response.dig('links', 'next')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['number'] = 0
+ closure['updated_after'] = Array.wrap(response['data']).last.dig('attributes', 'lastModifiedTime')
+ end
+ {
+ events: items || [],
+ next_poll: closure,
+ can_poll_more: response.dig('links', 'next').present?
+ }
+ end,
+
+ dedup: lambda do |item|
+ "#{item['id']}@#{item.dig('attributes', 'lastModifiedTime')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [{ name: 'hub_id' }, { name: 'project_id' }, { name: 'folder_id' }].
+ concat(object_definitions['folder_file']).
+ compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ get("/data/v1/projects/#{input['project_id']}/folders/#{input['folder_id']}/contents?page[limit]=1")&.dig('data', 0) || {}
+ end
+ },
+
+ new_updated_budget_in_project: {
+ title: 'New or updated budget in a project',
+
+ description: 'New or updated budget in a project in BIM 360',
+
+ help: {
+ body: 'Triggers when a budget in a project is created or updated.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ {
+ 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|
+ closure ||= {}
+ updated_after = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ closure['container_id'] ||= get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.
+ dig('data', 'relationships', 'cost', 'data', 'id')
+
+ get("/cost/v1/containers/#{closure['container_id']}/budgets").
+ params(limit: limit,
+ offset: closure['offset'] || 0,
+ filter: { lastModifiedSince: updated_after },
+ sort: 'updatedAt')
+ end
+
+ if (next_page_url = response.dig('pagination', 'nextUrl')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['offset'] = 0
+ closure['updated_after'] = (Array.wrap(response['results']).last||{}).dig('updatedAt')
+ end
+
+ records = response['results']&.map do |out|
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'])
+ end
+ {
+ events: records || [],
+ next_poll: closure,
+ can_poll_more: response.dig('pagination', 'nextUrl').present?
+ }
+
+ end,
+
+ dedup: lambda do |record|
+ "#{record['id']}@#{record.dig('updatedAt')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['budget']).compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/budgets?limit]=1")&.dig('results', 0) || {})
+ .merge(hub_id: input['hub_id'], project_id: input['project_id'])
+ end
+ },
+
+ new_updated_contract_in_project: {
+ title: 'New or updated contract in a project',
+
+ description: 'New or updated contract in a project in BIM 360',
+
+ help: {
+ body: 'Triggers when a contract in a project is created or updated.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ {
+ 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|
+ closure ||= {}
+ updated_after = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ closure['container_id'] ||= get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.
+ dig('data', 'relationships', 'cost', 'data', 'id')
+
+ get("/cost/v1/containers/#{closure['container_id']}/contracts").
+ params(limit: limit,
+ offset: closure['offset'] || 0,
+ filter: { lastModifiedSince: updated_after },
+ sort: 'updatedAt')
+ end
+
+ if (next_page_url = response.dig('pagination', 'nextUrl')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['offset'] = 0
+ closure['updated_after'] = (Array.wrap(response['results']).last||{}).dig('updatedAt')
+ end
+
+ records = response['results']&.map do |out|
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'])
+ end
+ {
+ events: records || [],
+ next_poll: closure,
+ can_poll_more: response.dig('pagination', 'nextUrl').present?
+ }
+
+ end,
+
+ dedup: lambda do |record|
+ "#{record['id']}@#{record.dig('updatedAt')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['contract']).compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/contracts?limit]=1")&.dig('results', 0) || {})
+ .merge(hub_id: input['hub_id'], project_id: input['project_id'])
+ end
+ },
+
+ new_updated_change_order_in_project: {
+ title: 'New or updated change order in a project',
+
+ description: 'New or updated change order in a project in BIM 360',
+
+ help: {
+ body: 'Triggers when a change order in a project is created or updated.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ {
+ name: 'type',
+ label: 'Change Order Type',
+ control_type: 'select',
+ pick_list: 'change_order_types',
+ optional: false,
+ sticky: true,
+ toggle_hint: 'Select type',
+ toggle_field: {
+ name: 'type',
+ label: 'Change Order Type',
+ type: 'string',
+ change_on_blur: true,
+ control_type: 'text',
+ toggle_hint: 'Enter type name'
+ }
+ },
+ {
+ 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|
+ closure ||= {}
+ updated_after = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ closure['container_id'] ||= get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.
+ dig('data', 'relationships', 'cost', 'data', 'id')
+
+ get("/cost/v1/containers/#{closure['container_id']}/change-orders/#{input['type']}").
+ params(limit: limit,
+ offset: closure['offset'] || 0,
+ filter: { lastModifiedSince: updated_after },
+ sort: 'updatedAt')
+ end
+
+ if (next_page_url = response.dig('pagination', 'nextUrl')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['offset'] = 0
+ closure['updated_after'] = (Array.wrap(response['results']).last||{}).dig('updatedAt')
+ end
+
+ records = response['results']&.map do |out|
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'])
+ end
+ {
+ events: records || [],
+ next_poll: closure,
+ can_poll_more: response.dig('pagination', 'nextUrl').present?
+ }
+
+ end,
+
+ dedup: lambda do |record|
+ "#{record['id']}@#{record.dig('updatedAt')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['change_order']).compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/change-orders/pco?limit]=1")&.dig('results', 0) || {})
+ .merge(hub_id: input['hub_id'], project_id: input['project_id'])
+ end
+ },
+
+ new_updated_cost_item_in_project: {
+ title: 'New or updated cost item in a project',
+
+ description: 'New or updated cost item in a project in BIM 360',
+
+ help: {
+ body: 'Triggers when a cost item in a project is created or updated.'
+ },
+
+ input_fields: lambda do |_object_definitions|
+ [
+ {
+ name: 'hub_id',
+ label: 'Hub name',
+ control_type: 'select',
+ pick_list: 'hub_list',
+ optional: false
+ },
+ {
+ name: 'project_id',
+ label: 'Project name',
+ control_type: 'select',
+ pick_list: 'project_list',
+ pick_list_params: { hub_id: 'hub_id' },
+ optional: false
+ },
+ {
+ 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|
+ closure ||= {}
+ updated_after = closure['updated_after'] || (input['since'] || 1.hour.ago).to_time.utc.iso8601
+ limit = 100
+
+ response = if closure['next_page_url'].present?
+ get(closure['next_page_url'])
+ else
+ closure['container_id'] ||= get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")&.
+ dig('data', 'relationships', 'cost', 'data', 'id')
+
+ get("/cost/v1/containers/#{closure['container_id']}/cost-items").
+ params(limit: limit,
+ offset: closure['offset'] || 0,
+ filter: { lastModifiedSince: updated_after },
+ sort: 'updatedAt')
+ end
+
+ if (next_page_url = response.dig('pagination', 'nextUrl')).present?
+ closure['next_page_url'] = next_page_url
+ else
+ closure['offset'] = 0
+ closure['updated_after'] = (Array.wrap(response['results']).last||{}).dig('updatedAt')
+ end
+
+ records = response['results']&.map do |out|
+ out.merge(project_id: input['project_id'], hub_id: input['hub_id'])
+ end
+ {
+ events: records || [],
+ next_poll: closure,
+ can_poll_more: response.dig('pagination', 'nextUrl').present?
+ }
+
+ end,
+
+ dedup: lambda do |record|
+ "#{record['id']}@#{record.dig('updatedAt')}"
+ end,
+
+ output_fields: lambda do |object_definitions|
+ [
+ { name: 'hub_id' },
+ { name: 'project_id' }
+ ].concat(object_definitions['cost_item']).compact
+ end,
+
+ sample_output: lambda do |_connection, input|
+ container_id = get("/project/v1/hubs/#{input['hub_id']}/projects/#{input['project_id']}")
+ .dig('data', 'relationships', 'cost', 'data', 'id') || {}
+
+ (get("/cost/v1/containers/#{container_id}/cost-items?limit]=1")&.dig('results', 0) || {})
+ .merge(hub_id: input['hub_id'], project_id: input['project_id'])
+ end
+ }
+
+ },
+
+ pick_lists: {
+ hub_list: lambda do |_connection|
+ get('/project/v1/hubs')['data']&.map do |hub|
+ [hub.dig('attributes', 'name'), hub['id']]
+ end
+ end,
+
+ file_types: lambda do |_connection|
+ [
+ ['BIM 360 Docs files', 'items:autodesk.bim360:File'],
+ ['All other service', 'items:autodesk.core:File']
+ ]
+ end,
+
+ folder_items: lambda do |_connection, project_id:, folder_id:|
+ if project_id.length == 38 && folder_id.present?
+ get("/data/v1/projects/#{project_id}/folders/#{folder_id}/contents?filter[type]=items")['data']&.map do |item|
+ [item.dig('attributes', 'displayName'), item['id']]
+ end
+ end
+ end,
+
+ item_versions: lambda do |_connection, project_id:, item_id:|
+ get("/data/v1/projects/#{project_id}/items/#{item_id}/versions")['data']&.map do |version|
+ ["Version #{version.dig('attributes', 'versionNumber')}", version.dig('id')]
+ end
+ end,
+
+ version_types: lambda do |_connection|
+ [
+ ['BIM 360 Docs files', 'versions:autodesk.bim360:File'],
+ ['A360 composite design files',
+ 'versions:autodesk.a360:CompositeDesign'],
+ ['A360 Personal, Fusion Team', 'versions:autodesk.core:File'],
+ ['BIM 360 Team files', 'versions:autodesk.core:File']
+ ]
+ end,
+
+ resource_types: lambda do |_connection|
+ %w[attachment overlay].map { |type| [type.labelize, type] }
+ end,
+
+ folders_list: lambda do |_connection, **args|
+ if args[:project_id].length == 38
+ if (parent_id = args&.[](:__parent_id)).present?
+ get("/data/v1/projects/#{args[:project_id]}/folders/#{parent_id}/contents?filter[type]=folders")['data']&.
+ map do |folder|
+ [folder.dig('attributes', 'displayName'), folder['id'], nil, true]
+ end
+ else
+ get("project/v1/hubs/#{args[:hub_id]}/projects/#{args[:project_id]}/topFolders?filter[type]=folders")['data']&.
+ map do |folder|
+ [folder.dig('attributes', 'displayName'), folder['id'], nil, true]
+ end || []
+ end
+ end
+ end,
+
+ issue_child_objects: lambda do |_connection|
+ %w[attachments comments container]&.map { |option| [option.labelize, option] }
+ end,
+
+ project_list: lambda do |_connection, hub_id:|
+ if hub_id.length == 38
+ get("project/v1/hubs/#{hub_id}/projects")['data']&.map do |project|
+ [project.dig('attributes', 'name'), project['id']]
+ end
+ end
+ end,
+
+ issue_container_lists: lambda do |_connection, hub_id:|
+ if hub_id.length == 38
+ get("project/v1/hubs/#{hub_id}/projects")['data']&.map do |project|
+ [project.dig('attributes', 'name'), project.dig('relationships', 'issues', 'data', 'id')]
+ end
+ end
+ end,
+
+ issue_type: lambda do |_connection, hub_id:, project_id:|
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")&.dig('data', 'relationships', 'issues', 'data', 'id')
+ if container_id.present?
+ get("issues/v1/containers/#{container_id}/ng-issue-types")['results']&.map do |issue|
+ [issue['title'], issue['id']]
+ end
+ end
+ end,
+
+ issue_sub_type: lambda do |_connection, hub_id:, project_id:, ng_issue_type_id:|
+ container_id = get("/project/v1/hubs/#{hub_id}/projects/#{project_id}")&.dig('data', 'relationships', 'issues', 'data', 'id')
+ if container_id.present?
+ issue_type = get("issues/v1/containers/#{container_id}/ng-issue-types?include=subtypes")['results']&.select { |issue| issue['id'] == ng_issue_type_id }.first
+ issue_type['subtypes']&.map do |subtype|
+ [subtype['title'], subtype['id']]
+ end
+ end
+ end,
+
+ project_types: lambda do
+ [
+ %w[Commercial Commercial], ['Convention Center', 'Convention Center'],
+ ['Data Center', 'Data Center'], ['Hotel / Motel', 'Hotel / Motel'],
+ %w[Office Office],
+ ['Parking Structure / Garage', 'Parking Structure / Garage'],
+ ['Performing Arts', 'Performing Arts'], %w[Retail Retail],
+ ['Stadium/Arena', 'Stadium/Arena'], ['Theme Park', 'Theme Park'],
+ ['Warehouse (non-manufacturing)', 'Warehouse (non-manufacturing)'],
+ %w[Healthcare Healthcare],
+ ['Assisted Living / Nursing Home', 'Assisted Living / Nursing Home'],
+ %w[Hospital Hospital], ['Medical Laboratory', 'Medical Laboratory'],
+ ['Medical Office', 'Medical Office'],
+ ['OutPatient Surgery Center', 'OutPatient Surgery Center'],
+ %w[Institutional Institutional], ['Court House', 'Court House'],
+ %w[Dormitory Dormitory], ['Education Facility', 'Education Facility'],
+ ['Government Building', 'Government Building'], %w[Library Library],
+ ['Military Facility', 'Military Facility'], %w[Museum Museum],
+ ['Prison / Correctional Facility', 'Prison / Correctional Facility'],
+ ['Recreation Building', 'Recreation Building'],
+ ['Religious Building', 'Religious Building'],
+ ['Research Facility / Laboratory', 'Research Facility / Laboratory'],
+ %w[Residential Residential],
+ ['Multi-Family Housing', 'Multi-Family Housing'],
+ ['Single-Family Housing', 'Single-Family Housing'],
+ %w[Infrastructure Infrastructure], %w[Airport Airport],
+ %w[Bridge Bridge], ['Canal / Waterway', 'Canal / Waterway'],
+ ['Dams / Flood Control / Reservoirs',
+ 'Dams / Flood Control / Reservoirs'],
+ ['Harbor / River Development', 'Harbor / River Development'],
+ %w[Rail Rail], %w[Seaport Seaport],
+ ['Streets / Roads / Highways', 'Streets / Roads / Highways'],
+ ['Transportation Building', 'Transportation Building'],
+ %w[Tunnel Tunnel], ['Waste Water / Sewers', 'Waste Water / Sewers'],
+ ['Water Supply', 'Water Supply'],
+ ['Industrial & Energy', 'Industrial & Energy'],
+ ['Manufacturing / Factory', 'Manufacturing / Factory'],
+ ['Oil & Gas', 'Oil & Gas'],
+ %w[Plant Plant], ['Power Plant', 'Power Plant'],
+ ['Solar Far', 'Solar Far'], %w[Utilities Utilities],
+ ['Wind Farm', 'Wind Farm'], ['Sample Projects', 'Sample Projects'],
+ ['Demonstration Project', 'Demonstration Project'],
+ ['Template Project', 'Template Project'],
+ ['Training Project', 'Training Project']
+ ]
+ end,
+
+ assigned_type_list: lambda do |_connection|
+ %w[user company role]&.map { |el| [el.labelize, el] }
+ end,
+
+ status_list: lambda do |_connection|
+ %w[active pending inactive archived]&.map { |el| [el.labelize, el] }
+ end,
+
+ issue_status_list: lambda do |_connection|
+ %w[open work_complete ready_to_inspect not_approved close in_dispute
+ void]&.map { |el| [el.labelize, el] }
+ end,
+
+ service_types: lambda do |_connection|
+ [
+ ['Field Service', 'field'],
+ ['Glue Service', 'glue'],
+ ['Schedule Service', 'schedule'],
+ ['Plan Service', 'plan'],
+ ['Docs Service', 'doc_manager']
+ ]
+ end,
+
+ change_order_types: lambda do |_connection|
+ %w[pco rfq rco oco sco]&.map { |el| [el.upcase, el] }
+ end,
+
+ cost_association_types: lambda do |_connection|
+ [
+ ['Budget', 'Budget'],
+ ['Contract', 'Contract'],
+ ['Cost Item', 'CostItem'],
+ ['Form Instance', 'FormInstance'],
+ ['Payment', 'Payment'],
+ ['Budget Payment', 'BudgetPayment']
+ ]
+ end,
+
+ contract_status: lambda do |_connection|
+ %w[draft open sent executed closed]&.map { |el| [el.labelize, el] }
+ end,
+
+ change_order_scope: lambda do |_connection|
+ %w[in out tbd contigency]&.map { |el| [el.labelize, el] }
+ end
+ }
+}