mints 0.0.14 → 0.0.18

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,13 +24,28 @@ module Mints
24
24
  response = @mints_contact.login(email, password)
25
25
  # Get session token from response
26
26
  session_token = response['session_token']
27
- id_token = response['contact']['id_token']
27
+ id_token = response['contact']['contact_token']
28
28
  # Set a permanent cookie with the session token
29
- cookies.permanent[:mints_contact_session_token] = session_token
30
- cookies.permanent[:mints_contact_id] = id_token
29
+ cookies.permanent[:mints_contact_session_token] = { value: session_token, secure: true, httponly: true }
30
+ cookies.permanent[:mints_contact_id] = { value: id_token, secure: true, httponly: true }
31
31
  @contact_token = id_token
32
32
  end
33
33
 
34
+ ##
35
+ # === Mints Contact Magic Link Login.
36
+ # Starts a contact session in mints.cloud and set a session cookie
37
+ def mints_contact_magic_link_login(token)
38
+ # Login in mints
39
+ response = @mints_contact.login(email, password)
40
+ # Get session token from response
41
+ session_token = response['session_token']
42
+ id_token = response['contact']['contact_token']
43
+ # Set a permanent cookie with the session token
44
+ cookies.permanent[:mints_contact_session_token] = { value: session_token, secure: true, httponly: true }
45
+ cookies.permanent[:mints_contact_id] = { value: id_token, secure: true, httponly: true }
46
+ @contact_token = id_token
47
+ end
48
+
34
49
  ##
35
50
  # === Mints Contact Logout.
36
51
  # Destroy session from mints.cloud and delete local session cookie
@@ -49,10 +64,25 @@ module Mints
49
64
  # === Register visit.
50
65
  # Call register visit method from the public client and set/renew the cookie mints_contact_id
51
66
  def register_visit
67
+ if @debug
68
+ puts "REQUEST IN REGISTER VISIT: #{request}"
69
+ puts "BODY REQUEST: #{request.body}"
70
+ puts "AUTH REQUEST: #{request.authorization}"
71
+ puts "LENGTH REQUEST: #{request.content_length}"
72
+ puts "FORM DATA REQUEST: #{request.form_data?}"
73
+ puts "FULLPATH REQUEST: #{request.fullpath}"
74
+ puts "HEADERS REQUEST: #{request.headers}"
75
+ puts "IP REQUEST: #{request.ip}"
76
+ puts "REQUEST IP ADRESS: #{request['ip_address']}"
77
+ puts "REQUEST REMOTE IP: #{request['remote_ip']}"
78
+ end
52
79
  response = @mints_pub.register_visit(request)
53
- @contact_token = response['user_token']
80
+ if @debug
81
+ puts "RESPONSE IN REGISTER VISIT: #{response}"
82
+ end
83
+ @contact_token = response['contact_token']
54
84
  @visit_id = response['visit_id']
55
- cookies.permanent[:mints_contact_id] = @contact_token
85
+ cookies.permanent[:mints_contact_id] = { value: @contact_token, secure: true, httponly: true }
56
86
  end
57
87
 
58
88
  ##
@@ -69,7 +99,7 @@ module Mints
69
99
  raise 'MintsBadCredentialsError'
70
100
  end
71
101
  # Initialize mints pub client, credentials taken from mints_config.yml.erb file
72
- @mints_pub = Mints::Pub.new(@host, @api_key, nil, @debug)
102
+ @mints_pub = Mints::Pub.new(@host, @api_key, @contact_token, @debug)
73
103
  # Set contact token from cookie
74
104
  @mints_pub.client.session_token = @contact_token
75
105
  end
@@ -86,7 +116,9 @@ module Mints
86
116
  # Initialize the public client and set the contact token
87
117
  def set_mints_contact_client
88
118
  # Initialize mints clontact client
89
- @mints_contact = Mints::Contact.new(@host, @api_key, nil, @debug)
119
+ session_token = cookies[:mints_contact_session_token] ? cookies[:mints_contact_session_token] : nil
120
+ contact_token_id = cookies[:mints_contact_id] ? cookies[:mints_contact_id] : nil
121
+ @mints_contact = Mints::Contact.new(@host, @api_key, session_token, contact_token_id, @debug)
90
122
  end
91
123
  end
92
124
  end
data/lib/pub.rb CHANGED
@@ -5,6 +5,10 @@ module Mints
5
5
  # == Public context API
6
6
  # Pub class contains functions that needs only an API key as authentication
7
7
  # == Usage example
