flipper 0.9.2 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e2d0c40eaed262ab192fa672996ccdee0cc43aaa
4
- data.tar.gz: 2c5117b307da9a9da90d85c820ce640494a8d865
3
+ metadata.gz: 47f1ba89a0c1968fcc2a07f210260f48df2c86e0
4
+ data.tar.gz: 0287caf882d254acb16259e617b73c663b01f64c
5
5
  SHA512:
6
- metadata.gz: 0ffdb49402a659f33ab147f24450440afda3a08afefe7459dd5cb045e9fe4ff607015c277b9d4697603ac7190607e2d34181a8f44cdb44ba74642efe040a52fe
7
- data.tar.gz: eef4104468dc78f364595081b77c27b17837383d916c7cb91486e4db42dd87449319d8eb38f4f611cddfdbbab463e0a60cc0290e303e2a03e5a766a582a2a46b
6
+ metadata.gz: be454d3e836cfc89e75767a56f0c460d31be2c6c7dfd0155943f732e14355c15ad6e5dc35b929231716b6316305aac9c58e82ea83adcc0053ba5aa5829f28a70
7
+ data.tar.gz: 88b93a792b1458b54c819e0d021ce2427c31079e5a3e9c926c6d74cf8702b1058a9e0a56dc203864ce3885b86114abc6dd6a44dbc120533fcf29d9b3d9f77c49
data/Changelog.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.10.0
2
+
3
+ * Added feature check context (https://github.com/jnunemaker/flipper/pull/158)
4
+ * Do not use mass assignment for active record adapter (https://github.com/jnunemaker/flipper/pull/171)
5
+ * Several documentation improvements
6
+ * Make Flipper::UI.app.inspect return a String (https://github.com/jnunemaker/flipper/pull/176)
7
+ * changes boolean gate route to api/v1/features/boolean (https://github.com/jnunemaker/flipper/pull/175)
8
+ * add api v1 percentage_of_actors endpoint (https://github.com/jnunemaker/flipper/pull/179)
9
+ * add api v1 percentage_of_time endpoint (https://github.com/jnunemaker/flipper/pull/180)
10
+ * add api v1 actors gate endpoint (https://github.com/jnunemaker/flipper/pull/181)
11
+ * wait for activesupport to tell us when active record is loaded for active record adapter (https://github.com/jnunemaker/flipper/pull/192)
12
+
1
13
  ## 0.9.2
2
14
 
3
15
  * GET /api/v1/features
data/README.md CHANGED
@@ -72,6 +72,7 @@ Of course there are more [examples for you to peruse](examples/). You could also
72
72
  * [Instrumentation](docs/Instrumentation.md) - ActiveSupport::Notifications, Statsd and Metriks
73
73
  * [Optimization](docs/Optimization.md) - Memoization middleware and Cache adapters
74
74
  * [Web Interface](docs/ui/README.md) - Point and click...
75
+ * [API](docs/api/README.md) - HTTP API interface
75
76
  * [Caveats](docs/Caveats.md) - Flipper beware! (see what I did there)
76
77
 
77
78
  ## Contributing
@@ -87,3 +88,10 @@ Of course there are more [examples for you to peruse](examples/). You could also
87
88
  1. Update the version to be whatever it should be and commit.
88
89
  2. `script/release`
89
90
  3. Profit.
91
+
92
+ ## Brought To You By
93
+
94
+ | pic | @mention |
95
+ |---|---|
96
+ | ![@jnunemaker](https://avatars3.githubusercontent.com/u/235?s=64) | [@jnunemaker](https://github.com/jnunemaker) |
97
+ | ![@alexwheeler](https://avatars3.githubusercontent.com/u/3260042?s=64) | [@alexwheeler](https://github.com/alexwheeler) |
data/docs/Adapters.md CHANGED
@@ -8,7 +8,7 @@ I plan on supporting the adapters in the flipper repo. Other adapters are welcom
8
8
  * [PStore adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/pstore.rb) – great for when a local file is enough
9
9
  * [Mongo adapter](https://github.com/jnunemaker/flipper/blob/master/docs/mongo)
10
10
  * [Redis adapter](https://github.com/jnunemaker/flipper/blob/master/docs/redis)
11
- * [ActiveRecord adapter](https://github.com/jnunemaker/flipper/blob/master/docs/active_record) - Rails 3 and 4.
11
+ * [ActiveRecord adapter](https://github.com/jnunemaker/flipper/blob/master/docs/active_record) - Rails 3, 4, and 5.
12
12
  * [Cassanity adapter](https://github.com/jnunemaker/flipper-cassanity)
13
13
 
14
14
  ## Community Supported
@@ -0,0 +1,775 @@
1
+ # Flipper::Api
2
+
3
+ API for the [Flipper](https://github.com/jnunemaker/flipper) gem.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'flipper-api'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install flipper-api
18
+
19
+ ## Usage
20
+
21
+ `Flipper::Api` is a mountable application that can be included in your Rails/Ruby apps. In a Rails application, you can mount `Flipper::Api` to a route of your choice:
22
+
23
+ ```ruby
24
+ # config/routes.rb
25
+ YourRailsApp::Application.routes.draw do
26
+ mount Flipper::Api.app(flipper) => '/flipper-api'
27
+ end
28
+ ```
29
+
30
+ For more advanced mounting techniques and for suggestions on how to mount in a non-Rails application, it is recommend that you review the [`Flipper::UI` usage documentation](https://github.com/jnunemaker/flipper/blob/master/docs/ui/README.md#usage) as the same approaches apply to `Flipper::Api`.
31
+
32
+ ## Endpoints
33
+
34
+ **Note:** Example CURL requests below assume a mount point of `/flipper-api`.
35
+
36
+ ### Get all features
37
+
38
+ **URL**
39
+
40
+ `GET /api/v1/features`
41
+
42
+ **Request**
43
+
44
+ ```
45
+ curl http://example.com/flipper-api/api/v1/features
46
+ ```
47
+
48
+ **Response**
49
+
50
+ Returns an array of feature objects:
51
+
52
+ ```json
53
+ {
54
+ "features": [
55
+ {
56
+ "key": "search",
57
+ "state": "on",
58
+ "gates": [
59
+ {
60
+ "key": "boolean",
61
+ "name": "boolean",
62
+ "value": false
63
+ },
64
+ {
65
+ "key": "groups",
66
+ "name": "group",
67
+ "value": []
68
+ },
69
+ {
70
+ "key": "actors",
71
+ "name": "actor",
72
+ "value": []
73
+ },
74
+ {
75
+ "key": "percentage_of_actors",
76
+ "name": "percentage_of_actors",
77
+ "value": 0
78
+ },
79
+ {
80
+ "key": "percentage_of_time",
81
+ "name": "percentage_of_time",
82
+ "value": 0
83
+ }
84
+ ]
85
+ },
86
+ {
87
+ "key": "history",
88
+ "state": "off",
89
+ "gates": [
90
+ {
91
+ "key": "boolean",
92
+ "name": "boolean",
93
+ "value": false
94
+ },
95
+ {
96
+ "key": "groups",
97
+ "name": "group",
98
+ "value": []
99
+ },
100
+ {
101
+ "key": "actors",
102
+ "name": "actor",
103
+ "value": []
104
+ },
105
+ {
106
+ "key": "percentage_of_actors",
107
+ "name": "percentage_of_actors",
108
+ "value": 0
109
+ },
110
+ {
111
+ "key": "percentage_of_time",
112
+ "name": "percentage_of_time",
113
+ "value": 0
114
+ }
115
+ ]
116
+ }
117
+ ]
118
+ }
119
+ ```
120
+
121
+ ### Create a new feature
122
+
123
+ **URL**
124
+
125
+ `POST /api/v1/features`
126
+
127
+ **Parameters**
128
+
129
+ * `name` - The name of the feature (Recommended naming conventions: lower case, snake case, underscores over dashes. Good: foo_bar, foo. Bad: FooBar, Foo Bar, foo bar, foo-bar.)
130
+
131
+ **Request**
132
+
133
+ ```
134
+ curl -X POST -d "name=reports" http://example.com/flipper-api/api/v1/features
135
+ ```
136
+
137
+ **Response**
138
+
139
+ On successful creation, the API will respond with an empty JSON response.
140
+
141
+ ### Retrieve a feature
142
+
143
+ **URL**
144
+
145
+ `GET /api/v1/features/{feature_name}`
146
+
147
+ **Parameters**
148
+
149
+ * `feature_name` - The name of the feature to retrieve
150
+
151
+ **Request**
152
+
153
+ ```
154
+ curl http://example.com/flipper-api/api/v1/features/reports
155
+ ```
156
+
157
+ **Response**
158
+
159
+ Returns an individual feature object:
160
+
161
+ ```json
162
+ {
163
+ "key": "search",
164
+ "state": "off",
165
+ "gates": [
166
+ {
167
+ "key": "boolean",
168
+ "name": "boolean",
169
+ "value": false
170
+ },
171
+ {
172
+ "key": "groups",
173
+ "name": "group",
174
+ "value": []
175
+ },
176
+ {
177
+ "key": "actors",
178
+ "name": "actor",
179
+ "value": []
180
+ },
181
+ {
182
+ "key": "percentage_of_actors",
183
+ "name": "percentage_of_actors",
184
+ "value": 0
185
+ },
186
+ {
187
+ "key": "percentage_of_time",
188
+ "name": "percentage_of_time",
189
+ "value": 0
190
+ }
191
+ ]
192
+ }
193
+ ```
194
+
195
+ ### Delete a feature
196
+
197
+ **URL**
198
+
199
+ `DELETE /api/v1/features/{feature_name}`
200
+
201
+ **Parameters**
202
+
203
+ * `feature_name` - The name of the feature to delete
204
+
205
+ **Request**
206
+
207
+ ```
208
+ curl -X DELETE http://example.com/flipper-api/api/v1/features/reports
209
+ ```
210
+
211
+ **Response**
212
+
213
+ Successful deletion of a feature will return a 204 No Content response.
214
+
215
+ ## Gates
216
+
217
+ The API supports enabling / disabling any of the Flipper [gates](https://github.com/jnunemaker/flipper/blob/master/docs/Gates.md). Gate endpoints follow the url convention:
218
+
219
+ **enable**
220
+
221
+ `POST /api/v1/{feature_name}/{gate_name}`
222
+
223
+ **disable**
224
+
225
+ `DELETE /api/v1/{feature_name}/{gate_name}`
226
+
227
+ and on a succesful request return a 200 HTTP status and the feature object as the response body.
228
+
229
+ ### Boolean enable a feature
230
+
231
+ **URL**
232
+
233
+ `POST /api/v1/features/{feature_name}/boolean`
234
+
235
+ **Parameters**
236
+
237
+ * `feature_name` - The name of the feature to enable
238
+
239
+ **Request**
240
+
241
+ ```
242
+ curl -X POST http://example.com/flipper-api/api/v1/features/reports/boolean
243
+ ```
244
+
245
+ **Response**
246
+
247
+ Successful enabling of the boolean gate will return a 200 HTTP status and the feature object as the response body.
248
+
249
+ ```json
250
+ {
251
+ "key": "reports",
252
+ "state": "on",
253
+ "gates": [
254
+ {
255
+ "key": "boolean",
256
+ "name": "boolean",
257
+ "value": true
258
+ },
259
+ {
260
+ "key": "groups",
261
+ "name": "group",
262
+ "value": []
263
+ },
264
+ {
265
+ "key": "actors",
266
+ "name": "actor",
267
+ "value": []
268
+ },
269
+ {
270
+ "key": "percentage_of_actors",
271
+ "name": "percentage_of_actors",
272
+ "value": 0
273
+ },
274
+ {
275
+ "key": "percentage_of_time",
276
+ "name": "percentage_of_time",
277
+ "value": 0
278
+ }
279
+ ]
280
+ }
281
+ ```
282
+
283
+
284
+ ### Boolean disable a feature
285
+
286
+ **URL**
287
+
288
+ `DELETE /api/v1/features/{feature_name}/boolean`
289
+
290
+ **Parameters**
291
+
292
+ * `feature_name` - The name of the feature to disable
293
+
294
+ **Request**
295
+
296
+ ```
297
+ curl -X DELETE http://example.com/flipper-api/api/v1/features/reports/boolean
298
+ ```
299
+
300
+ **Response**
301
+
302
+ Successful disabling of the boolean gate will return a 200 HTTP status and the feature object.
303
+
304
+ ```json
305
+ {
306
+ "key": "reports",
307
+ "state": "off",
308
+ "gates": [
309
+ {
310
+ "key": "boolean",
311
+ "name": "boolean",
312
+ "value": false
313
+ },
314
+ {
315
+ "key": "groups",
316
+ "name": "group",
317
+ "value": []
318
+ },
319
+ {
320
+ "key": "actors",
321
+ "name": "actor",
322
+ "value": []
323
+ },
324
+ {
325
+ "key": "percentage_of_actors",
326
+ "name": "percentage_of_actors",
327
+ "value": 0
328
+ },
329
+ {
330
+ "key": "percentage_of_time",
331
+ "name": "percentage_of_time",
332
+ "value": 0
333
+ }
334
+ ]
335
+ }
336
+ ```
337
+
338
+ ### Enable Group
339
+
340
+ **URL**
341
+
342
+ `POST /api/v1/features/{feature_name}/groups`
343
+
344
+ **Parameters**
345
+
346
+ * `feature_name` - The name of the feature
347
+
348
+ * `name` - The name of a registered group to enable
349
+
350
+ **Request**
351
+
352
+ ```
353
+ curl -X POST -d "name=admins" http://example.com/flipper-api/api/v1/features/reports/groups
354
+ ```
355
+
356
+ **Response**
357
+
358
+ Successful enabling of the group will return a 200 HTTP status and the feature object as the response body.
359
+
360
+ ```json
361
+ {
362
+ "key": "reports",
363
+ "state": "conditional",
364
+ "gates": [
365
+ {
366
+ "key": "boolean",
367
+ "name": "boolean",
368
+ "value": false
369
+ },
370
+ {
371
+ "key": "groups",
372
+ "name": "group",
373
+ "value": ["admins"]
374
+ },
375
+ {
376
+ "key": "actors",
377
+ "name": "actor",
378
+ "value": []
379
+ },
380
+ {
381
+ "key": "percentage_of_actors",
382
+ "name": "percentage_of_actors",
383
+ "value": 0
384
+ },
385
+ {
386
+ "key": "percentage_of_time",
387
+ "name": "percentage_of_time",
388
+ "value": 0
389
+ }
390
+ ]
391
+ }
392
+ ```
393
+
394
+ ### Disable Group
395
+
396
+ **URL**
397
+
398
+ `DELETE /api/v1/features/{feature_name}/groups`
399
+
400
+ **Parameters**
401
+
402
+ * `feature_name` - The name of the feature
403
+
404
+ * `name` - The name of a registered group to disable
405
+
406
+ **Request**
407
+
408
+ ```
409
+ curl -X DELETE -d "name=admins" http://example.com/flipper-api/api/v1/features/reports/groups
410
+ ```
411
+
412
+ **Response**
413
+
414
+ Successful disabling of the group will return a 200 HTTP status and the feature object as the response body.
415
+
416
+ ```json
417
+ {
418
+ "key": "reports",
419
+ "state": "off",
420
+ "gates": [
421
+ {
422
+ "key": "boolean",
423
+ "name": "boolean",
424
+ "value": false
425
+ },
426
+ {
427
+ "key": "groups",
428
+ "name": "group",
429
+ "value": []
430
+ },
431
+ {
432
+ "key": "actors",
433
+ "name": "actor",
434
+ "value": []
435
+ },
436
+ {
437
+ "key": "percentage_of_actors",
438
+ "name": "percentage_of_actors",
439
+ "value": 0
440
+ },
441
+ {
442
+ "key": "percentage_of_time",
443
+ "name": "percentage_of_time",
444
+ "value": 0
445
+ }
446
+ ]
447
+ }
448
+ ```
449
+ ### Enable Actor
450
+
451
+ **URL**
452
+
453
+ `POST /api/v1/features/{feature_name}/actors`
454
+
455
+ **Parameters**
456
+
457
+ * `feature_name` - The name of the feature
458
+
459
+ * `flipper_id` - The flipper_id of actor to enable
460
+
461
+ **Request**
462
+
463
+ ```
464
+ curl -X POST -d "flipper_id=User:1" http://example.com/flipper-api/api/v1/features/reports/actors
465
+ ```
466
+
467
+ **Response**
468
+
469
+ Successful enabling of the actor will return a 200 HTTP status and the feature object as the response body.
470
+
471
+ ```json
472
+ {
473
+ "key": "reports",
474
+ "state": "conditional",
475
+ "gates": [
476
+ {
477
+ "key": "boolean",
478
+ "name": "boolean",
479
+ "value": false
480
+ },
481
+ {
482
+ "key": "groups",
483
+ "name": "group",
484
+ "value": []
485
+ },
486
+ {
487
+ "key": "actors",
488
+ "name": "actor",
489
+ "value": ["User:1"]
490
+ },
491
+ {
492
+ "key": "percentage_of_actors",
493
+ "name": "percentage_of_actors",
494
+ "value": 0
495
+ },
496
+ {
497
+ "key": "percentage_of_time",
498
+ "name": "percentage_of_time",
499
+ "value": 0
500
+ }
501
+ ]
502
+ }
503
+ ```
504
+ ### Disable Actor
505
+
506
+ **URL**
507
+
508
+ `DELETE /api/v1/features/{feature_name}/actors`
509
+
510
+ **Parameters**
511
+
512
+ * `feature_name` - The name of the feature
513
+
514
+ * `flipper_id` - The flipper_id of actor to disable
515
+
516
+ **Request**
517
+
518
+ ```
519
+ curl -X DELETE -d "flipper_id=User:1" http://example.com/flipper-api/api/v1/features/reports/actors
520
+ ```
521
+
522
+ **Response**
523
+
524
+ Successful disabling of the actor will return a 200 HTTP status and the feature object as the response body.
525
+
526
+ ```json
527
+ {
528
+ "key": "reports",
529
+ "state": "off",
530
+ "gates": [
531
+ {
532
+ "key": "boolean",
533
+ "name": "boolean",
534
+ "value": false
535
+ },
536
+ {
537
+ "key": "groups",
538
+ "name": "group",
539
+ "value": []
540
+ },
541
+ {
542
+ "key": "actors",
543
+ "name": "actor",
544
+ "value": []
545
+ },
546
+ {
547
+ "key": "percentage_of_actors",
548
+ "name": "percentage_of_actors",
549
+ "value": 0
550
+ },
551
+ {
552
+ "key": "percentage_of_time",
553
+ "name": "percentage_of_time",
554
+ "value": 0
555
+ }
556
+ ]
557
+ }
558
+ ```
559
+
560
+ ### Enable Percentage of Actors
561
+
562
+ **URL**
563
+
564
+ `POST /api/v1/features/{feature_name}/percentage_of_actors`
565
+
566
+ **Parameters**
567
+
568
+ * `feature_name` - The name of the feature
569
+
570
+ * `percentage` - The percentage of actors to enable
571
+
572
+ **Request**
573
+
574
+ ```
575
+ curl -X POST -d "percentage=20" http://example.com/flipper-api/api/v1/features/reports/percentage_of_actors
576
+ ```
577
+
578
+ **Response**
579
+
580
+ Successful enabling of a percentage of actors will return a 200 HTTP status and the feature object as the response body.
581
+
582
+ ```json
583
+ {
584
+ "key": "reports",
585
+ "state": "conditional",
586
+ "gates": [
587
+ {
588
+ "key": "boolean",
589
+ "name": "boolean",
590
+ "value": false
591
+ },
592
+ {
593
+ "key": "groups",
594
+ "name": "group",
595
+ "value": []
596
+ },
597
+ {
598
+ "key": "actors",
599
+ "name": "actor",
600
+ "value": []
601
+ },
602
+ {
603
+ "key": "percentage_of_actors",
604
+ "name": "percentage_of_actors",
605
+ "value": 20
606
+ },
607
+ {
608
+ "key": "percentage_of_time",
609
+ "name": "percentage_of_time",
610
+ "value": 0
611
+ }
612
+ ]
613
+ }
614
+ ```
615
+ ### Disable Percentage of Actors
616
+
617
+ **URL**
618
+
619
+ `DELETE /api/v1/features/{feature_name}/percentage_of_actors`
620
+
621
+ **Parameters**
622
+
623
+ * `feature_name` - The name of the feature
624
+
625
+ **Request**
626
+
627
+ ```
628
+ curl -X DELETE http://example.com/flipper-api/api/v1/features/reports/percentage_of_actors
629
+ ```
630
+
631
+ **Response**
632
+
633
+ Successful disabling of a percentage of actors will set the percentage to 0 and return a 200 HTTP status and the feature object as the response body.
634
+
635
+ ```json
636
+ {
637
+ "key": "reports",
638
+ "state": "off",
639
+ "gates": [
640
+ {
641
+ "key": "boolean",
642
+ "name": "boolean",
643
+ "value": false
644
+ },
645
+ {
646
+ "key": "groups",
647
+ "name": "group",
648
+ "value": []
649
+ },
650
+ {
651
+ "key": "actors",
652
+ "name": "actor",
653
+ "value": []
654
+ },
655
+ {
656
+ "key": "percentage_of_actors",
657
+ "name": "percentage_of_actors",
658
+ "value": 0
659
+ },
660
+ {
661
+ "key": "percentage_of_time",
662
+ "name": "percentage_of_time",
663
+ "value": 0
664
+ }
665
+ ]
666
+ }
667
+ ```
668
+ ### Enable Percentage of Time
669
+
670
+ **URL**
671
+
672
+ `POST /api/v1/features/{feature_name}/percentage_of_time`
673
+
674
+ **Parameters**
675
+
676
+ * `feature_name` - The name of the feature
677
+
678
+ * `percentage` - The percentage of time to enable
679
+
680
+ **Request**
681
+
682
+ ```
683
+ curl -X POST -d "percentage=20" http://example.com/flipper-api/api/v1/features/reports/percentage_of_time
684
+ ```
685
+
686
+ **Response**
687
+
688
+ Successful enabling of a percentage of time will return a 200 HTTP status and the feature object as the response body.
689
+
690
+ ```json
691
+ {
692
+ "key": "reports",
693
+ "state": "conditional",
694
+ "gates": [
695
+ {
696
+ "key": "boolean",
697
+ "name": "boolean",
698
+ "value": false
699
+ },
700
+ {
701
+ "key": "groups",
702
+ "name": "group",
703
+ "value": []
704
+ },
705
+ {
706
+ "key": "actors",
707
+ "name": "actor",
708
+ "value": []
709
+ },
710
+ {
711
+ "key": "percentage_of_actors",
712
+ "name": "percentage_of_actors",
713
+ "value": 0
714
+ },
715
+ {
716
+ "key": "percentage_of_time",
717
+ "name": "percentage_of_time",
718
+ "value": 20
719
+ }
720
+ ]
721
+ }
722
+ ```
723
+ ### Disable Percentage of Time
724
+
725
+ **URL**
726
+
727
+ `DELETE /api/v1/features/{feature_name}/percentage_of_time`
728
+
729
+ **Parameters**
730
+
731
+ * `feature_name` - The name of the feature
732
+
733
+ **Request**
734
+
735
+ ```
736
+ curl -X DELETE http://example.com/flipper-api/api/v1/features/reports/percentage_of_time
737
+ ```
738
+
739
+ **Response**
740
+
741
+ Successful disabling of a percentage of time will set the percentage to 0 and return a 200 HTTP status and the feature object as the response body.
742
+
743
+ ```json
744
+ {
745
+ "key": "reports",
746
+ "state": "off",
747
+ "gates": [
748
+ {
749
+ "key": "boolean",
750
+ "name": "boolean",
751
+ "value": false
752
+ },
753
+ {
754
+ "key": "groups",
755
+ "name": "group",
756
+ "value": []
757
+ },
758
+ {
759
+ "key": "actors",
760
+ "name": "actor",
761
+ "value": []
762
+ },
763
+ {
764
+ "key": "percentage_of_actors",
765
+ "name": "percentage_of_actors",
766
+ "value": 0
767
+ },
768
+ {
769
+ "key": "percentage_of_time",
770
+ "name": "percentage_of_time",
771
+ "value": 0
772
+ }
773
+ ]
774
+ }
775
+ ```