arbetsformedlingen 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.rubocop.yml +6 -0
 - data/.ruby-style-guide.yml +270 -0
 - data/.travis.yml +0 -1
 - data/CHANGELOG.md +12 -0
 - data/Gemfile +2 -0
 - data/LICENSE.txt +1 -1
 - data/README.md +1 -1
 - data/Rakefile +2 -0
 - data/arbetsformedlingen.gemspec +10 -9
 - data/bin/console +1 -0
 - data/lib/arbetsformedlingen/api/client.rb +48 -30
 - data/lib/arbetsformedlingen/api/ledigtarbete_client.rb +6 -1
 - data/lib/arbetsformedlingen/api/matchning_client.rb +16 -10
 - data/lib/arbetsformedlingen/api/request.rb +10 -13
 - data/lib/arbetsformedlingen/api/response.rb +71 -0
 - data/lib/arbetsformedlingen/api/results/ad_result.rb +36 -18
 - data/lib/arbetsformedlingen/api/results/matchning_result.rb +42 -3
 - data/lib/arbetsformedlingen/api/results/soklista_result.rb +16 -2
 - data/lib/arbetsformedlingen/api/soap_request.rb +11 -10
 - data/lib/arbetsformedlingen/api/values/ad_result_values.rb +6 -1
 - data/lib/arbetsformedlingen/api/values/create_ad_page.rb +3 -1
 - data/lib/arbetsformedlingen/api/values/matchning_result_values.rb +9 -1
 - data/lib/arbetsformedlingen/api/values/soklista_values.rb +17 -1
 - data/lib/arbetsformedlingen/api/ws_occupation_client.rb +26 -1
 - data/lib/arbetsformedlingen/codes/country_code.rb +3 -1
 - data/lib/arbetsformedlingen/codes/drivers_license_code.rb +6 -3
 - data/lib/arbetsformedlingen/codes/experience_required_code.rb +3 -1
 - data/lib/arbetsformedlingen/codes/municipality_code.rb +3 -1
 - data/lib/arbetsformedlingen/codes/occupation_code.rb +3 -1
 - data/lib/arbetsformedlingen/codes/salary_type_code.rb +3 -1
 - data/lib/arbetsformedlingen/key_struct.rb +5 -3
 - data/lib/arbetsformedlingen/models/application_method.rb +3 -1
 - data/lib/arbetsformedlingen/models/company.rb +2 -2
 - data/lib/arbetsformedlingen/models/document.rb +4 -2
 - data/lib/arbetsformedlingen/models/dry/predicates.rb +2 -0
 - data/lib/arbetsformedlingen/models/dry/types.rb +5 -3
 - data/lib/arbetsformedlingen/models/model.rb +2 -0
 - data/lib/arbetsformedlingen/models/packet.rb +3 -1
 - data/lib/arbetsformedlingen/models/packet_xml_builder.rb +8 -3
 - data/lib/arbetsformedlingen/models/position.rb +5 -3
 - data/lib/arbetsformedlingen/models/publication.rb +3 -1
 - data/lib/arbetsformedlingen/models/qualification.rb +3 -1
 - data/lib/arbetsformedlingen/models/salary.rb +3 -1
 - data/lib/arbetsformedlingen/models/schedule.rb +4 -2
 - data/lib/arbetsformedlingen/soap_builder.rb +3 -1
 - data/lib/arbetsformedlingen/version.rb +3 -1
 - metadata +43 -25
 
| 
         @@ -1,13 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'arbetsformedlingen/api/request'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'arbetsformedlingen/api/values/create_ad_page'
         
     | 
| 
       3 
5 
     | 
    
         | 
| 
       4 
6 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       5 
7 
     | 
    
         
             
              module API
         
     | 
| 
      
 8 
     | 
    
         
            +
                # API client for ledigtarbete
         
     | 
| 
       6 
9 
     | 
    
         
             
                class LedigtarbeteClient
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # Base URL for ledigtarbete
         
     | 
| 
       7 
11 
     | 
    
         
             
                  BASE_URL = 'http://api.arbetsformedlingen.se/ledigtarbete'.freeze
         
     | 
| 
       8 
12 
     | 
    
         | 
| 
      
 13 
     | 
    
         
            +
                  # HTTP headers
         
     | 
| 
       9 
14 
     | 
    
         
             
                  HEADERS = {
         
     | 
| 
       10 
     | 
    
         
            -
                    'Content-type' => 'text/xml'
         
     | 
| 
      
 15 
     | 
    
         
            +
                    'Content-type' => 'text/xml',
         
     | 
| 
       11 
16 
     | 
    
         
             
                  }.freeze
         
     | 