8
+ # === For Mints::BaseController inheritance:
9
+ # If the controller is inheriting from Mints::BaseController, Only use the class variable *mints_pub* _Example:_
10
+ # @mints_pub.get_stories
11
+ # === For standalone usage:
8
12
  # Initialize
9
13
  # pub = Mints::Pub.new(mints_url, api_key)
10
14
  # or if host and api_key are provided by mints_config.yml.erb
@@ -12,23 +16,58 @@ module Mints
12
16
  # Call any function
13
17
  # pub.get_products
14
18
  # == Single resource options
15
- # * +include+ - [String] include a relationship
16
- # * +attributes+ - [Boolean] attach attributes to response
17
- # * +categories+ - [Boolean] attach categories to response
18
- # * +tags+ - [Boolean] attach tags to response
19
+ # * +include+ - [_String_] Specify additional information to be included in the results from the objects relations. _Example:_
20
+ # { "include": "events" }
21
+ # * +attributes+ - [_Boolean_] If present, attributes will be returned for each record in the results. _Example:_
22
+ # { "attributes": true }
23
+ # * +categories+ - [_Boolean_] If present, categories will be returned for each record in the results. _Example:_
24
+ # { "categories": true }
25
+ # * +tags+ - [_Boolean_] If present, tags will be returned for each record in the results. _Example:_
26
+ # { "tags": true }
27
+ # * +fields+ - [_String_] Specify the fields that you want to be returned. If empty, all fields are returned. The object index can also be used to specify specific fields from relations. _Example:_
28
+ # { "fields": "id, title, slug" }
29
+ # { "fields[products]": "id, title, slug" }
30
+ #
19
31
  # == Resource collections options
20
- # * +search+ - [String] filter by a search word
21
- # * +scopes+ - [String] filter by a scope
22
- # * +filters+ - [String] filter by where clauses
23
- # * +jfilters+ - [String] filter using complex condition objects
24
- # * +catfilters+ - [String] filter by categories
25
- # * +fields+ - [String] indicates the columns that will be selected
26
- # * +sort+ - [String] indicates the columns that will be selected
27
- # * +include+ - [String] include a relationship
28
- # * +attributes+ - [Boolean] attach attributes to response
29
- # * +categories+ - [Boolean] attach categories to response
30
- # * +taxonomies+ - [Boolean] attach categories to response
31
- # * +tags+ - [Boolean] attach tags to response
32
+ # * +search+ - [_String_] If present, it will search for records matching the search string. _Example:_
33
+ # { "search": "searchstring" }
34
+ # * +scopes+ - [_String_] If present, it will apply the specified Model's scopes. _Example:_
35
+ # { "scopes": "approved, recent" }
36
+ # * +filters+ - [_String_] This is a powerful parameter that allows the data to be filtered by any of its fields. Currently only exact matches are supported. _Example:_
37
+ # { "filters[title]": "titleToFilter" }
38
+ # * +jfilters+ - [_String_] A complex filter configuration, as used in segments, in JSON format, base64 encoded and URLencoded. _Example:_
39
+ # jfilter = {
40
+ # "type":"group",
41
+ # "items":[
42
+ # {
43
+ # "type":"attribute",
44
+ # "operator":"==",
45
+ # "slug":"title",
46
+ # "value":"Action movies"
47
+ # }
48
+ # ],
49
+ # "operator":"or"
50
+ # }
51
+ # options = { "jfilters": jfilter }
52
+ # * +catfilters+ - [_String_] filter by categories. _Example:_
53
+ # { "catfilters": "categoryName" }
54
+ # * +fields+ - [_String_] Specify the fields that you want to be returned. If empty, all fields are returned. The object index can also be used to specify specific fields from relations. _Example:_
55
+ # { "fields": "id, title, slug" }
56
+ # { "fields[products]": "id, title, slug" }
57
+ # * +sort+ - [_String_] The name of the field to perform the sort. Prefix the value with a minus sign - for ascending order. _Example:_
58
+ # { "sort": "title" }
59
+ # { "sort": "-title" }
60
+ # * +include+ - [_String_] Specify additional information to be included in the results from the objects relations. _Example:_
61
+ # { "include": "events" }
62
+ # * +attributes+ - [_Boolean_] If present, attributes will be returned for each record in the results. _Example:_
63
+ # { "attributes": true }
64
+ # * +categories+ - [_Boolean_] If present, categories will be returned for each record in the results. _Example:_
65
+ # { "categories": true }
66
+ # * +taxonomies+ - [_Boolean_] If present, taxonomies will be returned for each record in the results. _Example:_
67
+ # { "taxonomies": true }
68
+ # * +tags+ - [_Boolean_] If present, tags will be returned for each record in the results. _Example:_
69
+ # { "tags": true }
70
+
32
71
  class Pub
