Commit 7a208025 authored by Nick Thomas's avatar Nick Thomas

Merge branch 'mo-add-json-schema-doc' into 'master'

Proposal: Add JSONB specs with documentation

See merge request gitlab-org/gitlab!33641
parents 6a0e0980 39e22d6b
......@@ -810,6 +810,14 @@ class BuildMetadata
end
```
When using a `JSONB` column, use the [JsonSchemaValidator](https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/validators/json_schema_validator.rb) to keep control of the data being inserted over time.
```ruby
class BuildMetadata
validates: :config_options, json_schema: { filename: 'build_metadata_config_option' }
end
```
## Testing
See the [Testing Rails migrations](testing_guide/testing_migrations_guide.md) style guide.
......
......@@ -16,7 +16,7 @@ module Gitlab
end
# Namespace
class Namespace < ApplicationRecord
class Namespace < ActiveRecord::Base
self.table_name = 'namespaces'
self.inheritance_column = :_type_disabled
......
......@@ -8,6 +8,7 @@ RSpec.describe 'Database schema' do
let(:connection) { ActiveRecord::Base.connection }
let(:tables) { connection.tables }
let(:columns_name_with_jsonb) { retrieve_columns_name_with_jsonb }
# Use if you are certain that this column should not have a foreign key
# EE: edit the ee/spec/db/schema_support.rb
......@@ -169,8 +170,54 @@ RSpec.describe 'Database schema' do
end
end
# These pre-existing columns does not use a schema validation yet
IGNORED_JSONB_COLUMNS = {
"ApplicationSetting" => %w[repository_storages_weighted],
"AlertManagement::Alert" => %w[payload],
"Ci::BuildMetadata" => %w[config_options config_variables],
"Geo::Event" => %w[payload],
"GeoNodeStatus" => %w[status],
"Operations::FeatureFlagScope" => %w[strategies],
"Operations::FeatureFlags::Strategy" => %w[parameters],
"Packages::Composer::Metadatum" => %w[composer_json],
"Releases::Evidence" => %w[summary]
}.freeze
# We are skipping GEO models for now as it adds up complexity
describe 'for jsonb columns' do
it 'uses json schema validator' do
columns_name_with_jsonb.each do |hash|
next if models_by_table_name[hash["table_name"]].nil?
models_by_table_name[hash["table_name"]].each do |model|
jsonb_columns = [hash["column_name"]] - ignored_jsonb_columns(model.name)
expect(model).to validate_jsonb_schema(jsonb_columns)
end
end
end
end
private
def retrieve_columns_name_with_jsonb
sql = <<~SQL
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE table_catalog = '#{ApplicationRecord.connection_config[:database]}'
AND table_schema = 'public'
AND table_name NOT LIKE 'pg_%'
AND data_type = 'jsonb'
ORDER BY table_name, column_name, data_type
SQL
ApplicationRecord.connection.select_all(sql).to_a
end
def models_by_table_name
@models_by_table_name ||= ApplicationRecord.descendants.reject(&:abstract_class).group_by(&:table_name)
end
def ignored_fk_columns(column)
IGNORED_FK_COLUMNS.fetch(column, [])
end
......@@ -178,4 +225,8 @@ RSpec.describe 'Database schema' do
def ignored_limit_enums(model)
IGNORED_LIMIT_ENUMS.fetch(model, [])
end
def ignored_jsonb_columns(model)
IGNORED_JSONB_COLUMNS.fetch(model, [])
end
end
# frozen_string_literal: true
RSpec::Matchers.define :validate_jsonb_schema do |jsonb_columns|
match do |actual|
next true if jsonb_columns.blank?
expect(actual.validators).to include(a_kind_of(JsonSchemaValidator))
end
failure_message do
<<~FAILURE_MESSAGE
Expected #{actual.name} to validate the schema of #{jsonb_columns.join(', ')}.
Use JsonSchemaValidator in your model when using a jsonb column.
See doc/development/migration_style_guide.html#storing-json-in-database for more information.
To fix this, please add `validates :#{jsonb_columns.first}, json_schema: { filename: "filename" }` in your model file, for example:
class #{actual.name}
validates :#{jsonb_columns.first}, json_schema: { filename: "filename" }
end
FAILURE_MESSAGE
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment