noresponse 2.0.17

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 (231) hide show
  1. checksums.yaml +7 -0
  2. data/lib/crystalline/enum.rbi +17 -0
  3. data/lib/crystalline/metadata_fields.rb +263 -0
  4. data/lib/crystalline/module.rb +186 -0
  5. data/lib/crystalline/types.rb +168 -0
  6. data/lib/crystalline/utils.rb +67 -0
  7. data/lib/crystalline.rb +11 -0
  8. data/lib/no_response/assessments.rb +1252 -0
  9. data/lib/no_response/auth.rb +263 -0
  10. data/lib/no_response/issued_credential.rb +21 -0
  11. data/lib/no_response/keys.rb +318 -0
  12. data/lib/no_response/late_results.rb +301 -0
  13. data/lib/no_response/models/callbacks.rb +10 -0
  14. data/lib/no_response/models/errors/apistatuserror.rb +41 -0
  15. data/lib/no_response/models/errors/apistatuserror.rbi +30 -0
  16. data/lib/no_response/models/errors/authenticationerror.rb +108 -0
  17. data/lib/no_response/models/errors/authenticationerror.rbi +56 -0
  18. data/lib/no_response/models/errors/badrequesterror.rb +108 -0
  19. data/lib/no_response/models/errors/badrequesterror.rbi +56 -0
  20. data/lib/no_response/models/errors/conflicterror.rb +108 -0
  21. data/lib/no_response/models/errors/conflicterror.rbi +56 -0
  22. data/lib/no_response/models/errors/notfounderror.rb +108 -0
  23. data/lib/no_response/models/errors/notfounderror.rbi +56 -0
  24. data/lib/no_response/models/errors/permissiondeniederror.rb +108 -0
  25. data/lib/no_response/models/errors/permissiondeniederror.rbi +56 -0
  26. data/lib/no_response/models/errors/ratelimiterror.rb +108 -0
  27. data/lib/no_response/models/errors/ratelimiterror.rbi +56 -0
  28. data/lib/no_response/models/errors.rb +16 -0
  29. data/lib/no_response/models/operations/createassessment_request.rb +36 -0
  30. data/lib/no_response/models/operations/createassessment_request.rbi +20 -0
  31. data/lib/no_response/models/operations/createassessment_response.rb +32 -0
  32. data/lib/no_response/models/operations/createassessment_response.rbi +20 -0
  33. data/lib/no_response/models/operations/getassessment_request.rb +41 -0
  34. data/lib/no_response/models/operations/getassessment_request.rbi +20 -0
  35. data/lib/no_response/models/operations/getassessment_response.rb +32 -0
  36. data/lib/no_response/models/operations/getassessment_response.rbi +20 -0
  37. data/lib/no_response/models/operations/queryaccountassessments_response.rb +44 -0
  38. data/lib/no_response/models/operations/queryaccountassessments_response.rbi +20 -0
  39. data/lib/no_response/models/operations/recordassessmentoutcome_request.rb +52 -0
  40. data/lib/no_response/models/operations/recordassessmentoutcome_request.rbi +26 -0
  41. data/lib/no_response/models/operations/recordassessmentoutcome_response.rb +32 -0
  42. data/lib/no_response/models/operations/recordassessmentoutcome_response.rbi +20 -0
  43. data/lib/no_response/models/operations/rotateapikey_response.rb +32 -0
  44. data/lib/no_response/models/operations/rotateapikey_response.rbi +20 -0
  45. data/lib/no_response/models/operations.rb +17 -0
  46. data/lib/no_response/models/shared/alternate_name.rb +23 -0
  47. data/lib/no_response/models/shared/alternate_name.rbi +9 -0
  48. data/lib/no_response/models/shared/alternate_name_union.rb +23 -0
  49. data/lib/no_response/models/shared/alternate_name_union.rbi +9 -0
  50. data/lib/no_response/models/shared/assessment.rb +235 -0
  51. data/lib/no_response/models/shared/assessment.rbi +116 -0
  52. data/lib/no_response/models/shared/assessmentlist.rb +78 -0
  53. data/lib/no_response/models/shared/assessmentlist.rbi +38 -0
  54. data/lib/no_response/models/shared/assessmentoutcome.rb +61 -0
  55. data/lib/no_response/models/shared/assessmentoutcome.rbi +32 -0
  56. data/lib/no_response/models/shared/assessmentoutcomerequest.rb +71 -0
  57. data/lib/no_response/models/shared/assessmentoutcomerequest.rbi +32 -0
  58. data/lib/no_response/models/shared/assessmentquery.rb +70 -0
  59. data/lib/no_response/models/shared/assessmentquery.rbi +32 -0
  60. data/lib/no_response/models/shared/assessmentrequest.rb +125 -0
  61. data/lib/no_response/models/shared/assessmentrequest.rbi +62 -0
  62. data/lib/no_response/models/shared/assessmentrequestclientcontext.rb +125 -0
  63. data/lib/no_response/models/shared/assessmentrequestclientcontext.rbi +62 -0
  64. data/lib/no_response/models/shared/assessmentrequestidentifiers.rb +74 -0
  65. data/lib/no_response/models/shared/assessmentrequestidentifiers.rbi +38 -0
  66. data/lib/no_response/models/shared/assessmentrequestissuedcredential.rb +33 -0
  67. data/lib/no_response/models/shared/assessmentrequestissuedcredential.rbi +14 -0
  68. data/lib/no_response/models/shared/assessmentrequestpolicycontext.rb +46 -0
  69. data/lib/no_response/models/shared/assessmentrequestpolicycontext.rbi +20 -0
  70. data/lib/no_response/models/shared/assessmenttiming.rb +41 -0
  71. data/lib/no_response/models/shared/assessmenttiming.rbi +20 -0
  72. data/lib/no_response/models/shared/assurancelevel.rb +18 -0
  73. data/lib/no_response/models/shared/assurancelevel.rbi +9 -0
  74. data/lib/no_response/models/shared/authcontext.rb +285 -0
  75. data/lib/no_response/models/shared/authcontext.rbi +152 -0
  76. data/lib/no_response/models/shared/authcontextmfa.rb +55 -0
  77. data/lib/no_response/models/shared/authcontextmfa.rbi +26 -0
  78. data/lib/no_response/models/shared/authenticationerrorcode.rb +18 -0
  79. data/lib/no_response/models/shared/authenticationerrorcode.rbi +9 -0
  80. data/lib/no_response/models/shared/badrequesterrorcode.rb +20 -0
  81. data/lib/no_response/models/shared/badrequesterrorcode.rbi +9 -0
  82. data/lib/no_response/models/shared/band.rb +21 -0
  83. data/lib/no_response/models/shared/band.rbi +9 -0
  84. data/lib/no_response/models/shared/breach.rb +108 -0
  85. data/lib/no_response/models/shared/breach.rbi +56 -0
  86. data/lib/no_response/models/shared/breachexposed.rb +28 -0
  87. data/lib/no_response/models/shared/breachexposed.rbi +9 -0
  88. data/lib/no_response/models/shared/breachkind.rb +24 -0
  89. data/lib/no_response/models/shared/breachkind.rbi +9 -0
  90. data/lib/no_response/models/shared/breachselector.rb +20 -0
  91. data/lib/no_response/models/shared/breachselector.rbi +9 -0
  92. data/lib/no_response/models/shared/breachsummary.rb +109 -0
  93. data/lib/no_response/models/shared/breachsummary.rbi +56 -0
  94. data/lib/no_response/models/shared/confidence.rb +21 -0
  95. data/lib/no_response/models/shared/confidence.rbi +9 -0
  96. data/lib/no_response/models/shared/conflicterrorcode.rb +19 -0
  97. data/lib/no_response/models/shared/conflicterrorcode.rbi +9 -0
  98. data/lib/no_response/models/shared/consistency.rb +85 -0
  99. data/lib/no_response/models/shared/consistency.rbi +32 -0
  100. data/lib/no_response/models/shared/consistencyvalue.rb +21 -0
  101. data/lib/no_response/models/shared/consistencyvalue.rbi +9 -0
  102. data/lib/no_response/models/shared/country.rb +20 -0
  103. data/lib/no_response/models/shared/country.rbi +9 -0
  104. data/lib/no_response/models/shared/email.rb +23 -0
  105. data/lib/no_response/models/shared/email.rbi +9 -0
  106. data/lib/no_response/models/shared/email_union.rb +23 -0
  107. data/lib/no_response/models/shared/email_union.rbi +9 -0
  108. data/lib/no_response/models/shared/failurereason.rb +24 -0
  109. data/lib/no_response/models/shared/failurereason.rbi +9 -0
  110. data/lib/no_response/models/shared/flow.rb +18 -0
  111. data/lib/no_response/models/shared/flow.rbi +9 -0
  112. data/lib/no_response/models/shared/freshness.rb +20 -0
  113. data/lib/no_response/models/shared/freshness.rbi +9 -0
  114. data/lib/no_response/models/shared/hookpoint.rb +19 -0
  115. data/lib/no_response/models/shared/hookpoint.rbi +9 -0
  116. data/lib/no_response/models/shared/identifierkind.rb +21 -0
  117. data/lib/no_response/models/shared/identifierkind.rbi +9 -0
  118. data/lib/no_response/models/shared/incompletereason.rb +23 -0
  119. data/lib/no_response/models/shared/incompletereason.rbi +9 -0
  120. data/lib/no_response/models/shared/incompletesource.rb +53 -0
  121. data/lib/no_response/models/shared/incompletesource.rbi +20 -0
  122. data/lib/no_response/models/shared/ipsummary.rb +61 -0
  123. data/lib/no_response/models/shared/ipsummary.rbi +26 -0
  124. data/lib/no_response/models/shared/ipsummarytype.rb +24 -0
  125. data/lib/no_response/models/shared/ipsummarytype.rbi +9 -0
  126. data/lib/no_response/models/shared/keyrotation.rb +79 -0
  127. data/lib/no_response/models/shared/keyrotation.rbi +44 -0
  128. data/lib/no_response/models/shared/keyrotationrequest.rb +33 -0
  129. data/lib/no_response/models/shared/keyrotationrequest.rbi +14 -0
  130. data/lib/no_response/models/shared/lane.rb +21 -0
  131. data/lib/no_response/models/shared/lane.rbi +9 -0
  132. data/lib/no_response/models/shared/linetype.rb +22 -0
  133. data/lib/no_response/models/shared/linetype.rbi +9 -0
  134. data/lib/no_response/models/shared/location.rb +23 -0
  135. data/lib/no_response/models/shared/location.rbi +9 -0
  136. data/lib/no_response/models/shared/location_union.rb +23 -0
  137. data/lib/no_response/models/shared/location_union.rbi +9 -0
  138. data/lib/no_response/models/shared/name_candidate.rb +23 -0
  139. data/lib/no_response/models/shared/name_candidate.rbi +9 -0
  140. data/lib/no_response/models/shared/name_candidate_union.rb +23 -0
  141. data/lib/no_response/models/shared/name_candidate_union.rbi +9 -0
  142. data/lib/no_response/models/shared/notfounderrorcode.rb +18 -0
  143. data/lib/no_response/models/shared/notfounderrorcode.rbi +9 -0
  144. data/lib/no_response/models/shared/outcome.rb +23 -0
  145. data/lib/no_response/models/shared/outcome.rbi +9 -0
  146. data/lib/no_response/models/shared/permissiondeniederrorcode.rb +18 -0
  147. data/lib/no_response/models/shared/permissiondeniederrorcode.rbi +9 -0
  148. data/lib/no_response/models/shared/phoneenrichment.rb +32 -0
  149. data/lib/no_response/models/shared/phoneenrichment.rbi +14 -0
  150. data/lib/no_response/models/shared/phoneenrichmentfields.rb +451 -0
  151. data/lib/no_response/models/shared/phoneenrichmentfields.rbi +212 -0
  152. data/lib/no_response/models/shared/platform.rb +18 -0
  153. data/lib/no_response/models/shared/platform.rbi +9 -0
  154. data/lib/no_response/models/shared/probleminvalidparam.rb +48 -0
  155. data/lib/no_response/models/shared/probleminvalidparam.rbi +26 -0
  156. data/lib/no_response/models/shared/ratelimiterrorcode.rb +18 -0
  157. data/lib/no_response/models/shared/ratelimiterrorcode.rbi +9 -0
  158. data/lib/no_response/models/shared/reasoncode.rb +52 -0
  159. data/lib/no_response/models/shared/reasoncode.rbi +9 -0
  160. data/lib/no_response/models/shared/reasonidentifier.rb +23 -0
  161. data/lib/no_response/models/shared/reasonidentifier.rbi +9 -0
  162. data/lib/no_response/models/shared/recommendation.rb +20 -0
  163. data/lib/no_response/models/shared/recommendation.rbi +9 -0
  164. data/lib/no_response/models/shared/responsemode.rb +16 -0
  165. data/lib/no_response/models/shared/responsemode.rbi +9 -0
  166. data/lib/no_response/models/shared/risk.rb +171 -0
  167. data/lib/no_response/models/shared/risk.rbi +80 -0
  168. data/lib/no_response/models/shared/riskconfidence.rb +20 -0
  169. data/lib/no_response/models/shared/riskconfidence.rbi +9 -0
  170. data/lib/no_response/models/shared/riskcoverage.rb +49 -0
  171. data/lib/no_response/models/shared/riskcoverage.rbi +26 -0
  172. data/lib/no_response/models/shared/riskdetails.rb +55 -0
  173. data/lib/no_response/models/shared/riskdetails.rbi +26 -0
  174. data/lib/no_response/models/shared/riskdetailsbreachname.rb +40 -0
  175. data/lib/no_response/models/shared/riskdetailsbreachname.rbi +20 -0
  176. data/lib/no_response/models/shared/riskdetailsphonename.rb +40 -0
  177. data/lib/no_response/models/shared/riskdetailsphonename.rbi +20 -0
  178. data/lib/no_response/models/shared/riskreason.rb +67 -0
  179. data/lib/no_response/models/shared/riskreason.rbi +26 -0
  180. data/lib/no_response/models/shared/risksummary.rb +73 -0
  181. data/lib/no_response/models/shared/risksummary.rbi +38 -0
  182. data/lib/no_response/models/shared/risksummaryemail.rb +55 -0
  183. data/lib/no_response/models/shared/risksummaryemail.rbi +26 -0
  184. data/lib/no_response/models/shared/risksummaryphone.rb +69 -0
  185. data/lib/no_response/models/shared/risksummaryphone.rbi +32 -0
  186. data/lib/no_response/models/shared/risksummaryusername.rb +41 -0
  187. data/lib/no_response/models/shared/risksummaryusername.rbi +20 -0
  188. data/lib/no_response/models/shared/scope.rb +21 -0
  189. data/lib/no_response/models/shared/scope.rbi +9 -0
  190. data/lib/no_response/models/shared/security.rb +32 -0
  191. data/lib/no_response/models/shared/security.rbi +14 -0
  192. data/lib/no_response/models/shared/signal.rb +149 -0
  193. data/lib/no_response/models/shared/signal.rbi +68 -0
  194. data/lib/no_response/models/shared/signaloutcome.rb +22 -0
  195. data/lib/no_response/models/shared/signaloutcome.rbi +9 -0
  196. data/lib/no_response/models/shared/social_profile.rb +23 -0
  197. data/lib/no_response/models/shared/social_profile.rbi +9 -0
  198. data/lib/no_response/models/shared/social_profile_union.rb +23 -0
  199. data/lib/no_response/models/shared/social_profile_union.rbi +9 -0
  200. data/lib/no_response/models/shared/status.rb +20 -0
  201. data/lib/no_response/models/shared/status.rbi +9 -0
  202. data/lib/no_response/models/shared/timezone.rb +23 -0
  203. data/lib/no_response/models/shared/timezone.rbi +9 -0
  204. data/lib/no_response/models/shared/timezone_union.rb +23 -0
  205. data/lib/no_response/models/shared/timezone_union.rbi +9 -0
  206. data/lib/no_response/models/shared/transport.rb +17 -0
  207. data/lib/no_response/models/shared/transport.rbi +9 -0
  208. data/lib/no_response/models/shared/website.rb +23 -0
  209. data/lib/no_response/models/shared/website.rbi +9 -0
  210. data/lib/no_response/models/shared/website_union.rb +23 -0
  211. data/lib/no_response/models/shared/website_union.rbi +9 -0
  212. data/lib/no_response/models/shared.rb +92 -0
  213. data/lib/no_response/noresponseclient.rb +110 -0
  214. data/lib/no_response/sdk_hooks/hooks.rb +97 -0
  215. data/lib/no_response/sdk_hooks/idempotency_key_hook.rb +25 -0
  216. data/lib/no_response/sdk_hooks/registration.rb +43 -0
  217. data/lib/no_response/sdk_hooks/types.rb +178 -0
  218. data/lib/no_response/sdkconfiguration.rb +107 -0
  219. data/lib/no_response/sdkconfiguration.rbi +83 -0
  220. data/lib/no_response/utils/forms.rb +230 -0
  221. data/lib/no_response/utils/headers.rb +80 -0
  222. data/lib/no_response/utils/query_params.rb +165 -0
  223. data/lib/no_response/utils/request_bodies.rb +77 -0
  224. data/lib/no_response/utils/retries.rb +137 -0
  225. data/lib/no_response/utils/security.rb +156 -0
  226. data/lib/no_response/utils/url.rb +122 -0
  227. data/lib/no_response/utils/utils.rb +243 -0
  228. data/lib/no_response/wait.rb +66 -0
  229. data/lib/no_response/webhooks.rb +177 -0
  230. data/lib/noresponse.rb +25 -0
  231. metadata +501 -0
