Skip to content

Commit

Permalink
Merge pull request #6 from jmatsu/change_option_handling
Browse files Browse the repository at this point in the history
Changeed option handling and deprecated some options
  • Loading branch information
jmatsu authored Aug 8, 2018
2 parents ebd570f + 6897adb commit 097f514
Show file tree
Hide file tree
Showing 19 changed files with 593 additions and 117 deletions.
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <firebase adminsdk json>` 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
Expand Down Expand Up @@ -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=</path/to/config json> --etag=</path/to/etag>

# Use the fixed etag value
bundle exec remocon push --raw_etag=<raw etag value>
```

## Installation
Expand Down
1 change: 1 addition & 0 deletions lib/remocon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
32 changes: 22 additions & 10 deletions lib/remocon/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,51 @@
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

private

def execute(klass)
klass.new(options).run
exit(1) unless klass.new(options).run
end
end
end
32 changes: 16 additions & 16 deletions lib/remocon/command/create_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
82 changes: 82 additions & 0 deletions lib/remocon/command/lib/config.rb
Original file line number Diff line number Diff line change
@@ -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
16 changes: 13 additions & 3 deletions lib/remocon/command/lib/interpreter_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
68 changes: 33 additions & 35 deletions lib/remocon/command/pull_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 097f514

Please sign in to comment.