Commit b1f75a22 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'default-oj' into 'master'

Remove :oj_json feature flag

See merge request gitlab-org/gitlab!46012
parents cb4ec474 7d57c135
---
name: oj_json
introduced_by_url:
rollout_issue_url:
group:
type: development
default_enabled: true
...@@ -67,15 +67,6 @@ module Gitlab ...@@ -67,15 +67,6 @@ module Gitlab
::JSON.pretty_generate(object, opts) ::JSON.pretty_generate(object, opts)
end end
# Feature detection for using Oj instead of the `json` gem.
#
# @return [Boolean]
def enable_oj?
return false unless feature_table_exists?
Feature.enabled?(:oj_json, default_enabled: true)
end
private private
# Convert JSON string into Ruby through toggleable adapters. # Convert JSON string into Ruby through toggleable adapters.
...@@ -91,11 +82,7 @@ module Gitlab ...@@ -91,11 +82,7 @@ module Gitlab
def adapter_load(string, *args, **opts) def adapter_load(string, *args, **opts)
opts = standardize_opts(opts) opts = standardize_opts(opts)
if enable_oj? Oj.load(string, opts)
Oj.load(string, opts)
else
::JSON.parse(string, opts)
end
rescue Oj::ParseError, Encoding::UndefinedConversionError => ex rescue Oj::ParseError, Encoding::UndefinedConversionError => ex
raise parser_error.new(ex) raise parser_error.new(ex)
end end
...@@ -120,11 +107,7 @@ module Gitlab ...@@ -120,11 +107,7 @@ module Gitlab
# #
# @return [String] # @return [String]
def adapter_dump(object, *args, **opts) def adapter_dump(object, *args, **opts)
if enable_oj? Oj.dump(object, opts)
Oj.dump(object, opts)
else
::JSON.dump(object, *args)
end
end end
# Generates JSON for an object but with fewer options, using toggleable adapters. # Generates JSON for an object but with fewer options, using toggleable adapters.
...@@ -135,11 +118,7 @@ module Gitlab ...@@ -135,11 +118,7 @@ module Gitlab
def adapter_generate(object, opts = {}) def adapter_generate(object, opts = {})
opts = standardize_opts(opts) opts = standardize_opts(opts)
if enable_oj? Oj.generate(object, opts)
Oj.generate(object, opts)
else
::JSON.generate(object, opts)
end
end end
# Take a JSON standard options hash and standardize it to work across adapters # Take a JSON standard options hash and standardize it to work across adapters
...@@ -149,11 +128,8 @@ module Gitlab ...@@ -149,11 +128,8 @@ module Gitlab
# @return [Hash] # @return [Hash]
def standardize_opts(opts) def standardize_opts(opts)
opts ||= {} opts ||= {}
opts[:mode] = :rails
if enable_oj? opts[:symbol_keys] = opts[:symbolize_keys] || opts[:symbolize_names]
opts[:mode] = :rails
opts[:symbol_keys] = opts[:symbolize_keys] || opts[:symbolize_names]
end
opts opts
end end
...@@ -213,7 +189,7 @@ module Gitlab ...@@ -213,7 +189,7 @@ module Gitlab
# @param object [Object] # @param object [Object]
# @return [String] # @return [String]
def self.call(object, env = nil) def self.call(object, env = nil)
if Gitlab::Json.enable_oj? && Feature.enabled?(:grape_gitlab_json, default_enabled: true) if Feature.enabled?(:grape_gitlab_json, default_enabled: true)
Gitlab::Json.dump(object) Gitlab::Json.dump(object)
else else
Grape::Formatter::Json.call(object, env) Grape::Formatter::Json.call(object, env)
......
...@@ -7,342 +7,306 @@ RSpec.describe Gitlab::Json do ...@@ -7,342 +7,306 @@ RSpec.describe Gitlab::Json do
stub_feature_flags(json_wrapper_legacy_mode: true) stub_feature_flags(json_wrapper_legacy_mode: true)
end end
shared_examples "json" do describe ".parse" do
describe ".parse" do context "legacy_mode is disabled by default" do
context "legacy_mode is disabled by default" do it "parses an object" do
it "parses an object" do expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
expect(subject.parse('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end
it "parses a string" do
expect(subject.parse('"foo"', legacy_mode: false)).to eq("foo")
end
it "parses a true bool" do
expect(subject.parse("true", legacy_mode: false)).to be(true)
end
it "parses a false bool" do
expect(subject.parse("false", legacy_mode: false)).to be(false)
end
end end
context "legacy_mode is enabled" do it "parses an array" do
it "parses an object" do expect(subject.parse('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
it "raises an error on a string" do
expect { subject.parse('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a true bool" do
expect { subject.parse("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
it "raises an error on a false bool" do
expect { subject.parse("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
end end
context "feature flag is disabled" do it "parses a string" do
before do expect(subject.parse('"foo"', legacy_mode: false)).to eq("foo")
stub_feature_flags(json_wrapper_legacy_mode: false) end
end
it "parses an object" do
expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end
it "parses an array" do
expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end
it "parses a string" do
expect(subject.parse('"foo"', legacy_mode: true)).to eq("foo")
end
it "parses a true bool" do it "parses a true bool" do
expect(subject.parse("true", legacy_mode: true)).to be(true) expect(subject.parse("true", legacy_mode: false)).to be(true)
end end
it "parses a false bool" do it "parses a false bool" do
expect(subject.parse("false", legacy_mode: true)).to be(false) expect(subject.parse("false", legacy_mode: false)).to be(false)
end
end end
end end
describe ".parse!" do context "legacy_mode is enabled" do
context "legacy_mode is disabled by default" do it "parses an object" do
it "parses an object" do expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" }) end
end
it "parses an array" do it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }]) expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end end
it "parses a string" do it "raises an error on a string" do
expect(subject.parse!('"foo"', legacy_mode: false)).to eq("foo") expect { subject.parse('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end end
it "parses a true bool" do it "raises an error on a true bool" do
expect(subject.parse!("true", legacy_mode: false)).to be(true) expect { subject.parse("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end end
it "parses a false bool" do it "raises an error on a false bool" do
expect(subject.parse!("false", legacy_mode: false)).to be(false) expect { subject.parse("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
end
end end
end
context "legacy_mode is enabled" do context "feature flag is disabled" do
it "parses an object" do before do
expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" }) stub_feature_flags(json_wrapper_legacy_mode: false)
end end
it "parses an array" do it "parses an object" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }]) expect(subject.parse('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end end
it "raises an error on a string" do it "parses an array" do
expect { subject.parse!('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError) expect(subject.parse('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end end
it "raises an error on a true bool" do it "parses a string" do
expect { subject.parse!("true", legacy_mode: true) }.to raise_error(JSON::ParserError) expect(subject.parse('"foo"', legacy_mode: true)).to eq("foo")
end end
it "raises an error on a false bool" do it "parses a true bool" do
expect { subject.parse!("false", legacy_mode: true) }.to raise_error(JSON::ParserError) expect(subject.parse("true", legacy_mode: true)).to be(true)
end
end end
context "feature flag is disabled" do it "parses a false bool" do
before do expect(subject.parse("false", legacy_mode: true)).to be(false)
stub_feature_flags(json_wrapper_legacy_mode: false) end
end end
end
it "parses an object" do describe ".parse!" do
expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" }) context "legacy_mode is disabled by default" do
end it "parses an object" do
expect(subject.parse!('{ "foo": "bar" }')).to eq({ "foo" => "bar" })
end
it "parses an array" do it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }]) expect(subject.parse!('[{ "foo": "bar" }]')).to eq([{ "foo" => "bar" }])
end end
it "parses a string" do it "parses a string" do
expect(subject.parse!('"foo"', legacy_mode: true)).to eq("foo") expect(subject.parse!('"foo"', legacy_mode: false)).to eq("foo")
end end
it "parses a true bool" do it "parses a true bool" do
expect(subject.parse!("true", legacy_mode: true)).to be(true) expect(subject.parse!("true", legacy_mode: false)).to be(true)
end end
it "parses a false bool" do it "parses a false bool" do
expect(subject.parse!("false", legacy_mode: true)).to be(false) expect(subject.parse!("false", legacy_mode: false)).to be(false)
end
end end
end end
describe ".dump" do context "legacy_mode is enabled" do
it "dumps an object" do it "parses an object" do
expect(subject.dump({ "foo" => "bar" })).to eq('{"foo":"bar"}') expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
end end
it "dumps an array" do it "parses an array" do
expect(subject.dump([{ "foo" => "bar" }])).to eq('[{"foo":"bar"}]') expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end end
it "dumps a string" do it "raises an error on a string" do
expect(subject.dump("foo")).to eq('"foo"') expect { subject.parse!('"foo"', legacy_mode: true) }.to raise_error(JSON::ParserError)
end end
it "dumps a true bool" do it "raises an error on a true bool" do
expect(subject.dump(true)).to eq("true") expect { subject.parse!("true", legacy_mode: true) }.to raise_error(JSON::ParserError)
end end
it "dumps a false bool" do it "raises an error on a false bool" do
expect(subject.dump(false)).to eq("false") expect { subject.parse!("false", legacy_mode: true) }.to raise_error(JSON::ParserError)
end end
end end
describe ".generate" do context "feature flag is disabled" do
let(:obj) do before do
{ test: true, "foo.bar" => "baz", is_json: 1, some: [1, 2, 3] } stub_feature_flags(json_wrapper_legacy_mode: false)
end end
it "generates JSON" do it "parses an object" do
expected_string = <<~STR.chomp expect(subject.parse!('{ "foo": "bar" }', legacy_mode: true)).to eq({ "foo" => "bar" })
{"test":true,"foo.bar":"baz","is_json":1,"some":[1,2,3]} end
STR
expect(subject.generate(obj)).to eq(expected_string) it "parses an array" do
expect(subject.parse!('[{ "foo": "bar" }]', legacy_mode: true)).to eq([{ "foo" => "bar" }])
end end
it "allows you to customise the output" do it "parses a string" do
opts = { expect(subject.parse!('"foo"', legacy_mode: true)).to eq("foo")
indent: " ", end
space: " ",
space_before: " ",
object_nl: "\n",
array_nl: "\n"
}
json = subject.generate(obj, opts) it "parses a true bool" do
expect(subject.parse!("true", legacy_mode: true)).to be(true)
expected_string = <<~STR.chomp end
{
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
]
}
STR
expect(json).to eq(expected_string) it "parses a false bool" do
expect(subject.parse!("false", legacy_mode: true)).to be(false)
end end
end end
end
describe ".pretty_generate" do describe ".dump" do
let(:obj) do it "dumps an object" do
{ expect(subject.dump({ "foo" => "bar" })).to eq('{"foo":"bar"}')
test: true, end
"foo.bar" => "baz",
is_json: 1,
some: [1, 2, 3],
more: { test: true },
multi_line_empty_array: [],
multi_line_empty_obj: {}
}
end
it "generates pretty JSON" do it "dumps an array" do
expected_string = <<~STR.chomp expect(subject.dump([{ "foo" => "bar" }])).to eq('[{"foo":"bar"}]')
{ end
"test": true,
"foo.bar": "baz",
"is_json": 1,
"some": [
1,
2,
3
],
"more": {
"test": true
},
"multi_line_empty_array": [
],
"multi_line_empty_obj": {
}
}
STR
expect(subject.pretty_generate(obj)).to eq(expected_string) it "dumps a string" do
end expect(subject.dump("foo")).to eq('"foo"')
end
it "allows you to customise the output" do it "dumps a true bool" do
opts = { expect(subject.dump(true)).to eq("true")
space_before: " " end
}
json = subject.pretty_generate(obj, opts) it "dumps a false bool" do
expect(subject.dump(false)).to eq("false")
expected_string = <<~STR.chomp end
{ end
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
],
"more" : {
"test" : true
},
"multi_line_empty_array" : [
],
"multi_line_empty_obj" : {
}
}
STR
expect(json).to eq(expected_string) describe ".generate" do
end let(:obj) do
{ test: true, "foo.bar" => "baz", is_json: 1, some: [1, 2, 3] }
end end
context "the feature table is missing" do it "generates JSON" do
before do expected_string = <<~STR.chomp
allow(Feature::FlipperFeature).to receive(:table_exists?).and_return(false) {"test":true,"foo.bar":"baz","is_json":1,"some":[1,2,3]}
end STR
expect(subject.generate(obj)).to eq(expected_string)
end
it "skips legacy mode handling" do it "allows you to customise the output" do
expect(Feature).not_to receive(:enabled?).with(:json_wrapper_legacy_mode, default_enabled: true) opts = {
indent: " ",
space: " ",
space_before: " ",
object_nl: "\n",
array_nl: "\n"
}
subject.send(:handle_legacy_mode!, {}) json = subject.generate(obj, opts)
end
it "skips oj feature detection" do expected_string = <<~STR.chomp
expect(Feature).not_to receive(:enabled?).with(:oj_json, default_enabled: true) {
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
]
}
STR
subject.send(:enable_oj?) expect(json).to eq(expected_string)
end
end end
end
context "the database is missing" do describe ".pretty_generate" do
before do let(:obj) do
allow(Feature::FlipperFeature).to receive(:table_exists?).and_raise(PG::ConnectionBad) {
end test: true,
"foo.bar" => "baz",
is_json: 1,
some: [1, 2, 3],
more: { test: true },
multi_line_empty_array: [],
multi_line_empty_obj: {}
}
end
it "still parses json" do it "generates pretty JSON" do
expect(subject.parse("{}")).to eq({}) expected_string = <<~STR.chomp
end {
"test": true,
"foo.bar": "baz",
"is_json": 1,
"some": [
1,
2,
3
],
"more": {
"test": true
},
"multi_line_empty_array": [
],
"multi_line_empty_obj": {
}
}
STR
it "still generates json" do expect(subject.pretty_generate(obj)).to eq(expected_string)
expect(subject.dump({})).to eq("{}") end
end
it "allows you to customise the output" do
opts = {
space_before: " "
}
json = subject.pretty_generate(obj, opts)
expected_string = <<~STR.chomp
{
"test" : true,
"foo.bar" : "baz",
"is_json" : 1,
"some" : [
1,
2,
3
],
"more" : {
"test" : true
},
"multi_line_empty_array" : [
],
"multi_line_empty_obj" : {
}
}
STR
expect(json).to eq(expected_string)
end end
end end
context "oj gem" do context "the feature table is missing" do
before do before do
stub_feature_flags(oj_json: true) allow(Feature::FlipperFeature).to receive(:table_exists?).and_return(false)
end end
it_behaves_like "json" it "skips legacy mode handling" do
expect(Feature).not_to receive(:enabled?).with(:json_wrapper_legacy_mode, default_enabled: true)
describe "#enable_oj?" do subject.send(:handle_legacy_mode!, {})
it "returns true" do
expect(subject.enable_oj?).to be(true)
end
end end
end end
context "json gem" do context "the database is missing" do
before do before do
stub_feature_flags(oj_json: false) allow(Feature::FlipperFeature).to receive(:table_exists?).and_raise(PG::ConnectionBad)
end end
it_behaves_like "json" it "still parses json" do
expect(subject.parse("{}")).to eq({})
end
describe "#enable_oj?" do it "still generates json" do
it "returns false" do expect(subject.dump({})).to eq("{}")
expect(subject.enable_oj?).to be(false)
end
end end
end end
...@@ -353,47 +317,25 @@ RSpec.describe Gitlab::Json do ...@@ -353,47 +317,25 @@ RSpec.describe Gitlab::Json do
let(:env) { {} } let(:env) { {} }
let(:result) { "{\"test\":true}" } let(:result) { "{\"test\":true}" }
context "oj is enabled" do context "grape_gitlab_json flag is enabled" do
before do before do
stub_feature_flags(oj_json: true) stub_feature_flags(grape_gitlab_json: true)
end end
context "grape_gitlab_json flag is enabled" do it "generates JSON" do
before do expect(subject).to eq(result)
stub_feature_flags(grape_gitlab_json: true)
end
it "generates JSON" do
expect(subject).to eq(result)
end
it "uses Gitlab::Json" do
expect(Gitlab::Json).to receive(:dump).with(obj)
subject
end
end end
context "grape_gitlab_json flag is disabled" do it "uses Gitlab::Json" do
before do expect(Gitlab::Json).to receive(:dump).with(obj)
stub_feature_flags(grape_gitlab_json: false)
end
it "generates JSON" do
expect(subject).to eq(result)
end
it "uses Grape::Formatter::Json" do subject
expect(Grape::Formatter::Json).to receive(:call).with(obj, env)
subject
end
end end
end end
context "oj is disabled" do context "grape_gitlab_json flag is disabled" do
before do before do
stub_feature_flags(oj_json: false) stub_feature_flags(grape_gitlab_json: false)
end end
it "generates JSON" do it "generates JSON" do
......
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