33
72
  attr_reader :client
34
73
 
@@ -55,6 +94,15 @@ module Mints
55
94
  # * +ip+ - [String] It's the visitor IP
56
95
  # * +user_agent+ - The visitor's browser user agent
57
96
  # * +url+ - [String] URL visited
97
+ #
98
+ # ==== Example
99
+ # request = {
100
+ # "remote_ip" => "http://864.401.156.12/",
101
+ # "user_agent" => "User Agent",
102
+ # "fullpath" => "https://mints.cloud/blog"
103
+ # }
104
+ # @mints_pub.register_visit(request, request["remote_ip"], request["user_agent"], request["fullpath"])
105
+ #
58
106
  def register_visit(request, ip = nil, user_agent = nil, url = nil)
59
107
  data = {
60
108
  ip_address: ip || request.remote_ip,
@@ -72,69 +120,38 @@ module Mints
72
120
  # ==== Parameters
73
121
  # * +visit+ - [String] It's the visitor IP
74
122
  # * +time+ - [Integer] The visitor's browser user agent
123
+ #
124
+ # ==== Example
125
+ # @mints_pub.register_visit_timer("60da2325d29acc7e55684472", 4)
126
+ #
75
127
  def register_visit_timer(visit, time)
76
128
  return @client.raw("get", "/register-visit-timer?visit=#{visit}&time=#{time}")
77
129
  end
78
130
 
79
131
  ##
80
- # === Get Content Page.
81
- # Get a single content page
132
+ # === Get Asset Info.
133
+ # Get a description of an Asset
82
134
  #
83
135
  # ==== Parameters
84
- # * +slug+ - [String] It's the slug
85
- # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
86
- def get_content_page(slug, options = nil)
87
- return @client.raw("get", "/content/content-pages/#{slug}", options)
88
- end
89
-
90
- ##
91
- # === Get Content Templates.
92
- # Get a collection of content templates
93
- #
94
- # ==== Parameters
95
- # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
96
- def get_content_templates(options = nil)
97
- return @client.raw("get", "/content/content-templates", options)
98
- end
99
-
100
- ##
101
- # === Get Content Template.
102
- # Get a single content template.
136
+ # * +slug+ - [String] It's the string identifier of the asset.
103
137
  #
104
- # ==== Parameters
105
- # * +slug+ - [String] It's the string identifier generated by Mints
106
- # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
107
- def get_content_template(slug, options = nil)
108
- return @client.raw("get", "/content/content-templates/#{slug}", options)
109
- end
110
-
111
- ##
112
- # === Get Content Instances.
113
- # Get a collection of content instances
138
+ # ==== Example
139
+ # @mints_pub.get_asset_info("quaerat")
114
140
  #
115
- # ==== Parameters
116
- # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
117
- def get_content_instances(options)
118
- return @client.raw("get", "/content/content-instances", options)
141
+ def get_asset_info(slug)
142
+ return @client.raw("get", "/content/asset-info/#{slug}")
119
143
  end
120
-
121
- ##
122
- # === Get Content Instance.
123
- # Get a single content instance.
124
- #
125
- # ==== Parameters
126
- # * +slug+ - [String] It's the string identifier generated by Mints
127
- # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
128
- def get_content_instance(slug, options = nil)
129
- return @client.raw("get", "/content/content-instances/#{slug}", options)
130
- end
131
-
144
+
132
145
  ##
133
146
  # === Get Stories.
134
147
  # Get a collection of stories
135
148
  #
136
149
  # ==== Parameters
137
150
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
151
+ #
152
+ # ==== Example
153
+ # @mints_pub.get_stories
154
+ #
138
155
  def get_stories(options = nil)
139
156
  return @client.raw("get", "/content/stories", options)
140
157
  end
@@ -146,6 +163,10 @@ module Mints
146
163
  # ==== Parameters
147
164
  # * +slug+ - [String] It's the string identifier generated by Mints
148
165
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
166
+ #
167
+ # ==== Example
168
+ # @mints_pub.get_story("getting-ready-for-e3")
169
+ #
149
170
  def get_story(slug, options = nil)
150
171
  return @client.raw("get", "/content/stories/#{slug}", options)
151
172
  end
@@ -156,6 +177,10 @@ module Mints
156
177
  #
157
178
  # ==== Parameters
158
179
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
180
+ #
181
+ # ==== Example
182
+ # @mints_pub.get_forms
183
+ #
159
184
  def get_forms(options = nil)
160
185
  return @client.raw("get", "/content/forms", options)
161
186
  end
@@ -167,6 +192,10 @@ module Mints
167
192
  # ==== Parameters
168
193
  # * +slug+ - [String] It's the string identifier generated by Mints
169
194
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
195
+ #
196
+ # ==== Example
197
+ # @mints_pub.get_form("survey")
198
+ #
170
199
  def get_form(slug, options = nil)
171
200
  return @client.raw("get", "/content/forms/#{slug}", options)
172
201
  end
@@ -177,79 +206,126 @@ module Mints
177
206
  #
178
207
  # ==== Parameters
179
208
  # * +data+ - [Hash] Data to be submited
209
+ #
210
+ # ==== Example
211
+ # data_form = {
212
+ # "data": {
213
+ # 'form_slug': 'formulario',
214
+ # 'email': 'oscar@mints.cloud',
215
+ # 'ingrese-un-texto': 'hola'
216
+ # }
217
+ # }
218
+ # @mints_pub.submit_form(data_form.to_json)
219
+ #
180
220
  def submit_form(data)
181
221
  return @client.raw("post", "/content/forms/submit", nil, data)
182
- end
222
+ end
183
223
 
184
224
  ##
185
- # === Get Products.
186
- # Get a collection of products.
225
+ # === Get Content Instances.
226
+ # Get a collection of content instances. _Note:_ Options must be specified.
187
227
  #
188
228
  # ==== Parameters
189
229
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
190
- def get_products(options = nil)
191
- return @client.raw("get", "/ecommerce/products", options)
230
+ def get_content_instances(options = nil)
231
+ return @client.raw("get", "/content/content-instances", options)
192
232
  end
193
233
 
194
234
  ##
195
- # === Get Product.
196
- # Get a single product.
235
+ # === Get Content Instance.
236
+ # Get a single content instance.
197
237
  #
198
238
  # ==== Parameters
199
239
  # * +slug+ - [String] It's the string identifier generated by Mints
200
240
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
201
- def get_product(slug, options = nil)
202
- return @client.raw("get", "/ecommerce/products/#{slug}", options)
241
+ #
242
+ # ==== Example
243
+ # @mints_pub.get_content_instance("bill-gates")
244
+ #
245
+ def get_content_instance(slug, options = nil)
246
+ return @client.raw("get", "/content/content-instances/#{slug}", options)
203
247
  end
204
248
 
205
249
  ##
206
- # === Get Product Brands.
207
- # Get a collection of product brands.
250
+ # === Get Content Pages.
251
+ # Get all content pages.
208
252
  #
209
253
  # ==== Parameters
210
254
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
211
- def get_product_brands(options = nil)
212
- return @client.raw("get", "/ecommerce/product-brands", options)
255
+ def get_content_pages(options = nil)
256
+ return @client.raw("get", "/content/content-pages", options)
213
257
  end
214
258
 
215
259
  ##
216
- # === Get Product Brand.
217
- # Get a product brand.
260
+ # === Get Content Page.
261
+ # Get a single content page
218
262
  #
219
263
  # ==== Parameters
220
- # * +slug+ - [String] It's the string identifier generated by Mints
264
+ # * +slug+ - [String] It's the slug
221
265
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
222
- def get_product_brand(slug, options = nil)
223
- return @client.raw("get", "/ecommerce/product-brands/#{slug}", options)
266
+ #
267
+ # ==== Example
268
+ # @mints_pub.get_content_page("test-page")
269
+ #
270
+ def get_content_page(slug, options = nil)
271
+ return @client.raw("get", "/content/content-pages/#{slug}", options)
224
272
  end
225
273
 
226
274
  ##
227
- # === Get SKUs.
228
- # Get a collection of SKUs.
275
+ # === Get Locations.
276
+ # Get all locations.
229
277
  #
230
278
  # ==== Parameters
231
279
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
232
- def get_skus(options = nil)
233
- return @client.raw("get", "/ecommerce/skus", options)
280
+ #
281
+ # ==== Example
282
+ # @mints_pub.get_locations
283
+ #
284
+ def get_locations(options = nil)
285
+ return @client.raw("get", "/ecommerce/locations", options)
234
286
  end
235
287
 
236
288
  ##
237
- # === Get SKU.
238
- # Get a single SKU.
289
+ # === Get Products.
290
+ # Get a collection of products.
291
+ #
292
+ # ==== Parameters
293
+ # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
294
+ #
295
+ # ==== Example
296
+ # @mints_pub.get_products
297
+ #
298
+ def get_products(options = nil)
299
+ return @client.raw("get", "/ecommerce/products", options)
300
+ end
301
+
302
+ ##
303
+ # === Get Product.
304
+ # Get a single product.
239
305
  #
240
306
  # ==== Parameters
241
307
  # * +slug+ - [String] It's the string identifier generated by Mints
242
308
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
243
- def get_sku(slug, options = nil)
244
- return @client.raw("get", "/ecommerce/skus/#{slug}", options)
309
+ #
310
+ # ==== Example
311
+ # @mints_pub.get_product("batman-shirt")
312
+ #
313
+ def get_product(slug, options = nil)
314
+ return @client.raw("get", "/ecommerce/products/#{slug}", options)
245
315
  end
246
-
316
+
247
317
  ##
248
318
  # === Get categories.
249
319
  # Get a collection of categories.
250
320
  #
251
321
  # ==== Parameters
252
322
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
323
+ #
324
+ # ==== Example
325
+ # options = {
326
+ # "object_type": "stories"
327
+ # }
328
+ # @mints_pub.get_categories(options)
253
329
  def get_categories(options = nil)
254
330
  return @client.raw("get", "/config/categories", options)
255
331
  end
@@ -261,6 +337,13 @@ module Mints
261
337
  # ==== Parameters
262
338
  # * +slug+ - [String] It's the string identifier generated by Mints
263
339
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
340
+ #
341
+ # ==== Example
342
+ # options = {
343
+ # "object_type": "locations"
344
+ # }
345
+ # @mints_pub.get_category("atm", options)
346
+ #
264
347
  def get_category(slug, options = nil)
265
348
  return @client.raw("get", "/config/categories/#{slug}", options)
266
349
  end
@@ -271,6 +354,10 @@ module Mints
271
354
  #
272
355
  # ==== Parameters
273
356
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
357
+ #
358
+ # ==== Example
359
+ # @mints_pub.get_tags
360
+ #
274
361
  def get_tags(options = nil)
275
362
  return @client.raw("get", "/config/tags", options)
276
363
  end
@@ -282,26 +369,24 @@ module Mints
282
369
  # ==== Parameters
283
370
  # * +slug+ - [String] It's the string identifier generated by Mints
284
371
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
372
+ #
373
+ # ==== Example
374
+ # @mints_pub.get_tag("ad-0")
375
+ #
285
376
  def get_tag(slug, options = nil)
286
377
  return @client.raw("get", "/config/tags/#{slug}", options)
287
378
  end
288
379
 
289
- ##
290
- # === Get Attributes.
291
- # Get a collection of attributes.
292
- #
293
- # ==== Parameters
294
- # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
295
- def get_attributes(options = nil)
296
- return @client.raw("get", "/config/attributes", options)
297
- end
298
-
299
380
  ##
300
381
  # === Get Taxonomies.
301
382
  # Get a collection of taxonomies.
302
383
  #
303
384
  # ==== Parameters
304
385
  # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
386
+ #
387
+ # ==== Example
388
+ # @mints_pub.get_taxonomies
389
+ #
305
390
  def get_taxonomies(options = nil)
306
391
  return @client.raw("get", "/config/taxonomies", options)
307
392
  end
@@ -313,8 +398,26 @@ module Mints
313
398
  # ==== Parameters
314
399
  # * +slug+ - [String] It's the string identifier generated by Mints
315
400
  # * +options+ - [Hash] List of {Single Resource Options}[#class-Mints::Pub-label-Single+resource+options] shown above can be used as parameter
401
+ #
402
+ # ==== Example
403
+ # @mints_pub.get_taxonomy("unit_pricing_measure")
404
+ #
316
405
  def get_taxonomy(slug, options = nil)
317
406
  return @client.raw("get", "/config/taxonomies/#{slug}", options)
318
407
  end
408
+
409
+ ##
410
+ # === Get Attributes.
411
+ # Get a collection of attributes.
412
+ #
413
+ # ==== Parameters
414
+ # * +options+ - [Hash] List of {Resource collection Options}[#class-Mints::Pub-label-Resource+collections+options+] shown above can be used as parameter
415
+ #
416
+ # ==== Example
417
+ # @mints_pub.get_attributes
418
+ #
419
+ def get_attributes(options = nil)
420
+ return @client.raw("get", "/config/attributes", options)
421
+ end
319
422
  end
320
423
  end