usps-imis-api 0.13.4 → 0.13.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f34a885284d3ccfeced54ef7b76cda38b69922d80c61ba22d4950945ee4bc1ea
4
- data.tar.gz: fe1a7952665d65adabddf3f13eed3720b267b20ee2d5029715df096bbc82d52f
3
+ metadata.gz: 9d753667e0811d6bf6018675ed3959338bce46eee41dbcc021f1e2adff0eb30a
4
+ data.tar.gz: b12e309cbb83288fab1a6a9fb39f3bf03bb56e6972c90a564644e34f3df05b87
5
5
  SHA512:
6
- metadata.gz: f99eb59125c90b6e1c7dc11b2e32c6b3eea327825785614629919b9548aa429ed30458d68f59943557e07e37c3faef6c22deddc9b5e5da127c05d26f674912d3
7
- data.tar.gz: 0dd03c7791e6d1e68a0365aa87c741a44fb5b145aec6002c264e2931c6fa14f35554321ec1cb6d59dfcd36c012ccfcd2f4e0adae514886f48a31ba87a484930a
6
+ metadata.gz: f84414f2237be27ea64fee65287f68e9275dc294beb1b4dffb62fc8f1281a859f3e98bd257ebb25ba1d08e135ced88d6804e1071fe3b124259e4cfbbbdf2fa4c
7
+ data.tar.gz: 5c9e1b6fc200af2b1422047424097afef8881d3a78420f91a72f8eb72b86aa2940ab3364c7f670c25b27f9e28d351b52657ac2249e176342efd95af7b963ec3c
data/Readme.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # USPS iMIS API for Ruby
2
2
 
