From e38335b7ed84a89c6ce68d40a5f847d0b09590db Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Wed, 6 Oct 2021 15:25:55 +0530 Subject: [PATCH 1/7] feat/ruby-client: initialize ruby client --- clients/ruby/Gemfile | 10 ++++++++++ clients/ruby/README.md | 26 ++++++++++++++++++++++++++ clients/ruby/Rakefile | 8 ++++++++ clients/ruby/bin/console | 15 +++++++++++++++ clients/ruby/bin/setup | 8 ++++++++ clients/ruby/lib/stencil.rb | 8 ++++++++ clients/ruby/stencil.gemspec | 35 +++++++++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+) create mode 100644 clients/ruby/Gemfile create mode 100644 clients/ruby/README.md create mode 100644 clients/ruby/Rakefile create mode 100755 clients/ruby/bin/console create mode 100755 clients/ruby/bin/setup create mode 100644 clients/ruby/lib/stencil.rb create mode 100644 clients/ruby/stencil.gemspec diff --git a/clients/ruby/Gemfile b/clients/ruby/Gemfile new file mode 100644 index 00000000..79ccf0af --- /dev/null +++ b/clients/ruby/Gemfile @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +# Specify your gem's dependencies in stencil.gemspec +gemspec + +gem "rake", "~> 13.0" + +gem "rubocop", "~> 1.7" diff --git a/clients/ruby/README.md b/clients/ruby/README.md new file mode 100644 index 00000000..b75d3df0 --- /dev/null +++ b/clients/ruby/README.md @@ -0,0 +1,26 @@ +# Stencil + +Stencil ruby gem provides a store to lookup protobuf descriptors and options to keep the protobuf descriptors upto date. + +## Installation + +```ruby +gem 'stencil' +``` + +And then execute: + + $ bundle install + +Or install it yourself as: + + $ gem install stencil + +## Usage + + +## Development + +After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. + +To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org). diff --git a/clients/ruby/Rakefile b/clients/ruby/Rakefile new file mode 100644 index 00000000..19241434 --- /dev/null +++ b/clients/ruby/Rakefile @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require "bundler/gem_tasks" +require "rubocop/rake_task" + +RuboCop::RakeTask.new + +task default: :rubocop diff --git a/clients/ruby/bin/console b/clients/ruby/bin/console new file mode 100755 index 00000000..13d1e32e --- /dev/null +++ b/clients/ruby/bin/console @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "bundler/setup" +require "stencil" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/clients/ruby/bin/setup b/clients/ruby/bin/setup new file mode 100755 index 00000000..dce67d86 --- /dev/null +++ b/clients/ruby/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/clients/ruby/lib/stencil.rb b/clients/ruby/lib/stencil.rb new file mode 100644 index 00000000..19861c52 --- /dev/null +++ b/clients/ruby/lib/stencil.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +require_relative "stencil/version" + +module Stencil + class Error < StandardError; end + # Your code goes here... +end diff --git a/clients/ruby/stencil.gemspec b/clients/ruby/stencil.gemspec new file mode 100644 index 00000000..1a6eed7e --- /dev/null +++ b/clients/ruby/stencil.gemspec @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require_relative "lib/stencil/version" + +Gem::Specification.new do |spec| + spec.name = "stencil" + spec.version = Stencil::VERSION + spec.authors = ["Daval Pargal"] + spec.email = ["davalpargal@gmail.com"] + + spec.summary = "Stencil ruby gem provides a store to lookup protobuf descriptors and options to keep the protobuf descriptors upto date." + spec.homepage = "https://odpf.gitbook.io/stencil/" + spec.required_ruby_version = ">= 2.4.0" + + spec.metadata["allowed_push_host"] = "" + + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = "https://github.com/odpf/stencil" + spec.metadata["changelog_uri"] = "https://github.com/odpf/stencil/blob/master/CHANGELOG.md" + + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + spec.files = Dir.chdir(File.expand_path(__dir__)) do + `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) } + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + # Uncomment to register a new dependency of your gem + # spec.add_dependency "example-gem", "~> 1.0" + + # For more information and examples about making a new gem, checkout our + # guide at: https://bundler.io/guides/creating_gem.html +end From 384da70a593b9020037e9ddbac4e7c9ca109e011 Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Tue, 12 Oct 2021 23:03:54 +0530 Subject: [PATCH 2/7] Add configuration for stencil --- clients/ruby/.gitignore | 16 ++++ clients/ruby/.rspec | 5 ++ clients/ruby/Gemfile | 8 +- clients/ruby/lib/stencil.rb | 7 +- clients/ruby/lib/stencil/configuration.rb | 55 ++++++++++++++ clients/ruby/lib/stencil/constants.rb | 4 + clients/ruby/lib/stencil/version.rb | 5 ++ clients/ruby/spec/spec_helper.rb | 34 +++++++++ .../ruby/spec/stencil/configuration_spec.rb | 76 +++++++++++++++++++ clients/ruby/stencil.gemspec | 6 +- 10 files changed, 206 insertions(+), 10 deletions(-) create mode 100644 clients/ruby/.gitignore create mode 100644 clients/ruby/.rspec create mode 100644 clients/ruby/lib/stencil/configuration.rb create mode 100644 clients/ruby/lib/stencil/constants.rb create mode 100644 clients/ruby/lib/stencil/version.rb create mode 100644 clients/ruby/spec/spec_helper.rb create mode 100644 clients/ruby/spec/stencil/configuration_spec.rb diff --git a/clients/ruby/.gitignore b/clients/ruby/.gitignore new file mode 100644 index 00000000..72c4ecbc --- /dev/null +++ b/clients/ruby/.gitignore @@ -0,0 +1,16 @@ +.mine +*.gem +.config +.rvmrc +.yardoc +InstalledFiles +_yardoc + +.bundle +.ruby-version +doc +coverage +pkg +spec/examples.txt +tmp +Gemfile.lock \ No newline at end of file diff --git a/clients/ruby/.rspec b/clients/ruby/.rspec new file mode 100644 index 00000000..584d9ceb --- /dev/null +++ b/clients/ruby/.rspec @@ -0,0 +1,5 @@ +--backtrace +--color +--format=documentation +--order random +--require spec_helper \ No newline at end of file diff --git a/clients/ruby/Gemfile b/clients/ruby/Gemfile index 79ccf0af..4e6eb691 100644 --- a/clients/ruby/Gemfile +++ b/clients/ruby/Gemfile @@ -6,5 +6,11 @@ source "https://rubygems.org" gemspec gem "rake", "~> 13.0" +gem "http", "4.4.1" -gem "rubocop", "~> 1.7" +group :test do + gem "simplecov", ">= 0.9" + gem "rspec", "~> 3.0" + gem "rubocop", "~> 1.7" + gem "pry" +end diff --git a/clients/ruby/lib/stencil.rb b/clients/ruby/lib/stencil.rb index 19861c52..d4a37352 100644 --- a/clients/ruby/lib/stencil.rb +++ b/clients/ruby/lib/stencil.rb @@ -1,8 +1,7 @@ -# frozen_string_literal: true - -require_relative "stencil/version" +require "stencil/version" +require "stencil/configuration" +require "stencil/constants" module Stencil class Error < StandardError; end - # Your code goes here... end diff --git a/clients/ruby/lib/stencil/configuration.rb b/clients/ruby/lib/stencil/configuration.rb new file mode 100644 index 00000000..42c11b07 --- /dev/null +++ b/clients/ruby/lib/stencil/configuration.rb @@ -0,0 +1,55 @@ +module Stencil + class Configuration + def initialize + @config = ::OpenStruct.new + end + + def registry_urls + @config.registry_urls || [] + end + + def registry_urls=(registry_urls) + @config.registry_urls = registry_urls + end + + def http_timeout + @config.http_timeout || DEFAULT_TIMEOUT_IN_MS + end + + def http_timeout=(timeout) + @config.http_timeout = timeout + end + + def refresh_enabled + @config.refresh_enabled.nil? ? true : @config.refresh_enabled + end + + def refresh_enabled=(refresh_enabled = true) + @config.refresh_enabled = refresh_enabled + end + + def refresh_ttl_in_secs + @config.refresh_ttl_in_secs || DEFAULT_REFRESH_INTERVAL_IN_SECONDS + end + + def refresh_ttl_in_secs=(refresh_ttl_in_secs) + @config.refresh_ttl_in_secs = refresh_ttl_in_secs + end + + def bearer_token=(token) + @config.bearer_token = "Bearer " + token + end + + def bearer_token + @config.bearer_token + end + end + + def self.configuration + @config ||= Configuration.new + end + + def self.configure + yield(configuration) + end +end \ No newline at end of file diff --git a/clients/ruby/lib/stencil/constants.rb b/clients/ruby/lib/stencil/constants.rb new file mode 100644 index 00000000..90e779bd --- /dev/null +++ b/clients/ruby/lib/stencil/constants.rb @@ -0,0 +1,4 @@ +module Stencil + DEFAULT_TIMEOUT_IN_MS = 10000 + DEFAULT_REFRESH_INTERVAL_IN_SECONDS = 43200 +end \ No newline at end of file diff --git a/clients/ruby/lib/stencil/version.rb b/clients/ruby/lib/stencil/version.rb new file mode 100644 index 00000000..74c0d88f --- /dev/null +++ b/clients/ruby/lib/stencil/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Stencil + VERSION = "0.1.0" +end diff --git a/clients/ruby/spec/spec_helper.rb b/clients/ruby/spec/spec_helper.rb new file mode 100644 index 00000000..c0b08d9a --- /dev/null +++ b/clients/ruby/spec/spec_helper.rb @@ -0,0 +1,34 @@ +require 'simplecov' + +SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( + [ + SimpleCov::Formatter::HTMLFormatter, + ] +) + +SimpleCov.start do + add_filter "/spec/" + minimum_coverage 80 +end + +require 'bundler/setup' +Bundler.setup + +require 'pry' +require 'stencil' + +RSpec.configure do |config| + config.filter_run_when_matching focus: true + config.disable_monkey_patching! + config.profile_examples = 10 + config.order = :random +end + +module JSON + module_function + + def reader(file_name) + path = File.join(File.dirname(__FILE__), "test_data/#{file_name}") + File.read(path) + end +end diff --git a/clients/ruby/spec/stencil/configuration_spec.rb b/clients/ruby/spec/stencil/configuration_spec.rb new file mode 100644 index 00000000..872d1a64 --- /dev/null +++ b/clients/ruby/spec/stencil/configuration_spec.rb @@ -0,0 +1,76 @@ +module Stencil + RSpec.describe Configuration do + let(:configuration) { Stencil.configuration } + + it 'should set registry urls correctly' do + expect(configuration.registry_urls).to eq([]) + + expected_value = ['http://localhost:3000'] + configuration.registry_urls = expected_value + expect(configuration.registry_urls).to eq(expected_value) + end + + + it 'should set http_timeout correctly' do + expect(configuration.http_timeout).to eq(DEFAULT_TIMEOUT_IN_MS) + + timeout = 3000 + configuration.http_timeout = timeout + expect(configuration.http_timeout).to eq(3000) + end + + it 'should set refresh_enabled correctly' do + expect(configuration.refresh_enabled).to eq(true) + + refresh_enabled = false + configuration.refresh_enabled = refresh_enabled + expect(configuration.refresh_enabled).to eq(refresh_enabled) + end + + it 'should set refresh_ttl_in_secs correctly' do + expect(configuration.refresh_ttl_in_secs).to eq(DEFAULT_REFRESH_INTERVAL_IN_SECONDS) + + refresh_ttl_in_secs = 50000 + configuration.refresh_ttl_in_secs = refresh_ttl_in_secs + expect(configuration.refresh_ttl_in_secs).to eq(refresh_ttl_in_secs) + end + + it 'should set bearer_token correctly' do + token = "sampletoken" + expected_bearer_token = "Bearer sampletoken" + configuration.bearer_token = token + expect(configuration.bearer_token).to eq(expected_bearer_token) + end + + + + describe '#configure' do + let(:refresh_enabled) {true} + let(:refresh_ttl_in_secs) {60000} + let(:registry_urls) {["abc.com/latest"]} + let(:token) {"ABCD1234"} + let(:bearer_token) {"Bearer " + token} + let(:http_timeout) {6000} + + before(:each) do + Stencil.configure do |config| + config.registry_urls = registry_urls + config.bearer_token = token + config.refresh_enabled = refresh_enabled + config.refresh_ttl_in_secs = refresh_ttl_in_secs + config.http_timeout = http_timeout + end + end + + subject { Stencil.configuration } + + it 'should set configuration correctly' do + expect(subject.registry_urls).to eq(registry_urls) + expect(subject.bearer_token).to eq(bearer_token) + expect(subject.refresh_enabled).to eq(refresh_enabled) + expect(subject.refresh_ttl_in_secs).to eq(refresh_ttl_in_secs) + expect(subject.http_timeout).to eq(http_timeout) + end + end + end +end diff --git a/clients/ruby/stencil.gemspec b/clients/ruby/stencil.gemspec index 1a6eed7e..49676067 100644 --- a/clients/ruby/stencil.gemspec +++ b/clients/ruby/stencil.gemspec @@ -27,9 +27,5 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - # Uncomment to register a new dependency of your gem - # spec.add_dependency "example-gem", "~> 1.0" - - # For more information and examples about making a new gem, checkout our - # guide at: https://bundler.io/guides/creating_gem.html + spec.add_development_dependency "bundler", "~> 1.17.3" end From 8d4c54c6bc0e3c3139d9f9c7d06bd6538ccabe4f Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Wed, 1 Dec 2021 00:47:31 -0500 Subject: [PATCH 3/7] Add client for stencil to get file set descriptor --- clients/ruby/Gemfile | 3 + clients/ruby/lib/stencil.rb | 15 +++- clients/ruby/lib/stencil/client.rb | 48 +++++++++++++ clients/ruby/lib/stencil/configuration.rb | 8 +-- clients/ruby/spec/data/desc-proto-bin | Bin 0 -> 3712 bytes clients/ruby/spec/spec_helper.rb | 10 +-- clients/ruby/spec/stencil/client_spec.rb | 68 ++++++++++++++++++ .../ruby/spec/stencil/configuration_spec.rb | 16 ++--- clients/ruby/stencil.gemspec | 1 + 9 files changed, 145 insertions(+), 24 deletions(-) create mode 100644 clients/ruby/lib/stencil/client.rb create mode 100644 clients/ruby/spec/data/desc-proto-bin create mode 100644 clients/ruby/spec/stencil/client_spec.rb diff --git a/clients/ruby/Gemfile b/clients/ruby/Gemfile index 4e6eb691..b30038a9 100644 --- a/clients/ruby/Gemfile +++ b/clients/ruby/Gemfile @@ -7,10 +7,13 @@ gemspec gem "rake", "~> 13.0" gem "http", "4.4.1" +gem 'concurrent-ruby', require: 'concurrent' +gem 'protobuf' group :test do gem "simplecov", ">= 0.9" gem "rspec", "~> 3.0" gem "rubocop", "~> 1.7" gem "pry" + gem "google-protobuf" end diff --git a/clients/ruby/lib/stencil.rb b/clients/ruby/lib/stencil.rb index d4a37352..76e79d9f 100644 --- a/clients/ruby/lib/stencil.rb +++ b/clients/ruby/lib/stencil.rb @@ -1,7 +1,16 @@ -require "stencil/version" -require "stencil/configuration" -require "stencil/constants" +require_relative "stencil/version" +require_relative "stencil/configuration" +require_relative "stencil/constants" +require_relative "stencil/client" + +require "http" +require "concurrent" +require "protobuf" module Stencil class Error < StandardError; end + class InvalidConfiguration < Error; end + class InvalidProtoClass < Error; end + class HTTPClientError < Error; end + class HTTPServerError < Error; end end diff --git a/clients/ruby/lib/stencil/client.rb b/clients/ruby/lib/stencil/client.rb new file mode 100644 index 00000000..2116de21 --- /dev/null +++ b/clients/ruby/lib/stencil/client.rb @@ -0,0 +1,48 @@ +module Stencil + class Client + attr_reader :root + def initialize + begin + @config = Stencil.configuration + validate_configuration(@config) + @root = nil + setup_http_client + load_descriptors + end + end + + def get_type(proto_name) + @root.file.each do |file_desc| + file_desc.message_type.each do |message| + if proto_name == "#{file_desc.options.java_package}.#{message.name}" + return message + end + end + end + raise InvalidProtoClass.new + end + + private + + def validate_configuration(configuration) + raise Stencil::InvalidConfiguration.new() if configuration.registry_url.nil? || configuration.bearer_token.nil? || configuration.bearer_token == "Bearer " + end + + def setup_http_client + @http_client = HTTP.auth(@config.bearer_token).timeout(@config.http_timeout) + end + + def load_descriptors + begin + response = @http_client.get(@config.registry_url) + if response.code != 200 + raise HTTPServerError.new("Error while fetching descriptor file: Got #{response.code} from stencil server") + end + rescue StandardError => e + raise HTTPClientError.new(e.message) + end + + @root = Google::Protobuf::FileDescriptorSet.decode(response.body) + end + end +end \ No newline at end of file diff --git a/clients/ruby/lib/stencil/configuration.rb b/clients/ruby/lib/stencil/configuration.rb index 42c11b07..85353e20 100644 --- a/clients/ruby/lib/stencil/configuration.rb +++ b/clients/ruby/lib/stencil/configuration.rb @@ -4,12 +4,12 @@ def initialize @config = ::OpenStruct.new end - def registry_urls - @config.registry_urls || [] + def registry_url + @config.registry_url end - def registry_urls=(registry_urls) - @config.registry_urls = registry_urls + def registry_url=(registry_url) + @config.registry_url = registry_url end def http_timeout diff --git a/clients/ruby/spec/data/desc-proto-bin b/clients/ruby/spec/data/desc-proto-bin new file mode 100644 index 0000000000000000000000000000000000000000..64e4a3b04f22670a384f89167cf96e1ce48b1f5d GIT binary patch literal 3712 zcmeHJ&2!sC6t5k}N!Iz;C~51uZQ8m(zb5vO0+${bN#3}sWyvGSP0|Az#a@iSmVza> z(*w+K=fIHz7dSESpTL=afPVrG9C)i;IgaDdLVN0P)7yQYzqjxAguc(BTfOj8`dO7m zht>G`5sj)XZy5U>|HunsJNDvHR5==kaVV`NiWN|-B#Pya(E2IevW9Xb7!9NXT4);$ z!`w6QIVPwUksVUQ8EF|U%Bo6QOv*CJB9Dm5q&cRj)eX}EA*XzSHag*;G8I<2a4Kkv z<@dyh!qKopLBH=EMioD-cD>khY0&cnN{4>XtM_w@@}iH9i*}== z>cf0e_I>o*0=hAckxhqB{SI{iu(QzlF^>upYUOY9XCPBIT}!JGKnAJ@!6pU+MhFkh z7T~gm3QgH+5T~WfD#1B%yK9;?JTC-LaByBiC=X5)b005=Z%5n5i$Y1)b_rf$sDn~N z(`5%E38Uq@nFy}U;uVBp6WXvWzE#=6D_oP?xdz$8tHP726WPFP(kepWw<+7UTqhWd zpmkkt;&ma&ZO6ot@TBN%;wqX|!yA%>R$29${+L+&Y*Tn|wRKy@=hM>K2l#@tj#g|L z5|j@_85h|Rs~o(!s@YUbTukjv&2aD~7BjU;KrV@t_DrjRFQ>_Bj|sk#T3Tk)#8*Y+ zJc z0EJgUEPR)}I!FvU@9sO6jPD5;Km~WC6|`j6U4D;u#MLf5wfn-eF1L-Eb%5VX;{sv_ z`292vu^hPx!SM%5fA*l`iSgkhuD;5MM|@_-bNzD$m8R#~p;3IsK;PX%g_iex0L0M@ zyHqNoO+j!j0MaCe<2Fqg<8#s?%Biyb5C(b)&DTu^SHg4f!I4dPt_9<&$vW`d<=N%u zSTf^m|Hz^{GWDN}4_kd`}DIRfp z9cTJyCi6pfuHI69%5K$@aw>@%$hNXa`~5}`J_{JFM&rtVSws(x`{ea{L)!CVfZ>?3 zt8#kUomdV66@U#ET(Y;u#+3=<(u;5{z3hrCy?9TQf6t=g%xoL~x8c`Yd!CK=+u&swNJCzh9j$r$Bxfj;jfA+G%y^x@r_}D^)Xvg)t zZl6A-{o-8mG?tqC_r+ZDm~!k!`Vi&$_AJiB>FUm{>Fr&a=nv$}{w~<> zf+ftP8d?@}>av$p%qMPcoN_Zk4ctLNn6bB0{ws^F%mlefqsZ&g|ABrzKr`le>h(vo zxR5*oE-2q*(9M&7ZZii@Db4IrN-K^3M)u=SlLRp)ldl)hyR*ooO9x)io#2sLav&&# z?};f2;U+I;kCz~Y@3Ls)7z-Zyjo^dnaptV>Z^!x9EV?)i&l@wzuLUCM4#K0r|9nK< t&M1n*0UbsOcg|UB{9H|sm9H{r>#XVIZ(2D&jdnVdyRSNvJU}{*e*?Y0gyH}I literal 0 HcmV?d00001 diff --git a/clients/ruby/spec/spec_helper.rb b/clients/ruby/spec/spec_helper.rb index c0b08d9a..9da8ec7f 100644 --- a/clients/ruby/spec/spec_helper.rb +++ b/clients/ruby/spec/spec_helper.rb @@ -16,6 +16,7 @@ require 'pry' require 'stencil' +require 'webmock/rspec' RSpec.configure do |config| config.filter_run_when_matching focus: true @@ -24,11 +25,4 @@ config.order = :random end -module JSON - module_function - - def reader(file_name) - path = File.join(File.dirname(__FILE__), "test_data/#{file_name}") - File.read(path) - end -end +WebMock.disable_net_connect!(allow_localhost: true) \ No newline at end of file diff --git a/clients/ruby/spec/stencil/client_spec.rb b/clients/ruby/spec/stencil/client_spec.rb new file mode 100644 index 00000000..b3c4213e --- /dev/null +++ b/clients/ruby/spec/stencil/client_spec.rb @@ -0,0 +1,68 @@ +module Stencil + RSpec.describe Client do + let(:registry_url) { 'http://stencil.test' } + let(:bearer_token) { 'sample-token-123' } + let(:service_type_message) do + service_type = nil + Google::Protobuf::FileDescriptorSet.decode(File.read('spec/data/desc-proto-bin')).file.each do |file| + file.message_type.each do |msg| + if msg.name == "ServiceType" + service_type = msg + end + end + end + service_type + end + + context '#get_type' do + subject { Stencil::Client.new } + + before(:each) do + Stencil.configure do |config| + config.registry_url = registry_url + config.bearer_token = bearer_token + end + + @stencil_get_stub = stub_request(:get, registry_url). + with( + headers: { + 'Authorization' => 'Bearer ' + bearer_token, + 'Connection' => 'close', + 'Host' => 'stencil.test', + 'User-Agent' => 'http.rb/4.4.1' + }) + end + + it 'should raise error if configs are invalid' do + config = Stencil.configuration + config.bearer_token = "" + expect { subject }.to raise_error(Stencil::InvalidConfiguration) + end + + it 'should raise error if http client returns error on stencil get api' do + @stencil_get_stub.to_raise(StandardError.new('some error')) + expect { subject.get_type }.to raise_error(Stencil::HTTPClientError) + end + + it 'should raise error if http client returns 500' do + @stencil_get_stub.to_return(status: 500, body: 'Internal server error', headers: {}) + expect { subject.get_type }.to raise_error(Stencil::HTTPClientError) + end + + it 'should raise error for invalid proto type' do + @stencil_get_stub.to_return(status: 200, body: File.new('spec/data/desc-proto-bin'), headers: {}) + + proto_name = "incorrect" + expect { subject.get_type(proto_name) }.to raise_error(Stencil::InvalidProtoClass) + end + + it 'should successfully return proto type' do + @stencil_get_stub.to_return(status: 200, body: File.new('spec/data/desc-proto-bin'), headers: {}) + + proto_name = "com.gojek.esb.types.ServiceType" + actual_type = subject.get_type(proto_name) + expect(actual_type).to eq(service_type_message) + end + end + end +end \ No newline at end of file diff --git a/clients/ruby/spec/stencil/configuration_spec.rb b/clients/ruby/spec/stencil/configuration_spec.rb index 872d1a64..30b81966 100644 --- a/clients/ruby/spec/stencil/configuration_spec.rb +++ b/clients/ruby/spec/stencil/configuration_spec.rb @@ -3,11 +3,11 @@ module Stencil let(:configuration) { Stencil.configuration } it 'should set registry urls correctly' do - expect(configuration.registry_urls).to eq([]) + expect(configuration.registry_url).to eq(nil) - expected_value = ['http://localhost:3000'] - configuration.registry_urls = expected_value - expect(configuration.registry_urls).to eq(expected_value) + expected_value = 'http://localhost:3000' + configuration.registry_url = expected_value + expect(configuration.registry_url).to eq(expected_value) end @@ -42,19 +42,17 @@ module Stencil expect(configuration.bearer_token).to eq(expected_bearer_token) end - - describe '#configure' do let(:refresh_enabled) {true} let(:refresh_ttl_in_secs) {60000} - let(:registry_urls) {["abc.com/latest"]} + let(:registry_url) {"abc.com/latest"} let(:token) {"ABCD1234"} let(:bearer_token) {"Bearer " + token} let(:http_timeout) {6000} before(:each) do Stencil.configure do |config| - config.registry_urls = registry_urls + config.registry_url = registry_url config.bearer_token = token config.refresh_enabled = refresh_enabled config.refresh_ttl_in_secs = refresh_ttl_in_secs @@ -65,7 +63,7 @@ module Stencil subject { Stencil.configuration } it 'should set configuration correctly' do - expect(subject.registry_urls).to eq(registry_urls) + expect(subject.registry_url).to eq(registry_url) expect(subject.bearer_token).to eq(bearer_token) expect(subject.refresh_enabled).to eq(refresh_enabled) expect(subject.refresh_ttl_in_secs).to eq(refresh_ttl_in_secs) diff --git a/clients/ruby/stencil.gemspec b/clients/ruby/stencil.gemspec index 49676067..6d6f9109 100644 --- a/clients/ruby/stencil.gemspec +++ b/clients/ruby/stencil.gemspec @@ -28,4 +28,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.17.3" + spec.add_development_dependency "webmock", "~> 3.14.0" end From dbd1aa0b6a05ec2036ea0255c37af6b275d9eab7 Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Sun, 5 Dec 2021 06:59:00 -0500 Subject: [PATCH 4/7] Add store for saving current proto descriptor file set --- clients/ruby/lib/stencil.rb | 4 +++- clients/ruby/lib/stencil/client.rb | 23 ++++++++++++++++--- clients/ruby/lib/stencil/store.rb | 18 +++++++++++++++ clients/ruby/spec/stencil/store_spec.rb | 30 +++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 clients/ruby/lib/stencil/store.rb create mode 100644 clients/ruby/spec/stencil/store_spec.rb diff --git a/clients/ruby/lib/stencil.rb b/clients/ruby/lib/stencil.rb index 76e79d9f..600d36a6 100644 --- a/clients/ruby/lib/stencil.rb +++ b/clients/ruby/lib/stencil.rb @@ -2,9 +2,11 @@ require_relative "stencil/configuration" require_relative "stencil/constants" require_relative "stencil/client" +require_relative "stencil/store" require "http" -require "concurrent" +require "concurrent/timer_task" +require "concurrent/mutable_struct" require "protobuf" module Stencil diff --git a/clients/ruby/lib/stencil/client.rb b/clients/ruby/lib/stencil/client.rb index 2116de21..b4378dbe 100644 --- a/clients/ruby/lib/stencil/client.rb +++ b/clients/ruby/lib/stencil/client.rb @@ -5,14 +5,18 @@ def initialize begin @config = Stencil.configuration validate_configuration(@config) - @root = nil + setup_http_client + + @store = Store.new load_descriptors + setup_store_update_job end end def get_type(proto_name) - @root.file.each do |file_desc| + file_descriptor_set = @store.read(@config.registry_url) + file_descriptor_set.file.each do |file_desc| file_desc.message_type.each do |message| if proto_name == "#{file_desc.options.java_package}.#{message.name}" return message @@ -22,6 +26,10 @@ def get_type(proto_name) raise InvalidProtoClass.new end + def close + @task.shutdown + end + private def validate_configuration(configuration) @@ -42,7 +50,16 @@ def load_descriptors raise HTTPClientError.new(e.message) end - @root = Google::Protobuf::FileDescriptorSet.decode(response.body) + file_descriptor_set = Google::Protobuf::FileDescriptorSet.decode(response.body) + @store.write(@config.registry_url, file_descriptor_set) + end + + def setup_store_update_job + begin + @task = Concurrent::TimerTask.new(execution_interval: @config.refresh_ttl_in_secs) do + load_descriptors + end + end end end end \ No newline at end of file diff --git a/clients/ruby/lib/stencil/store.rb b/clients/ruby/lib/stencil/store.rb new file mode 100644 index 00000000..280d8b8a --- /dev/null +++ b/clients/ruby/lib/stencil/store.rb @@ -0,0 +1,18 @@ +module Stencil + class Store + def initialize + @lock = Concurrent::ReadWriteLock.new + @data = Hash.new + end + + def write(key, value) + @lock.with_write_lock do + @data.store(key, value) + end + end + + def read(key) + @data[key] + end + end +end \ No newline at end of file diff --git a/clients/ruby/spec/stencil/store_spec.rb b/clients/ruby/spec/stencil/store_spec.rb new file mode 100644 index 00000000..62ac7fe0 --- /dev/null +++ b/clients/ruby/spec/stencil/store_spec.rb @@ -0,0 +1,30 @@ +module Stencil + RSpec.describe Store do + context "#read" do + let(:sample_key) { "sample_key" } + let(:sample_value) { 123 } + + it "should be able to handle concurrent reads of data" do + store = Store.new + store.write(sample_key, sample_value) + + 5.times do + Thread.start do + expect(store.read(sample_key)).to eq(sample_value) + end + end + end + end + + context "#write" do + let(:sample_key) { "sample_key" } + let(:sample_value) { 100 } + + it "should be able to handle concurrent writes of data by locking data" do + store = Store.new + store.write(sample_key, sample_value) + expect(store.read(sample_key)).to eq(sample_value) + end + end + end +end From e6c68111b30902f5d6d7a0ba2000c44e626343df Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Sun, 5 Dec 2021 07:53:09 -0500 Subject: [PATCH 5/7] Add workflow for ruby client testing --- .github/workflows/ruby-client-test.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/ruby-client-test.yaml diff --git a/.github/workflows/ruby-client-test.yaml b/.github/workflows/ruby-client-test.yaml new file mode 100644 index 00000000..9cd336a9 --- /dev/null +++ b/.github/workflows/ruby-client-test.yaml @@ -0,0 +1,21 @@ +name: Test stencil RUBY client +on: + push: + paths: + - "clients/ruby/**" + branches: + - master + pull_request: + paths: + - "clients/ruby/**" + branches: + - master +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-ruby@v1 + with: + ruby-version: '2.5' + - run: cd clients/ruby; bin/setup; rspec -fd From b75752718aa592fc1a31d95e75b6dd17edaf5073 Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Sun, 5 Dec 2021 09:06:16 -0500 Subject: [PATCH 6/7] Update README.md --- clients/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/clients/README.md b/clients/README.md index c2afffe0..fd8e90d5 100644 --- a/clients/README.md +++ b/clients/README.md @@ -20,3 +20,4 @@ Stencil clients abstracts handling of descriptorset file on client side. Current - [Java](java) - [Go](go) - [Javascript](js) + - [Ruby](ruby) From 4dd7e95df865d4c5a6909fa59bfa430a492f457f Mon Sep 17 00:00:00 2001 From: Daval Pargal Date: Tue, 14 Dec 2021 01:09:27 -0500 Subject: [PATCH 7/7] Fix repository setup --- .github/workflows/release.yml | 25 +++++++++++++++++++++++++ clients/ruby/bin/setup | 1 + 2 files changed, 26 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91b98640..48c7aa54 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -73,6 +73,31 @@ jobs: working-directory: clients/js env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + publish-ruby-client: + runs-on: ubuntu-latest + needs: create-release + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v2 + - name: Set up Ruby 2.6 + uses: ruby/setup-ruby@477b21f02be01bcb8030d50f37cfec92bfa615b6 + with: + ruby-version: 2.6 + - run: bundle install + working-directory: clients/ruby + - name: Publish to RubyGems + run: | + mkdir -p $HOME/.gem + touch $HOME/.gem/credentials + chmod 0600 $HOME/.gem/credentials + printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials + gem build *.gemspec + gem push *.gem + env: + GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" + working-directory: clients/ruby publish-server: runs-on: ubuntu-latest needs: create-release diff --git a/clients/ruby/bin/setup b/clients/ruby/bin/setup index dce67d86..ed41db96 100755 --- a/clients/ruby/bin/setup +++ b/clients/ruby/bin/setup @@ -3,6 +3,7 @@ set -euo pipefail IFS=$'\n\t' set -vx +gem install bundler:1.17.3 bundle install # Do any other automated setup that you need to do here