diff --git a/lib/travis/api/v3/insights_client.rb b/lib/travis/api/v3/insights_client.rb new file mode 100644 index 0000000000..7d69c9422d --- /dev/null +++ b/lib/travis/api/v3/insights_client.rb @@ -0,0 +1,258 @@ +# frozen_string_literal: true + +module Travis::API::V3 + class InsightsClient + class ConfigurationError < StandardError; end + + def initialize(user_id) + @user_id = user_id + end + + def user_notifications(filter, page, active, sort_by, sort_direction) + query_string = query_string_from_params( + value: filter, + page: page || '1', + active: active, + order: sort_by, + order_dir: sort_direction + ) + response = connection.get("/user_notifications?#{query_string}") + + handle_errors_and_respond(response) do |body| + notifications = body['data'].map do |notification| + Travis::API::V3::Models::InsightsNotification.new(notification) + end + + Travis::API::V3::Models::InsightsCollection.new(notifications, body.fetch('total_count')) + end + end + + def toggle_snooze_user_notifications(notification_ids) + response = connection.put('/user_notifications/toggle_snooze', snooze_ids: notification_ids) + + handle_errors_and_respond(response) + end + + def user_plugins(filter, page, active, sort_by, sort_direction) + query_string = query_string_from_params( + value: filter, + page: page || '1', + active: active, + order: sort_by, + order_dir: sort_direction + ) + response = connection.get("/user_plugins?#{query_string}") + + handle_errors_and_respond(response) do |body| + plugins = body['data'].map do |plugin| + Travis::API::V3::Models::InsightsPlugin.new(plugin) + end + + Travis::API::V3::Models::InsightsCollection.new(plugins, body.fetch('total_count')) + end + end + + def create_plugin(params) + response = connection.post("/user_plugins", user_plugin: params) + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsPlugin.new(body['plugin']) + end + end + + def toggle_active_plugins(plugin_ids) + response = connection.put('/user_plugins/toggle_active', toggle_ids: plugin_ids) + + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsCollection.new([], 0) + end + end + + def delete_many_plugins(plugin_ids) + response = connection.delete('/user_plugins/delete_many', delete_ids: plugin_ids) + + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsCollection.new([], 0) + end + end + + def generate_key(plugin_name, plugin_type) + response = connection.get('/user_plugins/generate_key', name: plugin_name, plugin_type: plugin_type) + + handle_errors_and_respond(response) do |body| + body + end + end + + def authenticate_key(params) + response = connection.post('/user_plugins/authenticate_key', params) + + handle_errors_and_respond(response) do |body| + body + end + end + + def template_plugin_tests(plugin_type) + response = connection.get("/user_plugins/#{plugin_type}/template_plugin_tests") + + handle_errors_and_respond(response) do |body| + body + end + end + + def get_scan_logs(plugin_id, last_id) + params = last_id ? { last: last_id, poll: true } : {} + response = connection.get("/user_plugins/#{plugin_id}/get_scan_logs", params) + + handle_errors_and_respond(response) do |body| + body + end + end + + def probes(filter, page, active, sort_by, sort_direction) + query_string = query_string_from_params( + value: filter, + page: page || '1', + active: active, + order: sort_by, + order_dir: sort_direction + ) + response = connection.get("/probes?#{query_string}") + + handle_errors_and_respond(response) do |body| + probes = body['data'].map do |probe| + Travis::API::V3::Models::InsightsProbe.new(probe) + end + + Travis::API::V3::Models::InsightsCollection.new(probes, body.fetch('total_count')) + end + end + + def create_probe(params) + response = connection.post("/probes", test_template: params) + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsProbe.new(body) + end + end + + def update_probe(params) + response = connection.patch("/probes/#{params['probe_id']}", params) + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsProbe.new(body) + end + end + + def get_probe(params) + response = connection.get("/probes/#{params['probe_id']}/template_test", params) + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsProbe.new(body) + end + end + + def toggle_active_probes(probe_ids) + response = connection.put('/probes/toggle_active', toggle_ids: probe_ids) + + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsCollection.new([], 0) + end + end + + def delete_many_probes(probe_ids) + response = connection.delete('/probes/delete_many', delete_ids: probe_ids) + + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsCollection.new([], 0) + end + end + + def sandbox_plugins(plugin_type) + response = connection.post('/sandbox/plugins', plugin_type: plugin_type) + + handle_errors_and_respond(response) do |body| + body + end + end + + def sandbox_plugin_data(plugin_id) + response = connection.post('/sandbox/plugin_data', plugin_id: plugin_id) + + handle_errors_and_respond(response) do |body| + body + end + end + + def sandbox_run_query(plugin_id, query) + response = connection.post('/sandbox/run_query', plugin_id: plugin_id, query: query) + + handle_errors_and_respond(response) do |body| + body + end + end + + def public_key + response = connection.get('/api/v1/public_keys/latest.json') + + handle_errors_and_respond(response) do |body| + Travis::API::V3::Models::InsightsPublicKey.new(body) + end + end + + def search_tags + response = connection.get('/tags') + + handle_errors_and_respond(response) do |body| + tags = body.map do |tag| + Travis::API::V3::Models::InsightsTag.new(tag) + end + end + end + + private + + def handle_errors_and_respond(response) + case response.status + when 200, 201 + yield(response.body) if block_given? + when 202 + true + when 204 + true + when 400 + raise Travis::API::V3::ClientError, response.body.fetch('error', '') + when 403 + raise Travis::API::V3::InsufficientAccess, response.body['rejection_code'] + when 404 + raise Travis::API::V3::NotFound, response.body.fetch('error', '') + when 422 + raise Travis::API::V3::UnprocessableEntity, response.body.fetch('error', '') + else + raise Travis::API::V3::ServerError, 'Insights API failed' + end + end + + def connection(timeout: 20) + @connection ||= Faraday.new(url: insights_url, ssl: { ca_path: '/usr/lib/ssl/certs' }) do |conn| + conn.headers[:Authorization] = "Token token=\"#{insights_auth_token}\"" + conn.headers['X-Travis-User-Id'] = @user_id.to_s + conn.headers['Content-Type'] = 'application/json' + conn.request :json + conn.response :json + conn.options[:open_timeout] = timeout + conn.options[:timeout] = timeout + conn.use OpenCensus::Trace::Integrations::FaradayMiddleware if Travis::Api::App::Middleware::OpenCensus.enabled? + conn.adapter :net_http + end + end + + def insights_url + Travis.config.new_insights.insights_url || raise(ConfigurationError, 'No Insights API URL configured!') + end + + def insights_auth_token + Travis.config.new_insights.insights_auth_token || raise(ConfigurationError, 'No Insights Auth Token configured!') + end + + def query_string_from_params(params) + params.delete_if { |_, v| v.nil? || v.empty? }.to_query + end + end +end diff --git a/lib/travis/api/v3/models/insights_collection.rb b/lib/travis/api/v3/models/insights_collection.rb new file mode 100644 index 0000000000..4b4f0c03f7 --- /dev/null +++ b/lib/travis/api/v3/models/insights_collection.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Travis::API::V3 + class Models::InsightsCollection + def initialize(collection, total_count) + @collection = collection + @total_count = total_count + end + + def count(*) + @total_count + end + + def limit(*) + self + end + + def offset(*) + self + end + + def map + return @collection.map unless block_given? + + @collection.map { |x| yield x } + end + + def to_sql + "insights_query:#{Time.now.to_i}" + end + end +end diff --git a/lib/travis/api/v3/models/insights_notification.rb b/lib/travis/api/v3/models/insights_notification.rb new file mode 100644 index 0000000000..a619616965 --- /dev/null +++ b/lib/travis/api/v3/models/insights_notification.rb @@ -0,0 +1,19 @@ +module Travis::API::V3 + class Models::InsightsNotification + attr_reader :id, :type, :active, :weight, :message, :plugin_name, :plugin_type, :plugin_category, :probe_severity, :description, :description_link + + def initialize(attributes = {}) + @id = attributes.fetch('id') + @type = attributes.fetch('type') + @active = attributes.fetch('active') + @weight = attributes.fetch('weight') + @message = attributes.fetch('message') + @plugin_name = attributes.fetch('plugin_name') + @plugin_type = attributes.fetch('plugin_type') + @plugin_category = attributes.fetch('plugin_category') + @probe_severity = attributes.fetch('probe_severity') + @description = attributes.fetch('description', '') + @description_link = attributes.fetch('description_link', '') + end + end +end diff --git a/lib/travis/api/v3/models/insights_plugin.rb b/lib/travis/api/v3/models/insights_plugin.rb new file mode 100644 index 0000000000..30fa4b9204 --- /dev/null +++ b/lib/travis/api/v3/models/insights_plugin.rb @@ -0,0 +1,17 @@ +module Travis::API::V3 + class Models::InsightsPlugin + attr_reader :id, :name, :public_id, :plugin_type, :plugin_category, :last_scan_end, :scan_status, :plugin_status, :active + + def initialize(attributes = {}) + @id = attributes.fetch('id') + @name = attributes.fetch('name') + @public_id = attributes.fetch('public_id') + @plugin_type = attributes.fetch('plugin_type') + @plugin_category = attributes.fetch('plugin_category') + @last_scan_end = attributes.fetch('last_scan_end') + @scan_status = attributes.fetch('scan_status') + @plugin_status = attributes.fetch('plugin_status') + @active = attributes.fetch('active') + end + end +end diff --git a/lib/travis/api/v3/models/insights_probe.rb b/lib/travis/api/v3/models/insights_probe.rb new file mode 100644 index 0000000000..1bf47a8cfd --- /dev/null +++ b/lib/travis/api/v3/models/insights_probe.rb @@ -0,0 +1,37 @@ +module Travis::API::V3 + class Models::InsightsProbe + attr_reader :id, :user_id, :user_plugin_id, :test_template_id, :uuid, :uuid_group, :type, + :notification, :description, :description_link, :test, :base_object_locator, :preconditions, :conditionals, + :object_key_locator, :active, :editable, :template_type, :cruncher_type, :status, :labels, :plugin_type, + :plugin_type_name, :plugin_category, :tag_list, :severity + + def initialize(attributes = {}) + @id = attributes.fetch('id') + @user_id = attributes.fetch('user_id') + @user_plugin_id = attributes.fetch('user_plugin_id') + @test_template_id = attributes.fetch('test_template_id') + @uuid = attributes.fetch('uuid') + @uuid_group = attributes.fetch('uuid_group') + @type = attributes.fetch('type') + @notification = attributes.fetch('notification') + @description = attributes.fetch('description') + @description_link = attributes.fetch('description_link') + @test = attributes.fetch('test') + @base_object_locator = attributes.fetch('base_object_locator') + @preconditions = attributes.fetch('preconditions') + @conditionals = attributes.fetch('conditionals') + @object_key_locator = attributes.fetch('object_key_locator') + @active = attributes.fetch('active') + @editable = attributes.fetch('editable') + @template_type = attributes.fetch('template_type') + @cruncher_type = attributes.fetch('cruncher_type') + @status = attributes.fetch('status', '') + @labels = attributes.fetch('labels') + @plugin_type = attributes.fetch('plugin_type') + @plugin_type_name = attributes.fetch('plugin_type_name', '') + @plugin_category = attributes.fetch('plugin_category') + @tag_list = attributes.fetch('tag_list') + @severity = attributes.fetch('severity') + end + end +end diff --git a/lib/travis/api/v3/models/insights_public_key.rb b/lib/travis/api/v3/models/insights_public_key.rb new file mode 100644 index 0000000000..62fae05d3b --- /dev/null +++ b/lib/travis/api/v3/models/insights_public_key.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Models::InsightsPublicKey + attr_reader :key_hash, :key_body, :ordinal_value + + def initialize(attributes = {}) + @key_hash = attributes.fetch('key_hash') + @key_body = attributes.fetch('key_body') + @ordinal_value = attributes.fetch('ordinal_value') + end + end +end diff --git a/lib/travis/api/v3/models/insights_tag.rb b/lib/travis/api/v3/models/insights_tag.rb new file mode 100644 index 0000000000..2566dd42f5 --- /dev/null +++ b/lib/travis/api/v3/models/insights_tag.rb @@ -0,0 +1,9 @@ +module Travis::API::V3 + class Models::InsightsTag + attr_reader :name + + def initialize(attributes = {}) + @name = attributes.fetch('name') + end + end +end diff --git a/lib/travis/api/v3/models/user_preferences.rb b/lib/travis/api/v3/models/user_preferences.rb index 91878210d5..622c47a1d1 100644 --- a/lib/travis/api/v3/models/user_preferences.rb +++ b/lib/travis/api/v3/models/user_preferences.rb @@ -13,5 +13,15 @@ class Models::UserPreferences < Models::JsonSlice # repositories are always public) attribute :private_insights_visibility, String, default: 'private' validates :private_insights_visibility, inclusion: { in: %w{private public}, message: "'%{value}' is not allowed" } + + attribute :insights_scan_notifications, Boolean, default: true + + attribute :insights_time_zone, String, default: '' + + attribute :insights_date_format, String, default: 'DD/MM/YYYY' + validates :insights_date_format, inclusion: { in: %w{DD/MM/YYYY MM/DD/YYYY YYYY/MM/DD}, message: "'%{value}' is not allowed" } + + attribute :insights_time_format, String, default: 'HH:mm:ss' + validates :insights_time_format, inclusion: { in: ['h:mm:ss A', 'HH:mm:ss'], message: "'%{value}' is not allowed" } end end diff --git a/lib/travis/api/v3/queries/insights_notifications.rb b/lib/travis/api/v3/queries/insights_notifications.rb new file mode 100644 index 0000000000..1f5d39b57a --- /dev/null +++ b/lib/travis/api/v3/queries/insights_notifications.rb @@ -0,0 +1,25 @@ +module Travis::API::V3 + class Queries::InsightsNotifications < Query + params :filter, :page, :limit, :active, :sort_by, :sort_direction + + def all(user_id) + insights_client(user_id).user_notifications( + params['filter'], + params['page'], + params['active'], + params['sort_by'], + params['sort_direction'] + ) + end + + def toggle_snooze(user_id) + insights_client(user_id).toggle_snooze_user_notifications(params['notification_ids']) + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/queries/insights_plugins.rb b/lib/travis/api/v3/queries/insights_plugins.rb new file mode 100644 index 0000000000..228caa8cc5 --- /dev/null +++ b/lib/travis/api/v3/queries/insights_plugins.rb @@ -0,0 +1,50 @@ +module Travis::API::V3 + class Queries::InsightsPlugins < Query + params :filter, :page, :limit, :active, :sort_by, :sort_direction, :key_hash, :plugin_id, :name, :plugin_type, :public_id, + :private_key, :account_name, :app_key, :domain, :sub_plugin, :ids + + def all(user_id) + insights_client(user_id).user_plugins( + params['filter'], + params['page'], + params['active'], + params['sort_by'], + params['sort_direction'] + ) + end + + def create(user_id) + insights_client(user_id).create_plugin(params.slice(*%w(key_hash name plugin_type public_id private_key account_name app_key domain sub_plugin))) + end + + def toggle_active(user_id) + insights_client(user_id).toggle_active_plugins(params['ids']) + end + + def delete_many(user_id) + insights_client(user_id).delete_many_plugins(params['ids']) + end + + def generate_key(user_id) + insights_client(user_id).generate_key(params['plugin_name'], params['plugin_type']) + end + + def authenticate_key(user_id) + insights_client(user_id).authenticate_key(params) + end + + def template_plugin_tests(user_id) + insights_client(user_id).template_plugin_tests(params['plugin_type']) + end + + def get_scan_logs(user_id) + insights_client(user_id).get_scan_logs(params['plugin_id'], params['last_id']) + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/queries/insights_probes.rb b/lib/travis/api/v3/queries/insights_probes.rb new file mode 100644 index 0000000000..a8195280a5 --- /dev/null +++ b/lib/travis/api/v3/queries/insights_probes.rb @@ -0,0 +1,43 @@ +module Travis::API::V3 + class Queries::InsightsProbes < Query + params :filter, :page, :limit, :active, :sort_by, :sort_direction, :ids, + :test_template_id, :test, :plugin_type, + :notification, :description, :description_link, :type, :labels, :tag_list + + def all(user_id) + insights_client(user_id).probes( + params['filter'], + params['page'], + params['active'], + params['sort_by'], + params['sort_direction'] + ) + end + + def create(user_id) + insights_client(user_id).create_probe(params) + end + + def update(user_id) + insights_client(user_id).update_probe(params) + end + + def get(user_id) + insights_client(user_id).get_probe(params) + end + + def toggle_active(user_id) + insights_client(user_id).toggle_active_probes(params['ids']) + end + + def delete_many(user_id) + insights_client(user_id).delete_many_probes(params['ids']) + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/queries/insights_public_key.rb b/lib/travis/api/v3/queries/insights_public_key.rb new file mode 100644 index 0000000000..1c60fecb14 --- /dev/null +++ b/lib/travis/api/v3/queries/insights_public_key.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Queries::InsightsPublicKey < Query + def latest(user_id) + insights_client(user_id).public_key + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/queries/insights_sandbox.rb b/lib/travis/api/v3/queries/insights_sandbox.rb new file mode 100644 index 0000000000..3b9099a336 --- /dev/null +++ b/lib/travis/api/v3/queries/insights_sandbox.rb @@ -0,0 +1,23 @@ +module Travis::API::V3 + class Queries::InsightsSandbox < Query + params :plugin_type, :plugin_id, :query + + def plugins(user_id) + insights_client(user_id).sandbox_plugins(params['plugin_type']) + end + + def plugin_data(user_id) + insights_client(user_id).sandbox_plugin_data(params['plugin_id']) + end + + def run_query(user_id) + insights_client(user_id).sandbox_run_query(params['plugin_id'], params['query']) + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/queries/insights_tags.rb b/lib/travis/api/v3/queries/insights_tags.rb new file mode 100644 index 0000000000..0f885810e9 --- /dev/null +++ b/lib/travis/api/v3/queries/insights_tags.rb @@ -0,0 +1,13 @@ +module Travis::API::V3 + class Queries::InsightsTags < Query + def search_tags(user_id) + insights_client(user_id).search_tags + end + + private + + def insights_client(user_id) + @_insights_client ||= InsightsClient.new(user_id) + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_notification.rb b/lib/travis/api/v3/renderer/insights_notification.rb new file mode 100644 index 0000000000..27f2d6ba2c --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_notification.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsNotification < ModelRenderer + representation :standard, :id, :type, :active, :weight, :message, :plugin_name, :plugin_type, :plugin_category, :probe_severity, :description, :description_link + representation :minimal, :id, :type, :active, :weight, :probe_severity, :description, :description_link + end +end diff --git a/lib/travis/api/v3/renderer/insights_notifications.rb b/lib/travis/api/v3/renderer/insights_notifications.rb new file mode 100644 index 0000000000..ccd756a049 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_notifications.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsNotifications < CollectionRenderer + type :notifications + collection_key :notifications + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugin.rb b/lib/travis/api/v3/renderer/insights_plugin.rb new file mode 100644 index 0000000000..3a69f8eae0 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugin.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsPlugin < ModelRenderer + representation :standard, :id, :name, :public_id, :plugin_type, :plugin_category, :last_scan_end, :scan_status, :plugin_status, :active + representation :minimal, :id, :name, :public_id, :plugin_type, :last_scan_end, :scan_status, :plugin_status, :active + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugin_authenticate.rb b/lib/travis/api/v3/renderer/insights_plugin_authenticate.rb new file mode 100644 index 0000000000..bf65e8e7a2 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugin_authenticate.rb @@ -0,0 +1,19 @@ +module Travis::API::V3 + module Renderer::InsightsPluginAuthenticate + extend self + + AVAILABLE_ATTRIBUTES = [:success, :error_msg] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_plugin_authenticate'.freeze, + success: object['success'], + error_msg: object['error_msg'] + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugin_key.rb b/lib/travis/api/v3/renderer/insights_plugin_key.rb new file mode 100644 index 0000000000..40a7cbf0bf --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugin_key.rb @@ -0,0 +1,18 @@ +module Travis::API::V3 + module Renderer::InsightsPluginKey + extend self + + AVAILABLE_ATTRIBUTES = [:keys] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_plugin_key'.freeze, + keys: object['keys'] + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugin_scan_logs.rb b/lib/travis/api/v3/renderer/insights_plugin_scan_logs.rb new file mode 100644 index 0000000000..e8ed9a8914 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugin_scan_logs.rb @@ -0,0 +1,19 @@ +module Travis::API::V3 + module Renderer::InsightsPluginScanLogs + extend self + + AVAILABLE_ATTRIBUTES = [:meta, :scan_logs] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_plugin_scan_logs'.freeze, + meta: object.fetch('meta', {}), + scan_logs: object.fetch('scan_logs', []) + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugin_tests.rb b/lib/travis/api/v3/renderer/insights_plugin_tests.rb new file mode 100644 index 0000000000..d3b04ef9fd --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugin_tests.rb @@ -0,0 +1,19 @@ +module Travis::API::V3 + module Renderer::InsightsPluginTests + extend self + + AVAILABLE_ATTRIBUTES = [:template_tests, :plugin_category] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_plugin_tests'.freeze, + template_tests: object['template_tests'], + plugin_category: object['plugin_category'] + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_plugins.rb b/lib/travis/api/v3/renderer/insights_plugins.rb new file mode 100644 index 0000000000..38af87c850 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_plugins.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsPlugins < CollectionRenderer + type :plugins + collection_key :plugins + end +end diff --git a/lib/travis/api/v3/renderer/insights_probe.rb b/lib/travis/api/v3/renderer/insights_probe.rb new file mode 100644 index 0000000000..ac35db1c4e --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_probe.rb @@ -0,0 +1,9 @@ +module Travis::API::V3 + class Renderer::InsightsProbe < ModelRenderer + representation :standard, :id, :user_id, :user_plugin_id, :test_template_id, :uuid, :uuid_group, :type, + :notification, :description, :description_link, :test, :base_object_locator, :preconditions, :conditionals, + :object_key_locator, :active, :editable, :template_type, + :cruncher_type, :status, :labels, :plugin_type, :plugin_type_name, :plugin_category, :tag_list, :severity + representation :minimal, :id, :type, :plugin_type, :plugin_type_name, :plugin_category, :label, :notification, :status, :tag_list, :severity + end +end diff --git a/lib/travis/api/v3/renderer/insights_probes.rb b/lib/travis/api/v3/renderer/insights_probes.rb new file mode 100644 index 0000000000..950797fd37 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_probes.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsProbes < CollectionRenderer + type :tests + collection_key :tests + end +end diff --git a/lib/travis/api/v3/renderer/insights_public_key.rb b/lib/travis/api/v3/renderer/insights_public_key.rb new file mode 100644 index 0000000000..fc3cf695d5 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_public_key.rb @@ -0,0 +1,5 @@ +module Travis::API::V3 + class Renderer::InsightsPublicKey < ModelRenderer + representation(:standard, :key_hash, :key_body, :ordinal_value) + end +end diff --git a/lib/travis/api/v3/renderer/insights_sandbox_plugin_data.rb b/lib/travis/api/v3/renderer/insights_sandbox_plugin_data.rb new file mode 100644 index 0000000000..04ea4a9f1f --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_sandbox_plugin_data.rb @@ -0,0 +1,18 @@ +module Travis::API::V3 + module Renderer::InsightsSandboxPluginData + extend self + + AVAILABLE_ATTRIBUTES = [:data] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_sandbox_plugin_data'.freeze, + data: object + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_sandbox_plugins.rb b/lib/travis/api/v3/renderer/insights_sandbox_plugins.rb new file mode 100644 index 0000000000..75cfb8ce8a --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_sandbox_plugins.rb @@ -0,0 +1,20 @@ +module Travis::API::V3 + module Renderer::InsightsSandboxPlugins + extend self + + AVAILABLE_ATTRIBUTES = [:plugins, :in_progress, :no_plugins] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_sandbox_plugins'.freeze, + plugins: object.fetch('plugins', []), + in_progress: object.fetch('in_progress', false), + no_plugins: object.fetch('no_plugins', false) + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_sandbox_query_result.rb b/lib/travis/api/v3/renderer/insights_sandbox_query_result.rb new file mode 100644 index 0000000000..f8d6d2b258 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_sandbox_query_result.rb @@ -0,0 +1,20 @@ +module Travis::API::V3 + module Renderer::InsightsSandboxQueryResult + extend self + + AVAILABLE_ATTRIBUTES = [:negative_results, :positive_results, :success] + + def available_attributes + AVAILABLE_ATTRIBUTES + end + + def render(object, **) + { + '@type': 'insights_sandbox_query_result'.freeze, + negative_results: object['negative_results'], + positive_results: object['positive_results'], + success: object['success'] + } + end + end +end diff --git a/lib/travis/api/v3/renderer/insights_tag.rb b/lib/travis/api/v3/renderer/insights_tag.rb new file mode 100644 index 0000000000..9531035702 --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_tag.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsTag < ModelRenderer + representation :standard, :name + representation :minimal, :name + end +end diff --git a/lib/travis/api/v3/renderer/insights_tags.rb b/lib/travis/api/v3/renderer/insights_tags.rb new file mode 100644 index 0000000000..5489fd49ea --- /dev/null +++ b/lib/travis/api/v3/renderer/insights_tags.rb @@ -0,0 +1,6 @@ +module Travis::API::V3 + class Renderer::InsightsTags < CollectionRenderer + type :tags + collection_key :tags + end +end diff --git a/lib/travis/api/v3/routes.rb b/lib/travis/api/v3/routes.rb index 5ecd8407fa..71f531dc24 100644 --- a/lib/travis/api/v3/routes.rb +++ b/lib/travis/api/v3/routes.rb @@ -426,5 +426,58 @@ module Routes route '/leads' post :create end + + resource :insights_notifications do + route '/insights_notifications' + get :all + patch :toggle_snooze, '/toggle_snooze' + end + + resource :insights_plugin do + route '/insights_plugin/{plugin_id}' + get :get_scan_logs, '/get_scan_logs' + end + + resource :insights_plugins do + route '/insights_plugins' + get :all + get :generate_key, '/generate_key' + post :create + post :authenticate_key, '/authenticate_key' + patch :toggle_active, '/toggle_active' + delete :delete_many, '/delete_many' + get :template_plugin_tests, '/template_plugin_tests' + end + + resource :insights_probe do + route '/insights_probe/{probe_id}' + patch :update + get :get + end + + resource :insights_probes do + route '/insights_probes' + get :all + post :create + patch :toggle_active, '/toggle_active' + delete :delete_many, '/delete_many' + end + + resource :insights_sandbox do + route '/insights_sandbox' + post :plugins, '/plugins' + post :plugin_data, '/plugin_data' + post :run_query, '/run_query' + end + + resource :insights_public_key do + route '/insights_public_key' + get :latest + end + + resource :insights_tags do + route '/insights_tags' + get :search_tags + end end end diff --git a/lib/travis/api/v3/services.rb b/lib/travis/api/v3/services.rb index dc47f38fde..0a93bd8a90 100644 --- a/lib/travis/api/v3/services.rb +++ b/lib/travis/api/v3/services.rb @@ -30,6 +30,14 @@ module Services Executions = Module.new { extend Services } Gdpr = Module.new { extend Services } Insights = Module.new { extend Services } + InsightsNotifications = Module.new { extend Services } + InsightsPlugin = Module.new { extend Services } + InsightsPlugins = Module.new { extend Services } + InsightsProbe = Module.new { extend Services } + InsightsProbes = Module.new { extend Services } + InsightsPublicKey = Module.new { extend Services } + InsightsSandbox = Module.new { extend Services } + InsightsTags = Module.new { extend Services } Installation = Module.new { extend Services } Job = Module.new { extend Services } Jobs = Module.new { extend Services } diff --git a/lib/travis/api/v3/services/insights_notifications/all.rb b/lib/travis/api/v3/services/insights_notifications/all.rb new file mode 100644 index 0000000000..df4f0555ef --- /dev/null +++ b/lib/travis/api/v3/services/insights_notifications/all.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsNotifications::All < Service + params :filter, :page, :limit, :active, :sort_by, :sort_direction + paginate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_notifications).all(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_notifications/toggle_snooze.rb b/lib/travis/api/v3/services/insights_notifications/toggle_snooze.rb new file mode 100644 index 0000000000..6cce4e475c --- /dev/null +++ b/lib/travis/api/v3/services/insights_notifications/toggle_snooze.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsNotifications::ToggleSnooze < Service + params :notification_ids + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + query(:insights_notifications).toggle_snooze(access_control.user.id) + no_content + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugin/get_scan_logs.rb b/lib/travis/api/v3/services/insights_plugin/get_scan_logs.rb new file mode 100644 index 0000000000..fa50996b9e --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugin/get_scan_logs.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugin::GetScanLogs < Service + params :plugin_id, :last_id + result_type :insights_plugin_scan_logs + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).get_scan_logs(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/all.rb b/lib/travis/api/v3/services/insights_plugins/all.rb new file mode 100644 index 0000000000..26c98ff58e --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/all.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsPlugins::All < Service + params :filter, :page, :limit, :active, :sort_by, :sort_direction + result_type :insights_plugins + paginate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).all(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/authenticate_key.rb b/lib/travis/api/v3/services/insights_plugins/authenticate_key.rb new file mode 100644 index 0000000000..a88b1e9ae3 --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/authenticate_key.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugins::AuthenticateKey < Service + params :plugin_type, :public_id, :private_key, :app_key, :domain, :key_hash + result_type :insights_plugin_authenticate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).authenticate_key(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/create.rb b/lib/travis/api/v3/services/insights_plugins/create.rb new file mode 100644 index 0000000000..b2fe669589 --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/create.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugins::Create < Service + params :key_hash, :name, :plugin_type, :public_id, :private_key, :account_name, :app_key, :domain, :sub_plugin + result_type :insights_plugin + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).create(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/delete_many.rb b/lib/travis/api/v3/services/insights_plugins/delete_many.rb new file mode 100644 index 0000000000..1474847f9d --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/delete_many.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsPlugins::DeleteMany < Service + params :ids + result_type :insights_plugins + paginate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).delete_many(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/generate_key.rb b/lib/travis/api/v3/services/insights_plugins/generate_key.rb new file mode 100644 index 0000000000..6adcbd2e82 --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/generate_key.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugins::GenerateKey < Service + params :plugin_name, :plugin_type + result_type :insights_plugin_key + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).generate_key(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/template_plugin_tests.rb b/lib/travis/api/v3/services/insights_plugins/template_plugin_tests.rb new file mode 100644 index 0000000000..bcac097a46 --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/template_plugin_tests.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugins::TemplatePluginTests < Service + params :plugin_type, :public_id, :private_key, :app_key, :domain, :key_hash + result_type :insights_plugin_tests + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).template_plugin_tests(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_plugins/toggle_active.rb b/lib/travis/api/v3/services/insights_plugins/toggle_active.rb new file mode 100644 index 0000000000..c20beb4113 --- /dev/null +++ b/lib/travis/api/v3/services/insights_plugins/toggle_active.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsPlugins::ToggleActive < Service + params :ids + paginate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_plugins).toggle_active(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_probe/get.rb b/lib/travis/api/v3/services/insights_probe/get.rb new file mode 100644 index 0000000000..6766ba031f --- /dev/null +++ b/lib/travis/api/v3/services/insights_probe/get.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsProbe::Get < Service + params :test_template_id, :test, :plugin_type, + :notification, :description, :description_link, :type, :labels, :tag_list, :severity + result_type :insights_probe + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_probes).get(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_probe/update.rb b/lib/travis/api/v3/services/insights_probe/update.rb new file mode 100644 index 0000000000..44220dd973 --- /dev/null +++ b/lib/travis/api/v3/services/insights_probe/update.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsProbe::Update < Service + params :test_template_id, :test, :plugin_type, + :notification, :description, :description_link, :type, :labels, :tag_list, :severity + result_type :insights_probe + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_probes).update(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_probes/all.rb b/lib/travis/api/v3/services/insights_probes/all.rb new file mode 100644 index 0000000000..1b859be39b --- /dev/null +++ b/lib/travis/api/v3/services/insights_probes/all.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsProbes::All < Service + params :filter, :page, :limit, :active, :sort_by, :sort_direction + result_type :insights_probes + paginate + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_probes).all(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_probes/create.rb b/lib/travis/api/v3/services/insights_probes/create.rb new file mode 100644 index 0000000000..d3043f11ee --- /dev/null +++ b/lib/travis/api/v3/services/insights_probes/create.rb @@ -0,0 +1,12 @@ +module Travis::API::V3 + class Services::InsightsProbes::Create < Service + params :test_template_id, :test, :plugin_type, + :notification, :description, :description_link, :type, :labels, :tag_list, :severity + result_type :insights_probe + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_probes).create(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_probes/delete_many.rb b/lib/travis/api/v3/services/insights_probes/delete_many.rb new file mode 100644 index 0000000000..c21f648bfa --- /dev/null +++ b/lib/travis/api/v3/services/insights_probes/delete_many.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsProbes::DeleteMany < Service + params :ids + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + query(:insights_probes).delete_many(access_control.user.id) + no_content + end + end +end diff --git a/lib/travis/api/v3/services/insights_probes/toggle_active.rb b/lib/travis/api/v3/services/insights_probes/toggle_active.rb new file mode 100644 index 0000000000..4aff1d0d26 --- /dev/null +++ b/lib/travis/api/v3/services/insights_probes/toggle_active.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsProbes::ToggleActive < Service + params :ids + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + query(:insights_probes).toggle_active(access_control.user.id) + no_content + end + end +end diff --git a/lib/travis/api/v3/services/insights_public_key/latest.rb b/lib/travis/api/v3/services/insights_public_key/latest.rb new file mode 100644 index 0000000000..1235b7e52b --- /dev/null +++ b/lib/travis/api/v3/services/insights_public_key/latest.rb @@ -0,0 +1,8 @@ +module Travis::API::V3 + class Services::InsightsPublicKey::Latest < Service + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_public_key).latest(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_sandbox/plugin_data.rb b/lib/travis/api/v3/services/insights_sandbox/plugin_data.rb new file mode 100644 index 0000000000..778ef5b99a --- /dev/null +++ b/lib/travis/api/v3/services/insights_sandbox/plugin_data.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsSandbox::PluginData < Service + params :plugin_type + result_type :insights_sandbox_plugin_data + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_sandbox).plugin_data(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_sandbox/plugins.rb b/lib/travis/api/v3/services/insights_sandbox/plugins.rb new file mode 100644 index 0000000000..a25f408972 --- /dev/null +++ b/lib/travis/api/v3/services/insights_sandbox/plugins.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsSandbox::Plugins < Service + params :plugin_type + result_type :insights_sandbox_plugins + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_sandbox).plugins(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_sandbox/run_query.rb b/lib/travis/api/v3/services/insights_sandbox/run_query.rb new file mode 100644 index 0000000000..5300de58af --- /dev/null +++ b/lib/travis/api/v3/services/insights_sandbox/run_query.rb @@ -0,0 +1,11 @@ +module Travis::API::V3 + class Services::InsightsSandbox::RunQuery < Service + params :plugin_id, :query + result_type :insights_sandbox_query_result + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_sandbox).run_query(access_control.user.id) + end + end +end diff --git a/lib/travis/api/v3/services/insights_tags/search_tags.rb b/lib/travis/api/v3/services/insights_tags/search_tags.rb new file mode 100644 index 0000000000..dc93f860cc --- /dev/null +++ b/lib/travis/api/v3/services/insights_tags/search_tags.rb @@ -0,0 +1,10 @@ +module Travis::API::V3 + class Services::InsightsTags::SearchTags < Service + result_type :insights_tags + + def run! + raise LoginRequired unless access_control.full_access_or_logged_in? + result query(:insights_tags).search_tags(access_control.user.id) + end + end +end diff --git a/lib/travis/config/defaults.rb b/lib/travis/config/defaults.rb index caedc2019c..bf4f37a577 100644 --- a/lib/travis/config/defaults.rb +++ b/lib/travis/config/defaults.rb @@ -45,6 +45,7 @@ def fallback_logs_api_auth_token closeio: { key: 'key' }, gdpr: {}, insights: { endpoint: 'https://insights.travis-ci.dev/', auth_token: 'secret' }, + new_insights: { insights_url: 'http://insights:3000', insights_auth_token: '660e632d4f3bfc6398ba5f6376be986d9ab18d6ee3723cdea8fbfe80c224dad8' }, database: { adapter: 'postgresql', database: "travis_#{Travis.env}", encoding: 'unicode', min_messages: 'warning', variables: { statement_timeout: 10_000 } }, fallback_logs_api: { url: fallback_logs_api_auth_url, token: fallback_logs_api_auth_token }, logs_api: { url: logs_api_url, token: logs_api_auth_token }, @@ -88,7 +89,7 @@ def fallback_logs_api_auth_token force_authentication: false, yml: { url: 'https://yml.travis-ci.org', token: 'secret', auth_key: 'abc123' }, read_only: ENV['READ_ONLY'] || false, - vcs: {} + vcs: {} default :_access => [:key] diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index f384c29916..eaeadaae78 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -43,6 +43,7 @@ require 'support/shared_examples' require 'support/ssl_keys' require 'support/test_helpers' +require 'support/insights_spec_helper' module TestHelpers include Sinatra::TestHelpers @@ -80,6 +81,7 @@ def parsed_body c.include Support::AuthHelpers, auth_helpers: true c.include Support::BillingSpecHelper, billing_spec_helper: true c.include Support::GdprSpecHelper, gdpr_spec_helper: true + c.include Support::InsightsSpecHelper, insights_spec_helper: true # for auth tests against staging, how the hell does this work, if at all # c.filter_run mode: :private, repo: :private diff --git a/spec/support/insights_spec_helper.rb b/spec/support/insights_spec_helper.rb new file mode 100644 index 0000000000..6dffcf2724 --- /dev/null +++ b/spec/support/insights_spec_helper.rb @@ -0,0 +1,290 @@ +module Support + module InsightsSpecHelper + def stub_insights_request(method, path, query: '', auth_key:, user_id:) + url = URI(insights_url).tap do |url| + url.path = path + url.query = query + end.to_s + stub_request(method, url).with(headers: { 'X-Travis-User-Id' => user_id, 'Authorization' => "Token token=\"#{auth_key}\"" }) + end + + def insights_notifications_response + { + "data" => [ + { + "id"=>8, + "type"=>nil, + "active"=>true, + "weight"=>nil, + "message"=>"This is a test notification", + "plugin_name"=>"Travis Insights", + "plugin_type"=>"Travis Insights", + "plugin_category"=>"Monitoring", + "probe_severity"=>"high", + "description"=>"This is a test notification", + "description_link"=>nil + }, + { + "id"=>7, + "type"=>nil, + "active"=>true, + "weight"=>nil, + "message"=>"This is a test notification", + "plugin_name"=>"Travis Insights", + "plugin_type"=>"Travis Insights", + "plugin_category"=>"Monitoring", + "probe_severity"=>"high", + "description"=>"This is a test notification", + "description_link"=>nil + }, + ], + "total_count"=>2 + } + end + + def insights_probes_response + { + "data" => [ + { + "id"=>312, + "user_id"=>45, + "user_plugin_id"=>nil, + "test_template_id"=>312, + "uuid"=>"d0286ba6-ee08-4d87-9fb5-e8709fd9d2c3", + "uuid_group"=>"4bf1205a-e030-4da9-ad16-6d4ac2c654c3", + "type"=>"native", + "notification"=>"You need more plugins.", + "description"=>"Travis Insights description.", + "description_link"=>"link", + "test"=>"assert count($.Plugins) > 4", + "base_object_locator"=>nil, + "preconditions"=>nil, + "conditionals"=>nil, + "object_key_locator"=>nil, + "active"=>true, + "editable"=>false, + "template_type"=>"TestDefinitions::Sre::YouNeedMorePlugins", + "cruncher_type"=>"sreql", + "status"=>"Active", + "labels"=>{}, + "plugin_type"=>"sre", + "plugin_type_name"=>"Travis Insights", + "plugin_category"=>"Monitoring", + "tag_list"=> [ + { + "id"=>3, + "name"=>"TI", + "created_at"=>"2022-01-03T09:21:12.390Z", + "updated_at"=>"2022-01-03T09:21:12.390Z", + "taggings_count"=>1 + } + ], + "severity"=>"info" + }, + { + "id"=>313, + "user_id"=>45, + "user_plugin_id"=>nil, + "test_template_id"=>313, + "uuid"=>"f3abd5e4-8231-4afa-9b84-56bfa0264f34", + "uuid_group"=>"d968832e-1b04-4b05-b810-884d0fb5fdee", + "type"=>"native", + "notification"=>"You need some deployment pipeline plugins.", + "description"=>"Description", + "description_link"=>"", + "test"=>"assert count($.Plugins[@.plugin_category is \"deployment_pipeline\"]) > 0", + "base_object_locator"=>nil, + "preconditions"=>nil, + "conditionals"=>nil, + "object_key_locator"=>nil, + "active"=>true, + "editable"=>false, + "template_type"=>"TestDefinitions::Sre::YouNeedSomeDeploymentPipelinePlugins", + "cruncher_type"=>"sreql", + "status"=>"Active", + "labels"=>{}, + "plugin_type"=>"sre", + "plugin_type_name"=>"Travis Insights", + "plugin_category"=>"Monitoring", + "tag_list"=>[], + "severity"=>"high" + }, + ], + "total_count"=>2 + } + end + + def insights_create_probe_response(attributes={}) + { + "id"=>313, + "user_id"=>45, + "user_plugin_id"=>nil, + "test_template_id"=>313, + "uuid"=>"f3abd5e4-8231-4afa-9b84-56bfa0264f34", + "uuid_group"=>"d968832e-1b04-4b05-b810-884d0fb5fdee", + "type"=>"native", + "notification"=>"You need some deployment pipeline plugins.", + "description"=>"Description", + "description_link"=>"", + "test"=>"assert count($.Plugins[@.plugin_category is \"deployment_pipeline\"]) > 0", + "base_object_locator"=>nil, + "preconditions"=>nil, + "conditionals"=>nil, + "object_key_locator"=>nil, + "active"=>true, + "editable"=>false, + "template_type"=>"TestDefinitions::Sre::YouNeedSomeDeploymentPipelinePlugins", + "cruncher_type"=>"sreql", + "status"=>"Active", + "labels"=>{}, + "plugin_type"=>"sre", + "plugin_type_name"=>"Travis Insights", + "plugin_category"=>"Monitoring", + "tag_list"=>[], + "severity"=>"high" + }.deep_merge(attributes) + end + + def insights_plugins_response + { + "data"=> [ + { + "id"=>5, + "name"=>"KubePlugin", + "public_id"=>"TID6CD47CD6E26", + "plugin_type"=>"Kubernetes Cluster", + "plugin_category"=>"Monitoring", + "last_scan_end"=>nil, + "scan_status"=>"In Progress", + "plugin_status"=>"Active", + "active"=>true + }, + { + "id"=>3, + "name"=>"KubePlugin2", + "public_id"=>"TI74D0AACAC0BD", + "plugin_type"=>"Kubernetes Cluster", + "plugin_category"=>"Monitoring", + "last_scan_end"=>"2021-12-01 10:44:32", + "scan_status"=>"Success", + "plugin_status"=>"Active", + "active"=>true + } + ], + "total_count"=>2 + } + end + + def insights_create_plugin_response(attributes={}) + { + "plugin" => { + "id"=>3, + "name"=>"KubePlugin2", + "public_id"=>"TI74D0AACAC0BD", + "plugin_type"=>"Kubernetes Cluster", + "plugin_category"=>"Monitoring", + "last_scan_end"=>"2021-12-01 10:44:32", + "scan_status"=>"Success", + "plugin_status"=>"Active", + "active"=>true + }.deep_merge(attributes) + } + end + + def insights_public_key_response + { + "key_hash"=>"KEY_HASH", + "key_body"=>"PUBLIC_KEY", + "ordinal_value"=>1 + } + end + + def insights_authenticate_key_response + { + "success"=>true, + "error_msg"=>"" + } + end + + def insights_scan_log_response + { + "scan_logs"=>[ + { + "id"=>97396, + "user_plugin_id"=>255, + "test_template_id"=>nil, + "log_type"=>"notifications", + "text"=>"Scheduling scan", + "additional_text_type"=>nil, + "additional_text"=>nil, + "created_at"=>"2021-11-18 04:00:05" + }, + { + "id"=>97432, + "user_plugin_id"=>255, + "test_template_id"=>nil, + "log_type"=>"plugin", + "text"=>"Scan started at", + "additional_text_type"=>nil, + "additional_text"=>nil, + "created_at"=>"2021-11-18 04:00:07" + } + ], + "meta"=>{ + "scan_status_in_progress"=>true + } + } + end + + def insights_template_plugin_tests_response + { + "template_tests"=>[ + { + "id"=>3232, + "name"=>"This is a test probe" + }, + { + "id"=>3234, + "name"=>"This is a test probe 2" + } + ], + "plugin_category"=>"Monitoring" + } + end + + def insights_sandbox_plugins_response + { + "plugins"=>[ + { + "id"=>4, + "name"=>"Travis Insights", + "data"=>"{\n \"Plugins\": [\n {\n \"id\": 255,\n \"plugin_category\": \"monitoring\",\n \"plugin_type\": \"sre\",\n \"scan_logs\": [\n {\n \"additional_text\": null,\n \"additional_text_type\": null,\n \"created_at\": \"2021-11-18T04:00:05.072Z\",\n \"id\": 97396,\n \"log_type\": \"notifications\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Scheduling scan\",\n \"updated_at\": \"2021-11-18T04:00:05.072Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": null,\n \"additional_text_type\": null,\n \"created_at\": \"2021-11-18T04:00:07.010Z\",\n \"id\": 97432,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Scan started at\",\n \"updated_at\": \"2021-11-18T04:00:07.010Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": \"\",\n \"additional_text_type\": \"\",\n \"created_at\": \"2021-11-18T04:00:07.068Z\",\n \"id\": 97435,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Accessing APIs:\",\n \"updated_at\": \"2021-11-18T04:00:07.068Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": \"- User Plugins\",\n \"additional_text_type\": \"info\",\n \"created_at\": \"2021-11-18T04:00:07.148Z\",\n \"id\": 97438,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"\",\n \"updated_at\": \"2021-11-18T04:00:07.148Z\",\n \"user_plugin_id\": 255\n }\n ],\n \"user_id\": 28\n }\n ]\n}", + "ready"=>true + } + ] + } + end + + def insights_sandbox_plugin_data_response + "{\n \"Plugins\": [\n {\n \"id\": 255,\n \"plugin_category\": \"monitoring\",\n \"plugin_type\": \"sre\",\n \"scan_logs\": [\n {\n \"additional_text\": null,\n \"additional_text_type\": null,\n \"created_at\": \"2021-11-18T04:00:05.072Z\",\n \"id\": 97396,\n \"log_type\": \"notifications\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Scheduling scan\",\n \"updated_at\": \"2021-11-18T04:00:05.072Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": null,\n \"additional_text_type\": null,\n \"created_at\": \"2021-11-18T04:00:07.010Z\",\n \"id\": 97432,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Scan started at\",\n \"updated_at\": \"2021-11-18T04:00:07.010Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": \"\",\n \"additional_text_type\": \"\",\n \"created_at\": \"2021-11-18T04:00:07.068Z\",\n \"id\": 97435,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"Accessing APIs:\",\n \"updated_at\": \"2021-11-18T04:00:07.068Z\",\n \"user_plugin_id\": 255\n },\n {\n \"additional_text\": \"- User Plugins\",\n \"additional_text_type\": \"info\",\n \"created_at\": \"2021-11-18T04:00:07.148Z\",\n \"id\": 97438,\n \"log_type\": \"plugin\",\n \"tenant_id\": 39,\n \"test_template_id\": null,\n \"text\": \"\",\n \"updated_at\": \"2021-11-18T04:00:07.148Z\",\n \"user_plugin_id\": 255\n }\n ],\n \"user_id\": 28\n }\n ]\n}" + end + + def insights_sandbox_query_response + { + "negative_results"=>[ + false + ], + "positive_results"=>nil, + "success"=>true + } + end + + def insights_tags_response + [ + { 'name'=>'Tag1' }, + { 'name'=>'Tag2' }, + { 'name'=>'Tag3' } + ] + end + end +end diff --git a/spec/v3/insights_client_spec.rb b/spec/v3/insights_client_spec.rb new file mode 100644 index 0000000000..21f33c7285 --- /dev/null +++ b/spec/v3/insights_client_spec.rb @@ -0,0 +1,325 @@ +describe Travis::API::V3::InsightsClient, insights_spec_helper: true do + let(:insights) { described_class.new(user_id) } + let(:user_id) { rand(999) } + let(:insights_url) { 'https://new-insights.travis-ci.com/' } + let(:auth_key) { 'supersecret' } + + before do + Travis.config.new_insights.insights_url = insights_url + Travis.config.new_insights.insights_auth_token = auth_key + end + + describe '#user_notifications' do + let(:filter) { nil } + let(:page) { '1' } + let(:active) { '0' } + let(:sort_by) { 'probe_severity' } + let(:sort_direction) { 'desc' } + + subject { insights.user_notifications(filter, page, active, sort_by, sort_direction) } + + it 'requests user notifications with specified query' do + stub_insights_request(:get, '/user_notifications', query: "page=#{page}&active=#{active}&order=#{sort_by}&order_dir=#{sort_direction}", auth_key: auth_key, user_id: user_id) + .to_return(body: JSON.dump(insights_notifications_response)) + expect(subject).to be_a(Travis::API::V3::Models::InsightsCollection) + expect(subject.map { |e| e }.first).to be_a(Travis::API::V3::Models::InsightsNotification) + expect(subject.map { |e| e }.size).to eq(insights_notifications_response['data'].size) + expect(subject.count).to eq(insights_notifications_response['total_count']) + end + end + + describe '#toggle_snooze_user_notifications' do + let(:notification_ids) { [123, 345] } + subject { insights.toggle_snooze_user_notifications(notification_ids) } + + it 'requests the toggle of user notifications' do + stubbed_request = stub_insights_request(:put, '/user_notifications/toggle_snooze', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(snooze_ids: notification_ids)) + .to_return(status: 204) + + expect { subject }.to_not raise_error + expect(stubbed_request).to have_been_made + end + end + + describe '#user_plugins' do + let(:filter) { nil } + let(:page) { '1' } + let(:active) { '1' } + let(:sort_by) { 'name' } + let(:sort_direction) { 'desc' } + + subject { insights.user_plugins(filter, page, active, sort_by, sort_direction) } + + it 'requests user plugins with specified query' do + stub_insights_request(:get, '/user_plugins', query: "page=#{page}&active=#{active}&order=#{sort_by}&order_dir=#{sort_direction}", auth_key: auth_key, user_id: user_id) + .to_return(body: JSON.dump(insights_plugins_response)) + expect(subject).to be_a(Travis::API::V3::Models::InsightsCollection) + expect(subject.map { |e| e }.first).to be_a(Travis::API::V3::Models::InsightsPlugin) + expect(subject.map { |e| e }.size).to eq(insights_plugins_response['data'].size) + expect(subject.count).to eq(insights_plugins_response['total_count']) + end + end + + describe '#create_plugin' do + let(:name) { 'Test Plugin' } + let(:plugin_data) { { 'name' => name } } + subject { insights.create_plugin(plugin_data) } + + it 'requests the creation and returns the representation' do + stubbed_request = stub_insights_request(:post, '/user_plugins', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(user_plugin: plugin_data)) + .to_return(status: 201, body: JSON.dump(insights_create_plugin_response('name' => name))) + + expect(subject).to be_a(Travis::API::V3::Models::InsightsPlugin) + expect(subject.name).to eq(name) + expect(stubbed_request).to have_been_made + end + end + + describe '#toggle_active_plugins' do + let(:plugin_ids) { [123, 345] } + subject { insights.toggle_active_plugins(plugin_ids) } + + it 'requests the toggle of plugins' do + stubbed_request = stub_insights_request(:put, '/user_plugins/toggle_active', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(toggle_ids: plugin_ids)) + .to_return(status: 204) + + expect { subject }.to_not raise_error + expect(stubbed_request).to have_been_made + end + end + + describe '#delete_many_plugins' do + let(:plugin_ids) { [123, 345] } + subject { insights.delete_many_plugins(plugin_ids) } + + it 'requests the delete' do + stubbed_request = stub_insights_request(:delete, '/user_plugins/delete_many', query: { delete_ids: plugin_ids }.to_query, auth_key: auth_key, user_id: user_id) + .to_return(status: 204) + + expect { subject }.to_not raise_error + expect(stubbed_request).to have_been_made + end + end + + describe '#authenticate_key' do + let(:key_data) { { 'public_id' => 'id', 'private_key' => 'key' } } + subject { insights.authenticate_key(key_data) } + + it 'requests the creation and returns the representation' do + stubbed_request = stub_insights_request(:post, '/user_plugins/authenticate_key', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(key_data)) + .to_return(status: 201, body: JSON.dump(insights_authenticate_key_response)) + + expect(subject['success']).to be_truthy + expect(stubbed_request).to have_been_made + end + end + + describe '#template_plugin_tests' do + let(:plugin_type) { 'sre' } + subject { insights.template_plugin_tests(plugin_type) } + + it 'requests the template tests' do + stubbed_request = stub_insights_request(:get, "/user_plugins/#{plugin_type}/template_plugin_tests", auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_template_plugin_tests_response)) + + expect(subject['template_tests']).to eq(insights_template_plugin_tests_response['template_tests']) + expect(subject['plugin_category']).to eq(insights_template_plugin_tests_response['plugin_category']) + expect(stubbed_request).to have_been_made + end + end + + describe '#get_scan_logs' do + subject { insights.get_scan_logs(plugin_id, last_id) } + + context 'when last_id is not present' do + let(:plugin_id) { rand(999) } + let(:last_id) { nil } + + it 'requests scan logs for specified plugin' do + stub_insights_request(:get, "/user_plugins/#{plugin_id}/get_scan_logs", auth_key: auth_key, user_id: user_id) + .to_return(body: JSON.dump(insights_scan_log_response)) + + expect(subject['scan_logs']).to eq(insights_scan_log_response['scan_logs']) + end + end + + context 'when last_id is present' do + let(:plugin_id) { rand(999) } + let(:last_id) { rand(999) } + + it 'requests scan logs for specified plugin' do + stub_insights_request(:get, "/user_plugins/#{plugin_id}/get_scan_logs", query: "last=#{last_id}&poll=true", auth_key: auth_key, user_id: user_id) + .to_return(body: JSON.dump(insights_scan_log_response)) + + expect(subject['scan_logs']).to eq(insights_scan_log_response['scan_logs']) + end + end + end + + describe '#probes' do + let(:filter) { nil } + let(:page) { '1' } + let(:active) { '1' } + let(:sort_by) { 'name' } + let(:sort_direction) { 'desc' } + + subject { insights.probes(filter, page, active, sort_by, sort_direction) } + + it 'requests probes with specified query' do + stub_insights_request(:get, '/probes', query: "page=#{page}&active=#{active}&order=#{sort_by}&order_dir=#{sort_direction}", auth_key: auth_key, user_id: user_id) + .to_return(body: JSON.dump(insights_probes_response)) + expect(subject).to be_a(Travis::API::V3::Models::InsightsCollection) + expect(subject.map { |e| e }.first).to be_a(Travis::API::V3::Models::InsightsProbe) + expect(subject.map { |e| e }.size).to eq(insights_probes_response['data'].size) + expect(subject.count).to eq(insights_probes_response['total_count']) + end + end + + describe '#create_probe' do + let(:notification) { 'Test Probe' } + let(:probe_data) { { 'notification' => notification } } + subject { insights.create_probe(probe_data) } + + it 'requests the creation and returns the representation' do + stubbed_request = stub_insights_request(:post, '/probes', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(test_template: probe_data)) + .to_return(status: 201, body: JSON.dump(insights_create_probe_response('notification' => notification))) + + expect(subject).to be_a(Travis::API::V3::Models::InsightsProbe) + expect(subject.notification).to eq(notification) + expect(stubbed_request).to have_been_made + end + end + + describe '#update_probe' do + let(:probe_id) { 444 } + let(:notification) { 'Test Probe' } + let(:probe_data) { { 'probe_id' => probe_id, 'notification' => notification } } + subject { insights.update_probe(probe_data) } + + it 'requests the update' do + stubbed_request = stub_insights_request(:patch, "/probes/#{probe_id}", auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(probe_data)) + .to_return(status: 201, body: JSON.dump(insights_create_probe_response('id' => probe_id, 'notification' => notification))) + + expect(subject).to be_a(Travis::API::V3::Models::InsightsProbe) + expect(subject.id).to eq(probe_id) + expect(subject.notification).to eq(notification) + expect(stubbed_request).to have_been_made + end + end + + describe '#get_probe' do + let(:probe_id) { 444 } + let(:probe_data) { { 'probe_id' => probe_id } } + subject { insights.get_probe(probe_data) } + + it 'requests the probe returns the representation' do + stubbed_request = stub_insights_request(:get, "/probes/#{probe_id}/template_test", query: "probe_id=#{probe_id}", auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_create_probe_response('id' => probe_id))) + + expect(subject).to be_a(Travis::API::V3::Models::InsightsProbe) + expect(subject.id).to eq(probe_id) + expect(stubbed_request).to have_been_made + end + end + + describe '#toggle_active_probes' do + let(:probe_ids) { [123, 345] } + subject { insights.toggle_active_probes(probe_ids) } + + it 'requests the toggle of probes' do + stubbed_request = stub_insights_request(:put, '/probes/toggle_active', auth_key: auth_key, user_id: user_id) + .with(body: JSON.dump(toggle_ids: probe_ids)) + .to_return(status: 204) + + expect { subject }.to_not raise_error + expect(stubbed_request).to have_been_made + end + end + + describe '#delete_many_probes' do + let(:probe_ids) { [123, 345] } + subject { insights.delete_many_probes(probe_ids) } + + it 'requests the delete' do + stubbed_request = stub_insights_request(:delete, '/probes/delete_many', query: { delete_ids: probe_ids }.to_query, auth_key: auth_key, user_id: user_id) + .to_return(status: 204) + + expect { subject }.to_not raise_error + expect(stubbed_request).to have_been_made + end + end + + describe '#sandbox_plugins' do + let(:plugin_type) { 'sre' } + subject { insights.sandbox_plugins(plugin_type) } + + it 'requests the sandbox plugins and returns the representation' do + stubbed_request = stub_insights_request(:post, '/sandbox/plugins', auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_sandbox_plugins_response)) + + expect(subject['name']).to eq(insights_sandbox_plugins_response['name']) + expect(subject['data']).to eq(insights_sandbox_plugins_response['data']) + expect(stubbed_request).to have_been_made + end + end + + describe '#sandbox_plugin_data' do + let(:plugin_id) { 333 } + subject { insights.sandbox_plugin_data(plugin_id) } + + it 'requests the plugin sandbox data and returns the representation' do + stubbed_request = stub_insights_request(:post, '/sandbox/plugin_data', auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: insights_sandbox_plugin_data_response) + + expect(subject).to eq(JSON.parse(insights_sandbox_plugin_data_response)) + expect(stubbed_request).to have_been_made + end + end + + describe '#sandbox_run_query' do + let(:plugin_id) { 333 } + let(:query) { 'assert count($.Plugins) > 4' } + subject { insights.sandbox_run_query(plugin_id, query) } + + it 'requests the result of query and returns the representation' do + stubbed_request = stub_insights_request(:post, '/sandbox/run_query', auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_sandbox_query_response)) + + expect(subject['positive_results']).to eq(insights_sandbox_query_response['positive_results']) + expect(subject['negative_results']).to eq(insights_sandbox_query_response['negative_results']) + expect(stubbed_request).to have_been_made + end + end + + describe '#public_key' do + subject { insights.public_key } + + it 'requests the public key' do + stubbed_request = stub_insights_request(:get, '/api/v1/public_keys/latest.json', auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_public_key_response)) + + expect(subject).to be_a(Travis::API::V3::Models::InsightsPublicKey) + expect(subject.key_hash).to eq(insights_public_key_response['key_hash']) + expect(stubbed_request).to have_been_made + end + end + + describe '#search_tags' do + subject { insights.search_tags } + + it 'requests available tags' do + stubbed_request = stub_insights_request(:get, '/tags', auth_key: auth_key, user_id: user_id) + .to_return(status: 201, body: JSON.dump(insights_tags_response)) + + expect(subject.first).to be_a(Travis::API::V3::Models::InsightsTag) + expect(subject.first.name).to eq(insights_tags_response[0]['name']) + expect(stubbed_request).to have_been_made + end + end +end \ No newline at end of file diff --git a/spec/v3/services/preferences/for_user_spec.rb b/spec/v3/services/preferences/for_user_spec.rb index c0b57f55e4..6d791de014 100644 --- a/spec/v3/services/preferences/for_user_spec.rb +++ b/spec/v3/services/preferences/for_user_spec.rb @@ -38,6 +38,30 @@ "@representation" => "standard", "name" => "private_insights_visibility", "value" => "private" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_scan_notifications", + "@representation" => "standard", + "name" => "insights_scan_notifications", + "value" => true + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_time_zone", + "@representation" => "standard", + "name" => "insights_time_zone", + "value" => "" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_date_format", + "@representation" => "standard", + "name" => "insights_date_format", + "value" => "DD/MM/YYYY" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_time_format", + "@representation" => "standard", + "name" => "insights_time_format", + "value" => "HH:mm:ss" } ] ) @@ -49,6 +73,10 @@ user.preferences.update(:build_emails, false) user.preferences.update(:consume_oss_credits, false) user.preferences.update(:private_insights_visibility, 'public') + user.preferences.update(:insights_scan_notifications, false) + user.preferences.update(:insights_time_zone, '(GMT-12:00) Etc/GMT+12') + user.preferences.update(:insights_date_format, 'YYYY/MM/DD') + user.preferences.update(:insights_time_format, 'h:mm:ss A') get("/v3/preferences", {}, auth_headers) end @@ -78,6 +106,30 @@ "@representation" => "standard", "name" => "private_insights_visibility", "value" => "public" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_scan_notifications", + "@representation" => "standard", + "name" => "insights_scan_notifications", + "value" => false + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_time_zone", + "@representation" => "standard", + "name" => "insights_time_zone", + "value" => "(GMT-12:00) Etc/GMT+12" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_date_format", + "@representation" => "standard", + "name" => "insights_date_format", + "value" => "YYYY/MM/DD" + }, { + "@type" => "preference", + "@href" => "/v3/preference/insights_time_format", + "@representation" => "standard", + "name" => "insights_time_format", + "value" => "h:mm:ss A" } ] )