3
- [![Gem Version](https://img.shields.io/gem/v/usps-imis-api)](https://rubygems.org/gems/usps-imis-api)
3
+ [![Gem Version][gem-badge]](https://rubygems.org/gems/usps-imis-api)
4
+ [![iMIS API PHP Wrapper Version][php-badge]](https://github.com/unitedstatespowersquadrons/imis-api-php)
4
5
 
5
6
  ## Installation
6
7
 
@@ -29,7 +30,6 @@ Usps::Imis.configure do |config|
29
30
  config.environment = :development
30
31
 
31
32
  # These options will default to the listed `ENV` variable if available.
32
- config.imis_id_query_name = ENV['IMIS_ID_QUERY_NAME']
33
33
  config.username = ENV['IMIS_USERNAME']
34
34
  config.password = ENV['IMIS_PASSWORD']
35
35
 
@@ -83,7 +83,10 @@ file to [rubygems.org](https://rubygems.org).
83
83
 
84
84
  ## PHP
85
85
 
86
- A wrapper for the CLI from this API is
87
- [available for PHP](https://github.com/unitedstatespowersquadrons/imis-api-php).
86
+ A wrapper for the CLI from this API is [available for PHP][php-repo].
88
87
 
89
88
  You can also view the (***deprecated***) [original PHP API](https://github.com/unitedstatespowersquadrons/imis-api).
89
+
90
+ [gem-badge]: https://img.shields.io/gem/v/usps-imis-api?logo=rubygems&logoColor=white
91
+ [php-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Fadmin.aws.usps.org%2Flibrary_badge%2Fimis-api-php&label=wrapper&logo=php&logoColor=white
92
+ [php-repo]: https://github.com/unitedstatespowersquadrons/imis-api-php
data/lib/usps/imis/api.rb CHANGED
@@ -11,6 +11,7 @@ module Usps
11
11
  #
12
12
  class Api
13
13
  include Requests
14
+ include Imis::Helpers
14
15
 
15
16
  NAME = 'USPS iMIS API - Ruby'
16
17
 
@@ -77,15 +78,7 @@ module Usps
77
78
  def imis_id=(id)
78
79
  raise Errors::LockedIdError if lock_imis_id
79
80
 
80
- hex = '[0-9a-fA-F]'
81
- uuid_pattern = /^#{hex}{8}-#{hex}{4}-#{hex}{4}-#{hex}{4}-#{hex}{12}$/
82
- @imis_id =
83
- if id.to_s.match?(uuid_pattern) then id
84
- elsif id.to_i.to_s == id.to_s.gsub(' ', '') then id.to_i
85
- elsif id.nil? then nil
86
- else
87
- raise ArgumentError, "Invalid id: #{id.inspect}"
88
- end
81
+ @imis_id = format_id(id)
89
82
  end
90
83
 
91
84
  # Manually set the current record ID
@@ -119,11 +112,13 @@ module Usps
119
112
 
120
113
  logger.debug "Fetching iMIS ID for #{certificate}"
121
114
 
122
- result = query(Imis.configuration.imis_id_query_name, { certificate: })
123
- page = result.page.tap { logger.tagged('Response').debug it }
124
- raise Errors::NotFoundError, 'Member not found' if page.empty?
115
+ net_contact_data = query('NetContactData', MajorKey: certificate).one
116
+ raise Errors::NotFoundError, 'Member not found' if net_contact_data.nil?
125
117
 
126
- self.imis_id = page.first['ID'].to_i
118
+ party = query('Party', UniformId: net_contact_data.id).one
119
+ raise Errors::NotFoundError, 'Member not found' if party.nil?
120
+
121
+ self.imis_id = party.id
127
122
  rescue Errors::ResponseError
128
123
  raise Errors::NotFoundError, 'Member not found'
129
124
  end
@@ -8,9 +8,28 @@ module Usps
8
8
  # @private
9
9
  #
10
10
  module Help
11
- TERMINAL_WIDTH = 120
11
+ # Width of the terminal window
12
+ #
13
+ # :nocov:
14
+ TERMINAL_WIDTH = IO.console&.winsize&.at(1) || 80
15
+ # :nocov:
16
+
17
+ # Extra padding between columns
18
+ #
12
19
  GAP = 4
13
20
 
21
+ # Adjust the underlying command padding to account for non-printing characters
22
+ #
23
+ # Bold and gray each add 14:
24
+ # '-'.bold.length == 15
25
+ # '-'.gray.length == 15
26
+ #
27
+ ESCAPE_PADDING = 14
28
+
29
+ # Pattern for removing escape codes from strings
30
+ #
31
+ ESCAPE_PATTERN = /\e\[[0-9;]*m/
32
+
14
33
  def banner_header(version)
15
34
  <<~BANNER
16
35
  #{version.bold.blue}
@@ -19,6 +38,12 @@ module Usps
19
38
  end
20
39
 
21
40
  def banner_contents
41
+ wiki = word_wrap(
42
+ 'For an explanation of how to provide API configuration, ' \
43
+ 'more details on the options, and usage examples, please refer to the wiki:',
44
+ TERMINAL_WIDTH - indent(1).length
45
+ ).join("\n#{indent(1)}")
46
+
22
47
  <<~BANNER
23
48
  #{'Usage'.underline}
24
49
 
@@ -27,10 +52,9 @@ module Usps
27
52
 
28
53
  #{'Further Help'.underline}
29
54
 
30
- For an explanation of how to provide API configuration, more details on the options,
31
- and usage examples, please refer to the wiki:
55
+ #{wiki}
32
56
 
33
- https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki/Command-Line
57
+ #{'https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki/Command-Line'.light_blue}
34
58
 
35
59
  BANNER
36
60
  end
@@ -51,12 +75,13 @@ module Usps
51
75
  raw_description, details = options[key]
52
76
  details ||= {}
53
77
 
54
- flag = option_flag_string(key, details).ljust(option_column_width)
78
+ flag_string = option_flag_string(key, details)
79
+ flag = flag_string.ljust(option_column_width + invisible_length(flag_string))
55
80
  left_width = option_column_width + indent(3).length # Indent additional lines one step further
56
81
  tags = [
57
82
  (" (default: #{details[:default]})" if details[:default]),
58
83
  (" (max: #{details[:max]})" if details[:max])
59
- ].compact.join
84
+ ].compact.join.gray
60
85
 
61
86
  "#{flag}#{wrap_description(raw_description, left_width, suffix: tags)}"
62
87
  end
@@ -64,29 +89,21 @@ module Usps
64
89
  private
65
90
 
66
91
  def help_commands(lines)
67
- # Adjust the underlying command padding to account for non-printing characters
68
- #
69
- # Bold and gray each add 14:
70
- # '-'.bold.length == 15
71
- # '-'.gray.length == 15
72
- #
73
- escape_padding = 14
74
-
75
- command_lengths = subcommands.map { |name, details| name.length + details[:value].to_s.length }
76
- command_width = command_lengths.max + GAP + escape_padding
77
-
78
92
  lines << "#{'Commands'.underline}\n"
79
93
  subcommands.each do |name, details|
80
94
  combined_name = details[:value] ? "#{name.bold} #{"<#{details[:value]}>".gray}" : name.bold
81
- lines << [
82
- indent(1),
83
- combined_name.ljust(command_width + (details[:value] ? escape_padding : 0)),
84
- details[:description]
85
- ].join
95
+ command = combined_name.ljust(command_width + (details[:value] ? ESCAPE_PADDING : 0))
96
+ left_width = indent(1).length + command_width
97
+ lines << "#{indent(1)}#{command}#{wrap_description(details[:description], left_width)}"
86
98
  end
87
99
  lines << "\n"
88
100
  end
89
101
 
102
+ def command_width
103
+ command_lengths = subcommands.map { |name, details| name.length + details[:value].to_s.length }
104
+ command_lengths.max + GAP + ESCAPE_PADDING
105
+ end
106
+
90
107
  def help_options(lines)
91
108
  lines << "#{'Options'.underline}\n"
92
109
  option_groups.each do |group_name, option_keys|
@@ -98,9 +115,16 @@ module Usps
98
115
 
99
116
  def help_legacy_flags(lines)
100
117
  lines << "#{indent(1)}#{'Legacy Flags'.underline}\n"
101
- lines << "#{indent(2)}The following options are still supported, but have been replaced by subcommands:\n"
118
+ explanation = word_wrap(
119
+ 'The following options are still supported, but have been replaced by subcommands:',
120
+ TERMINAL_WIDTH - indent(2).length
121
+ ).join("\n#{indent(2)}")
122
+ lines << "#{indent(2)}#{explanation}\n"
123
+
102
124
  legacy_flags.each do |flags, replacement|
103
- lines << "#{indent(2)}#{flags.ljust(option_column_width)}use: #{replacement}"
125
+ left_width = indent(2).length + option_column_width
126
+ description = wrap_description("#{'use:'.light_magenta} #{replacement}", left_width)
127
+ lines << "#{indent(2)}#{flags.ljust(option_column_width + invisible_length(flags))}#{description}"
104
128
  end
105
129
  lines << "\n"
106
130
  end
@@ -128,7 +152,7 @@ module Usps
128
152
  when :io then '=<io>'
129
153
  when :ios then '=<io+>'
130
154
  else ''
131
- end
155
+ end.gray
132
156
  end
133
157
 
134
158
  def wrap_description(text, left_width, suffix: '')
@@ -162,17 +186,16 @@ module Usps
162
186
  lines
163
187
  end
164
188
 
165
- def visible_length(text)
166
- text.gsub(/\e\[[0-9;]*m/, '').length
167
- end
189
+ def visible_length(text) = text.gsub(ESCAPE_PATTERN, '').length
190
+ def invisible_length(text) = text.length - visible_length(text)
168
191
 
169
192
  def option_column_width
170
193
  option_widths = option_groups.values.flatten.map do |key|
171
194
  _, details = options[key]
172
- option_flag_string(key, details || {}).length
195
+ visible_length(option_flag_string(key, details || {}))
173
196
  end
174
197
 
175
- all_widths = option_widths + legacy_flags.keys.map(&:length)
198
+ all_widths = option_widths + legacy_flags.keys.map { visible_length(it) }
176
199
  all_widths.max + GAP
177
200
  end
178
201
  end
@@ -168,9 +168,11 @@ module Usps
168
168
 
169
169
  def default_options? = options[:log_level] == 'info' && options.except(:log_level).values.none?
170
170
 
171
- # :nocov:
172
- def handle_exception!(exception) = options[:trace] ? raise(exception) : exit(2)
173
- # :nocov:
171
+ def handle_exception!(exception)
172
+ raise(exception) if options[:trace] || ENV.fetch('TESTING', false) || ENV.fetch('CI', false)
173
+
174
+ exit(2)
175
+ end
174
176
  end
175
177
  end
176
178
  end
@@ -2,47 +2,40 @@ Subcommands:
2
2
  object:
3
3
  description: Interact with a Business Object
4
4
  value: name
5
- legacy_name: 'on'
5
+ legacy: 'on'
6
6
  option:
7
- - Business Object name
8
- - type: string
9
- short: O
7
+ type: string
8
+ short: O
10
9
  panel:
11
10
  description: Interact with a Panel
12
11
  value: name
13
12
  option:
14
- - Panel name
15
- - type: string
16
- short: P
13
+ type: string
14
+ short: P
17
15
  query:
18
16
  description: Run an IQA or Business Object Query
19
17
  value: name
20
18
  option:
21
- - IQA Query or Business Object name to query
22
- - type: string
23
- short: Q
19
+ type: string
20
+ short: Q
24
21
  mapper:
25
22
  description: Interact with mapped fields
26
23
  option:
27
- - Interact with mapped fields
28
- - short: M
24
+ short: M
29
25
  map:
30
- description: "Shorthand for `<%= 'mapper -f'.green %>` to access a single mapped field"
26
+ description: Shorthand for `%{green:mapper -f}` to access a single mapped field
31
27
  value: field
32
28
  option:
33
- - "Shorthand for <%= '-Mf'.green %> to access a single mapped field"
34
- - type: string
35
- short: m
29
+ type: string
30
+ short: m
36
31
  business-objects:
37
32
  description: List available Business Objects
38
33
  option:
39
- - List available Business Objects
40
- - short: B
34
+ short: B
41
35
  auth-token:
42
36
  description: Return an auth token for other language wrappers
43
37
  option:
44
- - Return an auth token for other language wrappers
45
- - short: T
38
+ short: T
46
39
 
47
40
  Options:
48
41
  Identity:
@@ -65,13 +58,13 @@ Options:
65
58
 
66
59
  Business Object or Panel:
67
60
  create:
68
- - "Send a <%= 'POST'.cyan %> request"
61
+ - Send a %{cyan:POST} request
69
62
  - short: C
70
63
  delete:
71
- - "Send a <%= 'DELETE'.cyan %> request"
64
+ - Send a %{cyan:DELETE} request
72
65
  - short: D
73
66
  all:
74
- - "Send a <%= 'GET'.cyan %> request for all records for a member"
67
+ - Send a %{cyan:GET} request for all records for a member
75
68
  - short: A
76
69
 
77
70
  Panel:
@@ -106,7 +99,7 @@ Options:
106
99
  - type: strings
107
100
  short: F
108
101
  data:
109
- - "JSON string input -- <%= 'STDIN'.red %> takes priority"
102
+ - JSON string input -- %{red:STDIN} takes priority
110
103
  - type: string
111
104
  short: d
112
105
 
@@ -126,21 +119,20 @@ Options:
126
119
 
127
120
  Configuration:
128
121
  config:
129
- - "Path to the JSON/YAML config file to use, or one of the following preset options:\
130
- \n`<%= 'local_dot'.yellow %>`, \
131
- `<%= 'local'.yellow %>`, \
132
- `<%= 'local_dot_config'.yellow %>`, \
133
- `<%= 'local_config'.yellow %>`, \
134
- `<%= 'user'.yellow %>`, \
135
- `<%= 'system'.yellow %>`\
136
- \nIf no option is provided, the first matching preset option will be automatically used."
122
+ - >-
123
+ Path to the JSON/YAML config file to use, or one of the following preset options:
124
+
125
+ `%{yellow:local_dot}`, `%{yellow:local}`, `%{yellow:local_dot_config}`,
126
+ `%{yellow:local_config}`, `%{yellow:user}`, `%{yellow:system}`
127
+
128
+ If no option is provided, the first matching preset option will be automatically used.
137
129
  - type: string
138
130
  short: Y
139
131
  show_config:
140
132
  - Return the active config file path
141
133
  - short: X
142
134
  include_ids:
143
- - "Include any <%= 'iMIS ID'.yellow %> and <%= 'Ordinal'.yellow %> properties in returned data"
135
+ - Include any %{yellow:iMIS ID} and %{yellow:Ordinal} properties in returned data
144
136
  - short: N
145
137
  token:
146
138
  - Provide an existing auth token
@@ -149,10 +141,10 @@ Options:
149
141
 
150
142
  Logging:
151
143
  quiet:
152
- - "Suppress logging to <%= 'STDERR'.red %>"
144
+ - Suppress logging to %{red:STDERR}
153
145
  - short: q
154
146
  log:
155
- - "Redirect logging to <%= 'STDOUT'.red %>"
147
+ - Redirect logging to %{red:STDOUT}
156
148
  - short: l
157
149
  log_level:
158
150
  - Set the logging level
@@ -165,10 +157,10 @@ Options:
165
157
 
166
158
  Misc:
167
159
  version:
168
- - Print version and exit
160
+ - Return API version
169
161
  - short: v
170
162
  environment:
171
- - Print configured environment and exit
163
+ - Return configured environment
172
164
  - short: E
173
165
  help:
174
166
  - Show this message
@@ -8,8 +8,15 @@ module Usps
8
8
  # @private
9
9
  #
10
10
  module OptionsParserConfig
11
+ STYLE_NAMES = (String.colors + String.modes).map(&:to_s).freeze
12
+ STYLE_PATTERN = /%\{(#{Regexp.union(STYLE_NAMES)}(?:\.#{Regexp.union(STYLE_NAMES)})*):(.*?)\}/
13
+
11
14
  def subcommands
12
- @subcommands ||= yaml['Subcommands'].transform_values { it.transform_keys(&:to_sym) }
15
+ @subcommands ||= yaml['Subcommands'].transform_values do |details|
16
+ details.transform_keys(&:to_sym).tap do |h|
17
+ h[:description] = colorize_text(h[:description]) if h[:description]
18
+ end
19
+ end
13
20
  end
14
21
 
15
22
  def option_groups
@@ -19,7 +26,7 @@ module Usps
19
26
  def options
20
27
  @options ||= merged_options.transform_keys(&:to_sym).transform_values do |description, details|
21
28
  [
22
- description,
29
+ colorize_text(description),
23
30
  details.transform_keys(&:to_sym).each_with_object({}) do |(key, value), hash|
24
31
  hash[key] = %i[type short].include?(key) ? value.to_sym : value
25
32
  end
@@ -29,15 +36,15 @@ module Usps
29
36
 
30
37
  def legacy_aliases
31
38
  @legacy_aliases ||= subcommands.each_with_object({}) do |(name, details), aliases|
32
- next unless details[:legacy_name]
39
+ next unless details[:legacy]
33
40
 
34
- aliases["--#{details[:legacy_name]}"] = "--#{name}"
41
+ aliases["--#{details[:legacy]}"] = "--#{name}"
35
42
  end
36
43
  end
37
44
 
38
45
  def legacy_flags
39
46
  @legacy_flags ||= subcommands.to_h do |name, details|
40
- flag_name = details[:legacy_name] || name.tr('-', '_')
47
+ flag_name = details[:legacy] || name.tr('-', '_')
41
48
  option_key = name.tr('-', '_').to_sym
42
49
  _, option_details = options[option_key]
43
50
 
@@ -51,15 +58,22 @@ module Usps
51
58
  private
52
59
 
53
60
  def yaml
54
- return @yaml if @yaml
61
+ @yaml ||= YAML.safe_load_file("#{File.dirname(__FILE__)}/options.yml")
62
+ end
55
63
 
56
- raw_yaml_erb = File.read("#{File.dirname(__FILE__)}/options.yml.erb")
57
- rendered_yaml = ERB.new(raw_yaml_erb).result.gsub("\x1b", '\u001b')
58
- @yaml = YAML.safe_load(rendered_yaml)
64
+ def colorize_text(text)
65
+ text.gsub(STYLE_PATTERN) do
66
+ Regexp.last_match(2).colorize(
67
+ Regexp.last_match(1).split('.').each_with_object({}) do |name, args|
68
+ key = String.colors.include?(name.to_sym) ? :color : :mode
69
+ args[key] = name.to_sym
70
+ end
71
+ )
72
+ end
59
73
  end
60
74
 
61
75
  def merged_options
62
- subcommand_options = subcommands.transform_values { it[:option] }.transform_keys { it.gsub('-', '_') }
76
+ subcommand_options = subcommands.transform_values { ['', it[:option]] }.transform_keys { it.gsub('-', '_') }
63
77
  yaml['Options'].values.inject(:merge).merge(subcommand_options)
64
78
  end
65
79
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'optimist'
4
4
  require 'colorize'
5
- require 'erb'
6
5
  require 'csv'
6
+ require 'io/console'
7
7
 
8
8
  module Usps
9
9
  module Imis
@@ -14,16 +14,15 @@ module Usps
14
14
  IMIS_ROOT_URL_PROD = 'https://portal.americasboatingclub.org'
15
15
  IMIS_ROOT_URL_DEV = 'https://abcdev.imiscloud.com'
16
16
  DEFAULT_GLOBAL_LOG_PATH = '/var/log/imis/imis.log'
17
- REQUIRED = %w[imis_id_query_name username password].freeze
17
+ REQUIRED = %w[username password].freeze
18
18
  OPTIONAL = %w[environment logger_file global_log_path].freeze
19
19
  SETTABLE = (REQUIRED + OPTIONAL).freeze
20
20
 
21
- attr_accessor :imis_id_query_name, :username, :password
21
+ attr_accessor :username, :password
22
22
  attr_reader :environment, :logger, :logger_level, :logger_file
23
23
 
24
24
  def initialize
25
25
  @environment = default_environment
26
- @imis_id_query_name = ENV.fetch('IMIS_ID_QUERY_NAME', nil)
27
26
  @username = ENV.fetch('IMIS_USERNAME', nil)
28
27
  @password = ENV.fetch('IMIS_PASSWORD', nil)
29
28
  @base_logger = Logger.new($stdout, level: :info)
@@ -10,18 +10,23 @@ module Usps
10
10
  # Convenience wrapper for accessing specific properties within an API data response
11
11
  #
12
12
  class Data < BaseData
13
+ include Imis::Helpers
14
+
13
15
  # The Business Object or Panel name
14
16
  #
15
17
  def entity = raw['EntityTypeName']
16
18
 
17
19
  # Access the iMIS ID (i.e. Party ID) property
18
20
  #
19
- def imis_id = raw['PrimaryParentIdentity']['IdentityElements']['$values'].first.to_i
21
+ def imis_id = format_id(raw['PrimaryParentIdentity']['IdentityElements']['$values'].first)
20
22
  alias id imis_id
21
23
 
22
24
  # Access the Record ID property
23
25
  #
24
- def record_id = raw['Identity']['IdentityElements']['$values'].first.to_i
26
+ def record_id
27
+ identity = format_id(raw['Identity']['IdentityElements']['$values'].first)
28
+ identity unless identity == imis_id
29
+ end
25
30
 
26
31
  # Access the Ordinal identifier property (if present)
27
32
  #
@@ -51,7 +56,7 @@ module Usps
51
56
  private
52
57
 
53
58
  def pretty_print_data
54
- { entity:, imis_id:, record_id: (record_id unless record_id == imis_id), ordinal: }.compact
59
+ { entity:, imis_id:, record_id:, ordinal: }.compact
55
60
  end
56
61
 
57
62
  def property_values = raw['Properties']['$values']
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Usps
4
+ module Imis
5
+ # General helper methods
6
+ #
7
+ module Helpers
8
+ # Helper for consistently formatting valid IDs
9
+ #
10
+ def format_id(raw_id)
11
+ hex = '[0-9a-fA-F]'
12
+ uuid_pattern = /^#{hex}{8}-#{hex}{4}-#{hex}{4}-#{hex}{4}-#{hex}{12}$/
13
+
14
+ if raw_id.to_s.match?(uuid_pattern) then raw_id
15
+ elsif raw_id.to_i.to_s == raw_id.to_s.gsub(' ', '') then raw_id.to_i
16
+ elsif raw_id.nil? then nil
17
+ else
18
+ raise ArgumentError, "Invalid id: #{raw_id.inspect}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -90,6 +90,10 @@ module Usps
90
90
  #
91
91
  def page = fetch_next['Items']['$values'].map { iqa? ? it.except('$type') : wrap(it) }
92
92
 
93
+ # Fetch the first result from a single query page
94
+ #
95
+ def one = page.first
96
+
93
97
  # Fetch the next raw query page, and update the current offset
94
98
  #
95
99
  def fetch_next
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Usps
4
4
  module Imis
5
- VERSION = '0.13.4'
5
+ VERSION = '0.13.6'
6
6
  end
7
7
  end
data/lib/usps/imis.rb CHANGED
@@ -20,6 +20,7 @@ require 'active_support/tagged_logging'
20
20
  require 'dotenv/load'
21
21
 
22
22
  # Internal requires
23
+ require_relative 'imis/helpers'
23
24
  require_relative 'imis/config'
24
25
  require_relative 'imis/error'
25
26
  require_relative 'imis/api'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: usps-imis-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.4
4
+ version: 0.13.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julian Fiander
@@ -97,7 +97,7 @@ files:
97
97
  - lib/usps/imis/command_line/formatters.rb
98
98
  - lib/usps/imis/command_line/help.rb
99
99
  - lib/usps/imis/command_line/interface.rb
100
- - lib/usps/imis/command_line/options.yml.erb
100
+ - lib/usps/imis/command_line/options.yml
101
101
  - lib/usps/imis/command_line/options_parser.rb
102
102
  - lib/usps/imis/command_line/options_parser_config.rb
103
103
  - lib/usps/imis/command_line/performers.rb
@@ -115,6 +115,7 @@ files:
115
115
  - lib/usps/imis/errors/pattern_matching_error.rb
116
116
  - lib/usps/imis/errors/response_error.rb
117
117
  - lib/usps/imis/errors/unexpected_property_type_error.rb
118
+ - lib/usps/imis/helpers.rb
118
119
  - lib/usps/imis/logger.rb
119
120
  - lib/usps/imis/logger_formatter.rb
120
121
  - lib/usps/imis/logger_helpers.rb