| 
       12 
17 
     | 
    
         | 
| 
       13 
18 
     | 
    
         
             
                  # Post ad to API
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'date'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'time'
         
     | 
| 
       3 
5 
     | 
    
         
             
            require 'arbetsformedlingen/api/request'
         
     | 
| 
         @@ -5,13 +7,16 @@ require 'arbetsformedlingen/api/results/matchning_result' 
     | 
|
| 
       5 
7 
     | 
    
         | 
| 
       6 
8 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       7 
9 
     | 
    
         
             
              module API
         
     | 
| 
      
 10 
     | 
    
         
            +
                # API client for matchning
         
     | 
| 
       8 
11 
     | 
    
         
             
                class MatchningClient
         
     | 
| 
       9 
12 
     | 
    
         
             
                  attr_reader :request
         
     | 
| 
       10 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  # Initialize client
         
     | 
| 
       11 
15 
     | 
    
         
             
                  def initialize(request: Request.new)
         
     | 
| 
       12 
16 
     | 
    
         
             
                    @request = request
         
     | 
| 
       13 
17 
     | 
    
         
             
                  end
         
     | 
| 
       14 
18 
     | 
    
         | 
| 
      
 19 
     | 
    
         
            +
                  # rubocop:disable Metrics/LineLength
         
     | 
| 
       15 
20 
     | 
    
         
             
                  # Find matching ads from API
         
     | 
| 
       16 
21 
     | 
    
         
             
                  # @return [MatchningResult] the result.
         
     | 
| 
       17 
22 
     | 
    
         
             
                  # @param area_id [String] Area ID.
         
     | 
| 
         @@ -36,6 +41,7 @@ module Arbetsformedlingen 
     | 
|
| 
       36 
41 
     | 
    
         
             
                  #    client.ads(keywrods: 'ruby', page: 3, page_size: 10)
         
     | 
| 
       37 
42 
     | 
    
         
             
                  # @example Get ads with keyword and organsiation numer
         
     | 
| 
       38 
43 
     | 
    
         
             
                  #    client.ads(keywrods: 'ruby', organization_number: org_no)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # rubocop:enable Metrics/LineLength
         
     | 
| 
       39 
45 
     | 
    
         
             
                  def ads(
         
     | 
| 
       40 
46 
     | 
    
         
             
                    # one of these must be present
         
     | 
| 
       41 
47 
     | 
    
         
             
                    county_id: nil,
         
     | 
| 
         @@ -55,7 +61,7 @@ module Arbetsformedlingen 
     | 
|
| 
       55 
61 
     | 
    
         | 
| 
       56 
62 
     | 
    
         
             
                    one_of_required = [county_id, municipality_id, occupation_id, keywords]
         
     | 
| 
       57 
63 
     | 
    
         
             
                    if one_of_required.all?(&:nil?)
         
     | 
| 
       58 
     | 
    
         
            -
                      error_message = 'One of: county_id, municipality_id, occupation_id, keywords is required'
         
     | 
| 
      
 64 
     | 
    
         
            +
                      error_message = 'One of: county_id, municipality_id, occupation_id, keywords is required' # rubocop:disable Metrics/LineLength
         
     | 
| 
       59 
65 
     | 
    
         
             
                      raise ArgumentError, error_message
         
     | 
| 
       60 
66 
     | 
    
         
             
                    end
         
     | 
| 
       61 
67 
     | 
    
         | 
| 
         @@ -77,12 +83,12 @@ module Arbetsformedlingen 
     | 
|
| 
       77 
83 
     | 
    
         
             
                      anstallningstyp: santize_employment_type_query(employment_type),
         
     | 
| 
       78 
84 
     | 
    
         
             
                      yrkesomradeid: occupation_field_id,
         
     | 
| 
       79 
85 
     | 
    
         
             
                      sokdatum: normalize_date_to_iso8601(published_after),
         
     | 
| 
       80 
     | 
    
         
            -
                      organisationsnummer: organization_number
         
     | 
| 
      
 86 
     | 
    
         
            +
                      organisationsnummer: organization_number,
         
     | 
| 
       81 
87 
     | 
    
         
             
                    }
         
     | 
| 
       82 
88 
     | 
    
         | 
| 
       83 
89 
     | 
    
         
             
                    response = request.get('matchning', query: query)
         
     | 
| 
       84 