@@ -0,0 +1,156 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "cgi"
5
+ require "date"
6
+ require "base64"
7
+ require "sorbet-runtime"
8
+
9
+ module NoResponse
10
+ module Utils
11
+ extend T::Sig
12
+
13
+ sig { params(req: Faraday::Request, security: Object, allowed_fields: T.nilable(T::Array[Symbol])).void }
14
+ def self.configure_request_security(req, security, allowed_fields = nil)
15
+ return if security.nil?
16
+
17
+ sec_fields = if allowed_fields.nil?
18
+ T.unsafe(security).fields
19
+ else
20
+ allowed_fields.filter_map { |name| T.unsafe(security).fields.find { |f| f.name == name } }
21
+ end
22
+
23
+ sec_fields.each do |sec_field|
24
+ value = security.send(sec_field.name)
25
+ next if value.nil?
26
+
27
+ metadata = sec_field.metadata[:security]
28
+ next if metadata.nil?
29
+
30
+ if metadata[:option]
31
+ _parse_security_option(req, value)
32
+ break unless metadata[:composite]
33
+ end
34
+
35
+ next unless metadata[:scheme]
36
+
37
+ # Special case for basic auth which could be a flattened struct
38
+ if metadata[:sub_type] == "basic" && !value.respond_to?(:fields)
39
+ _parse_security_scheme(req, metadata, security)
40
+ else
41
+ _parse_security_scheme(req, metadata, value)
42
+ end
43
+
44
+ break unless metadata[:composite]
45
+ end
46
+ end
47
+
48
+ sig { params(req: Faraday::Request, option: Object).void }
49
+ def self._parse_security_option(req, option)
50
+ # Check if this option uses basic auth (needs the whole object, not individual fields)
51
+ first_field = T.unsafe(option).fields.first
52
+ if first_field
53
+ first_meta = first_field.metadata[:security]
54
+ if first_meta && first_meta[:type] == "http" && first_meta[:sub_type] == "basic"
55
+ _parse_security_scheme(req, first_meta, option)
56
+ return
57
+ end
58
+ end
59
+
60
+ T.unsafe(option).fields.each do |opt_field|
61
+ metadata = opt_field.metadata[:security]
62
+ next if metadata.nil? || !metadata.include?(:scheme)
63
+
64
+ _parse_security_scheme(req, metadata, option.send(opt_field.name))
65
+ end
66
+ end
67
+
68
+ sig { params(req: Faraday::Request, scheme_metadata: T::Hash[Symbol, String], scheme: Object).void }
69
+ def self._parse_security_scheme(req, scheme_metadata, scheme)
70
+ scheme_type = scheme_metadata[:type]
71
+ sub_type = scheme_metadata[:sub_type]
72
+
73
+ if scheme.respond_to?(:fields)
74
+ if scheme_type == "http" && sub_type == "basic"
75
+ _parse_basic_auth_scheme(req, scheme)
76
+ return
77
+ end
78
+
79
+ T.unsafe(scheme).fields.each do |field|
80
+ metadata = field.metadata[:security]
81
+ next if metadata.nil? || metadata[:field_name].nil?
82
+
83
+ value = scheme.send(field.name)
84
+ _parse_security_scheme_value(req, scheme_metadata, metadata, T.cast(value, String))
85
+ end
86
+ else
87
+ _parse_security_scheme_value(req, scheme_metadata, scheme_metadata, T.cast(scheme, String))
88
+ end
89
+ end
90
+
91
+ sig do
92
+ params(
93
+ req: Faraday::Request,
94
+ scheme_metadata: T::Hash[Symbol, String],
95
+ security_metadata: T::Hash[Symbol, String],
96
+ value: String
97
+ )
98
+ .void
99
+ end
100
+ def self._parse_security_scheme_value(req, scheme_metadata, security_metadata, value)
101
+ scheme_type = scheme_metadata[:type]
102
+ sub_type = scheme_metadata[:sub_type]
103
+
104
+ header_name = security_metadata[:field_name]
105
+
106
+ case scheme_type
107
+ when "apiKey"
108
+ case sub_type
109
+ when "header"
110
+ req.headers[header_name] = value
111
+ when "query"
112
+ req.params[header_name] = value
113
+ when "cookie"
114
+ req.headers["Cookie"][header_name] = value
115
+ else
116
+ raise StandardError, "not supported"
117
+ end
118
+
119
+ when "openIdConnect"
120
+ req.headers[header_name] = value.downcase.start_with?("bearer ") ? value : "Bearer #{value}"
121
+ when "oauth2"
122
+ req.headers[header_name] = value.downcase.start_with?("bearer ") ? value : "Bearer #{value}"
123
+ when "http"
124
+ if sub_type == "bearer"
125
+ req.headers[header_name] = value.downcase.start_with?("bearer ") ? value : "Bearer #{value}"
126
+ elsif sub_type != "custom"
127
+ raise StandardError, "not supported"
128
+ end
129
+ else
130
+ raise StandardError, "not supported"
131
+ end
132
+ end
133
+
134
+ sig { params(req: Faraday::Request, scheme: Object).void }
135
+ def self._parse_basic_auth_scheme(req, scheme)
136
+ username = T.let("", String)
137
+ password = T.let("", String)
138
+
139
+ T.unsafe(scheme).fields.each do |scheme_field|
140
+ metadata = scheme_field.metadata[:security]
141
+ next if metadata.nil? || !metadata.include?(:field_name)
142
+
143
+ field_name = metadata[:field_name]
144
+ value = scheme.send(scheme_field.name)
145
+
146
+ username = T.let(value, String) if field_name == "username"
147
+ password = T.let(value, String) if field_name == "password"
148
+ end
149
+
150
+ data = "#{username}:#{password}".encode
151
+ # Use strict_encode, because encode adds newlines after 60 chars
152
+ # https://docs.ruby-lang.org/en/3.0/Base64.html#method-i-encode64
153
+ req.headers["Authorization"] = "Basic #{Base64.strict_encode64(data)}"
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,122 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "faraday/retry"
5
+ require "sorbet-runtime"
6
+
7
+ module NoResponse
8
+ module Utils
9
+ sig {
10
+ params(
11
+ clazz: Object,
12
+ server_url: String,
13
+ path: String,
14
+ path_params: Object,
15
+ gbls: T.nilable(T::Hash[Symbol, T::Hash[Symbol, T::Hash[Symbol, Object]]])
16
+ )
17
+ .returns(String)
18
+ }
19
+ def self.generate_url(clazz, server_url, path, path_params, gbls = nil)
20
+ T.unsafe(clazz).fields.each do |f|
21
+ param_metadata = f.metadata[:path_param]
22
+ next if param_metadata.nil?
23
+
24
+ if param_metadata.fetch(:style, "simple") == "simple"
25
+ param = path_params.send(f.name) if !path_params.nil?
26
+ param = _populate_from_globals(f.name, param, "pathParam", gbls)
27
+ end
28
+
29
+ f_name = param_metadata.fetch(:field_name, f.name)
30
+ serialization = param_metadata.fetch(:serialization, "")
31
+ if serialization != ""
32
+ serialized_params = _get_serialized_params(param_metadata, f_name, param)
33
+ serialized_params.each do |k, v|
34
+ path = path.gsub("{#{k}}", v.join(", "))
35
+ end
36
+ else
37
+ if param.is_a?(Array)
38
+ pp_vals = []
39
+ param.each do |pp_val|
40
+ pp_vals.append(pp_val.to_s)
41
+ end
42
+
43
+ path = path.gsub("{#{param_metadata.fetch(:field_name, f.name)}}", pp_vals.join(","))
44
+ elsif param.is_a?(Hash)
45
+ pp_vals = []
46
+ param.each do |pp_key, pp_val|
47
+ value = val_to_string(pp_val)
48
+
49
+ if param_metadata.fetch(:explode, false)
50
+ pp_vals.append("#{pp_key}=#{value}")
51
+ else
52
+ pp_vals.append("#{pp_key},#{value}")
53
+ end
54
+ end
55
+
56
+ path = path.gsub("{#{param_metadata.fetch(:field_name, f.name)}}", pp_vals.join(","))
57
+ elsif param.class.include?(::Crystalline::MetadataFields)
58
+ pp_vals = []
59
+ attrs = T
60
+ .unsafe(param)
61
+ .fields
62
+ .filter { |field| field.name && param.respond_to?(field.name.to_sym) }
63
+ .map(&:name)
64
+ attrs.each do |attr|
65
+ field = T.unsafe(param).field(attr)
66
+
67
+ param_value_metadata = field.metadata[:path_param]
68
+
69
+ next if param_value_metadata.nil?
70
+
71
+ parm_name = param_value_metadata.fetch(:field_name, f.name)
72
+
73
+ param_field_val = param.send(attr)
74
+
75
+ if param_field_val.class.respond_to?(:enums)
76
+ param_field_val = param_field_val.serialize
77
+ elsif param_field_val.is_a?(DateTime)
78
+ param_field_val = param_field_val.strftime("%Y-%m-%dT%H:%M:%S.%NZ")
79
+ end
80
+
81
+ if !field.nil? && ::Crystalline::Utils.nilable?(field.type) && param_field_val.nil?
82
+ next
83
+ elsif param_metadata.fetch(:explode, false)
84
+ pp_vals.append("#{parm_name}=#{param_field_val}")
85
+ else
86
+ pp_vals.append("#{parm_name},#{param_field_val}")
87
+ end
88
+ end
89
+
90
+ path = path.gsub("{#{param_metadata.fetch(:field_name, f.name)}}", pp_vals.join(","))
91
+ else
92
+ path = path.gsub("{#{param_metadata.fetch(:field_name, f.name)}}", _encode_path_param(val_to_string(param)))
93
+ end
94
+ end
95
+ end
96
+
97
+ server_url.delete_suffix("/") + path
98
+ end
99
+
100
+ # Percent-encode characters that are invalid in URI path segments
101
+ sig { params(value: String).returns(String) }
102
+ def self._encode_path_param(value)
103
+ value.gsub(/[<>{}\[\]|\\^`\s?#%]/) { |c| format("%%%02X", c.ord) }
104
+ end
105
+
106
+ sig { params(url_with_params: String, params: T::Hash[Symbol, T.any(String, T::Enum)]).returns(String) }
107
+ def self.template_url(url_with_params, params)
108
+ params.each do |key, value|
109
+ if value.class.respond_to?(:enums)
110
+ val_str = T.cast(value, T::Enum).serialize
111
+ else
112
+ val_str = value
113
+ end
114
+
115
+ url_with_params = url_with_params.gsub("{#{key}}", val_str)
116
+ end
117
+
118
+ url_with_params
119
+ end
120
+
121
+ end
122
+ end
@@ -0,0 +1,243 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require "cgi"
5
+ require "date"
6
+ require "base64"
7
+ require "stringio"
8
+ require "sorbet-runtime"
9
+
10
+ module NoResponse
11
+ module Utils
12
+ include Kernel
13
+ extend T::Sig
14
+
15
+ sig { params(val: Object).returns(String) }
16
+ def self.val_to_string(val)
17
+ if val.class.respond_to?(:enums)
18
+ T.unsafe(val).serialize.to_s
19
+ elsif val.is_a?(DateTime)
20
+ val.strftime("%Y-%m-%dT%H:%M:%S.%NZ").sub(/(\.\d*[1-9])0+Z\z/, "\\1Z").sub(/\.0+Z\z/, "Z")
21
+ else
22
+ val.to_s
23
+
24
+ end
25
+ end
26
+
27
+ MIME_TYPES = {
28
+ ".json" => "application/json",
29
+ ".xml" => "application/xml",
30
+ ".pdf" => "application/pdf",
31
+ ".zip" => "application/zip",
32
+ ".gz" => "application/gzip",
33
+ ".csv" => "text/csv",
34
+ ".txt" => "text/plain",
35
+ ".html" => "text/html",
36
+ ".htm" => "text/html",
37
+ ".css" => "text/css",
38
+ ".js" => "application/javascript",
39
+ ".png" => "image/png",
40
+ ".jpg" => "image/jpeg",
41
+ ".jpeg" => "image/jpeg",
42
+ ".gif" => "image/gif",
43
+ ".svg" => "image/svg+xml",
44
+ ".webp" => "image/webp",
45
+ ".mp3" => "audio/mpeg",
46
+ ".mp4" => "video/mp4",
47
+ ".yaml" => "application/yaml",
48
+ ".yml" => "application/yaml"
49
+ }.freeze
50
+
51
+ sig { params(filename: String).returns(String) }
52
+ def self.mime_type_for_filename(filename)
53
+ ext = File.extname(filename).downcase
54
+ MIME_TYPES.fetch(ext, "application/octet-stream")
55
+ end
56
+
57
+ sig do
58
+ params(metadata: T::Hash[Symbol, String], field_name: String, obj: Object)
59
+ .returns(T::Hash[Symbol, T::Array[String]])
60
+ end
61
+ def self._get_serialized_params(metadata, field_name, obj)
62
+ params = {}
63
+
64
+ serialization = metadata.fetch(:serialization, "")
65
+ params[metadata.fetch(:field_name, field_name)] = [obj.to_json] if serialization == "json"
66
+
67
+ params
68
+ end
69
+
70
+ sig { params(status: Integer).returns(T::Boolean) }
71
+ def self.error_status?(status)
72
+ status_major = status / 100
73
+ return true if status_major == 4
74
+ return true if status_major == 5
75
+
76
+ false
77
+ end
78
+
79
+ sig { params(content_type: String, pattern: String).returns(T::Boolean) }
80
+ def self.match_content_type(content_type, pattern)
81
+ return true if ["*", "*/*"].include?(pattern)
82
+
83
+ # Media types are case-insensitive (RFC 9110 section 8.3.1).
84
+ pattern = pattern.strip.downcase
85
+ return true if content_type.strip.downcase == pattern
86
+
87
+ pieces = content_type.split(";")
88
+ pieces.each do |piece|
89
+ return true if pattern == piece.strip.downcase
90
+ end
91
+
92
+ false
93
+ end
94
+
95
+ sig { params(status_code: Integer, status_codes: T::Array[String]).returns(T::Boolean) }
96
+ def self.match_status_code(status_code, status_codes)
97
+ return true if status_codes.include?("default")
98
+ status_code = status_code.to_s
99
+ status_codes.each do |code|
100
+ return true if code == status_code
101
+ return true if code.downcase.end_with?("xx") && status_code[0] == code[0]
102
+ end
103
+
104
+ false
105
+ end
106
+
107
+ sig { params(optional: T::Boolean).returns(T.proc.params(s: String).returns(T.nilable(DateTime))) }
108
+ def self.datetime_from_iso_format(optional)
109
+ Kernel.lambda do |s|
110
+ return nil if optional && s.nil?
111
+
112
+ return DateTime.strptime(s, "%Y-%m-%dT%H:%M:%S.%NZ")
113
+ end
114
+ end
115
+
116
+ sig { params(optional: T::Boolean).returns(T.proc.params(s: String).returns(T.nilable(Date))) }
117
+ def self.date_from_iso_format(optional)
118
+ Kernel.lambda do |s|
119
+ return nil if optional && s.nil?
120
+
121
+ return Date.iso8601(s)
122
+ end
123
+ end
124
+
125
+ sig do
126
+ params(enum_type: T.class_of(T::Enum), optional: T::Boolean)
127
+ .returns(T.nilable(T.proc.params(s: String).returns(T.nilable(T::Enum))))
128
+ end
129
+ def self.enum_from_string(enum_type, optional)
130
+ Kernel.lambda do |s|
131
+ return nil if optional && s.nil?
132
+
133
+ return enum_type.deserialize(s)
134
+ end
135
+ end
136
+
137
+ sig do
138
+ params(enum_type: Module, optional: T::Boolean)
139
+ .returns(T.proc.params(s: String).returns(T.untyped))
140
+ end
141
+ def self.open_enum_from_string(enum_type, optional)
142
+ Kernel.lambda do |s|
143
+ return nil if optional && s.nil?
144
+
145
+ return T.unsafe(enum_type).deserialize(s)
146
+ end
147
+ end
148
+
149
+ sig { params(name: String).returns(T.proc.returns(String)) }
150
+ def self.field_name(name)
151
+ T.let(proc { name }, T.proc.returns(String))
152
+ end
153
+
154
+ sig { params(field: ::Crystalline::MetadataFields::Field, data_class: Object).returns(Object) }
155
+ def self.parse_field(field, data_class)
156
+ field_metadata = field.metadata[:metadata_string]
157
+ return nil if field_metadata.nil?
158
+
159
+ field_value = data_class.send(field.name)
160
+ return nil if field_value.nil?
161
+
162
+ field_value
163
+ end
164
+
165
+ sig do
166
+ params(form: T::Array[T::Array[T.any(String, T::Array[T.nilable(String)])]])
167
+ .returns(T::Hash[Symbol, Object])
168
+ end
169
+ def self.encode_form(form)
170
+ payload = {}
171
+ form.each do |field_name, field|
172
+ field = T.must(field)
173
+ if field.length == 2
174
+ if field[0].nil?
175
+ # Handle multiple values for the same field name (arrays)
176
+ if payload.key?(field_name)
177
+ # Convert to array if not already
178
+ unless payload[field_name].is_a?(Array)
179
+ payload[field_name] = [payload[field_name]]
180
+ end
181
+
182
+ payload[field_name] << field[1]
183
+ else
184
+ payload[field_name] = field[1]
185
+ end
186
+ else
187
+ # Handle file uploads
188
+ file_part = Faraday::Multipart::FilePart.new(
189
+ StringIO.new(T.must(field[1])),
190
+ mime_type_for_filename(T.must(field[0])),
191
+ field[0]
192
+ )
193
+
194
+ # Handle multiple files for the same field name (arrays)
195
+ if payload.key?(field_name)
196
+ unless payload[field_name].is_a?(Array)
197
+ payload[field_name] = [payload[field_name]]
198
+ end
199
+
200
+ payload[field_name] << file_part
201
+ else
202
+ payload[field_name] = file_part
203
+ end
204
+ end
205
+ elsif field.length == 3
206
+ param_part = Faraday::Multipart::ParamPart.new(field[1], field[2])
207
+
208
+ # Handle multiple values for the same field name (arrays)
209
+ if payload.key?(field_name)
210
+ unless payload[field_name].is_a?(Array)
211
+ payload[field_name] = [payload[field_name]]
212
+ end
213
+
214
+ payload[field_name] << param_part
215
+ else
216
+ payload[field_name] = param_part
217
+ end
218
+ end
219
+ end
220
+
221
+ payload
222
+ end
223
+
224
+ sig {
225
+ params(
226
+ param_name: Symbol,
227
+ value: Object,
228
+ param_type: String,
229
+ gbls: T.nilable(T::Hash[Symbol, T::Hash[Symbol, T::Hash[Symbol, Object]]])
230
+ )
231
+ .returns(Object)
232
+ }
233
+ def self._populate_from_globals(param_name, value, param_type, gbls)
234
+ if value.nil? && !gbls.nil?
235
+ global_value = gbls.dig(:parameters, param_type.to_sym, param_name.to_sym)
236
+ value = global_value if !global_value.nil?
237
+ end
238
+
239
+ value
240
+ end
241
+
242
+ end
243
+ end
@@ -0,0 +1,66 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ # Wait for a pending assessment. Hand-written beside the generated client.
5
+
6
+ module NoResponse
7
+ # The assessment was still pending when the wait ran out. last is the
8
+ # pending assessment from the final poll; the webhook delivers the result
9
+ # when it settles.
10
+ class WaitTimeoutError < StandardError
11
+ attr_reader :assessment_id, :last
12
+
13
+ def initialize(assessment_id, last)
14
+ super("Assessment #{assessment_id} is still pending")
15
+ @assessment_id = assessment_id
16
+ @last = last
17
+ end
18
+ end
19
+
20
+ module Wait
21
+ DEFAULT_TIMEOUT_SECONDS = 30
22
+
23
+ class << self
24
+
25
+ # Poll client.assessments.get until the assessment is no longer pending,
26
+ # sleeping for the Retry-After the API sends (at least 1 s) between
27
+ # polls. Raises WaitTimeoutError once timeout seconds have passed.
28
+ def wait(client, assessment_id, **options)
29
+ poll(client.assessments, assessment_id, **options)
30
+ end
31
+
32
+ def poll(assessments, assessment_id, timeout: DEFAULT_TIMEOUT_SECONDS, sleeper: Kernel.method(:sleep),
33
+ clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) })
34
+ raise ArgumentError, 'timeout must be positive' unless timeout.positive?
35
+
36
+ deadline = clock.call + timeout
37
+ loop do
38
+ remaining_ms = [((deadline - clock.call) * 1000).ceil, 1].max
39
+ response = assessments.get(assessment_id: assessment_id, timeout_ms: remaining_ms)
40
+ return response.result unless response.result.status == Models::Shared::Status::PENDING
41
+
42
+ delay = retry_after(response.headers)
43
+ raise WaitTimeoutError.new(assessment_id, response.result) if clock.call + delay > deadline
44
+
45
+ sleeper.call(delay)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def retry_after(headers)
52
+ value = headers.find { |name, _| name.to_s.casecmp?('retry-after') }&.last
53
+ value = Array(value).first
54
+ seconds = Float(value.to_s, exception: false)
55
+ seconds.nil? ? 1.0 : [seconds, 1.0].max
56
+ end
57
+ end
58
+ end
59
+
60
+ # client.assessments.wait(assessment_id) reads the same as the generated methods.
61
+ class Assessments
62
+ def wait(assessment_id, **options)
63
+ Wait.poll(self, assessment_id, **options)
64
+ end
65
+ end
66
+ end