-
-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
34826d6
commit 9165a09
Showing
9 changed files
with
356 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# frozen_string_literal: true | ||
|
||
require "erb" unless defined?(Erb) | ||
require "fileutils" unless defined?(FileUtils) | ||
require_relative "helpers/string" | ||
|
||
# Responsible for generating Dart source and test files. | ||
module DartCodeGenerator | ||
# Generates Dart code and test files for the given template arguments. | ||
def self.generate(template_arguments:, config:) | ||
dart_sources_dir = File.join("lib", config.result_path.downcase) | ||
dart_tests_dir = File.join("test", config.result_path.downcase) | ||
set_up_dart_interfaces(dart_sources_dir, template_arguments, config) | ||
set_up_dart_classes(dart_sources_dir, dart_tests_dir, template_arguments, config) | ||
end | ||
|
||
def self.set_up_dart_interfaces(path, template_arguments, config) | ||
dirname = File.dirname(__FILE__) | ||
sources_dir = path | ||
source_template = File.read("#{dirname}/templates/dart/arkana_protocol.dart.erb") | ||
FileUtils.mkdir_p(path) | ||
render(source_template, template_arguments, File.join(sources_dir, "#{config.namespace.downcase}_environment.dart")) | ||
end | ||
|
||
def self.set_up_dart_classes(sources_dir, tests_dir, template_arguments, config) | ||
dirname = File.dirname(__FILE__) | ||
source_template = File.read("#{dirname}/templates/dart/arkana.dart.erb") | ||
tests_template = File.read("#{dirname}/templates/dart/arkana_tests.dart.erb") | ||
FileUtils.mkdir_p(sources_dir) | ||
if config.should_generate_unit_tests | ||
FileUtils.mkdir_p(tests_dir) | ||
end | ||
render(source_template, template_arguments, File.join(sources_dir, "#{config.namespace.downcase}.dart")) | ||
if config.should_generate_unit_tests | ||
render(tests_template, template_arguments, File.join(tests_dir, "#{config.namespace.downcase}_test.dart")) | ||
end | ||
end | ||
|
||
def self.render(template, template_arguments, destination_file) | ||
renderer = ERB.new(template, trim_mode: ">") # Don't automatically add newlines at the end of each template tag | ||
result = renderer.result(template_arguments.get_binding) | ||
File.write(destination_file, result) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# frozen_string_literal: true | ||
|
||
# Utilities to reduce the amount of boilerplate code in `.dart.erb` template files. | ||
module DartTemplateHelper | ||
def self.dart_type(type) | ||
case type | ||
when :string then "String" | ||
when :boolean then "bool" | ||
when :integer then "int" | ||
else raise "Unknown variable type '#{type}' received." | ||
end | ||
end | ||
|
||
def self.dart_decode_function(type) | ||
case type | ||
when :string then "decode" | ||
when :boolean then "decodeBoolean" | ||
when :integer then "decodeInt" | ||
else raise "Unknown variable type '#{type}' received." | ||
end | ||
end | ||
|
||
def self.relative_path_to_source(src) | ||
slash_count = src.count("/") | ||
padding = src.empty? ? 1 : 2 | ||
"../" * (slash_count + padding) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<% require "arkana/helpers/string" %> | ||
<% require "arkana/helpers/dart_template_helper" %> | ||
import '<%=(@namespace).downcase%>_environment.dart'; | ||
|
||
// DO NOT MODIFY | ||
// Automatically generated by Arkana (https://github.com/rogerluan/arkana) | ||
const _salt = [<%= @salt.formatted %>]; | ||
|
||
class <%= @namespace %> { | ||
|
||
static final Global global = Global(); | ||
|
||
<% for environment in @environments %> | ||
static final <%= @namespace %>Environment <%=environment.camel_case()%> = _<%= environment %>(); | ||
|
||
<% end %> | ||
static String decode({required List<int> encoded, required List<int> cipher}) { | ||
var index = -1; | ||
final decoded = encoded.map((item) => | ||
(item ^ cipher[++index % cipher.length]) & 0xff | ||
).toList(); | ||
return String.fromCharCodes(decoded); | ||
} | ||
|
||
static int decodeInt({required List<int> encoded, required List<int> cipher}) { | ||
return int.parse(decode(encoded: encoded, cipher: cipher)); | ||
} | ||
|
||
static bool decodeBoolean({required List<int> encoded, required List<int> cipher}) { | ||
return decode(encoded: encoded, cipher: cipher) == 'true'; | ||
} | ||
|
||
} | ||
|
||
class Global { | ||
|
||
<% @global_secrets.each_with_index do |secret, index| %> | ||
<%= DartTemplateHelper.dart_type(secret.type) %> get <%= secret.key.camel_case %> { | ||
final encoded = <int>[<%= secret.encoded_value %>]; | ||
return <%= @namespace %>.<%= DartTemplateHelper.dart_decode_function(secret.type) %>(encoded: encoded, cipher: _salt); | ||
} | ||
<% unless index == @global_secrets.length - 1 %> | ||
|
||
<% end %> | ||
<% end %> | ||
|
||
} | ||
|
||
<% @environments.each_with_index do |environment, env_index| %> | ||
class _<%= environment %> implements <%= @namespace %>Environment { | ||
|
||
<% environment_protocol_secrets(environment).each_with_index do |secret, secret_index| %> | ||
@override | ||
<%= DartTemplateHelper.dart_type(secret.type) %> get <%= secret.protocol_key.camel_case %> { | ||
final encoded = <int>[<%= secret.encoded_value %>]; | ||
return <%= @namespace %>.<%= DartTemplateHelper.dart_decode_function(secret.type) %>(encoded: encoded, cipher: _salt); | ||
} | ||
<% unless secret_index == environment_protocol_secrets(environment).length - 1 %> | ||
|
||
<% end %> | ||
<% end %> | ||
|
||
} | ||
<% unless env_index == @environments.length - 1 %> | ||
|
||
<% end %> | ||
<% end %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<% require "arkana/helpers/string" %> | ||
<% require "arkana/helpers/dart_template_helper" %> | ||
// DO NOT MODIFY | ||
// Automatically generated by Arkana (https://github.com/rogerluan/arkana) | ||
|
||
abstract class <%= @namespace %>Environment { | ||
|
||
<% for secret in @environment_secrets.uniq(&:protocol_key) %> | ||
<%=DartTemplateHelper.dart_type(secret.type)%> get <%= secret.protocol_key.camel_case %>; | ||
<% end %> | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
<% require "arkana/helpers/string" %> | ||
<% require "arkana/helpers/dart_template_helper" %> | ||
// DO NOT MODIFY | ||
// Automatically generated by Arkana (https://github.com/rogerluan/arkana) | ||
|
||
|
||
import 'package:test/test.dart'; | ||
import '<%=DartTemplateHelper.relative_path_to_source(@result_path.downcase)%>lib/<%= @result_path.downcase%>/<%= @namespace.downcase %>.dart'; | ||
|
||
void main(){ | ||
const List<int> salt = [<%= @salt.formatted %>]; | ||
|
||
test("decodeRandomHexKey_shouldDecode", () { | ||
<% hex_key = SecureRandom.hex(64) %> | ||
<% secret = generate_test_secret(key: hex_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= hex_key %>"); | ||
}); | ||
|
||
test("decodeRandomBase64Key_shouldDecode", () { | ||
<% base64_key = SecureRandom.base64(64) %> | ||
<% secret = generate_test_secret(key: base64_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= base64_key %>"); | ||
}); | ||
|
||
test("decodeUUIDKey_shouldDecode", () { | ||
<% uuid_key = SecureRandom.uuid %> | ||
<% secret = generate_test_secret(key: uuid_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "<%= uuid_key %>"); | ||
}); | ||
|
||
test("decodeTrueBoolValue_shouldDecode", () { | ||
<% bool_key = "true" %> | ||
<% secret = generate_test_secret(key: bool_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
assert(<%= @namespace %>.decodeBoolean(encoded: encoded, cipher: salt)); | ||
}); | ||
|
||
test("decodeFalseBoolValue_shouldDecode", () { | ||
<% bool_key = "false" %> | ||
<% secret = generate_test_secret(key: bool_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
assert(!<%= @namespace %>.decodeBoolean(encoded: encoded, cipher: salt)); | ||
}); | ||
|
||
test("decodeIntValue_shouldDecode", () { | ||
<% int_key = "42" %> | ||
<% secret = generate_test_secret(key: int_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decodeInt(encoded: encoded, cipher: salt), 42); | ||
}); | ||
|
||
test("decodeIntValueWithLeadingZeroes_shouldDecodeAsString", () { | ||
<% int_with_leading_zeroes_key = "0001" %> | ||
<% secret = generate_test_secret(key: int_with_leading_zeroes_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "0001"); | ||
}); | ||
|
||
test("decodeMassiveIntValue_shouldDecodeAsString", () { | ||
<% int_with_massive_number_key = "92233720368547758079223372036854775807" %> | ||
<% secret = generate_test_secret(key: int_with_massive_number_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "92233720368547758079223372036854775807"); | ||
}); | ||
|
||
test("decodeNegativeIntValue_shouldDecodeAsString", () { | ||
<% negative_int_key = "-42" %> | ||
<% secret = generate_test_secret(key: negative_int_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "-42"); | ||
}); | ||
|
||
test("decodeFloatingPointValue_shouldDecodeAsString", () { | ||
<% float_key = "3.14" %> | ||
<% secret = generate_test_secret(key: float_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "3.14"); | ||
}); | ||
|
||
test("encodeAndDecodeValueWithDollarSign_shouldDecode", () { | ||
<% dollar_sign_key = "real_$lim_shady" %> | ||
<% secret = generate_test_secret(key: dollar_sign_key) %> | ||
const encoded = [<%= secret.encoded_value %>]; | ||
expect(<%= @namespace %>.decode(encoded: encoded, cipher: salt), "real_\$lim_shady"); | ||
}); | ||
|
||
|
||
<% if ENV["ARKANA_RUNNING_CI_INTEGRATION_TESTS"] %> | ||
const globalSecrets = <%= @namespace %>.Global; | ||
|
||
test("decodeEnvVarFromDotfile_withDollarSign__andEscaped_andNoQuotes_shouldDecode", () { | ||
expect(globalSecrets.secretWithDollarSignEscapedAndAndNoQuotesKey, "real_\$lim_shady"); | ||
}); | ||
|
||
test("decodeEnvVarFromDotfile_withDollarSign__andEscaped_andDoubleQuotes_shouldDecode", () { | ||
expect(globalSecrets.secretWithDollarSignEscapedAndDoubleQuoteKey, "real_\$lim_shady"); | ||
}); | ||
|
||
test("decodeEnvVarFromDotfile_withDollarSign__andNotEscaped_andSingleQuotes_shouldDecode", () { | ||
expect(globalSecrets.secretWithDollarSignNotEscapedAndSingleQuoteKey, "real_\$lim_shady"); | ||
}); | ||
|
||
test("decodeEnvVarFromDotfile_withDollarSign__andNotEscaped_andDoubleQuotes_shouldDecodeWithUnexpectedValue", () { | ||
expect(globalSecrets.secretWithDollarSignNotEscapedAndDoubleQuotesKey, "real_\$lim_shady"); | ||
}); | ||
|
||
test("test_decodeEnvVarFromDotfile_withDollarSign__andNotEscaped_andNoQuotes_shouldDecodeWithUnexpectedValue", () { | ||
expect(globalSecrets.secretWithDollarSignNotEscapedAndNoQuotesKey, "real_\$lim_shady"); | ||
}); | ||
|
||
test("test_decodeEnvVarFromDotfile_withWeirdCharacters_shouldDecode", () { | ||
expect(globalSecrets.secretWithWeirdCharactersKey, "` ~ ! @ # % ^ & * ( ) _ - + = { [ } } | : ; ' < , > . ? /"); | ||
}); | ||
|
||
<% end %> | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe DartCodeGenerator do | ||
let(:config) { Config.new(YAML.load_file("spec/fixtures/arkana-fixture.yml")) } | ||
let(:salt) { SaltGenerator.generate } | ||
let(:environment_secrets) do | ||
Encoder.encode!( | ||
keys: config.environment_keys, | ||
salt: salt, | ||
current_flavor: config.current_flavor, | ||
environments: config.environments, | ||
) | ||
end | ||
|
||
let(:global_secrets) do | ||
Encoder.encode!( | ||
keys: config.global_secrets, | ||
salt: salt, | ||
current_flavor: config.current_flavor, | ||
environments: config.environments, | ||
) | ||
end | ||
|
||
let(:template_arguments) do | ||
TemplateArguments.new( | ||
environment_secrets: environment_secrets, | ||
global_secrets: global_secrets, | ||
config: config, | ||
salt: salt, | ||
) | ||
end | ||
|
||
before do | ||
config.all_keys.each do |key| | ||
allow(ENV).to receive(:[]).with(key).and_return("value") | ||
end | ||
allow(ENV).to receive(:[]).with("ARKANA_RUNNING_CI_INTEGRATION_TESTS").and_return(true) | ||
end | ||
|
||
after { FileUtils.rm_rf(config.result_path) } | ||
|
||
describe ".generate" do | ||
let(:dart_module_dir) { config.result_path } | ||
let(:dart_sources_dir) { File.join("lib", dart_module_dir) } | ||
let(:dart_tests_dir) { File.join("test", dart_module_dir) } | ||
|
||
def path(...) | ||
Pathname.new(File.join(...)) | ||
end | ||
|
||
it "generates all necessary directories and files" do | ||
described_class.generate(template_arguments: template_arguments, config: config) | ||
expect(path(dart_sources_dir, "#{config.namespace.downcase}_environment.dart")).to be_file | ||
expect(path(dart_sources_dir, "#{config.namespace.downcase}.dart")).to be_file | ||
end | ||
|
||
context "when 'config.should_generate_unit_tests' is false" do | ||
before do | ||
allow(config).to receive(:should_generate_unit_tests).and_return(false) | ||
described_class.generate(template_arguments: template_arguments, config: config) | ||
end | ||
|
||
it "does not generate test folder or files" do | ||
expect(path(dart_tests_dir, "#{config.namespace.downcase}_test.dart")).not_to be_file | ||
expect(Pathname.new(dart_tests_dir)).not_to be_directory | ||
end | ||
end | ||
|
||
context "when 'config.should_generate_unit_tests' is true" do | ||
before do | ||
allow(config).to receive(:should_generate_unit_tests).and_return(true) | ||
described_class.generate(template_arguments: template_arguments, config: config) | ||
end | ||
|
||
it "generates test folder and files" do | ||
expect(path(dart_tests_dir, "#{config.namespace.downcase}_test.dart")).to be_file | ||
end | ||
end | ||
end | ||
end |