From 004a1a3ece644922a5f1b01c956e4f2dd494c47c Mon Sep 17 00:00:00 2001 From: Bogdanov Anton Date: Wed, 10 Jul 2024 23:59:02 +0300 Subject: [PATCH] integrate panko serializers, change APi for using panko serializers --- Gemfile | 2 + Gemfile.lock | 7 ++ .../api/frontend/insights_controller.rb | 33 ++++---- .../repository_insights_controller.rb | 36 +++++---- .../api/v1/companies_controller.rb | 13 +-- app/controllers/api/v1/insights_controller.rb | 30 ++++--- app/controllers/api/v1_controller.rb | 18 ++--- app/javascript/components/Company/Company.jsx | 8 +- .../Company/requests/insightsRequest.jsx | 5 +- .../components/Repository/Repository.jsx | 11 +-- .../Repository/requests/insightsRequest.jsx | 5 +- .../requests/repositoryInsightsRequest.jsx | 2 +- app/serializers/company_serializer.rb | 11 +-- app/serializers/helpers/insight.rb | 4 +- app/serializers/insight_serializer.rb | 18 +++-- .../panko_application_serializer.rb | 4 + app/services/reports/insights/pdf.rb | 14 ++-- .../api/v1/companies_controller_spec.rb | 6 +- .../api/v1/insights_controller_spec.rb | 8 +- spec/requests/api/v1/companies_spec.rb | 44 ++++------- spec/requests/api/v1/repositories_spec.rb | 21 ++--- swagger/v1/swagger.yaml | 79 +++++++------------ 22 files changed, 175 insertions(+), 204 deletions(-) create mode 100644 app/serializers/panko_application_serializer.rb diff --git a/Gemfile b/Gemfile index 72eebdea..83f39848 100644 --- a/Gemfile +++ b/Gemfile @@ -90,6 +90,8 @@ gem 'emailbutler' # api serializer gem 'jsonapi-serializer', '2.2.0' +gem 'oj' +gem 'panko_serializer' # notification layer gem 'active_delivery' diff --git a/Gemfile.lock b/Gemfile.lock index def15378..c2edce24 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -277,7 +277,12 @@ GEM racc (~> 1.4) nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) + oj (3.16.4) + bigdecimal (>= 3.0) pagy (8.6.3) + panko_serializer (0.8.1) + activesupport + oj (> 3.11.0, < 4.0.0) parallel (1.25.1) parser (3.3.3.0) ast (~> 2.4.1) @@ -527,7 +532,9 @@ DEPENDENCIES jwt (~> 2.5) kudos letter_opener + oj pagy (~> 8.0) + panko_serializer pg (~> 1.4) pghero prawn diff --git a/app/controllers/api/frontend/insights_controller.rb b/app/controllers/api/frontend/insights_controller.rb index 5a774109..508fbcaf 100644 --- a/app/controllers/api/frontend/insights_controller.rb +++ b/app/controllers/api/frontend/insights_controller.rb @@ -7,7 +7,7 @@ class InsightsController < Api::Frontend::BaseController def index respond_to do |format| - format.json { render json: json_response, status: :ok } + format.json { render json: Panko::Response.new(json_response), status: :ok } format.pdf { send_data pdf_response, type: 'application/pdf', filename: 'insights.pdf' } end end @@ -16,9 +16,10 @@ def index def json_response { - insights: insights_json, + insights: insights, + insight_fields: insight_fields, ratio_type: ratio_enabled? ? ratio_type : nil - }.compact + } end def pdf_response @@ -26,23 +27,23 @@ def pdf_response page_size: Reports::Insights::Pdf::PAGE_SIZE, page_layout: Reports::Insights::Pdf::PAGE_LAYOUT ).to_pdf( - insights: insights_json, + insights: JSON.parse(insights.to_json), insight_fields: insight_fields ) end - def insights_json - InsightSerializer.new( - actual_insights, - { - params: { - previous_insights: previous_insights, - insight_fields: insight_fields, - ratio_enabled: ratio_enabled?, - ratio_type: ratio_type - } - } - ).serializable_hash + def insights + Panko::ArraySerializer.new( + actual_insights, + each_serializer: InsightSerializer, + only: %i[id values entity], + context: { + previous_insights: previous_insights, + insight_fields: insight_fields, + ratio_enabled: ratio_enabled?, + ratio_type: ratio_type + } + ) end def find_insightable diff --git a/app/controllers/api/frontend/repositories/repository_insights_controller.rb b/app/controllers/api/frontend/repositories/repository_insights_controller.rb index d3c4e867..423c33e0 100644 --- a/app/controllers/api/frontend/repositories/repository_insights_controller.rb +++ b/app/controllers/api/frontend/repositories/repository_insights_controller.rb @@ -7,25 +7,29 @@ class RepositoryInsightsController < Api::Frontend::BaseController before_action :find_repository def index - render json: { - insights: InsightSerializer.new( - actual_insights, - { - params: { - previous_insights: previous_insights, - insight_fields: ::Repositories::Insight::DEFAULT_ATTRIBUTES, - ratio_enabled: ratio_enabled?, - ratio_type: ratio_type, - no_entity: true - } - } - ).serializable_hash, - ratio_type: ratio_enabled? ? ratio_type : nil - }.compact, status: :ok + render json: Panko::Response.create { |response| json_response(response) }, status: :ok end private + def json_response(response) + { + insight: response.serializer( + actual_insight, + InsightSerializer, + only: %i[id values entity], + context: { + previous_insights: previous_insights, + insight_fields: ::Repositories::Insight::DEFAULT_ATTRIBUTES, + ratio_enabled: ratio_enabled?, + ratio_type: ratio_type, + no_entity: true + } + ), + ratio_type: ratio_enabled? ? ratio_type : nil + } + end + def find_repository @repository = authorized_scope(Repository.order(id: :desc)).find_by!(uuid: params[:repository_id]) end @@ -34,7 +38,7 @@ def visible_insights @visible_insights ||= @repository.repository_insights.to_a end - def actual_insights + def actual_insight visible_insights.find(&:actual?) end diff --git a/app/controllers/api/v1/companies_controller.rb b/app/controllers/api/v1/companies_controller.rb index 05169fde..50c6c4ad 100644 --- a/app/controllers/api/v1/companies_controller.rb +++ b/app/controllers/api/v1/companies_controller.rb @@ -6,16 +6,19 @@ class CompaniesController < Api::V1Controller SERIALIZER_FIELDS = %w[title repositories_count accessable].freeze def index - render json: { companies: companies }, status: :ok + render json: Panko::Response.new( + companies: Panko::ArraySerializer.new( + companies, + each_serializer: CompanySerializer, + **serializer_fields(CompanySerializer, SERIALIZER_FIELDS) + ) + ) end private def companies - CompanySerializer.new( - current_user.available_companies.order(id: :desc), - params: serializer_fields(CompanySerializer, SERIALIZER_FIELDS) - ).serializable_hash + current_user.available_companies.order(id: :desc) end end end diff --git a/app/controllers/api/v1/insights_controller.rb b/app/controllers/api/v1/insights_controller.rb index 1e482755..4f29e98e 100644 --- a/app/controllers/api/v1/insights_controller.rb +++ b/app/controllers/api/v1/insights_controller.rb @@ -6,24 +6,28 @@ class InsightsController < Api::V1Controller before_action :find_insightable def index - render json: { - insights: InsightSerializer.new( + render json: Panko::Response.new(json_response), status: :ok + end + + private + + def json_response + { + insights: Panko::ArraySerializer.new( actual_insights, - { - params: { - previous_insights: previous_insights, - insight_fields: insight_fields, - ratio_enabled: ratio_enabled?, - ratio_type: ratio_type - } + each_serializer: InsightSerializer, + only: %i[id values entity], + context: { + previous_insights: previous_insights, + insight_fields: insight_fields, + ratio_enabled: ratio_enabled?, + ratio_type: ratio_type } - ).serializable_hash, + ), ratio_type: ratio_enabled? ? ratio_type : nil - }.compact, status: :ok + } end - private - def find_insightable find_company if params[:company_id] find_repository if params[:repository_id] diff --git a/app/controllers/api/v1_controller.rb b/app/controllers/api/v1_controller.rb index 78a43991..4e6d753a 100644 --- a/app/controllers/api/v1_controller.rb +++ b/app/controllers/api/v1_controller.rb @@ -18,20 +18,20 @@ def set_current_user end def serializer_fields(serializer_class, default_include_fields=[], forbidden_fields=[]) - @serializer_attributes = serializer_class.attributes_to_serialize.keys.map(&:to_s) - return {} if response_include_fields.any? && response_exclude_fields.any? - return { include_fields: response_include_fields - forbidden_fields } if response_include_fields.any? - return { exclude_fields: response_exclude_fields + forbidden_fields } if response_exclude_fields.any? + @serializer_attributes = serializer_class::ATTRIBUTES + return {} if response_only_fields.any? && response_except_fields.any? + return { only: (response_only_fields - forbidden_fields).map(&:to_sym) } if response_only_fields.any? + return { except: (response_except_fields + forbidden_fields).map(&:to_sym) } if response_except_fields.any? - { include_fields: default_include_fields } + { only: default_include_fields.map(&:to_sym) } end - def response_include_fields - @response_include_fields ||= params[:response_include_fields]&.split(',').to_a & @serializer_attributes + def response_only_fields + @response_only_fields ||= params[:response_only_fields]&.split(',').to_a & @serializer_attributes end - def response_exclude_fields - @response_exclude_fields ||= params[:response_exclude_fields]&.split(',').to_a & @serializer_attributes + def response_except_fields + @response_except_fields ||= params[:response_except_fields]&.split(',').to_a & @serializer_attributes end def authentication_error diff --git a/app/javascript/components/Company/Company.jsx b/app/javascript/components/Company/Company.jsx index 9d580105..d6630fcd 100644 --- a/app/javascript/components/Company/Company.jsx +++ b/app/javascript/components/Company/Company.jsx @@ -30,13 +30,11 @@ export const Company = ({ const fetchInsights = async () => await insightsRequest(uuid); Promise.all([fetchInsights()]).then(([insightsData]) => { - const insightTypes = insightsData.data.length > 0 ? Object.keys(insightsData.data[0].values) : []; - const ratioType = insightsData.ratioType || null; setPageState({ ...pageState, - entities: insightsData.data, - insightTypes: insightTypes, - ratioType: ratioType, + entities: insightsData.insights, + insightTypes: insightsData.insight_fields, + ratioType: insightsData.ratio_type, }); }); }, [pageState, uuid]); diff --git a/app/javascript/components/Company/requests/insightsRequest.jsx b/app/javascript/components/Company/requests/insightsRequest.jsx index 32ad2b9e..aa77f8fe 100644 --- a/app/javascript/components/Company/requests/insightsRequest.jsx +++ b/app/javascript/components/Company/requests/insightsRequest.jsx @@ -4,8 +4,5 @@ export const insightsRequest = async (companyUuid) => { const result = await apiRequest({ url: `/api/frontend/companies/${companyUuid}/insights.json`, }); - return { - data: result.insights.data.map((element) => element.attributes), - ratioType: result.insights.ratio_type - }; + return result; }; diff --git a/app/javascript/components/Repository/Repository.jsx b/app/javascript/components/Repository/Repository.jsx index 440f55a2..35e70870 100644 --- a/app/javascript/components/Repository/Repository.jsx +++ b/app/javascript/components/Repository/Repository.jsx @@ -38,15 +38,12 @@ export const Repository = ({ Promise.all([fetchInsights(), fetchRepositoryInsights()]).then( ([insightsData, repositoryInsightsData]) => { - const insightTypes = insightsData.data.length > 0 ? Object.keys(insightsData.data[0].values) : []; - const ratioType = insightsData.ratioType || null; setPageState({ ...pageState, - entities: insightsData.data, - insights: - Object.keys(repositoryInsightsData).length === 0 ? undefined : repositoryInsightsData, - insightTypes: insightTypes, - ratioType: ratioType, + entities: insightsData.insights, + insights: repositoryInsightsData.insight.values, + insightTypes: insightsData.insight_fields, + ratioType: insightsData.ratio_type, }); }, ); diff --git a/app/javascript/components/Repository/requests/insightsRequest.jsx b/app/javascript/components/Repository/requests/insightsRequest.jsx index 57ee7a79..182c2f72 100644 --- a/app/javascript/components/Repository/requests/insightsRequest.jsx +++ b/app/javascript/components/Repository/requests/insightsRequest.jsx @@ -4,8 +4,5 @@ export const insightsRequest = async (repositoryUuid) => { const result = await apiRequest({ url: `/api/frontend/repositories/${repositoryUuid}/insights.json`, }); - return { - data: result.insights.data.map((element) => element.attributes), - ratioType: result.insights.ratio_type - }; + return result; }; diff --git a/app/javascript/components/Repository/requests/repositoryInsightsRequest.jsx b/app/javascript/components/Repository/requests/repositoryInsightsRequest.jsx index 4ba013cf..bac47dc0 100644 --- a/app/javascript/components/Repository/requests/repositoryInsightsRequest.jsx +++ b/app/javascript/components/Repository/requests/repositoryInsightsRequest.jsx @@ -4,5 +4,5 @@ export const repositoryInsightsRequest = async (repositoryUuid) => { const result = await apiRequest({ url: `/api/frontend/repositories/${repositoryUuid}/repository_insights.json`, }); - return result.insights.data?.attributes?.values || {}; + return result; }; diff --git a/app/serializers/company_serializer.rb b/app/serializers/company_serializer.rb index f2a5498f..3fb7cb53 100644 --- a/app/serializers/company_serializer.rb +++ b/app/serializers/company_serializer.rb @@ -1,12 +1,7 @@ # frozen_string_literal: true -class CompanySerializer < ApplicationSerializer - set_id :uuid +class CompanySerializer < PankoApplicationSerializer + ATTRIBUTES = %w[uuid title repositories_count accessable].freeze - attribute :uuid, if: proc { |_, params| required_field?(params, 'uuid') }, &:uuid - attribute :title, if: proc { |_, params| required_field?(params, 'title') }, &:title - # rubocop: disable Layout/LineLength - attribute :repositories_count, if: proc { |_, params| required_field?(params, 'repositories_count') }, &:repositories_count - # rubocop: enable Layout/LineLength - attribute :accessable, if: proc { |_, params| required_field?(params, 'accessable') }, &:accessable + attributes :uuid, :title, :repositories_count, :accessable end diff --git a/app/serializers/helpers/insight.rb b/app/serializers/helpers/insight.rb index fcb6012a..7fcb97a2 100644 --- a/app/serializers/helpers/insight.rb +++ b/app/serializers/helpers/insight.rb @@ -25,9 +25,9 @@ def ratio_value(insight, previous_insight, attribute) def change_value(insight, previous_insight, insight_field) method_name = ::Insight::DECIMAL_ATTRIBUTES.include?(insight_field) ? :to_f : :to_i - return insight[attribute].send(method_name) if previous_insight.nil? + return insight[insight_field].send(method_name) if previous_insight.nil? - insight[attribute].send(method_name) - previous_insight[insight_field].send(method_name) + insight[insight_field].send(method_name) - previous_insight[insight_field].send(method_name) end end end diff --git a/app/serializers/insight_serializer.rb b/app/serializers/insight_serializer.rb index 5475c7b4..eb30e3d8 100644 --- a/app/serializers/insight_serializer.rb +++ b/app/serializers/insight_serializer.rb @@ -1,19 +1,23 @@ # frozen_string_literal: true -class InsightSerializer < ApplicationSerializer - extend Helpers::Insight +class InsightSerializer < PankoApplicationSerializer + include Helpers::Insight - attribute :values do |object, params| - params[:insight_fields].index_with do |insight_field| + attributes :id, :values, :entity + + def id = SecureRandom.hex + + def values + context[:insight_fields].index_with do |insight_field| value = object[insight_field] { value: Insight::DECIMAL_ATTRIBUTES.include?(insight_field.to_sym) ? value.to_f : value, - ratio_value: params[:ratio_enabled] ? compare_with_previous_period(object, insight_field, params) : nil + ratio_value: context[:ratio_enabled] ? compare_with_previous_period(object, insight_field, context) : nil }.compact end end - attribute :entity do |object, params| - params[:no_entity] ? nil : (Rails.cache.read("entity_payload_#{object.entity_id}_v1") || Entity::EMPTY_PAYLOAD) + def entity + context[:no_entity] ? nil : (Rails.cache.read("entity_payload_#{object.entity_id}_v1") || Entity::EMPTY_PAYLOAD) end end diff --git a/app/serializers/panko_application_serializer.rb b/app/serializers/panko_application_serializer.rb new file mode 100644 index 00000000..2df959f7 --- /dev/null +++ b/app/serializers/panko_application_serializer.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +class PankoApplicationSerializer < Panko::Serializer +end diff --git a/app/services/reports/insights/pdf.rb b/app/services/reports/insights/pdf.rb index 2fec541d..7a64fb43 100644 --- a/app/services/reports/insights/pdf.rb +++ b/app/services/reports/insights/pdf.rb @@ -3,7 +3,7 @@ module Reports module Insights class Pdf < Prawn::Document - PAGE_SIZE = 'A4'.freeze + PAGE_SIZE = 'A4' PAGE_LAYOUT = :landscape def to_pdf(insights:, insight_fields:) @@ -14,18 +14,18 @@ def to_pdf(insights:, insight_fields:) data = [] data << ['', 'Developer', *headers_data(insight_fields)] - insights[:data].each do |insight| + insights.each do |insight| data << [ - { image: URI.open(insight.dig(:attributes, :entity, :avatar_url)), image_height: 12, image_width: 12 }, - insight.dig(:attributes, :entity, :login), - *insight_data(insight.dig(:attributes, :values), insight_fields) + { image: URI.open(insight.dig('entity', 'avatar_url')), image_height: 12, image_width: 12 }, + insight.dig('entity', 'login'), + *insight_data(insight['values'].symbolize_keys, insight_fields) ] end table( data, header: true, - row_colors: ['ffffff', 'f3f4f6'], + row_colors: %w[ffffff f3f4f6], cell_style: { border_width: 1, border_color: 'e5e7eb' @@ -42,7 +42,7 @@ def headers_data(insight_fields) end def insight_data(values, insight_fields) - insight_fields.map { |insight_field| values.dig(insight_field, :value) } + insight_fields.map { |insight_field| values.dig(insight_field, 'value') } end end end diff --git a/spec/controllers/api/v1/companies_controller_spec.rb b/spec/controllers/api/v1/companies_controller_spec.rb index 35638183..6ed08175 100644 --- a/spec/controllers/api/v1/companies_controller_spec.rb +++ b/spec/controllers/api/v1/companies_controller_spec.rb @@ -16,13 +16,13 @@ it 'returns companies data', :aggregate_failures do get :index, params: { - api_access_token: api_access_token.value, response_include_fields: 'uuid,title' + api_access_token: api_access_token.value, response_only_fields: 'uuid,title' } expect(response).to have_http_status :ok - expect(response.parsed_body.dig('companies', 'data').size).to eq 1 + expect(response.parsed_body['companies'].size).to eq 1 - attributes = response.parsed_body.dig('companies', 'data', 0, 'attributes') + attributes = response.parsed_body.dig('companies', 0) expect(attributes['uuid']).not_to be_nil expect(attributes['title']).not_to be_nil expect(attributes['repositories_count']).to be_nil diff --git a/spec/controllers/api/v1/insights_controller_spec.rb b/spec/controllers/api/v1/insights_controller_spec.rb index 68d87306..7cc04325 100644 --- a/spec/controllers/api/v1/insights_controller_spec.rb +++ b/spec/controllers/api/v1/insights_controller_spec.rb @@ -24,7 +24,7 @@ it 'returns data', :aggregate_failures do get :index, params: { company_id: company.uuid, api_access_token: api_access_token.value } - response_values = response.parsed_body.dig('insights', 'data', 0, 'attributes', 'values') + response_values = response.parsed_body.dig('insights', 0, 'values') expect(response).to have_http_status :ok expect(response_values.keys).to eq Insight::DEFAULT_ATTRIBUTES.map(&:to_s) @@ -40,7 +40,7 @@ it 'returns data', :aggregate_failures do get :index, params: { company_id: company.uuid, api_access_token: api_access_token.value } - response_values = response.parsed_body.dig('insights', 'data', 0, 'attributes', 'values') + response_values = response.parsed_body.dig('insights', 0, 'values') expect(response).to have_http_status :ok expect(response_values.keys).to contain_exactly('comments_count', 'reviews_count') @@ -57,7 +57,7 @@ it 'returns data', :aggregate_failures do get :index, params: { company_id: company.uuid, api_access_token: api_access_token.value } - response_values = response.parsed_body.dig('insights', 'data', 0, 'attributes', 'values') + response_values = response.parsed_body.dig('insights', 0, 'values') expect(response).to have_http_status :ok expect(response_values.keys).to eq Insight::DEFAULT_ATTRIBUTES.map(&:to_s) @@ -78,7 +78,7 @@ it 'returns data', :aggregate_failures do get :index, params: { repository_id: repository.uuid, api_access_token: api_access_token.value } - response_values = response.parsed_body.dig('insights', 'data', 0, 'attributes', 'values') + response_values = response.parsed_body.dig('insights', 0, 'values') expect(response).to have_http_status :ok expect(response_values.keys).to eq Insight::DEFAULT_ATTRIBUTES.map(&:to_s) diff --git a/spec/requests/api/v1/companies_spec.rb b/spec/requests/api/v1/companies_spec.rb index 59640c65..645b19aa 100644 --- a/spec/requests/api/v1/companies_spec.rb +++ b/spec/requests/api/v1/companies_spec.rb @@ -14,12 +14,12 @@ consumes 'application/json' parameter name: :api_access_token, in: :query, type: :string, description: 'API access token', required: true - parameter name: :include_fields, + parameter name: :only_fields, in: :query, type: :string, description: 'List of attributes should be included in response separated by comma', required: false - parameter name: :exclude_fields, + parameter name: :except_fields, in: :query, type: :string, description: 'List of attributes should be excluded from response separated by comma', @@ -37,22 +37,15 @@ end schema type: :object, properties: { - data: { + companies: { type: :array, items: { type: :object, properties: { - id: { type: :string }, - type: { type: :string }, - attributes: { - type: :object, - properties: { - uuid: { type: :string }, - title: { type: :string }, - repositories_count: { type: :integer, nullable: true }, - accessable: { type: :boolean } - } - } + uuid: { type: :string }, + title: { type: :string }, + repositories_count: { type: :integer, nullable: true }, + accessable: { type: :boolean } } } } @@ -112,27 +105,20 @@ end schema type: :object, properties: { - data: { + insights: { type: :array, items: { type: :object, properties: { - id: { type: :string }, - type: { type: :string }, - attributes: { + values: { + type: :object + }, + entity: { type: :object, properties: { - values: { - type: :object - }, - entity: { - type: :object, - properties: { - login: { type: :string }, - html_url: { type: :string }, - avatar_url: { type: :string } - } - } + login: { type: :string }, + html_url: { type: :string }, + avatar_url: { type: :string } } } } diff --git a/spec/requests/api/v1/repositories_spec.rb b/spec/requests/api/v1/repositories_spec.rb index 599877c4..41da62fe 100644 --- a/spec/requests/api/v1/repositories_spec.rb +++ b/spec/requests/api/v1/repositories_spec.rb @@ -38,22 +38,15 @@ items: { type: :object, properties: { - id: { type: :string }, - type: { type: :string }, - attributes: { + values: { + type: :object + }, + entity: { type: :object, properties: { - values: { - type: :object - }, - entity: { - type: :object, - properties: { - login: { type: :string }, - html_url: { type: :string }, - avatar_url: { type: :string } - } - } + login: { type: :string }, + html_url: { type: :string }, + avatar_url: { type: :string } } } } diff --git a/swagger/v1/swagger.yaml b/swagger/v1/swagger.yaml index 3a8c0410..6dea55a1 100644 --- a/swagger/v1/swagger.yaml +++ b/swagger/v1/swagger.yaml @@ -17,14 +17,14 @@ paths: required: true schema: type: string - - name: include_fields + - name: only_fields in: query description: List of attributes should be included in response separated by comma required: false schema: type: string - - name: exclude_fields + - name: except_fields in: query description: List of attributes should be excluded from response separated by comma @@ -39,27 +39,20 @@ paths: schema: type: object properties: - data: + companies: type: array items: type: object properties: - id: + uuid: type: string - type: + title: type: string - attributes: - type: object - properties: - uuid: - type: string - title: - type: string - repositories_count: - type: integer - nullable: true - accessable: - type: boolean + repositories_count: + type: integer + nullable: true + accessable: + type: boolean '401': description: Unauthorized content: @@ -98,29 +91,22 @@ paths: schema: type: object properties: - data: + insights: type: array items: type: object properties: - id: - type: string - type: - type: string - attributes: + values: + type: object + entity: type: object properties: - values: - type: object - entity: - type: object - properties: - login: - type: string - html_url: - type: string - avatar_url: - type: string + login: + type: string + html_url: + type: string + avatar_url: + type: string ratio_type: type: string nullable: true @@ -167,24 +153,17 @@ paths: items: type: object properties: - id: - type: string - type: - type: string - attributes: + values: + type: object + entity: type: object properties: - values: - type: object - entity: - type: object - properties: - login: - type: string - html_url: - type: string - avatar_url: - type: string + login: + type: string + html_url: + type: string + avatar_url: + type: string ratio_type: type: string nullable: true