90 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
                    MatchningResult.build(response 
     | 
| 
      
 91 
     | 
    
         
            +
                    MatchningResult.build(response)
         
     | 
| 
       86 
92 
     | 
    
         
             
                  end
         
     | 
| 
       87 
93 
     | 
    
         | 
| 
       88 
94 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -99,11 +105,11 @@ module Arbetsformedlingen 
     | 
|
| 
       99 
105 
     | 
    
         
             
                  end
         
     | 
| 
       100 
106 
     | 
    
         | 
| 
       101 
107 
     | 
    
         
             
                  def santize_employment_type_query(employment_type)
         
     | 
| 
       102 
     | 
    
         
            -
                    #  
     | 
| 
       103 
     | 
    
         
            -
                    #  
     | 
| 
      
 108 
     | 
    
         
            +
                    # Sökkriterier anställningstyp.
         
     | 
| 
      
 109 
     | 
    
         
            +
                    # Värdena ska ligga mellan 1 och 3.
         
     | 
| 
       104 
110 
     | 
    
         
             
                    # 1 är XXX (EJ DOKUMENTERAT)
         
     | 
| 
       105 
     | 
    
         
            -
                    # 2  
     | 
| 
       106 
     | 
    
         
            -
                    # 3  
     | 
| 
      
 111 
     | 
    
         
            +
                    # 2 är somarjobb / feriejobb
         
     | 
| 
      
 112 
     | 
    
         
            +
                    # 3 är utlandsjobb
         
     | 
| 
       107 
113 
     | 
    
         | 
| 
       108 
114 
     | 
    
         
             
                    # TODO: The question is what we do if an invalid parameter is passed
         
     | 
| 
       109 
115 
     | 
    
         
             
                    #       should we crash?
         
     | 
| 
         @@ -113,15 +119,15 @@ module Arbetsformedlingen 
     | 
|
| 
       113 
119 
     | 
    
         | 
| 
       114 
120 
     | 
    
         
             
                  def santize_keywords_query(keywords)
         
     | 
| 
       115 
121 
     | 
    
         
             
                    #
         
     | 
| 
       116 
     | 
    
         
            -
                    #  
     | 
| 
       117 
     | 
    
         
            -
                    # mellanslag ( 
     | 
| 
      
 122 
     | 
    
         
            +
                    # Sökord kan separeras eller kombineras med något av följande exempel:
         
     | 
| 
      
 123 
     | 
    
         
            +
                    # mellanslag (" ")
         
     | 
| 
       118 
124 
     | 
    
         
             
                    #
         
     | 
| 
       119 
125 
     | 
    
         
             
                    # [Example]
         
     | 
| 
       120 
126 
     | 
    
         
             
                    # /matchning?nyckelord="bagare""test"
         
     | 
| 
       121 
127 
     | 
    
         
             
                    # /matchning?nyckelord="bagare"OR"test" /matchning?nyckelord="automatisk"AND"test"
         
     | 
| 
       122 
128 
     | 
    
         | 
| 
       123 
129 
     | 
    
         
             
                    # Valid characters
         
     | 
| 
       124 
     | 
    
         
            -
                    #  
     | 
| 
      
 130 
     | 
    
         
            +
                    # abcdefghijklmnopqrstuvwxyzåäö0123456789: ,.-"
         
     | 
| 
       125 
131 
     | 
    
         | 
| 
       126 
132 
     | 
    
         
             
                    # TODO: What do we do if invalid characters are passed? Crash?
         
     | 
| 
       127 
133 
     | 
    
         
             
                    keywords
         
     | 
| 
         @@ -1,19 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'uri'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'net/http'
         
     | 
| 
       3 
5 
     | 
    
         
             
            require 'json'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'arbetsformedlingen/api/response'
         
     | 
| 
       4 
7 
     | 
    
         | 
| 
       5 
8 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       6 
9 
     | 
    
         
             
              module API
         
     | 
| 
      
 10 
     | 
    
         
            +
                # API request object
         
     | 
| 
       7 
11 
     | 
    
         
             
                class Request
         
     | 
| 
       8 
     | 
    
         
            -
                  Response = KeyStruct.new(:code, :body, :json)
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
12 
     | 
    
         
             
                  attr_reader :locale, :base_url
         
     | 
| 
       11 
13 
     | 
    
         | 
| 
      
 14 
     | 
    
         
            +
                  # Initialize request
         
     | 
| 
       12 
15 
     | 
    
         
             
                  def initialize(base_url: '', locale: 'sv')
         
     | 
| 
       13 
16 
     | 
    
         
             
                    @base_url = base_url
         
     | 
| 
       14 
17 
     | 
    
         
             
                    @locale = locale
         
     | 
| 
       15 
18 
     | 
    
         
             
                  end
         
     | 
| 
       16 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                  # Perform GEt request
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @param [String] url to be fetched
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param [Hash] query params
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @return [Response] response object
         
     | 
| 
       17 
24 
     | 
    
         
             
                  def get(url, query: {})
         
     | 
| 
       18 
25 
     | 
    
         
             
                    uri = URI("#{base_url}#{url}?#{URI.encode_www_form(query.to_a)}")
         
     | 
| 
       19 
26 
     | 
    
         | 
| 
         @@ -25,17 +32,7 @@ module Arbetsformedlingen 
     | 
|
| 
       25 
32 
     | 
    
         | 
| 
       26 
33 
     | 
    
         
             
                    response = http.request(request)
         
     | 
| 
       27 
34 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
                    Response.new(
         
     | 
| 
       29 
     | 
    
         
            -
                      code: response.code,
         
     | 
| 
       30 
     | 
    
         
            -
                      body: response.read_body,
         
     | 
| 
       31 
     | 
    
         
            -
                      json: parse_json(response.read_body)
         
     | 
| 
       32 
     | 
    
         
            -
                    )
         
     | 
| 
       33 
     | 
    
         
            -
                  end
         
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
       35 
     | 
    
         
            -
                  def parse_json(string)
         
     | 
| 
       36 
     | 
    
         
            -
                    JSON.parse(string.to_s)
         
     | 
| 
       37 
     | 
    
         
            -
                  rescue JSON::ParserError => _e
         
     | 
| 
       38 
     | 
    
         
            -
                    {}
         
     | 
| 
      
 35 
     | 
    
         
            +
                    Response.new(response)
         
     | 
| 
       39 
36 
     | 
    
         
             
                  end
         
     | 
| 
       40 
37 
     | 
    
         
             
                end
         
     | 
| 
       41 
38 
     | 
    
         
             
              end
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            begin
         
     | 
| 
      
 4 
     | 
    
         
            +
              require 'nokogiri'
         
     | 
| 
      
 5 
     | 
    
         
            +
            rescue LoadError
         
     | 
| 
      
 6 
     | 
    
         
            +
            end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module Arbetsformedlingen
         
     | 
| 
      
 9 
     | 
    
         
            +
              module API
         
     | 
| 
      
 10 
     | 
    
         
            +
                # API response object
         
     | 
| 
      
 11 
     | 
    
         
            +
                class Response
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # Initialize response
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def initialize(response)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @response = response
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @json = nil
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # True if response is 200
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @return [Boolean] true if response code is 200
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def success?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    response.code == '200'
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  # Response body
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @return [String] the response body
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def body
         
     | 
| 
      
 27 
     | 
    
         
            +
                    response.read_body
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  # Response JSON
         
     | 
| 
      
 31 
     | 
    
         
            +
                  # @return [Hash] response json - empty if JSON is invalid
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def json
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @json ||= parse_json(body)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  # Response XML
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [Nokogiri::XML::Document] response - empty is XML is invalid
         
     | 
| 
      
 38 
     | 
    
         
            +
                  def xml
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @xml ||= parse_xml(body)
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  # Delegate missing values to response
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def method_missing(method_name, *arguments, &block)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    if response.respond_to?(method_name)
         
     | 
| 
      
 45 
     | 
    
         
            +
                      response.public_send(method_name, *arguments, &block)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    else
         
     | 
| 
      
 47 
     | 
    
         
            +
                      super
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  # Return true if missing method can be delegated
         
     | 
| 
      
 52 
     | 
    
         
            +
                  def respond_to_missing?(method_name, include_private = false)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    response.respond_to?(method_name) || super
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  private
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  attr_reader :response
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def parse_json(string)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    JSON.parse(string.to_s)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  rescue JSON::ParserError => _e
         
     | 
| 
      
 63 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  def parse_xml(string)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    Nokogiri::XML(string).tap(&:remove_namespaces!)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,11 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'arbetsformedlingen/api/values/ad_result_values'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       4 
6 
     | 
    
         
             
              module API
         
     | 
| 
       5 
7 
     | 
    
         
             
                module AdResult
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
                  # Build API result object for ad result
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @param [API::Response] response
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @return [Values::Ad]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def self.build(response)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    return build_empty(response) unless response.success?
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    build_page(response)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
       8 
16 
     | 
    
         | 
| 
      
 17 
     | 
    
         
            +
                  # private
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.build_page(response)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    response_data = response.json
         
     | 
| 
      
 21 
     | 
    
         
            +
                    data = response_data.fetch('platsannons')
         
     | 
| 
       9 
22 
     | 
    
         
             
                    ad_data = data.fetch('annons')
         
     | 
| 
       10 
23 
     | 
    
         | 
| 
       11 
24 
     | 
    
         
             
                    Values::Ad.new(
         
     | 
| 
         @@ -19,32 +32,37 @@ module Arbetsformedlingen 
     | 
|
| 
       19 
32 
     | 
    
         
             
                      total_vacancies: ad_data.fetch('antal_platser'),
         
     | 
| 
       20 
33 
     | 
    
         
             
                      municipalities: ad_data.fetch('kommunnamn'),
         
     | 
| 
       21 
34 
     | 
    
         
             
                      municipality_id: ad_data.fetch('kommunkod'),
         
     | 
| 
       22 
     | 
    
         
            -
                      total_vacancies_with_visa: ad_data.fetch('antalplatserVisa'),
         
     | 
| 
      
 35 
     | 
    
         
            +
                      total_vacancies_with_visa: ad_data.fetch('antalplatserVisa', nil),
         
     | 
| 
       23 
36 
     | 
    
         
             
                      employment_type: ad_data.fetch('anstallningstyp'),
         
     | 
| 
       24 
37 
     | 
    
         
             
                      terms: build_terms(data.fetch('villkor')),
         
     | 
| 
       25 
38 
     | 
    
         
             
                      application: build_application(data.fetch('ansokan')),
         
     | 
| 
       26 
39 
     | 
    
         
             
                      workplace: build_workplace(data.fetch('arbetsplats')),
         
     | 
| 
       27 
     | 
    
         
            -
                      requirements: build_requirements(data.fetch('krav'))
         
     | 
| 
      
 40 
     | 
    
         
            +
                      requirements: build_requirements(data.fetch('krav')),
         
     | 
| 
      
 41 
     | 
    
         
            +
                      response: response
         
     | 
| 
       28 
42 
     | 
    
         
             
                    )
         
     | 
| 
       29 
43 
     | 
    
         
             
                  end
         
     | 
| 
       30 
44 
     | 
    
         | 
| 
      
 45 
     | 
    
         
            +
                  def self.build_empty(response)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Values::Ad.new(response: response)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
       31 
49 
     | 
    
         
             
                  def self.build_terms(data)
         
     | 
| 
       32 
50 
     | 
    
         
             
                    Values::Terms.new(
         
     | 
| 
       33 
     | 
    
         
            -
                      duration: data.fetch('varaktighet'),
         
     | 
| 
       34 
     | 
    
         
            -
                      working_hours: data.fetch('arbetstid'),
         
     | 
| 
       35 
     | 
    
         
            -
                      working_hours_description: data.fetch('arbetstidvaraktighet'),
         
     | 
| 
      
 51 
     | 
    
         
            +
                      duration: data.fetch('varaktighet', nil),
         
     | 
| 
      
 52 
     | 
    
         
            +
                      working_hours: data.fetch('arbetstid', nil),
         
     | 
| 
      
 53 
     | 
    
         
            +
                      working_hours_description: data.fetch('arbetstidvaraktighet', nil),
         
     | 
| 
       36 
54 
     | 
    
         
             
                      salary_type: data.fetch('lonetyp'),
         
     | 
| 
       37 
     | 
    
         
            -
                      salary_form: data.fetch('loneform')
         
     | 
| 
      
 55 
     | 
    
         
            +
                      salary_form: data.fetch('loneform', nil)
         
     | 
| 
       38 
56 
     | 
    
         
             
                    )
         
     | 
| 
       39 
57 
     | 
    
         
             
                  end
         
     | 
| 
       40 
58 
     | 
    
         | 
| 
       41 
59 
     | 
    
         
             
                  def self.build_application(data)
         
     | 
| 
       42 
60 
     | 
    
         
             
                    Values::Application.new(
         
     | 
| 
       43 
61 
     | 
    
         
             
                      reference: data['referens'],
         
     | 
| 
       44 
     | 
    
         
            -
                      application_url: data.fetch('webbplats'),
         
     | 
| 
      
 62 
     | 
    
         
            +
                      application_url: data.fetch('webbplats', nil),
         
     | 
| 
       45 
63 
     | 
    
         
             
                      email: data['epostadress'],
         
     | 
| 
       46 
64 
     | 
    
         
             
                      last_application_at: data.fetch('sista_ansokningsdag', nil),
         
     | 
| 
       47 
     | 
    
         
            -
                      application_comment: data.fetch('ovrigt_om_ansokan')
         
     | 
| 
      
 65 
     | 
    
         
            +
                      application_comment: data.fetch('ovrigt_om_ansokan', nil)
         
     | 
| 
       48 
66 
     | 
    
         
             
                    )
         
     | 
| 
       49 
67 
     | 
    
         
             
                  end
         
     | 
| 
       50 
68 
     | 
    
         | 
| 
         @@ -53,11 +71,11 @@ module Arbetsformedlingen 
     | 
|
| 
       53 
71 
     | 
    
         
             
                      name: data.fetch('arbetsplatsnamn'),
         
     | 
| 
       54 
72 
     | 
    
         
             
                      postal: build_postal(data),
         
     | 
| 
       55 
73 
     | 
    
         
             
                      country: data.fetch('land'),
         
     | 
| 
       56 
     | 
    
         
            -
                      visit_address: data.fetch('besoksadress'),
         
     | 
| 
       57 
     | 
    
         
            -
                      logotype_url: data.fetch('logotypurl'),
         
     | 
| 
       58 
     | 
    
         
            -
                      website: data.fetch('hemsida'),
         
     | 
| 
      
 74 
     | 
    
         
            +
                      visit_address: data.fetch('besoksadress', nil),
         
     | 
| 
      
 75 
     | 
    
         
            +
                      logotype_url: data.fetch('logotypurl', nil),
         
     | 
| 
      
 76 
     | 
    
         
            +
                      website: data.fetch('hemsida', nil),
         
     | 
| 
       59 
77 
     | 
    
         
             
                      contacts: (
         
     | 
| 
       60 
     | 
    
         
            -
                        data.dig('kontaktpersonlista', ' 
     | 
| 
      
 78 
     | 
    
         
            +
                        data.dig('kontaktpersonlista', 'kontaktpersondata') || []
         
     | 
| 
       61 
79 
     | 
    
         
             
                      ).map do |contact_data|
         
     | 
| 
       62 
80 
     | 
    
         
             
                        build_workplace_contacts(contact_data)
         
     | 
| 
       63 
81 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -66,10 +84,10 @@ module Arbetsformedlingen 
     | 
|
| 
       66 
84 
     | 
    
         | 
| 
       67 
85 
     | 
    
         
             
                  def self.build_postal(data)
         
     | 
| 
       68 
86 
     | 
    
         
             
                    Values::Postal.new(
         
     | 
| 
       69 
     | 
    
         
            -
                      code: data.fetch('postnummer'),
         
     | 
| 
       70 
     | 
    
         
            -
                      address: data.fetch('postadress'),
         
     | 
| 
       71 
     | 
    
         
            -
                      city: data.fetch('postort'),
         
     | 
| 
       72 
     | 
    
         
            -
                      country: data.fetch('postland')
         
     | 
| 
      
 87 
     | 
    
         
            +
                      code: data.fetch('postnummer', nil),
         
     | 
| 
      
 88 
     | 
    
         
            +
                      address: data.fetch('postadress', nil),
         
     | 
| 
      
 89 
     | 
    
         
            +
                      city: data.fetch('postort', nil),
         
     | 
| 
      
 90 
     | 
    
         
            +
                      country: data.fetch('postland', nil)
         
     | 
| 
       73 
91 
     | 
    
         
             
                    )
         
     | 
| 
       74 
92 
     | 
    
         
             
                  end
         
     | 
| 
       75 
93 
     | 
    
         | 
| 
         @@ -1,9 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'arbetsformedlingen/api/values/matchning_result_values'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       4 
6 
     | 
    
         
             
              module API
         
     | 
| 
       5 
7 
     | 
    
         
             
                module MatchningResult
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  # Build API result object for matchning result
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @param [API::Response] response
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @return [Values::MatchningPage]
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def self.build(response)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    return empty_matchning_page(response) unless response.success?
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    build_matchning_page(response)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  # private
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.empty_matchning_page(response)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    response_data = response.json
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    Values::MatchningPage.new(
         
     | 
| 
      
 23 
     | 
    
         
            +
                      list_name: 'annonser',
         
     | 
| 
      
 24 
     | 
    
         
            +
                      total_ads: 0,
         
     | 
| 
      
 25 
     | 
    
         
            +
                      total_ads_exact: 0,
         
     | 
| 
      
 26 
     | 
    
         
            +
                      total_ads_nearby: 0,
         
     | 
| 
      
 27 
     | 
    
         
            +
                      total_vacancies_on_page: 0,
         
     | 
| 
      
 28 
     | 
    
         
            +
                      total_pages: 0,
         
     | 
| 
      
 29 
     | 
    
         
            +
                      raw_data: response_data,
         
     | 
| 
      
 30 
     | 
    
         
            +
                      data: [],
         
     | 
| 
      
 31 
     | 
    
         
            +
                      response: response
         
     | 
| 
      
 32 
     | 
    
         
            +
                    )
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def self.build_matchning_page(response)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    response_data = response.json
         
     | 
| 
       7 
37 
     | 
    
         
             
                    data = response_data.fetch('matchningslista')
         
     | 
| 
       8 
38 
     | 
    
         | 
| 
       9 
39 
     | 
    
         
             
                    Values::MatchningPage.new(
         
     | 
| 
         @@ -14,10 +44,19 @@ module Arbetsformedlingen 
     | 
|
| 
       14 
44 
     | 
    
         
             
                      total_vacancies_on_page: data.fetch('antal_platserTotal'),
         
     | 
| 
       15 
45 
     | 
    
         
             
                      total_pages: data.fetch('antal_sidor'),
         
     | 
| 
       16 
46 
     | 
    
         
             
                      raw_data: response_data,
         
     | 
| 
       17 
     | 
    
         
            -
                      data: data 
     | 
| 
      
 47 
     | 
    
         
            +
                      data: build_ad_results(data),
         
     | 
| 
      
 48 
     | 
    
         
            +
                      response: response
         
     | 
| 
       18 
49 
     | 
    
         
             
                    )
         
     | 
| 
       19 
50 
     | 
    
         
             
                  end
         
     | 
| 
       20 
51 
     | 
    
         | 
| 
      
 52 
     | 
    
         
            +
                  # private
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def self.build_ad_results(data)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    data.fetch('matchningdata', []).map do |ad_data|
         
     | 
| 
      
 56 
     | 
    
         
            +
                      build_ad_result(ad_data)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       21 
60 
     | 
    
         
             
                  def self.build_ad_result(ad_data)
         
     | 
| 
       22 
61 
     | 
    
         
             
                    Values::MatchningAd.new(
         
     | 
| 
       23 
62 
     | 
    
         
             
                      id: ad_data.fetch('annonsid'),
         
     | 
| 
         @@ -32,7 +71,7 @@ module Arbetsformedlingen 
     | 
|
| 
       32 
71 
     | 
    
         
             
                      url: ad_data.fetch('annonsurl'),
         
     | 
| 
       33 
72 
     | 
    
         
             
                      relevance: ad_data.fetch('relevans'),
         
     | 
| 
       34 
73 
     | 
    
         
             
                      total_vacancies: ad_data.fetch('antalplatser'),
         
     | 
| 
       35 
     | 
    
         
            -
                      total_vacancies_with_visa: ad_data.fetch('antalPlatserVisa'),
         
     | 
| 
      
 74 
     | 
    
         
            +
                      total_vacancies_with_visa: ad_data.fetch('antalPlatserVisa', nil),
         
     | 
| 
       36 
75 
     | 
    
         
             
                      duration_id: ad_data.fetch('varaktighetId', nil),
         
     | 
| 
       37 
76 
     | 
    
         
             
                      counties: ad_data.fetch('lan'),
         
     | 
| 
       38 
77 
     | 
    
         
             
                      country_id: ad_data.fetch('lanid'),
         
     | 
| 
         @@ -1,9 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'arbetsformedlingen/api/values/soklista_values'
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       4 
6 
     | 
    
         
             
              module API
         
     | 
| 
       5 
7 
     | 
    
         
             
                module SoklistaResult
         
     | 
| 
       6 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  # Build API result object for "soklista"
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @param [API::Response] response
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @param list_name [String] result list name
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @return [Values::SoklistaPage]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  def self.build(response, list_name: nil)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    build_page(response, list_name)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  # private
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def self.build_page(response, list_name)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    response_data = response.json
         
     | 
| 
       7 
20 
     | 
    
         
             
                    data = response_data.fetch('soklista', {})
         
     | 
| 
       8 
21 
     | 
    
         | 
| 
       9 
22 
     | 
    
         
             
                    Values::SoklistaPage.new(
         
     | 
| 
         @@ -13,7 +26,8 @@ module Arbetsformedlingen 
     | 
|
| 
       13 
26 
     | 
    
         
             
                      raw_data: response_data,
         
     | 
| 
       14 
27 
     | 
    
         
             
                      data: data.fetch('sokdata', []).map do |result|
         
     | 
| 
       15 
28 
     | 
    
         
             
                        build_search_result(result)
         
     | 
| 
       16 
     | 
    
         
            -
                      end
         
     | 
| 
      
 29 
     | 
    
         
            +
                      end,
         
     | 
| 
      
 30 
     | 
    
         
            +
                      response: response
         
     | 
| 
       17 
31 
     | 
    
         
             
                    )
         
     | 
| 
       18 
32 
     | 
    
         
             
                  end
         
     | 
| 
       19 
33 
     | 
    
         | 
| 
         @@ -1,5 +1,8 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            require 'uri'
         
     | 
| 
       2 
4 
     | 
    
         
             
            require 'net/http'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'arbetsformedlingen/api/response'
         
     | 
| 
       3 
6 
     | 
    
         | 
| 
       4 
7 
     | 
    
         
             
            begin
         
     | 
| 
       5 
8 
     | 
    
         
             
              require 'nokogiri'
         
     | 
| 
         @@ -8,11 +11,14 @@ end 
     | 
|
| 
       8 
11 
     | 
    
         | 
| 
       9 
12 
     | 
    
         
             
            module Arbetsformedlingen
         
     | 
| 
       10 
13 
     | 
    
         
             
              module API
         
     | 
| 
      
 14 
     | 
    
         
            +
                # API SOAP request
         
     | 
| 
       11 
15 
     | 
    
         
             
                class SOAPRequest
         
     | 
| 
       12 
     | 
    
         
            -
                   
     | 
| 
      
 16 
     | 
    
         
            +
                  # SOAP response
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # Response = KeyStruct.new(:code, :body, :xml)
         
     | 
| 
       13 
18 
     | 
    
         | 
| 
       14 
19 
     | 
    
         
             
                  attr_reader :locale, :uri, :url
         
     | 
| 
       15 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
                  # Initialize SOAP request
         
     | 
| 
       16 
22 
     | 
    
         
             
                  def initialize(url, locale: nil)
         
     | 
| 
       17 
23 
     | 
    
         
             
                    unless Object.const_defined?(:Nokogiri)
         
     | 
| 
       18 
24 
     | 
    
         
             
                      raise(ArgumentError, "unable to require 'nokogiri' gem, please install it")
         
     | 
| 
         @@ -23,6 +29,9 @@ module Arbetsformedlingen 
     | 
|
| 
       23 
29 
     | 
    
         
             
                    @locale = locale
         
     | 
| 
       24 
30 
     | 
    
         
             
                  end
         
     | 
| 
       25 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                  # Performs a POST request
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @param [String] the post body
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @return [Response] the response
         
     | 
| 
       26 
35 
     | 
    
         
             
                  def post(body)
         
     | 
| 
       27 
36 
     | 
    
         
             
                    http = Net::HTTP.new(uri.host, uri.port)
         
     | 
| 
       28 
37 
     | 
    
         
             
                    http.use_ssl = true if uri.scheme == 'https'
         
     | 
| 
         @@ -34,15 +43,7 @@ module Arbetsformedlingen 
     | 
|
| 
       34 
43 
     | 
    
         | 
| 
       35 
44 
     | 
    
         
             
                    response = http.request(request)
         
     | 
| 
       36 
45 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                    Response.new(
         
     | 
| 
       38 
     | 
    
         
            -
                      code: response.code,
         
     | 
| 
       39 
     | 
    
         
            -
                      body: response.read_body,
         
     | 
| 
       40 
     | 
    
         
            -
                      xml: parse_xml(response.read_body)
         
     | 
| 
       41 
     | 
    
         
            -
                    )
         
     | 
| 
       42 
     | 
    
         
            -
                  end
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                  def parse_xml(string)
         
     | 
| 
       45 
     | 
    
         
            -
                    Nokogiri::XML(string).tap { |doc| doc.remove_namespaces! }
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Response.new(response)
         
     | 
| 
       46 
47 
     | 
    
         
             
                  end
         
     | 
| 
       47 
48 
     | 
    
         
             
                end
         
     | 
| 
       48 
49 
     | 
    
         
             
              end
         
     |