usps-imis-api 0.11.25 → 1.0.0.pre.rc.1

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +57 -0
  3. data/.gitignore +4 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +88 -0
  6. data/.ruby-version +1 -0
  7. data/.simplecov +8 -0
  8. data/Gemfile +12 -0
  9. data/Gemfile.lock +95 -0
  10. data/Rakefile +12 -0
  11. data/Readme.md +191 -19
  12. data/bin/console +21 -0
  13. data/bin/setup +8 -0
  14. data/lib/ext/hash.rb +10 -0
  15. data/lib/usps/imis/api.rb +138 -177
  16. data/lib/usps/imis/config.rb +10 -68
  17. data/lib/usps/imis/error/api.rb +26 -0
  18. data/lib/usps/imis/error/mapper.rb +9 -0
  19. data/lib/usps/imis/{errors/response_error.rb → error/response.rb} +7 -34
  20. data/lib/usps/imis/mapper.rb +21 -90
  21. data/lib/usps/imis/panel/base_panel.rb +42 -0
  22. data/lib/usps/imis/panel/education.rb +111 -0
  23. data/lib/usps/imis/panel/vsc.rb +109 -0
  24. data/lib/usps/imis/version.rb +1 -1
  25. data/lib/usps/imis.rb +17 -32
  26. data/spec/lib/usps/imis/api_spec.rb +143 -0
  27. data/spec/lib/usps/imis/config_spec.rb +33 -0
  28. data/spec/lib/usps/imis/error/api_spec.rb +17 -0
  29. data/spec/lib/usps/imis/error/response_spec.rb +107 -0
  30. data/spec/lib/usps/imis/mapper_spec.rb +31 -0
  31. data/spec/lib/usps/imis/panel/base_panel_spec.rb +32 -0
  32. data/spec/lib/usps/imis/panel/education_spec.rb +55 -0
  33. data/spec/lib/usps/imis/panel/vsc_spec.rb +38 -0
  34. data/spec/lib/usps/imis_spec.rb +11 -0
  35. data/spec/spec_helper.rb +35 -0
  36. data/usps-imis-api.gemspec +18 -0
  37. metadata +33 -97
  38. data/bin/imis +0 -8
  39. data/lib/usps/imis/base_data.rb +0 -62
  40. data/lib/usps/imis/business_object.rb +0 -220
  41. data/lib/usps/imis/command_line/interface.rb +0 -158
  42. data/lib/usps/imis/command_line/options_parser.rb +0 -136
  43. data/lib/usps/imis/command_line/performers.rb +0 -80
  44. data/lib/usps/imis/command_line.rb +0 -15
  45. data/lib/usps/imis/data.rb +0 -56
  46. data/lib/usps/imis/error.rb +0 -55
  47. data/lib/usps/imis/errors/api_error.rb +0 -11
  48. data/lib/usps/imis/errors/command_line_error.rb +0 -11
  49. data/lib/usps/imis/errors/config_error.rb +0 -11
  50. data/lib/usps/imis/errors/locked_id_error.rb +0 -15
  51. data/lib/usps/imis/errors/mapper_error.rb +0 -29
  52. data/lib/usps/imis/errors/missing_id_error.rb +0 -15
  53. data/lib/usps/imis/errors/not_found_error.rb +0 -11
  54. data/lib/usps/imis/errors/panel_unimplemented_error.rb +0 -34
  55. data/lib/usps/imis/errors/unexpected_property_type_error.rb +0 -31
  56. data/lib/usps/imis/logger.rb +0 -19
  57. data/lib/usps/imis/logger_formatter.rb +0 -32
  58. data/lib/usps/imis/logger_helpers.rb +0 -17
  59. data/lib/usps/imis/mocks/business_object.rb +0 -47
  60. data/lib/usps/imis/mocks.rb +0 -11
  61. data/lib/usps/imis/panels/base_panel.rb +0 -158
  62. data/lib/usps/imis/panels/education.rb +0 -33
  63. data/lib/usps/imis/panels/vsc.rb +0 -32
  64. data/lib/usps/imis/panels.rb +0 -25
  65. data/lib/usps/imis/party_data.rb +0 -88
  66. data/lib/usps/imis/properties.rb +0 -50
  67. data/lib/usps/imis/query.rb +0 -153
  68. data/lib/usps/imis/requests.rb +0 -68
  69. data/spec/support/usps/vcr/config.rb +0 -47
  70. data/spec/support/usps/vcr/filters.rb +0 -89
  71. data/spec/support/usps/vcr.rb +0 -8
