neverbounce-api 1.0.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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +8 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +1161 -0
  6. data/.travis.yml +27 -0
  7. data/.yardopts +2 -0
  8. data/Gemfile +17 -0
  9. data/Gemfile.lock +48 -0
  10. data/LICENSE +9 -0
  11. data/README.md +235 -0
  12. data/lib/never_bounce/api/client.rb +259 -0
  13. data/lib/never_bounce/api/error.rb +21 -0
  14. data/lib/never_bounce/api/feature/basic_initialize.rb +20 -0
  15. data/lib/never_bounce/api/feature/eigencache.rb +48 -0
  16. data/lib/never_bounce/api/feature/igetset.rb +41 -0
  17. data/lib/never_bounce/api/feature/oattrs.rb +100 -0
  18. data/lib/never_bounce/api/feature/require_attr.rb +27 -0
  19. data/lib/never_bounce/api/request/account_info.rb +29 -0
  20. data/lib/never_bounce/api/request/base.rb +102 -0
  21. data/lib/never_bounce/api/request/jobs_create.rb +90 -0
  22. data/lib/never_bounce/api/request/jobs_delete.rb +34 -0
  23. data/lib/never_bounce/api/request/jobs_download.rb +34 -0
  24. data/lib/never_bounce/api/request/jobs_parse.rb +55 -0
  25. data/lib/never_bounce/api/request/jobs_results.rb +53 -0
  26. data/lib/never_bounce/api/request/jobs_search.rb +57 -0
  27. data/lib/never_bounce/api/request/jobs_start.rb +47 -0
  28. data/lib/never_bounce/api/request/jobs_status.rb +37 -0
  29. data/lib/never_bounce/api/request/single_check.rb +67 -0
  30. data/lib/never_bounce/api/response/account_info.rb +39 -0
  31. data/lib/never_bounce/api/response/account_info/job_counts.rb +26 -0
  32. data/lib/never_bounce/api/response/address_info.rb +43 -0
  33. data/lib/never_bounce/api/response/base.rb +15 -0
  34. data/lib/never_bounce/api/response/container.rb +126 -0
  35. data/lib/never_bounce/api/response/credits_info/base.rb +30 -0
  36. data/lib/never_bounce/api/response/credits_info/monthly.rb +16 -0
  37. data/lib/never_bounce/api/response/credits_info/paid.rb +20 -0
  38. data/lib/never_bounce/api/response/error_message.rb +17 -0
  39. data/lib/never_bounce/api/response/feature/job_status_fields.rb +68 -0
  40. data/lib/never_bounce/api/response/feature/job_status_fields/total.rb +46 -0
  41. data/lib/never_bounce/api/response/jobs_create.rb +10 -0
  42. data/lib/never_bounce/api/response/jobs_delete.rb +8 -0
  43. data/lib/never_bounce/api/response/jobs_download.rb +19 -0
  44. data/lib/never_bounce/api/response/jobs_parse.rb +10 -0
  45. data/lib/never_bounce/api/response/jobs_results.rb +34 -0
  46. data/lib/never_bounce/api/response/jobs_results/item.rb +65 -0
  47. data/lib/never_bounce/api/response/jobs_results/query.rb +20 -0
  48. data/lib/never_bounce/api/response/jobs_search.rb +33 -0
  49. data/lib/never_bounce/api/response/jobs_search/query.rb +16 -0
  50. data/lib/never_bounce/api/response/jobs_search/result.rb +13 -0
  51. data/lib/never_bounce/api/response/jobs_start.rb +10 -0
  52. data/lib/never_bounce/api/response/jobs_status.rb +11 -0
  53. data/lib/never_bounce/api/response/message.rb +32 -0
  54. data/lib/never_bounce/api/response/single_check.rb +68 -0
  55. data/lib/never_bounce/api/response/status_message.rb +17 -0
  56. data/lib/never_bounce/api/response/success_message.rb +12 -0
  57. data/lib/never_bounce/api/session.rb +141 -0
  58. data/lib/never_bounce/api/version.rb +4 -0
  59. data/lib/neverbounce-api.rb +4 -0
  60. data/lib/neverbounce.rb +3 -0
  61. data/neverbounce-api.gemspec +20 -0
  62. data/spec/lib/never_bounce/api/client_spec.rb +199 -0
  63. data/spec/lib/never_bounce/api/feature/basic_initialize_spec.rb +25 -0
  64. data/spec/lib/never_bounce/api/feature/eigencache_spec.rb +28 -0
  65. data/spec/lib/never_bounce/api/feature/igetset_spec.rb +45 -0
  66. data/spec/lib/never_bounce/api/feature/oattrs_spec.rb +72 -0
  67. data/spec/lib/never_bounce/api/feature/require_attr_spec.rb +25 -0
  68. data/spec/lib/never_bounce/api/request/account_info_spec.rb +29 -0
  69. data/spec/lib/never_bounce/api/request/base_spec.rb +6 -0
  70. data/spec/lib/never_bounce/api/request/jobs_create_spec.rb +89 -0
  71. data/spec/lib/never_bounce/api/request/jobs_delete_spec.rb +31 -0
  72. data/spec/lib/never_bounce/api/request/jobs_download_spec.rb +31 -0
  73. data/spec/lib/never_bounce/api/request/jobs_parse_spec.rb +44 -0
  74. data/spec/lib/never_bounce/api/request/jobs_results_spec.rb +42 -0
  75. data/spec/lib/never_bounce/api/request/jobs_search_spec.rb +40 -0
  76. data/spec/lib/never_bounce/api/request/jobs_start_spec.rb +44 -0
  77. data/spec/lib/never_bounce/api/request/jobs_status_spec.rb +31 -0
  78. data/spec/lib/never_bounce/api/request/single_check_spec.rb +44 -0
  79. data/spec/lib/never_bounce/api/response/account_info/job_counts_spec.rb +7 -0
  80. data/spec/lib/never_bounce/api/response/account_info_spec.rb +9 -0
  81. data/spec/lib/never_bounce/api/response/address_info_spec.rb +7 -0
  82. data/spec/lib/never_bounce/api/response/base_spec.rb +6 -0
  83. data/spec/lib/never_bounce/api/response/container_spec.rb +142 -0
  84. data/spec/lib/never_bounce/api/response/credits_info/base_spec.rb +31 -0
  85. data/spec/lib/never_bounce/api/response/credits_info/monthly_spec.rb +11 -0
  86. data/spec/lib/never_bounce/api/response/credits_info/paid_spec.rb +11 -0
  87. data/spec/lib/never_bounce/api/response/feature/job_status_fields/total_spec.rb +7 -0
  88. data/spec/lib/never_bounce/api/response/feature/job_status_fields_spec.rb +42 -0
  89. data/spec/lib/never_bounce/api/response/jobs_create_spec.rb +7 -0
  90. data/spec/lib/never_bounce/api/response/jobs_delete_spec.rb +7 -0
  91. data/spec/lib/never_bounce/api/response/jobs_download_spec.rb +7 -0
  92. data/spec/lib/never_bounce/api/response/jobs_parse_spec.rb +7 -0
  93. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_item_spec.rb +7 -0
  94. data/spec/lib/never_bounce/api/response/jobs_results/jobs_results_query_spec.rb +7 -0
  95. data/spec/lib/never_bounce/api/response/jobs_results_spec.rb +7 -0
  96. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_query_spec.rb +7 -0
  97. data/spec/lib/never_bounce/api/response/jobs_search/jobs_search_result_spec.rb +7 -0
  98. data/spec/lib/never_bounce/api/response/jobs_search_spec.rb +7 -0
  99. data/spec/lib/never_bounce/api/response/jobs_start_spec.rb +7 -0
  100. data/spec/lib/never_bounce/api/response/jobs_status_spec.rb +7 -0
  101. data/spec/lib/never_bounce/api/response/single_check_spec.rb +7 -0
  102. data/spec/lib/never_bounce/api/response/spec_helper.rb +8 -0
  103. data/spec/lib/never_bounce/api/session_spec.rb +140 -0
  104. data/spec/lib/never_bounce/api_spec.rb +8 -0
  105. data/spec/spec_helper.rb +49 -0
  106. data/spec/spec_support/include_dir_context.rb +22 -0
  107. data/spec/spec_support/simplecov.rb +18 -0
  108. metadata +210 -0
