diff options
| -rw-r--r-- | app/models/api/v1/api_key.rb | 22 | ||||
| -rw-r--r-- | app/models/organisation.rb | 2 | ||||
| -rw-r--r-- | app/models/user.rb | 2 | ||||
| -rw-r--r-- | app/services/file_service.rb | 35 | ||||
| -rw-r--r-- | app/services/http_service.rb | 11 | ||||
| -rw-r--r-- | app/workers/workbench_import_worker.rb | 83 | ||||
| -rw-r--r-- | spec/models/api/v1/api_key_spec.rb | 31 | ||||
| -rw-r--r-- | spec/models/organisation_spec.rb | 2 | ||||
| -rw-r--r-- | spec/services/file_service_spec.rb | 25 | ||||
| -rw-r--r-- | spec/workers/workbench_import_worker_spec.rb | 62 | 
10 files changed, 168 insertions, 107 deletions
| diff --git a/app/models/api/v1/api_key.rb b/app/models/api/v1/api_key.rb index 7390db232..e1a7ab5a4 100644 --- a/app/models/api/v1/api_key.rb +++ b/app/models/api/v1/api_key.rb @@ -3,9 +3,20 @@ module Api      class ApiKey < ::ActiveRecord::Base        before_create :generate_access_token        belongs_to :referential, :class_name => '::Referential' +      validates_presence_of :referential -      def self.model_name -        ActiveModel::Name.new self, Api::V1, self.name.demodulize +      class << self +        def from(referential, name:) +          find_or_create_by!(name: name, referential: referential) +        end +        def model_name +          ActiveModel::Name.new  Api::V1, self.name.demodulize +        end +        def referential_from_token(token) +          array = token.split('-') +          return nil unless array.size==2 +          ::Referential.find( array.first) +        end        end        def eql?(other) @@ -13,16 +24,11 @@ module Api          other.token == self.token        end -      def self.referential_from_token(token) -        array = token.split('-') -        return nil unless array.size==2 -        ::Referential.find( array.first) -      end      private        def generate_access_token          begin -          self.token = "#{referential.id}-#{SecureRandom.hex}" +          self.token = "#{referential_id}-#{SecureRandom.hex}"          end while self.class.exists?(:token => self.token)        end      end diff --git a/app/models/organisation.rb b/app/models/organisation.rb index a3c5da1af..7e571e78d 100644 --- a/app/models/organisation.rb +++ b/app/models/organisation.rb @@ -27,7 +27,7 @@ class Organisation < ActiveRecord::Base      conf = Rails.application.config.try(:stif_portail_api)      raise 'Rails.application.config.stif_portail_api configuration is not defined' unless conf -    AF83::HTTPFetcher.get_resource( +    HTTPService.get_resource(        host: conf[:url],        path: '/api/v1/organizations',        parse_json: true, diff --git a/app/models/user.rb b/app/models/user.rb index fbffd96be..1dc5975e1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -66,7 +66,7 @@ class User < ActiveRecord::Base      conf = Rails.application.config.try(:stif_portail_api)      raise 'Rails.application.config.stif_portail_api settings is not defined' unless conf -    AF83::HTTPFetcher.get_resource( +    HTTPService.get_resource(        host: conf[:url],        path: '/api/v1/users',        parse_json: true, diff --git a/app/services/file_service.rb b/app/services/file_service.rb index efccbe24f..3b3ff3561 100644 --- a/app/services/file_service.rb +++ b/app/services/file_service.rb @@ -1,23 +1,24 @@ -module FileService extend self +# TODO: Delete me after stable implementation of #1726 +# module FileService extend self -  def unique_filename( path, enum_with: with_ints ) -    file_names = enum_with.map( &file_name_maker(path) ) -    file_names -      .drop_while( &File.method(:exists?) ) -      .next -  end +#   def unique_filename( path, enum_with: with_ints ) +#     file_names = enum_with.map( &file_name_maker(path) ) +#     file_names +#       .drop_while( &File.method(:exists?) ) +#       .next +#   end -  def with_ints(format='%d') -    (0..Float::INFINITY) -      .lazy -      .map{ |n| format % n } -  end +#   def with_ints(format='%d') +#     (0..Float::INFINITY) +#       .lazy +#       .map{ |n| format % n } +#   end -  private +#   private -  def file_name_maker path -    ->(n){ [path, n].join('_') } -  end +#   def file_name_maker path +#     ->(n){ [path, n].join('_') } +#   end -end +# end diff --git a/app/services/http_service.rb b/app/services/http_service.rb index a3c4d2569..37f1621d5 100644 --- a/app/services/http_service.rb +++ b/app/services/http_service.rb @@ -1,7 +1,4 @@ -class HTTPService -  def self.get_resource(*args) -    new.get_resource(*args) -  end +module HTTPService extend self    def get_resource(host:, path:, token: nil, params: {}, parse_json: false)       Faraday.new(url: host) do |c| @@ -17,6 +14,12 @@ class HTTPService      end    end +  # host: 'http://localhost:3000', +  # path: '/api/v1/netex_imports.json', +  # resource_name: 'netex_import', +  # token: '13-74009c36638f587c9eafb1ce46e95585', +  # params: {referential_id: 13, workbench_id: 1}, +  # upload: {file: [StringIO.new('howdy'), 'application/zip', 'greeting']})    def post_resource(host:, path:, resource_name:, token: nil, params: {}, upload: nil)      Faraday.new(url: host) do |c|        c.headers['Authorization'] = "Token token=#{token.inspect}" if token diff --git a/app/workers/workbench_import_worker.rb b/app/workers/workbench_import_worker.rb index d1a381e0c..2973d97c5 100644 --- a/app/workers/workbench_import_worker.rb +++ b/app/workers/workbench_import_worker.rb @@ -3,65 +3,88 @@ class WorkbenchImportWorker    include Rails.application.routes.url_helpers    include Configurable -  attr_reader :import, :downloaded + +  # Workers +  # =======    def perform(import_id)      @import = Import.find(import_id)      @downloaded = nil      download +    @zip_service = ZipService.new(@zipfile_data) +    upload    end    def download      logger.warn  "HTTP GET #{import_url}" -    zipfile_data = AF83::HTTPFetcher.get_resource( +    @zipfile_data = HTTPService.get_resource(        host: import_host,        path: import_path, -      params: {token: import.token_download}) - -    path = File.join(config.dir, import.name.gsub(%r{\s+}, '-')) -    unique_path = FileService.unique_filename path -    Dir.mkdir unique_path -    @downloaded = File.join(unique_path, import.name) -    File.open(downloaded, 'wb') do | file | -      file.write zipfile_data -    end - -    if single_entry? -      upload(downloaded) -    else -      split_zip.each(&method(:upload)) -    end +      params: {token: @import.token_download}) + +    # TODO: Delete me after stable implementation of #1726 +    # path = File.join(config.dir, import.name.gsub(%r{\s+}, '-')) +    # unique_path = FileService.unique_filename path +    # Dir.mkdir unique_path +    # @downloaded = File.join(unique_path, import.name) +    # File.open(downloaded, 'wb') do | file | +    #   file.write zipfile_data +    # end    end -  def single_entry? -    true +  def upload +    @zip_service.entry_group_streams.each(&method(:upload_entry_group))    end -  def split_zip -    [] +  def upload_entry_group key_pair +    eg_name, eg_stream = key_pair +    logger.warn  "HTTP POST #{export_url} (for #{complete_entry_group_name(eg_name)})" +    HTTPService.post_resource( +      host: export_host, +      path: export_path, +      resource_name: 'netex_import', +      token: token(eg_name), +      params: params, +      upload: {file: [eg_stream, 'application/zip', eg_name]}) +  end + + +  # Queries +  # ======= + +  def complete_entry_group_name entry_group_name +    [@import.name, entry_group_name].join("--")    end -  def upload zip_file +  def token entry_group_name +    Api::V1::ApiKey.from(@import.referential, name: complete_entry_group_name(entry_group_name)).token    end -  # Memoized Values +  # Constants +  # ========= -  def dirname -    @__dirname__ ||= make_unique_dir +  def export_host +    Rails.application.config.front_end_host +  end +  def export_path +    '/api/v1/netex_imports.json' +  end +  def export_url +    @__export_url__ ||= File.join(export_host, export_path)    end    def import_host -    @__import_host__ ||= Rails.application.config.front_end_host +    Rails.application.config.front_end_host    end    def import_path -    @__import_path__ ||= File.join(download_workbench_import_path(import.workbench, import))  -  end -  def import_uri -    @__import_uri__ ||= URI(import_url)  +    @__import_path__ ||= File.join(download_workbench_import_path(@import.workbench, @import))    end    def import_url      @__import_url__ ||= File.join(import_host, import_path)    end +  def params +    @__params__ ||= { referential_id: @import.referential_id, workbench_id: @import.workbench_id } +  end  end diff --git a/spec/models/api/v1/api_key_spec.rb b/spec/models/api/v1/api_key_spec.rb index 8a34c9221..5f39a65e4 100644 --- a/spec/models/api/v1/api_key_spec.rb +++ b/spec/models/api/v1/api_key_spec.rb @@ -1,11 +1,34 @@  describe Api::V1::ApiKey, :type => :model do -  let!(:referential){create(:referential)} -  subject { Api::V1::ApiKey.create( :name => "test", :referential => referential)} -  it "test" do -    expect(subject).to be_valid +  let(:referential){ create :referential } + +  subject { described_class.create( :name => "test", :referential => referential)} + +  it "validity test" do +    expect_it.to be_valid      expect(subject.referential).to eq(referential) +  end + +  context 'Creation' do +    let( :name ){ SecureRandom.urlsafe_base64 } + +    it 'can be created from a referential with a name, iff needed' do +      # 1st time create a new record +      expect{ described_class.from(referential, name: name) }.to change{ described_class.count }.by(1) +      expect( described_class.last.attributes.values_at(*%w{referential_id name}) ).to eq([ +        referential.id, name +      ]) + +      # 2nd time get the same record +      expect{ described_class.from(referential, name: name) }.not_to change{ described_class.count } +      expect( described_class.last.attributes.values_at(*%w{referential_id name}) ).to eq([ +        referential.id, name +      ]) +    end +    it 'cannot be created without a referential' do +      expect{ described_class.from(nil, name:name) rescue nil }.not_to change{ described_class.count } +    end    end  end diff --git a/spec/models/organisation_spec.rb b/spec/models/organisation_spec.rb index 1cce7e846..b16324a56 100644 --- a/spec/models/organisation_spec.rb +++ b/spec/models/organisation_spec.rb @@ -1,5 +1,3 @@ -require 'spec_helper' -  describe Organisation, :type => :model do    it { should validate_presence_of(:name) }    it { should validate_uniqueness_of(:code) } diff --git a/spec/services/file_service_spec.rb b/spec/services/file_service_spec.rb index 90e164408..4426ee145 100644 --- a/spec/services/file_service_spec.rb +++ b/spec/services/file_service_spec.rb @@ -1,16 +1,17 @@ -RSpec.describe FileService do +# TODO: Delete me after stable implementation of #1726 +# RSpec.describe FileService do -  it 'computes a unique filename' do -    expect( File ).to receive(:exists?).with('xxx/yyy_0').and_return( false ) +#   it 'computes a unique filename' do +#     expect( File ).to receive(:exists?).with('xxx/yyy_0').and_return( false ) -    expect(described_class.unique_filename('xxx/yyy')).to eq('xxx/yyy_0') -  end +#     expect(described_class.unique_filename('xxx/yyy')).to eq('xxx/yyy_0') +#   end -  it 'handles duplicate names by means of a counter' do -    expect( File ).to receive(:exists?).with('xxx/yyy_0').and_return( true ) -    expect( File ).to receive(:exists?).with('xxx/yyy_1').and_return( true ) -    expect( File ).to receive(:exists?).with('xxx/yyy_2').and_return( false ) +#   it 'handles duplicate names by means of a counter' do +#     expect( File ).to receive(:exists?).with('xxx/yyy_0').and_return( true ) +#     expect( File ).to receive(:exists?).with('xxx/yyy_1').and_return( true ) +#     expect( File ).to receive(:exists?).with('xxx/yyy_2').and_return( false ) -    expect(described_class.unique_filename('xxx/yyy')).to eq('xxx/yyy_2') -  end -end +#     expect(described_class.unique_filename('xxx/yyy')).to eq('xxx/yyy_2') +#   end +# end diff --git a/spec/workers/workbench_import_worker_spec.rb b/spec/workers/workbench_import_worker_spec.rb index d227c7610..68e429b60 100644 --- a/spec/workers/workbench_import_worker_spec.rb +++ b/spec/workers/workbench_import_worker_spec.rb @@ -2,54 +2,60 @@ RSpec.describe WorkbenchImportWorker, type: [:worker, :request] do    let( :worker ) { described_class.new }    let( :import ){ build_stubbed :import, token_download: download_token, file: File.open(zip_file) } +    let( :workbench ){ import.workbench }    let( :referential ){ import.referential } +  let( :api_key ){ build_stubbed :api_key, referential: referential, token: "#{referential.id}-#{SecureRandom.hex}" } +  let( :params ){ {referential_id: referential.id, workbench_id: workbench.id} }    # http://www.example.com/workbenches/:workbench_id/imports/:id/download -  let( :url ){ "#{File.join(host, path)}?token=#{download_token}" }    let( :host ){ Rails.configuration.front_end_host }    let( :path ){ download_workbench_import_path(workbench, import) } -  let( :result ){ import.file.read } +  let( :downloaded_zip ){ double("downloaded zip") }    let( :download_token ){ SecureRandom.urlsafe_base64 } +  let( :upload_path ) { '/api/v1/netex_imports.json' } + +  let( :entry_group_streams ) do +    2.times.map{ |i| double( "entry group stream #{i}" ) } +  end +  let( :entry_groups ) do +    2.times.map do | i | +      {"entry_group_name#{i}" => entry_group_streams[i] } +    end +  end + +  let( :zip_service ){ double("zip service") } +    before do      # That should be `build_stubbed's` job, no?      allow(Import).to receive(:find).with(import.id).and_return(import) +    allow(Api::V1::ApiKey).to receive(:from).and_return(api_key) +    allow(ZipService).to receive(:new).with(downloaded_zip).and_return zip_service +    expect(zip_service).to receive(:entry_group_streams).and_return(entry_groups)    end -  context 'multireferential zipfile' do  +  context 'multireferential zipfile' do      let( :zip_file ){ File.join(fixture_path, 'multiref.zip') }      it 'downloads a zip file' do -      default_headers = {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} -      stub_request(:get, url) -        .with(headers: default_headers) -        .to_return(body: result) - -      worker.perform import.id - -  require 'pry' -  binding.pry -      expect( File.read(worker.downloaded) ).to eq( result ) -      expect( worker ).not_to be_single_entry -    end -  end - -  context 'unireferential zipfile' do  -    let( :zip_file ){ File.join(fixture_path, 'uniref.zip') } - -    it 'downloads a zip file' do -      default_headers = {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} -      stub_request(:get, url) -        .with(headers: default_headers) -        .to_return(body: result) - +      expect(HTTPService).to receive(:get_resource) +        .with(host: host, path: path, params: {token: download_token}) +        .and_return( downloaded_zip ) + +      entry_groups.each do | entry_group_name, entry_group_stream | +        expect( HTTPService ).to receive(:post_resource) +          .with(host: host, +                path: upload_path, +                resource_name: 'netex_import', +                token: api_key.token, +                params: params, +                upload: {file: [entry_group_stream, 'application/zip', entry_group_name]}) +      end        worker.perform import.id -      expect( File.read(worker.downloaded) ).to eq( result ) -      expect( worker ).to be_single_entry      end    end | 
