diff --git a/README.md b/README.md index 281205d..ba01dff 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,27 @@ Conditions and parameters are managed by YAML files. ## Usage -You need to get an access token for your firebase project. +To get an access token, `bin/get_access_token ` would help you. + +### Get the current configs into your local ```bash -export FIREBASE_PROJECT_ID='your project id' -export REMOTE_CONFIG_ACCESS_TOKEN='your access token' +bundle exec remocon pull --prefix=projects --id=my_project_dev --token=xyz ``` -### Get the current configs into your local +Then, you can see `paremeters.yml, conditions.yml, config.json, etag` files in `projects/my_project_dev` directory. +If you don't specify `--prefix`, then the command create the files in the working directory + +*Environment variables* + +Some variables can be injected by environment variables. ```bash -bundle exec remocon pull --dest=${path to dir} +export REMOCON_FIREBASE_PROJECT_ID=<--id> +export REMOCON_FIREBASE_ACCESS_TOKEN=<--token> +export REMOCON_PREFIX=<--prefix> # Optional -# you can see ${path to dir}/${FIREBASE_PROJECT_ID}/{paremeters.yml, conditions.yml, config.json, etag} +FIREBASE_PROJECT_ID and REMOTE_CONFIG_ACCESS_TOKEN are supported but they are deprecated now ``` ### Edit configs on your local @@ -52,7 +60,17 @@ key1: # key name ### Update configs on remote ```bash -bundle exec remocon push --source=${path to a json file} --etag=${string or path to a file} +# Create new configs as projects/my_project_dev/config.json +bundle exec remocon create --prefix=projects --id=my_project_dev + +# Upload projects/my_project_dev/config.json by using projects/my_project_dev/etag +bundle exec remocon push --prefix=projects --id=my_project_dev --token=xyz + +# You can use custom paths for config.json and etag +bundle exec remocon push --source= --etag= + +# Use the fixed etag value +bundle exec remocon push --raw_etag= ``` ## Installation diff --git a/lib/remocon.rb b/lib/remocon.rb index eb63cfa..31bdef4 100644 --- a/lib/remocon.rb +++ b/lib/remocon.rb @@ -37,6 +37,7 @@ require "remocon/normalizer/void_normalizer" require "remocon/normalizer/type_normalizer_factory" +require "remocon/command/lib/config" require "remocon/command/lib/interpreter_helper" require "remocon/command/create_command" diff --git a/lib/remocon/cli.rb b/lib/remocon/cli.rb index 90cee01..191e59e 100644 --- a/lib/remocon/cli.rb +++ b/lib/remocon/cli.rb @@ -3,31 +3,43 @@ module Remocon class CLI < ::Thor desc "create", "Create a json to be pushed" - option :parameters, type: :string, required: true, desc: "a filepath of parameters file" - option :conditions, type: :string, required: true, desc: "a filepath of conditions file" - option :dest, type: :string, desc: "a filepath or directory path of destination" + option :parameters, type: :string, desc: "Specify the filepath if you want to use a custom parameters file" + option :conditions, type: :string, desc: "Specify the filepath if you want to use a custom conditions file" + option :prefix, type: :string, desc: "the directory name which will contain project-related files" + option :id, type: :string, desc: "your project" + option :dest, type: :string, hide: true, desc: "[Deprecated] the same with --prefix" def create execute(Remocon::Command::Create) end desc "push", "Upload remote configs based on a source json file" - class_option :source, type: :string, desc: "a filepath of a source json file" - option :etag, type: :string, desc: "a filepath or raw value of etag" - option :dest, type: :string, desc: "a filepath or directory path of destination" + option :source, type: :string, desc: "the filepath of your config json file" + option :etag, type: :string, desc: "the file path of etag" + option :raw_etag, type: :string, desc: "the raw value of etag" + option :prefix, type: :string, desc: "the directory name which will contain project-related files" option :force, type: :boolean, default: false, desc: "force to ignore some warnings" + option :token, type: :string, desc: "access token to your project" + option :id, type: :string, desc: "your project id" + option :dest, type: :string, hide: true, desc: "[Deprecated] the same with --prefix" def push execute(Remocon::Command::Push) end desc "pull", "Pull remote configs" - option :dest, type: :string, desc: "a filepath or directory path of destination" + option :prefix, type: :string, desc: "the directory name which will contain project-related files" + option :token, type: :string, desc: "access token to your project" + option :id, type: :string, desc: "your project id" + option :dest, type: :string, hide: true, desc: "[Deprecated] the same with --prefix" def pull execute(Remocon::Command::Pull) end desc "validate", "Validate yml files" - option :parameters, type: :string, required: true, desc: "a filepath of parameters file" - option :conditions, type: :string, required: true, desc: "a filepath of conditions file" + option :parameters, type: :string, desc: "Specify the filepath if you want to use a custom parameters file" + option :conditions, type: :string, desc: "Specify the filepath if you want to use a custom conditions file" + option :prefix, type: :string, desc: "the directory name which will contain project-related files" + option :id, type: :string, desc: "your project id" + option :token, type: :string, desc: "access token to your project" def validate execute(Remocon::Command::Validate) end @@ -35,7 +47,7 @@ def validate private def execute(klass) - klass.new(options).run + exit(1) unless klass.new(options).run end end end diff --git a/lib/remocon/command/create_command.rb b/lib/remocon/command/create_command.rb index 140b1e6..1539356 100644 --- a/lib/remocon/command/create_command.rb +++ b/lib/remocon/command/create_command.rb @@ -5,15 +5,19 @@ module Command class Create include Remocon::InterpreterHelper + attr_reader :config, :cmd_opts + def initialize(opts) - @opts = opts + @config = Remocon::Config.new(opts) + @cmd_opts = { validate_only: false } + end - @project_id = ENV.fetch("FIREBASE_PROJECT_ID") - @conditions_filepath = @opts[:conditions] - @parameters_filepath = @opts[:parameters] - @dest_dir = File.join(@opts[:dest], @project_id) if @opts[:dest] + def require_parameters_file_path + config.parameters_file_path + end - @cmd_opts = { validate_only: false } + def require_conditions_file_path + config.conditions_file_path end def run @@ -24,14 +28,10 @@ def run parameters: parameter_hash }.skip_nil_values.stringify_values - if @dest_dir - File.open(File.join(@dest_dir, "config.json"), "w+") do |f| - # remote config allows only string values ;( - f.write(JSON.pretty_generate(artifact)) - f.flush - end - else - STDOUT.puts JSON.pretty_generate(artifact) + File.open(config.config_json_file_path, "w+") do |f| + # remote config allows only string values ;( + f.write(JSON.pretty_generate(artifact)) + f.flush end artifact @@ -40,8 +40,8 @@ def run private def validate_options - raise ValidationError, "A condition file must exist" unless File.exist?(@conditions_filepath) - raise ValidationError, "A parameter file must exist" unless File.exist?(@parameters_filepath) + raise ValidationError, "A condition file must exist" unless File.exist?(config.conditions_file_path) + raise ValidationError, "A parameter file must exist" unless File.exist?(config.parameters_file_path) end end end diff --git a/lib/remocon/command/lib/config.rb b/lib/remocon/command/lib/config.rb new file mode 100644 index 0000000..f43dfbc --- /dev/null +++ b/lib/remocon/command/lib/config.rb @@ -0,0 +1,82 @@ +# frozen_string_literal: true + +module Remocon + class Config + REMOCON_PROJECT_ID_KEY = "REMOCON_FIREBASE_PROJECT_ID" + REMOCON_ACCESS_TOKEN = "REMOCON_FIREBASE_ACCESS_TOKEN" + + REMOCON_PREFIX_KEY = "REMOCON_PREFIX" + + CONFIG_JSON_FILE = "config.json" + CONDITIONS_FILE_NAME = "conditions.yml" + PARAMETERS_FILE_NAME = "parameters.yml" + ETAG_FILE_NAME = "etag" + + attr_reader :opts + + def initialize(opts) + @opts = opts + end + + def endpoint + @endpoint ||= "https://firebaseremoteconfig.googleapis.com/v1/projects/#{project_id}/remoteConfig" + end + + def project_id + # FIREBASE_PROJECT_ID is for backward compatibility + @project_id ||= (opts[:id] || ENV[REMOCON_PROJECT_ID_KEY] || ENV["FIREBASE_PROJECT_ID"] || raise("--id or #{REMOCON_PROJECT_ID_KEY} env var is required")) + end + + def token + # REMOTE_CONFIG_ACCESS_TOKEN is for backward compatibility + @token ||= (opts[:token] || ENV[REMOCON_ACCESS_TOKEN] || ENV["REMOTE_CONFIG_ACCESS_TOKEN"] || raise("--token or #{REMOCON_ACCESS_TOKEN} env var is required")) + end + + def destination_dir_path + @destination_dir_path ||= (opts[:prefix] || opts[:dest] || ENV[REMOCON_PREFIX_KEY]) + end + + def project_dir_path + @project_dir_path ||= begin + dir_path = destination_dir_path + (dir_path ? File.join(dir_path, project_id) : project_id).tap do |dir| + FileUtils.mkdir_p(dir) + end + end + end + + def config_json_file_path + @config_json_file_path ||= opts[:source] || begin + File.join(project_dir_path, CONFIG_JSON_FILE) + end + end + + def conditions_file_path + @conditions_file_path ||= opts[:conditions] || begin + File.join(project_dir_path, CONDITIONS_FILE_NAME) + end + end + + def parameters_file_path + @parameters_file_path ||= opts[:parameters] || begin + File.join(project_dir_path, PARAMETERS_FILE_NAME) + end + end + + def etag_file_path + @etag_file_path ||= opts[:etag] || begin + File.join(project_dir_path, ETAG_FILE_NAME) + end + end + + def etag + @etag ||= begin + if opts[:force] && opts[:raw_etag] + raise "--force and --raw_etag cannot be specified" + end + + opts[:force] && "*" || opts[:raw_etag] || File.exist?(etag_file_path) && File.open(etag_file_path).read + end + end + end +end diff --git a/lib/remocon/command/lib/interpreter_helper.rb b/lib/remocon/command/lib/interpreter_helper.rb index f81d074..be3e553 100644 --- a/lib/remocon/command/lib/interpreter_helper.rb +++ b/lib/remocon/command/lib/interpreter_helper.rb @@ -5,11 +5,21 @@ module InterpreterHelper include Remocon::ConditionSorter include Remocon::ParameterSorter - attr_reader :parameters_filepath, :conditions_filepath, :cmd_opts + def cmd_opts + raise NotImplementedError + end + + def require_parameters_file_path + raise NotImplementedError + end + + def require_conditions_file_path + raise NotImplementedError + end def read_parameters @read_parameters ||= begin - parameter_interpreter = Remocon::ParameterFileInterpreter.new(parameters_filepath) + parameter_interpreter = Remocon::ParameterFileInterpreter.new(require_parameters_file_path) parameter_interpreter.read(condition_names, cmd_opts) end end @@ -24,7 +34,7 @@ def parameter_errors def read_conditions @read_conditions ||= begin - condition_interpreter = Remocon::ConditionFileInterpreter.new(conditions_filepath) + condition_interpreter = Remocon::ConditionFileInterpreter.new(require_conditions_file_path) condition_interpreter.read(cmd_opts) end end diff --git a/lib/remocon/command/pull_command.rb b/lib/remocon/command/pull_command.rb index ee7a101..835495a 100644 --- a/lib/remocon/command/pull_command.rb +++ b/lib/remocon/command/pull_command.rb @@ -5,62 +5,60 @@ module Command class Pull include Remocon::InterpreterHelper + attr_reader :config, :cmd_opts + def initialize(opts) - @opts = opts + @config = Remocon::Config.new(opts) + @cmd_opts = { validate_only: false } + end - @project_id = ENV.fetch("FIREBASE_PROJECT_ID") - @token = ENV.fetch("REMOTE_CONFIG_ACCESS_TOKEN") - @url = "https://firebaseremoteconfig.googleapis.com/v1/projects/#{@project_id}/remoteConfig" - @dest_dir = File.join(@opts[:dest], @project_id) if @opts[:dest] + def require_parameters_file_path + config.parameters_file_path + end - @cmd_opts = { validate_only: false } + def require_conditions_file_path + config.conditions_file_path end def run - if @dest_dir - FileUtils.mkdir_p(@dest_dir) + raw_json, etag = do_request - raw_hash = JSON.parse(raw_json).with_indifferent_access + raw_hash = JSON.parse(raw_json).with_indifferent_access - raise "etag cannot be fetched. please try again" unless @etag + raise "etag cannot be fetched. please try again" unless etag - conditions = raw_hash[:conditions] || [] - parameters = raw_hash[:parameters] || {} + conditions = raw_hash[:conditions] || [] + parameters = raw_hash[:parameters] || {} - File.open(File.join(@dest_dir, "conditions.yml"), "w+") do |f| - f.write(JSON.parse(Remocon::ConditionFileDumper.new(sort_conditions(conditions)).dump.to_json).to_yaml) - f.flush - end + File.open(config.conditions_file_path, "w+") do |f| + f.write(JSON.parse(Remocon::ConditionFileDumper.new(sort_conditions(conditions)).dump.to_json).to_yaml) + f.flush + end - File.open(File.join(@dest_dir, "parameters.yml"), "w+") do |f| - f.write(JSON.parse(Remocon::ParameterFileDumper.new(sort_parameters(parameters)).dump.to_json).to_yaml) - f.flush - end + File.open(config.parameters_file_path, "w+") do |f| + f.write(JSON.parse(Remocon::ParameterFileDumper.new(sort_parameters(parameters)).dump.to_json).to_yaml) + f.flush + end - File.open(File.join(@dest_dir, "config.json"), "w+") do |f| - f.write(JSON.pretty_generate({ conditions: sort_conditions(conditions), parameters: sort_parameters(parameters) })) - f.flush - end + File.open(config.config_json_file_path, "w+") do |f| + f.write(JSON.pretty_generate({ conditions: sort_conditions(conditions), parameters: sort_parameters(parameters) })) + f.flush + end - File.open(File.join(@dest_dir, "etag"), "w+") do |f| - f.write(@etag) - f.flush - end - else - STDOUT.puts raw_json + File.open(config.etag_file_path, "w+") do |f| + f.write(etag) + f.flush end end private - def raw_json - return @raw_json if @raw_json - - @raw_json, @etag = open(@url, "Authorization" => "Bearer #{@token}") do |io| + def do_request + raw_json, etag = open(config.endpoint, "Authorization" => "Bearer #{config.token}") do |io| [io.read, io.meta["etag"]] end - @raw_json + [raw_json, etag] end end end diff --git a/lib/remocon/command/push_command.rb b/lib/remocon/command/push_command.rb index 08b5bce..4c5b092 100644 --- a/lib/remocon/command/push_command.rb +++ b/lib/remocon/command/push_command.rb @@ -3,19 +3,10 @@ module Remocon module Command class Push - attr_reader :uri + attr_reader :config, :cmd_opts def initialize(opts) - @opts = opts - - @project_id = ENV.fetch("FIREBASE_PROJECT_ID") - @token = ENV.fetch("REMOTE_CONFIG_ACCESS_TOKEN") - @uri = URI.parse("https://firebaseremoteconfig.googleapis.com/v1/projects/#{@project_id}/remoteConfig") - @source_filepath = @opts[:source] - @etag = File.exist?(@opts[:etag]) ? File.open(@opts[:etag]).read : @opts[:etag] if @opts[:etag] - @ignore_etag = @opts[:force] - @dest_dir = File.join(@opts[:dest], @project_id) if @opts[:dest] - + @config = Remocon::Config.new(opts) @cmd_opts = { validate_only: false } end @@ -36,23 +27,27 @@ def client def request return @request if @request - raise "etag should be specified. If you want to ignore this error, then add --force option" unless @etag || @ignore_etag + raise "etag should be specified. If you want to ignore this error, then add --force option" unless config.etag headers = { - "Authorization" => "Bearer #{@token}", - "Content-Type" => "application/json; UTF8" + "Authorization" => "Bearer #{config.token}", + "Content-Type" => "application/json; UTF8", + "If-Match" => config.etag, } - headers["If-Match"] = @etag || "*" request = Net::HTTP::Put.new(uri.request_uri, headers) request.body = "" - request.body << File.read(@source_filepath).delete("\r\n") + request.body << File.read(config.config_json_file_path).delete("\r\n") @request = request end private + def uri + @uri ||= URI.parse(config.endpoint) + end + def do_request response = client.request(request) @@ -80,6 +75,8 @@ def do_request # local content is out-to-date STDERR.puts "409 Conflict. Remote was updated. Please update your local files" end + + response.kind_of?(Net::HTTPOK) end def parse_success_body(response, _success_body) @@ -87,8 +84,8 @@ def parse_success_body(response, _success_body) return unless etag - if @dest_dir - File.open(File.join(@dest_dir, "etag"), "w+") do |f| + if config.project_dir_path + File.open(config.etag_file_path, "w+") do |f| f.write(etag) f.flush end diff --git a/lib/remocon/command/validate_command.rb b/lib/remocon/command/validate_command.rb index 6ed6e92..c0f433b 100644 --- a/lib/remocon/command/validate_command.rb +++ b/lib/remocon/command/validate_command.rb @@ -5,33 +5,43 @@ module Command class Validate include Remocon::InterpreterHelper + attr_reader :config, :cmd_opts + def initialize(opts) - @opts = opts + @config = Remocon::Config.new(opts) + @cmd_opts = { validate_only: true } + end - @conditions_filepath = @opts[:conditions] - @parameters_filepath = @opts[:parameters] + def require_parameters_file_path + config.parameters_file_path + end - @cmd_opts = { validate_only: true } + def require_conditions_file_path + config.conditions_file_path end def run validate_options - if parameter_errors.empty? && condition_errors.empty? + errors = parameter_errors + condition_errors + + if errors.empty? STDOUT.puts "No error was found." else - (parameter_errors + condition_errors).each do |e| + errors.each do |e| STDERR.puts "#{e.class} #{e.message}" STDERR.puts e.backtrace.join("\n") end end + + errors.empty? end private def validate_options - raise ValidationError, "A condition file must exist" unless File.exist?(@conditions_filepath) - raise ValidationError, "A parameter file must exist" unless File.exist?(@parameters_filepath) + raise ValidationError, "A condition file must exist" unless File.exist?(config.conditions_file_path) + raise ValidationError, "A parameter file must exist" unless File.exist?(config.parameters_file_path) end end end diff --git a/prj/config.json b/prj/config.json new file mode 100644 index 0000000..7340ca8 --- /dev/null +++ b/prj/config.json @@ -0,0 +1,34 @@ +{ + "conditions": [ + { + "expression": "device.os == 'ios'", + "name": "condition1", + "tagColor": "INDIGO" + }, + { + "expression": "device.os == 'ios'", + "name": "zxczx", + "tagColor": "CYAN" + } + ], + "parameters": { + "key1": { + "defaultValue": { + "value": "100" + }, + "conditionalValues": { + "condition1": { + "value": "200" + }, + "zxczx": { + "value": "100" + } + } + }, + "key2": { + "defaultValue": { + "value": "123" + } + } + } +} \ No newline at end of file diff --git a/spec/fixture/config.json b/spec/fixture/config_file.json similarity index 100% rename from spec/fixture/config.json rename to spec/fixture/config_file.json diff --git a/spec/fixture/etag_file b/spec/fixture/etag_file new file mode 100644 index 0000000..037b012 --- /dev/null +++ b/spec/fixture/etag_file @@ -0,0 +1 @@ +XYZXYZXYZXYZ \ No newline at end of file diff --git a/spec/remocon/cli_spec.rb b/spec/remocon/cli_spec.rb index 5c4fbaa..46fdc98 100644 --- a/spec/remocon/cli_spec.rb +++ b/spec/remocon/cli_spec.rb @@ -6,10 +6,12 @@ module Remocon describe CLI do context "commands" do shared_examples "CLI examples" do - it "should run a command" do + before do # disable running - allow(klass).to receive(:run) + allow_any_instance_of(klass).to receive(:run).and_return(true) + end + it "should run a command" do expect_any_instance_of(klass).to receive(:run).once described_class.new.invoke(command, [], options) end diff --git a/spec/remocon/command/create_command_spec.rb b/spec/remocon/command/create_command_spec.rb index 1baaac6..e1e37fd 100644 --- a/spec/remocon/command/create_command_spec.rb +++ b/spec/remocon/command/create_command_spec.rb @@ -36,6 +36,7 @@ module Command context "files are valid" do let(:options) do { + id: "prj", parameters: fixture_path("valid_parameters.yml"), conditions: fixture_path("valid_conditions.yml") } diff --git a/spec/remocon/command/lib/conifg_spec.rb b/spec/remocon/command/lib/conifg_spec.rb new file mode 100644 index 0000000..7b560bf --- /dev/null +++ b/spec/remocon/command/lib/conifg_spec.rb @@ -0,0 +1,290 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Remocon + describe Config do + let(:config) { Config.new(opts) } + + context "#endpoint" do + context "opt-driven" do + let(:opts) { { id: "xxxxx" } } + + it "should return an opt value" do + expect(config.endpoint).to eq("https://firebaseremoteconfig.googleapis.com/v1/projects/xxxxx/remoteConfig") + end + + it "should return an opt value even if env exists" do + ENV[Remocon::Config::REMOCON_PROJECT_ID_KEY] = "yyyy" + + expect(config.endpoint).to eq("https://firebaseremoteconfig.googleapis.com/v1/projects/xxxxx/remoteConfig") + end + end + + context "env-driven" do + let(:opts) { {} } + + it "should return a complete endpoint" do + ENV[Remocon::Config::REMOCON_PROJECT_ID_KEY] = "yyyy" + + expect(config.endpoint).to eq("https://firebaseremoteconfig.googleapis.com/v1/projects/yyyy/remoteConfig") + end + + it "backward compatibility" do + ENV["FIREBASE_PROJECT_ID"] = "zzzzz" + + expect(config.endpoint).to eq("https://firebaseremoteconfig.googleapis.com/v1/projects/zzzzz/remoteConfig") + end + end + + context "no opts" do + let(:opts) { {} } + + it "should fail unless exists" do + expect { config.endpoint }.to raise_error(StandardError) + end + end + end + + context "#token" do + context "opt-driven" do + let(:opts) { { token: "x" } } + + it "should return an opt value" do + expect(config.token).to eq("x") + end + + it "should return an opt value even if env exists" do + ENV[Remocon::Config::REMOCON_ACCESS_TOKEN] = "yyyy" + + expect(config.token).to eq("x") + end + end + + context "env-driven" do + let(:opts) { {} } + + it "should return a complete endpoint" do + ENV[Remocon::Config::REMOCON_ACCESS_TOKEN] = "yyyy" + + expect(config.token).to eq("yyyy") + end + + it "backward compatibility" do + ENV["REMOTE_CONFIG_ACCESS_TOKEN"] = "xyz" + + expect(config.token).to eq("xyz") + end + end + + context "no opts" do + let(:opts) { {} } + + it "should fail unless exists" do + opts = {} + + expect { config.token }.to raise_error(StandardError) + end + end + end + + context "#destination_dir_path" do + context "opt-driven" do + let(:opts) { { prefix: "x" } } + + it "should return an opt value" do + expect(config.destination_dir_path).to eq("x") + end + + it "should return an opt value even if env exists" do + ENV[Remocon::Config::REMOCON_PREFIX_KEY] = "yyyy" + + expect(config.destination_dir_path).to eq("x") + end + end + context "backward compatibility" do + let(:opts) { { dest: "x" } } + + it "should return an opt value" do + expect(config.destination_dir_path).to eq("x") + end + + it "should return an opt value even if env exists" do + ENV[Remocon::Config::REMOCON_PREFIX_KEY] = "yyyy" + + expect(config.destination_dir_path).to eq("x") + end + end + + context "env-driven" do + let(:opts) { {} } + + it "should return a complete endpoint" do + ENV[Remocon::Config::REMOCON_PREFIX_KEY] = "yyyy" + + expect(config.destination_dir_path).to eq("yyyy") + end + end + + context "no opts" do + let(:opts) { {} } + + it "should return nil" do + expect(config.destination_dir_path).to be_falsey + end + end + end + + context "#project_dir_path" do + context "opt-driven" do + let(:opts) { { dest: "x", id: "pro" } } + + it "should return an opt value" do + expected = "#{config.destination_dir_path}/#{config.project_id}" + + expect(config.project_dir_path).to eq(expected) + end + end + + context "default value" do + let(:opts) { { id: "id" } } + + it "should be same with id" do + expect(config.project_dir_path).to eq("id") + end + end + end + + context "#config_json_file_path" do + context "opt-driven" do + let(:opts) { { dest: "x", id: "pro", source: "xyz" } } + + it "should return an opt value" do + expect(config.config_json_file_path).to eq("xyz") + end + end + + context "default value" do + let(:opts) { { dest: "x", id: "pro" } } + + it "should return an opt value" do + expected = "#{config.destination_dir_path}/#{config.project_id}/config.json" + + expect(config.config_json_file_path).to eq(expected) + end + end + end + + context "#conditions_file_path" do + context "opt-driven" do + let(:opts) { { dest: "x", id: "pro", conditions: "xyz" } } + + it "should return an opt value" do + expect(config.conditions_file_path).to eq("xyz") + end + end + + context "default value" do + let(:opts) { { dest: "x", id: "pro" } } + + it "should return an opt value" do + expected = "#{config.destination_dir_path}/#{config.project_id}/conditions.yml" + + expect(config.conditions_file_path).to eq(expected) + end + end + end + + context "#parameters_file_path" do + context "opt-driven" do + let(:opts) { { dest: "x", id: "pro", parameters: "xyz" } } + + it "should return an opt value" do + expect(config.parameters_file_path).to eq("xyz") + end + end + + context "default value" do + let(:opts) { { dest: "x", id: "pro" } } + + it "should return an opt value" do + expected = "#{config.destination_dir_path}/#{config.project_id}/parameters.yml" + + expect(config.parameters_file_path).to eq(expected) + end + end + end + + context "#etag_file_path" do + context "opt-driven but an etag file does not exist" do + let(:opts) { { dest: "x", id: "pro", etag: "xyz" } } + + it "should return an opt value" do + expect(config.etag_file_path).to eq("xyz") + end + end + + context "default value" do + let(:opts) { { dest: "x", id: "pro" } } + + it "should return an opt value" do + expected = "#{config.destination_dir_path}/#{config.project_id}/etag" + + expect(config.etag_file_path).to eq(expected) + end + end + end + + context "#etag" do + context "opt-driven" do + let(:opts) { { raw_etag: "xyz" } } + + it "should return an opt value" do + expect(config.etag).to eq("xyz") + end + + it "should raise an error if both are specified" do + opts[:force] = true + + expect { config.etag }.to raise_error(StandardError) + end + end + + context "a etag file exists" do + let(:opts) { { etag: fixture_path("etag_file") } } + + it "should return a content of a file" do + expect(config.etag).to eq("XYZXYZXYZXYZ") + end + + it "should return * if forced" do + opts[:force] = true + + expect(config.etag).to eq("*") + end + end + + context "a etag file does not exist" do + let(:opts) { { etag: "not found" } } + + it "should return nil" do + expect(config.etag).to be_falsey + end + + it "should return * if forced" do + opts[:force] = true + + expect(config.etag).to eq("*") + end + end + + context "no opts" do + let(:opts) { { id: "x" } } + + it "should return nil" do + expect(config.etag).to be_falsey + end + end + end + end +end diff --git a/spec/remocon/command/lib/interpreter_helper_spec.rb b/spec/remocon/command/lib/interpreter_helper_spec.rb index 508bf6c..dc17a3a 100644 --- a/spec/remocon/command/lib/interpreter_helper_spec.rb +++ b/spec/remocon/command/lib/interpreter_helper_spec.rb @@ -8,10 +8,16 @@ module Remocon Struct.new(:helper_klass) do include InterpreterHelper - def initialize - @parameters_filepath = "" - @conditions_filepath = "" - @cmd_opts = {} + def cmd_opts + {} + end + + def require_parameters_file_path + "" + end + + def require_conditions_file_path + "" end end end diff --git a/spec/remocon/command/pull_command_spec.rb b/spec/remocon/command/pull_command_spec.rb index 19c0a80..cabf1d0 100644 --- a/spec/remocon/command/pull_command_spec.rb +++ b/spec/remocon/command/pull_command_spec.rb @@ -5,7 +5,7 @@ module Remocon module Command describe Pull do - let(:command) { Pull.new({}) } + let(:command) { Pull.new({ token: "xyz", id: "dragon" }) } before do allow(command).to receive(:open) @@ -13,11 +13,11 @@ module Command context "#run" do it "should request with GET to a correct url" do - expect(command).to receive(:open).with("https://firebaseremoteconfig.googleapis.com/v1/projects/project_id/remoteConfig", { - "Authorization" => "Bearer token" + expect(command).to receive(:open).with("https://firebaseremoteconfig.googleapis.com/v1/projects/dragon/remoteConfig", { + "Authorization" => "Bearer xyz" }) - command.run + command.send("do_request") end end end diff --git a/spec/remocon/command/push_command_spec.rb b/spec/remocon/command/push_command_spec.rb index edc9c6f..cdc78da 100644 --- a/spec/remocon/command/push_command_spec.rb +++ b/spec/remocon/command/push_command_spec.rb @@ -8,7 +8,9 @@ module Command let(:command) { Push.new(options) } let(:base_options) do { - source: fixture_path("config.json") + source: fixture_path("config_file.json"), + token: "xyz", + id: "dragon", } end @@ -24,6 +26,13 @@ module Command expect { command.request }.to raise_error(StandardError) end end + context "a etag file is provided but not found" do + let(:options) { base_options.merge({ etag: "etag" }) } + + it "should raise an error without force option" do + expect { command.request }.to raise_error(StandardError) + end + end context "a etag is not provided but force option is provided" do let(:options) { base_options.merge({ force: true }) } @@ -33,16 +42,16 @@ module Command end end - context "a etag is provided" do - let(:options) { base_options.merge({ etag: "etag" }) } + context "a raw etag is provided" do + let(:options) { base_options.merge({ raw_etag: "ascasc" }) } it "can create a correct request without force option" do request = command.request - expect(request["Authorization"]).to eq("Bearer token") + expect(request["Authorization"]).to eq("Bearer xyz") expect(request["Content-Type"]).to eq("application/json; UTF8") - expect(request["If-Match"]).to eq("etag") - expect(request.path).to eq("/v1/projects/project_id/remoteConfig") + expect(request["If-Match"]).to eq("ascasc") + expect(request.path).to eq("/v1/projects/dragon/remoteConfig") end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e5c35e2..35a2da3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,14 @@ config.expect_with :rspec do |c| c.syntax = :expect end + + config.before(:each) do + ENV[Remocon::Config::REMOCON_PROJECT_ID_KEY] = nil + ENV[Remocon::Config::REMOCON_ACCESS_TOKEN] = nil + ENV[Remocon::Config::REMOCON_PREFIX_KEY] = nil + ENV["FIREBASE_PROJECT_ID"] = nil + ENV["REMOTE_CONFIG_ACCESS_TOKEN"] = nil + end end def fixture_path(fixture_name) @@ -65,8 +73,5 @@ def valid_conditions end def config_json - JSON.parse(File.open(fixture_path("config.json")).read).with_indifferent_access + JSON.parse(File.open(fixture_path("config_file.json")).read).with_indifferent_access end - -ENV["FIREBASE_PROJECT_ID"] = "project_id" -ENV["REMOTE_CONFIG_ACCESS_TOKEN"] = "token"