@@ -1,220 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'requests'
4
- require_relative 'data'
5
- require_relative 'party_data'
6
-
7
- module Usps
8
- module Imis
9
- # DEV
10
- class BusinessObject
11
- include Requests
12
-
13
- # Endpoint for general API requests
14
- #
15
- API_PATH = 'api'
16
-
17
- # The parent +Api+ object
18
- #
19
- attr_reader :api
20
-
21
- # Name of the iMIS Business Object
22
- #
23
- attr_reader :business_object_name
24
-
25
- # Ordinal to build override ID param of the URL (e.g. used for Panels)
26
- #
27
- attr_reader :ordinal
28
-
29
- # Tagged logger
30
- #
31
- attr_reader :logger
32
-
33
- # A new instance of +BusinessObject+
34
- #
35
- def initialize(api, business_object_name, ordinal: nil)
36
- @api = api
37
- @business_object_name = business_object_name
38
- @ordinal = ordinal
39
- @logger ||= Imis.logger('BusinessObject')
40
- end
41
-
42
- # Run a query on the entire business object
43
- #
44
- # @return [Usps::Imis::Query] Query wrapper
45
- #
46
- def query = api.query(business_object_name)
47
-
48
- # Support passthrough for Api#with
49
- #
50
- def with(imis_id, &)
51
- # Bring into local scope
52
- wrapper_business_object_name = business_object_name
53
- wrapper_ordinal = ordinal
54
-
55
- api.with(imis_id) do
56
- on(wrapper_business_object_name, ordinal: wrapper_ordinal, &)
57
- end
58
- end
59
-
60
- # Get a business object for the current member
61
- #
62
- # If +fields+ is provided, will return only those field values
63
- #
64
- # @param fields [String] Field names to return
65
- #
66
- # @return [Usps::Imis::Data, Array<Usps::Imis::Data>] Response data from the API
67
- #
68
- def get(*fields) = fields.any? ? get_fields(*fields) : raw_object
69
- alias read get
70
-
71
- # Get a single named field from a business object for the current member
72
- #
73
- # @param field [String] Field name to return
74
- #
75
- # @return Response data field value from the API
76
- #
77
- def get_field(field) = raw_object[field]
78
- alias fetch get_field
79
- alias [] get_field
80
-
81
- # Get named fields from a business object for the current member
82
- #
83
- # @param names [Array<String>] Field names to return
84
- #
85
- # @return [Array] Response data from the API
86
- #
87
- def get_fields(*fields)
88
- values = raw_object
89
- fields.map { values[it] }
90
- end
91
- alias fetch_all get_fields
92
-
93
- # Update a single named field on a business object for the current member
94
- #
95
- # @param field [String] Name of the field
96
- # @param value Value of the field
97
- #
98
- # @return [Usps::Imis::Data] Response data from the API
99
- #
100
- def put_field(field, value) = put(filter_fields(field => value))
101
- alias []= put_field
102
-
103
- # Update only specific fields on a business object for the current member
104
- #
105
- # @param fields [Hash] Conforms to pattern +{ field_key => value }+
106
- #
107
- # @return [Usps::Imis::Data] Response data from the API
108
- #
109
- def put_fields(fields) = put(filter_fields(fields))
110
- alias patch put_fields
111
-
112
- # Update a business object for the current member
113
- #
114
- # Any properties not included will be left unmodified
115
- #
116
- # @param body [Hash] Full raw API object data
117
- #
118
- # @return [Usps::Imis::Data] Response data from the API
119
- #
120
- def put(body) = put_object(http_put, body)
121
- alias update put
122
-
123
- # Create a business object for the current member
124
- #
125
- # @param body [Hash] Full raw API object data
126
- #
127
- # @return [Usps::Imis::Data] Response data from the API
128
- #
129
- def post(body) = put_object(http_post, body)
130
- alias create post
131
-
132
- # Remove a business object for the current member
133
- #
134
- # @return [true] Only on success response (i.e. blank string from the API)
135
- #
136
- def delete = submit(uri, authorize(http_delete)).body == '' # rubocop:disable Naming/PredicateMethod
137
- alias destroy delete
138
-
139
- # Ruby 3.5 instance variable filter
140
- #
141
- def instance_variables_to_inspect = instance_variables - %i[@api @logger]
142
-
143
- private
144
-
145
- # Construct a business object API endpoint address
146
- #
147
- def uri(id: nil)
148
- full_path = "#{API_PATH}/#{business_object_name}/#{id_for_uri(id)}"
149
- URI(File.join(Imis.configuration.hostname, full_path))
150
- end
151
-
152
- # Select the correct ID to use in the URI
153
- #
154
- def id_for_uri(id = nil)
155
- return CGI.escape(id) unless id.nil?
156
- return CGI.escape("~#{api.imis_id}|#{ordinal}") if ordinal
157
-
158
- api.imis_id.to_s
159
- end
160
-
161
- # Manually assemble the matching data structure, with fields in the correct order
162
- #
163
- def filter_fields(fields)
164
- block_not_supported!
165
-
166
- existing = get
167
-
168
- JSON.parse(JSON.dump(existing)).tap do |updated|
169
- # Preserve the iMIS ID, as well as the Ordinal (if present)
170
- updated['Properties']['$values'], properties =
171
- existing.raw['Properties']['$values'].partition { %w[ID Ordinal].include?(it['Name']) }
172
-
173
- # Iterate through all existing fields
174
- properties.each do |value|
175
- # Skip unmodified fields
176
- next unless fields.keys.include?(value['Name'])
177
-
178
- value['Value'] = Properties.wrap(fields[value['Name']])
179
-
180
- # Add the completed field with the updated value
181
- updated['Properties']['$values'] << value
182
- end
183
- end
184
- end
185
-
186
- # Get a raw object response from the API
187
- #
188
- # Useful for stubbing data in tests
189
- #
190
- def raw_object
191
- raise Errors::MissingIdError if api.imis_id.nil?
192
-
193
- response = submit(uri, authorize(http_get))
194
- klass = business_object_name == 'Party' ? PartyData : Data
195
- result = klass.from_json(response.body)
196
- logger.json result
197
- result
198
- end
199
-
200
- # Upload an object to the API
201
- #
202
- def put_object(request, body)
203
- raise Errors::MissingIdError if api.imis_id.nil?
204
-
205
- request.body = JSON.dump(body)
206
- response = submit(uri, authorize(request))
207
- klass = business_object_name == 'Party' ? PartyData : Data
208
- result = klass.from_json(response.body)
209
- logger.json result
210
- result
211
- end
212
-
213
- def block_not_supported!
214
- return unless business_object_name == 'Party'
215
-
216
- raise Errors::ApiError, 'The Party business object does not support updating specific fields'
217
- end
218
- end
219
- end
220
- end
@@ -1,158 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'performers'
4
-
5
- module Usps
6
- module Imis
7
- module CommandLine
8
- # Command line interface for the Api
9
- #
10
- class Interface
11
- include Performers
12
-
13
- NAME = 'USPS iMIS API - Ruby'
14
-
15
- # CLI options that indicate the response is a raw Hash rather than a Data object,
16
- # and should not be simplified
17
- #
18
- RAW_HASH_RESPONSE_OPTIONS = %i[business_objects auth_token].freeze
19
-
20
- # Options passed in from the command line
21
- #
22
- attr_reader :options
23
-
24
- # Tagged logger
25
- #
26
- attr_reader :logger
27
-
28
- # Initialize an +Interface+ and run it with the provided options
29
- #
30
- # @param [**Hash] Options from the CLI options parser
31
- #
32
- def self.run(...) = new(...).run
33
-
34
- # A new instance of +Interface+
35
- #
36
- # @param [**Hash] Options from the CLI options parser
37
- #
38
- def initialize(**)
39
- @options = input_options.merge(**)
40
- options[:version] = true if default_options?
41
-
42
- configure! if options[:config]
43
- logging!
44
- @logger ||= Imis.logger('CommandLine')
45
- end
46
-
47
- # Run the configured action on the API
48
- #
49
- def run
50
- logger.info 'Running'
51
- logger.debug 'CLI Options:'
52
- logger.json(options.dup.tap { it[:token] = '[FILTERED]' if it[:token] })
53
-
54
- set_member
55
-
56
- result = simplify(perform!)
57
-
58
- output { result }
59
-
60
- result
61
- end
62
-
63
- private
64
-
65
- # :nocov:
66
- def input_options
67
- return OptionsParser.new.options unless ENV.fetch('CI', false) || ENV.fetch('TESTING', false)
68
-
69
- {
70
- raw: false,
71
- quiet: false,
72
- log: false,
73
- log_level: 'info',
74
- version: false
75
- }
76
- end
77
- # :nocov:
78
-
79
- def api
80
- @api ||=
81
- if options[:token]
82
- Usps::Imis::Api.with_token(options[:token])
83
- else
84
- Usps::Imis::Api.new
85
- end
86
- end
87
-
88
- def set_member
89
- case options
90
- in certificate: then api.imis_id_for(certificate)
91
- in id: then api.imis_id = id
92
- else
93
- # Query
94
- end
95
-
96
- logger.debug "iMIS ID: #{api.imis_id}" if api.imis_id
97
- end
98
-
99
- def simplify(data)
100
- return data.to_a if data.is_a?(Query)
101
- return data if options[:raw] || RAW_HASH_RESPONSE_OPTIONS.any? { options[it] }
102
-
103
- # Hash includes Usps::Imis::Data
104
- if data.is_a?(Hash)
105
- logger.debug 'Returning simplified Data#properties'
106
- data.properties(include_ids: options[:include_ids])
107
- elsif data.is_a?(Array) && data.all? { it.is_a?(Hash) }
108
- logger.debug 'Returning simplified Array<Data#properties>'
109
- data.map { it.properties(include_ids: options[:include_ids]) }
110
- else
111
- data
112
- end
113
- end
114
-
115
- # :nocov:
116
- def output(&)
117
- if ENV.fetch('SUPPRESS_OUTPUT', false)
118
- logger.debug 'Output suppressed'
119
- return
120
- end
121
-
122
- puts JSON.dump(yield) if block_given?
123
- end
124
- # :nocov:
125
-
126
- # Supports YAML or JSON config data
127
- #
128
- def configure!
129
- config_data = YAML.safe_load_file(options[:config])
130
-
131
- Usps::Imis.configure do |config|
132
- config_data.each do |key, value|
133
- config.public_send("#{key}=", value)
134
- end
135
- end
136
- end
137
-
138
- def logging!
139
- Usps::Imis.configure do |config|
140
- # :nocov:
141
- if options[:quiet] || ENV.fetch('SUPPRESS_OUTPUT', false)
142
- config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
143
- elsif options[:log] || config.logger_file
144
- # Use default, or configured file
145
- else
146
- config.logger = ActiveSupport::TaggedLogging.new(Logger.new($stderr))
147
- end
148
- # :nocov:
149
-
150
- config.logger.level = options[:log_level]
151
- end
152
- end
153
-
154
- def default_options? = options[:log_level] == 'info' && options.except(:log_level).values.none?
155
- end
156
- end
157
- end
158
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Usps
4
- module Imis
5
- module CommandLine
6
- # Command line options parser
7
- #
8
- class OptionsParser
9
- OPTIONS = {
10
- # IDs
11
- certificate: ['Member certificate number', { type: :string }],
12
- id: ['Member iMIS ID', { type: :integer }],
13
-
14
- # Primary interactions
15
- on: ['Business Object name', { type: :string }],
16
- panel: ['Panel name', { type: :string }],
17
- query: ['IQA Query or Business Object name to query', { type: :string, short: :Q }],
18
- mapper: ['Interact with mapped fields', { short: :M }],
19
- map: ["Shorthand for #{'-Mf'.green} to access a single mapped field", { type: :string }],
20
- business_objects: ['List available Business Objects'],
21
-
22
- # Alternate verbs
23
- create: ["Send a #{'POST'.cyan} request", { short: :P }],
24
- delete: ["Send a #{'DELETE'.cyan} request", { short: :D }],
25
-
26
- # Data
27
- ordinal: ['Ordinal ID within a Panel', { type: :integer }],
28
- field: ['Specific field to return or update', { type: :string }],
29
- fields: ['Specific field(s) to return', { type: :strings, short: :F }],
30
- data: ['JSON string input', { type: :string }],
31
-
32
- # Iteractions for supporting other language wrappers
33
- auth_token: ['Return an auth token for other language wrappers', { short: :T }],
34
- token: ['Provide an existing auth token', { type: :string }],
35
-
36
- # General config
37
- config: ['Path to the JSON/YAML config file to use', { type: :string, short: :C }],
38
- raw: ['Return raw JSON output, rather than simplified data', { short: :R }],
39
- include_ids: ["Include any #{'iMIS ID'.yellow} and #{'Ordinal'.yellow} properties in returned data"],
40
- quiet: ["Suppress logging to #{'STDERR'.red}"],
41
- log: ["Redirect logging to #{'STDOUT'.red}"],
42
- log_level: ['Set the logging level', { type: :string, default: 'info', short: :L }]
43
- }.freeze
44
-
45
- CONFLICTING_OPTION_GROUPS = [
46
- %i[certificate id],
47
- %i[on panel query mapper map business_objects auth_token],
48
- %i[field fields map query],
49
- %i[raw include_ids],
50
- %i[quiet log_level],
51
- %i[quiet log],
52
-
53
- %i[create delete],
54
-
55
- %i[create mapper],
56
- %i[create query],
57
- %i[create map],
58
- %i[create field],
59
- %i[create fields],
60
-
61
- %i[delete mapper],
62
- %i[delete query],
63
- %i[delete map],
64
- %i[delete field],
65
- %i[delete fields],
66
- %i[delete data],
67
- %i[delete raw]
68
- ].freeze
69
-
70
- attr_reader :arguments, :options
71
-
72
- def self.banner_header(version)
73
- <<~BANNER
74
- #{version.bold.blue}
75
- #{'P/R/C Julian Fiander, SN'.gray}\n \n
76
- BANNER
77
- end
78
-
79
- def self.banner_contents
80
- <<~BANNER
81
- #{'Usage'.underline}
82
-
83
- #{'imis'.bold} #{'[options]'.gray}
84
-
85
-
86
- #{'Further Help'.underline}
87
-
88
- For an explanation of how to provide API configuration, more details on the options,
89
- and usage examples, please refer to the wiki:
90
-
91
- https://github.com/unitedstatespowersquadrons/imis-api-ruby/wiki/Command-Line
92
-
93
-
94
- #{'Options'.underline}
95
- BANNER
96
- end
97
-
98
- def initialize
99
- @options = parse_options.compact
100
- @arguments = ARGV # Not currently used
101
-
102
- Optimist.educate if ARGV.empty? && defaults? # DEV: This shadows setting the --version flag by default
103
-
104
- # :nocov:
105
- @options[:data] = read_stdin if stdin?
106
- # :nocov:
107
-
108
- @options[:data] = JSON.parse(@options[:data]) if @options[:data]
109
- end
110
-
111
- private
112
-
113
- def parse_options
114
- Optimist.options do
115
- version "#{Interface::NAME} (v#{Usps::Imis::VERSION})"
116
-
117
- banner OptionsParser.banner_header(version)
118
- banner OptionsParser.banner_contents
119
-
120
- OPTIONS.each { |option, data| opt(option, *data) }
121
- CONFLICTING_OPTION_GROUPS.each { |group| conflicts(*group) }
122
-
123
- educate_on_error
124
- end
125
- end
126
-
127
- # :nocov:
128
- def stdin? = $stdin.wait_readable(0)
129
- def read_stdin = $stdin.read.chomp
130
- # :nocov:
131
-
132
- def defaults? = options[:log_level] == 'info' && options.except(:log_level).values.none?
133
- end
134
- end
135
- end
136
- end
@@ -1,80 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Usps
4
- module Imis
5
- module CommandLine
6
- # Command line interface perform matchers
7
- #
8
- # @private
9
- #
10
- module Performers
11
- private
12
-
13
- def perform!
14
- case convert(options)
15
- in mapper:, **extra then perform_mapper(mapper, **extra)
16
- in on:, **extra then perform_on(on, **extra)
17
- in panel:, **extra then perform_panel(panel, **extra)
18
- in query:, **extra then api.query(query, extra[:data])
19
- in business_objects: true then api.business_objects
20
- in auth_token: true then api.auth_token
21
- in certificate: then api.imis_id
22
- in version: true then "#{Interface::NAME} (v#{Imis::VERSION})"
23
- end
24
- rescue NoMatchingPatternError => e
25
- raise Errors::CommandLineError, "Unable to match pattern from options: #{e.message}"
26
- end
27
-
28
- def convert(options)
29
- options.dup.tap do |converted|
30
- case converted
31
- in map: then converted.merge!(mapper: api.mapper, field: map)
32
- in mapper: true then converted[:mapper] = api.mapper
33
- in on: then converted[:on] = api.on(on)
34
- in panel: then converted[:panel] = api.panels.public_send(panel)
35
- else
36
- # Nothing to convert
37
- end
38
-
39
- # Remove mapper flag when false
40
- converted.delete(:mapper) unless converted[:mapper]
41
- end
42
- end
43
-
44
- def perform_mapper(mapper, **options)
45
- case options
46
- in field:, data: then mapper.put_field(field.to_sym, data)
47
- in fields: then mapper.get_fields(*fields)
48
- in field: then mapper.get_field(field.to_sym)
49
- in data: then mapper.update(data)
50
- end
51
- end
52
-
53
- def perform_on(on, **options)
54
- case options
55
- in delete: true then on.delete
56
- in create: true, data: then on.post(data)
57
- in data:, field: then on.put_field(field, data)
58
- in data: then on.put_fields(data)
59
- in fields: then on.get_fields(*fields)
60
- in field: then on.get_field(field)
61
- else
62
- on.get
63
- end
64
- end
65
-
66
- def perform_panel(panel, **options)
67
- case options
68
- in delete: true, ordinal: then panel.delete(ordinal)
69
- in create: true, data: then panel.post(data)
70
- in ordinal:, data:, field: then panel.put_field(ordinal, field, data)
71
- in ordinal:, data: then panel.put_fields(ordinal, data)
72
- in ordinal:, fields: then panel.get_fields(ordinal, *fields)
73
- in ordinal:, field: then panel.get_field(ordinal, field)
74
- in ordinal: then panel.get(ordinal)
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'optimist'
4
- require 'colorize'
5
-
6
- module Usps
7
- module Imis
8
- # Wrapper for the command line interface
9
- #
10
- module CommandLine; end
11
- end
12
- end
13
-
14
- require_relative 'command_line/options_parser'
15
- require_relative 'command_line/interface'
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'pp'
4
- require 'stringio'
5
-
6
- require_relative 'base_data'
7
-
8
- module Usps
9
- module Imis
10
- # Convenience wrapper for accessing specific properties within an API data response
11
- #
12
- class Data < BaseData
13
- # The Business Object or Panel name
14
- #
15
- def entity = raw['EntityTypeName']
16
-
17
- # Access the iMIS ID property
18
- #
19
- def imis_id = self['ID'].to_i
20
- alias id imis_id
21
-
22
- # Access the Ordinal identifier property (if present)
23
- #
24
- def ordinal = self['Ordinal']&.to_i
25
-
26
- # Access an individual property value by name
27
- #
28
- def [](property_name)
29
- property = property_values.find { it['Name'] == property_name }
30
- return if property.nil?
31
-
32
- value = property['Value']
33
- value.nil? || value.is_a?(String) ? value : value['$value']
34
- end
35
-
36
- # Hash of all property names to values
37
- #
38
- # @param include_ids [Boolean] Whether to include the iMIS ID and Ordinal
39
- #
40
- def properties(include_ids: false)
41
- property_values
42
- .map { it['Name'] }
43
- .select { include_ids || !%w[ID Ordinal].include?(it) }
44
- .index_with { self[it] }
45
- end
46
-
47
- private
48
-
49
- def pretty_print_data
50
- { entity:, imis_id:, ordinal: }.compact
51
- end
52
-
53
- def property_values = raw['Properties']['$values']
54
- end
55
- end
56
- end