@@ -0,0 +1,34 @@
1
+
2
+ require_relative "success_message"
3
+
4
+ module NeverBounce; module API; module Response
5
+ class JobsResults < SuccessMessage
6
+ require_relative "jobs_results/item"
7
+ require_relative "jobs_results/query"
8
+
9
+ # @!attribute items
10
+ # @return [Array<Item>]
11
+ oattr :items, :writer
12
+
13
+ # @!attribute query
14
+ # @return [Query]
15
+ oattr :query, :writer
16
+
17
+ # @!attribute total_pages
18
+ # @return [Integer]
19
+ oattr :total_pages, :scalar, type: :integer
20
+
21
+ # @!attribute total_results
22
+ # @return [Integer]
23
+ oattr :total_results, :scalar, type: :integer
24
+
25
+ def items
26
+ # NOTE: I take courage to rename original response key since having class `Results::Result` is total ugliness.
27
+ @items ||= body_hash.fetch("results").map { |h| Item.new(body_hash: h) }
28
+ end
29
+
30
+ def query
31
+ @query ||= Query.new(body_hash: body_hash.fetch("query"))
32
+ end
33
+ end
34
+ end; end; end
@@ -0,0 +1,65 @@
1
+
2
+ require "never_bounce/api/response/address_info"
3
+ require "never_bounce/api/response/container"
4
+
5
+ require_relative "../jobs_results"
6
+
7
+ module NeverBounce; module API; module Response; class JobsResults
8
+ class Item < Container
9
+ # @!attribute address_info
10
+ # @return [AddressInfo]
11
+ oattr :address_info, :writer
12
+
13
+ # @!attribute data
14
+ # @return [Hash]
15
+ oattr :data, :writer
16
+
17
+ # @!attribute email
18
+ # @return [String]
19
+ oattr :email, :writer
20
+
21
+ # @!attribute flags
22
+ # @return [Array<String>]
23
+ oattr :flags, :writer
24
+
25
+ # @!attribute result
26
+ # @return [String]
27
+ oattr :result, :writer
28
+
29
+ # @!attribute suggested_correction
30
+ # @return [String]
31
+ oattr :suggested_correction, :writer
32
+
33
+ def address_info
34
+ @address_info = AddressInfo.new(body_hash: body_hash.fetch("verification").fetch("address_info"))
35
+ end
36
+
37
+ def email
38
+ @email ||= data["email"]
39
+ end
40
+
41
+ def flags
42
+ @flags ||= body_hash.fetch("verification").fetch("flags")
43
+ end
44
+
45
+ def data
46
+ @data ||= body_hash.fetch("data")
47
+ end
48
+
49
+ def result
50
+ @result ||= body_hash.fetch("verification").fetch("result")
51
+ end
52
+
53
+ def suggested_correction
54
+ @suggested_correction ||= body_hash.fetch("verification").fetch("suggested_correction")
55
+ end
56
+ end
57
+ end; end; end; end
58
+
59
+ #
60
+ # Implementation notes:
61
+ #
62
+ # * I use a more streamlined structure here, not reflecting API JSON reply structure.
63
+ # I skip `verification` key entirely, since it has zero value to client code.
64
+ # * According to API spec, `data` has varying keys based on originally uploaded CSV.
65
+ # * We extract some important keys like `email` from `data`. `data` stays intact.
@@ -0,0 +1,20 @@
1
+
2
+ require "never_bounce/api/response/container"
3
+
4
+ require_relative "../jobs_results"
5
+
6
+ module NeverBounce; module API; module Response; class JobsResults
7
+ class Query < Container
8
+ # @!attribute items_per_page
9
+ # @return [Integer]
10
+ oattr :items_per_page, :scalar, type: :integer
11
+
12
+ # @!attribute job_id
13
+ # @return [Integer]
14
+ oattr :job_id, :scalar, type: :integer
15
+
16
+ # @!attribute page
17
+ # @return [Integer]
18
+ oattr :page, :scalar, type: :integer
19
+ end
20
+ end; end; end; end
@@ -0,0 +1,33 @@
1
+
2
+ require_relative "success_message"
3
+
4
+ module NeverBounce; module API; module Response
5
+ class JobsSearch < SuccessMessage
6
+ require_relative "jobs_search/result"
7
+ require_relative "jobs_search/query"
8
+
9
+ # @!attribute query
10
+ # @return [Query]
11
+ oattr :query, :writer
12
+
13
+ # @!attribute results
14
+ # @return [Array<Result>]
15
+ oattr :results, :writer
16
+
17
+ # @!attribute total_pages
18
+ # @return [Integer]
19
+ oattr :total_pages, :scalar, type: :integer
20
+
21
+ # @!attribute total_results
22
+ # @return [Integer]
23
+ oattr :total_results, :scalar, type: :integer
24
+
25
+ def query
26
+ @query ||= Query.new(body_hash: body_hash.fetch("query"))
27
+ end
28
+
29
+ def results
30
+ @results ||= body_hash.fetch("results").map { |h| Result.new(body_hash: h) }
31
+ end
32
+ end
33
+ end; end; end
@@ -0,0 +1,16 @@
1
+
2
+ require "never_bounce/api/response/container"
3
+
4
+ require_relative "../jobs_search"
5
+
6
+ module NeverBounce; module API; module Response; class JobsSearch
7
+ class Query < Container
8
+ # @!attribute items_per_page
9
+ # @return [Integer]
10
+ oattr :items_per_page, :scalar, type: :integer
11
+
12
+ # @!attribute page
13
+ # @return [Integer]
14
+ oattr :page, :scalar, type: :integer
15
+ end
16
+ end; end; end; end
@@ -0,0 +1,13 @@
1
+
2
+ require "never_bounce/api/response/container"
3
+ require "never_bounce/api/response/feature/job_status_fields"
4
+
5
+ require_relative "../jobs_search"
6
+
7
+ module NeverBounce; module API; module Response; class JobsSearch
8
+ # Bulk job status.
9
+ # @see Response::Feature::JobStatusFields
10
+ class Result < Container
11
+ Response::Feature::JobStatusFields.load(self)
12
+ end
13
+ end; end; end; end
@@ -0,0 +1,10 @@
1
+
2
+ require_relative "success_message"
3
+
4
+ module NeverBounce; module API; module Response
5
+ class JobsStart < SuccessMessage
6
+ # @!attribute queue_id
7
+ # @return [String]
8
+ oattr :queue_id, :scalar
9
+ end
10
+ end; end; end
@@ -0,0 +1,11 @@
1
+
2
+ require "never_bounce/api/response/feature/job_status_fields"
3
+
4
+ require_relative "success_message"
5
+
6
+ module NeverBounce; module API; module Response
7
+ # @see Feature::JobStatusFields
8
+ class JobsStatus < SuccessMessage
9
+ Response::Feature::JobStatusFields.load(self)
10
+ end
11
+ end; end; end
@@ -0,0 +1,32 @@
1
+
2
+ require_relative "container"
3
+
4
+ module NeverBounce; module API; module Response
5
+ # A response message from the server.
6
+ # A top-level container with a few extra features.
7
+ class Message < Container
8
+ # <tt>true</tt> if this an error message.
9
+ # @see #success?
10
+ def error?
11
+ !success?
12
+ end
13
+
14
+ # An alias to {#success?}.
15
+ def ok?
16
+ success?
17
+ end
18
+
19
+ # <tt>true</tt> if this is a success message.
20
+ # @abstract
21
+ def success?
22
+ raise NotImplementedError, "Redefine `success?` in your class: #{self.class}"
23
+ end
24
+ end
25
+ end; end; end
26
+
27
+ #
28
+ # Implementation notes:
29
+ #
30
+ # * `ErrorMessage` and `SuccessMessage` are ancestors of this class.
31
+ # I don't go for more correct `Message::Error` since I want to keep this sub-hierarchy flat.
32
+ # There aren't going to be a big family of `*Message`, so simplified suffix grouping should be okay.
@@ -0,0 +1,68 @@
1
+
2
+ require "never_bounce/api/response/address_info"
3
+ require "never_bounce/api/response/credits_info/monthly"
4
+ require "never_bounce/api/response/credits_info/paid"
5
+
6
+ require_relative "success_message"
7
+
8
+ module NeverBounce; module API; module Response;
9
+ class SingleCheck < SuccessMessage
10
+ # @!attribute flags
11
+ # @return [String]
12
+ oattr :flags, :scalar
13
+
14
+ # @!attribute result
15
+ # @return [String]
16
+ oattr :result, :scalar
17
+
18
+ # @!attribute suggested_correction
19
+ # @return [String]
20
+ oattr :suggested_correction, :scalar
21
+
22
+ # @!attribute address_info
23
+ # @return [AddressInfo]
24
+ # @return [nil]
25
+ oattr :address_info, :writer
26
+
27
+ # @!attribute credits_info
28
+ # @return [CreditsInfo::Monthly]
29
+ # @return [CreditsInfo::Paid]
30
+ # @return [nil]
31
+ oattr :credits_info, :writer
32
+
33
+ def address_info
34
+ igetset(:address_info) do
35
+ if body_hash.has_key?(k = "address_info")
36
+ Response::AddressInfo.new(body_hash: body_hash.fetch(k))
37
+ end
38
+ end
39
+ end
40
+
41
+ # <tt>true</tt> if {#address_info} is present.
42
+ def address_info?
43
+ !address_info.nil?
44
+ end
45
+
46
+ def credits_info
47
+ igetset(:credits_info) do
48
+ if (body_hash.has_key?(k = "credits_info"))
49
+ h = body_hash.fetch(k)
50
+ klass = if h.has_key? "monthly_api_usage"
51
+ CreditsInfo::Monthly
52
+ elsif h.has_key? "paid_credits_remaining"
53
+ CreditsInfo::Paid
54
+ else
55
+ raise "Unknown `credits_info`: #{h.inspect}"
56
+ end
57
+
58
+ klass.new(h)
59
+ end
60
+ end
61
+ end
62
+
63
+ # <tt>true</tt> if {#credits_info} is present.
64
+ def credits_info?
65
+ !credits_info.nil?
66
+ end
67
+ end
68
+ end; end; end
@@ -0,0 +1,17 @@
1
+
2
+ require_relative "message"
3
+
4
+ module NeverBounce; module API; module Response
5
+ # A message having <tt>status</tt> and <tt>execution_time</tt>.
6
+ class StatusMessage < Message
7
+ # @!attribute execution_time
8
+ # Request execution time in milliseconds.
9
+ # @return [Integer]
10
+ oattr :execution_time, :scalar, type: :integer
11
+
12
+ # @!attribute status
13
+ # Status mnemo as returned by the server.
14
+ # @return [String]
15
+ oattr :status, :scalar
16
+ end
17
+ end; end; end
@@ -0,0 +1,12 @@
1
+
2
+ require_relative "status_message"
3
+
4
+ module NeverBounce; module API; module Response
5
+ # A meaningful (success) response from the server.
6
+ class SuccessMessage < StatusMessage
7
+ # @return [true]
8
+ def success?
9
+ true
10
+ end
11
+ end
12
+ end; end; end
@@ -0,0 +1,141 @@
1
+
2
+ require "httparty"
3
+
4
+ require "never_bounce/api/feature/basic_initialize"
5
+ require "never_bounce/api/feature/igetset"
6
+ require "never_bounce/api/feature/require_attr"
7
+
8
+ module NeverBounce; module API
9
+ # A single request-response session (server dialog).
10
+ # @see API::Feature::BasicInitialize
11
+ # @see API::Feature::Igetset
12
+ # @see API::Feature::RequireAttr
13
+ class Session
14
+ API::Feature::BasicInitialize.load(self)
15
+ API::Feature::Igetset.load(self)
16
+ API::Feature::RequireAttr.load(self)
17
+
18
+ attr_writer :httparty, :response, :robj_hash_preview, :robj_klass_and_attrs, :server_content_type, :server_obj, :server_ok, :server_raw
19
+
20
+ # An instance of <tt>Request::Base</tt> successor.
21
+ # @return [Object]
22
+ attr_accessor :request
23
+
24
+ # HTTParty module. Default is <tt>HTTParty</tt>.
25
+ # @!attribute httparty
26
+ # @return [Module]
27
+ def httparty
28
+ @httparty ||= HTTParty
29
+ end
30
+
31
+ # Meaningful response object.
32
+ # @!attribute response
33
+ # @return [Response::Message]
34
+ # @return [Response::ErrorMessage]
35
+ def response
36
+ @response ||= begin
37
+ klass, attrs = require_attr(:robj_klass_and_attrs)
38
+ klass.new(attrs)
39
+ end
40
+ end
41
+
42
+ # Render response object class and constructor attributes based on peeking in the data.
43
+ # @!attribute robj_klass_and_attrs
44
+ # @return [Array<Class,Hash>]
45
+ def robj_klass_and_attrs
46
+ @robj_klass_and_attrs ||= begin
47
+ if (h = robj_hash_preview).is_a? Hash
48
+ # Determine response class based on API convention.
49
+ begin
50
+ [
51
+ h.fetch("status") == "success" ? request.class.response_klass : Response::ErrorMessage,
52
+ body_hash: robj_hash_preview,
53
+ ]
54
+ rescue KeyError
55
+ raise FormatError, "Key 'status' not found: #{h.inspect}"
56
+ end
57
+ elsif robj_hash_preview == false
58
+ # Let response class handle it on its own.
59
+ [
60
+ request.class.response_klass,
61
+ raw: server_raw,
62
+ ]
63
+ else
64
+ raise AttributeError, "Unknown `robj_hash_preview`: #{robj_hash_preview.inspect}"
65
+ end
66
+ end
67
+ end
68
+
69
+ # Response body, opportunistically JSON-parsed based on <tt>server_content_type</tt>.
70
+ # @return [Hash]
71
+ # @return [false] If <tt>server_content_type</tt> isn't a JSON.
72
+ def robj_hash_preview
73
+ igetset(:robj_hash_preview) do
74
+ case require_attr(:server_content_type)
75
+ when "application/json"
76
+ begin
77
+ JSON.parse(server_raw)
78
+ rescue JSON::ParserError => e
79
+ raise FormatError, "#{e.class}: #{e.message}"
80
+ end
81
+ else
82
+ false
83
+ end
84
+ end
85
+ end
86
+
87
+ # Server response code. Default is <tt>server_obj.code</tt>.
88
+ # @!attribute server_code
89
+ # @return [Integer]
90
+ def server_code
91
+ @server_code ||= require_attr(:server_obj).code
92
+ end
93
+
94
+ # Server response content type. Default is <tt>server_obj.content_type</tt>.
95
+ # @!attribute server_content_type
96
+ # @return [String]
97
+ def server_content_type
98
+ @server_content_type ||= begin
99
+ require_attr(:server_obj).content_type
100
+ end
101
+ end
102
+
103
+ # Make a request, return server response object from HTTParty.
104
+ # @return [Object] An <tt>HTTParty::Response</tt>.
105
+ def server_obj
106
+ @server_obj ||= begin
107
+ httparty.send(*require_attr(:request).to_httparty)
108
+ end
109
+ end
110
+
111
+ # <tt>true</tt> if response is an OK response.
112
+ # @!attribute server_ok
113
+ # @return [Boolean]
114
+ def server_ok
115
+ igetset(:server_ok) { require_attr(:server_obj).ok? }
116
+ end
117
+
118
+ alias_method :server_ok?, :server_ok
119
+
120
+ # Raw server response body. Default is <tt>server_obj.body</tt>.
121
+ # @!attribute server_raw
122
+ # @return [String]
123
+ def server_raw
124
+ @server_raw ||= begin
125
+ raise RequestError, "Code not OK: #{server_code}" if not server_ok?
126
+ server_obj.body
127
+ end
128
+ end
129
+ end
130
+ end; end
131
+
132
+ #
133
+ # Implementation notes:
134
+ #
135
+ # * `server_*` methods deal with stage 1, received directly from the server via HTTParty.
136
+ # `robj_*` methods deal with stage 2, converting raw data into response object.
137
+ # Everything `response*` relates to meaningful response object.
138
+ # * Response classes can natively parse their raw content to JSON, and that is right.
139
+ # BUT we also use JSON parsing here, for the purpose of detecting success/error response class.
140
+ # * We inherit response "OK?" condition from HTTParty, which inherits it from `net/http`.
141
+ # `ok?` maps to `Net::HTTPOK` class equality check.