fluentd-ui 1.0.0.alpha.3 → 1.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd-ui might be problematic. Click here for more details.

Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +45 -0
  3. data/.travis.yml +17 -3
  4. data/Gemfile +2 -2
  5. data/Gemfile.lock +62 -63
  6. data/README.md +11 -0
  7. data/app/controllers/api/config_definitions_controller.rb +41 -17
  8. data/app/controllers/concerns/setting_concern.rb +0 -4
  9. data/app/controllers/fluentd/settings/in_tail_controller.rb +1 -1
  10. data/app/form_builders/fluentd_form_builder.rb +18 -6
  11. data/app/javascript/packs/application.js +20 -20
  12. data/app/javascript/packs/aws_credential.js +61 -0
  13. data/app/javascript/packs/codemirror.js +33 -37
  14. data/app/javascript/packs/config_field.js +93 -0
  15. data/app/javascript/packs/fluent_log.js +10 -9
  16. data/app/javascript/packs/in_tail_parse.js +77 -76
  17. data/app/javascript/packs/nested_settings.js +17 -16
  18. data/app/javascript/packs/notification.js +14 -14
  19. data/app/javascript/packs/out_forward_setting.js +14 -0
  20. data/app/javascript/packs/out_s3_setting.js +14 -0
  21. data/app/javascript/packs/owned_plugin_form.js +60 -57
  22. data/app/javascript/packs/parser_multiline_form.js +15 -14
  23. data/app/javascript/packs/plugin_setting.js +13 -13
  24. data/app/javascript/packs/settings.js +23 -18
  25. data/app/javascript/packs/transport_config.js +62 -0
  26. data/app/javascript/packs/transport_section.js +72 -0
  27. data/app/javascript/packs/treeview.js +11 -10
  28. data/app/models/concerns/fluentd/setting/configurable.rb +1 -3
  29. data/app/models/concerns/fluentd/setting/plugin.rb +29 -1
  30. data/app/models/concerns/fluentd/setting/plugin_config.rb +32 -8
  31. data/app/models/concerns/fluentd/setting/plugin_parameter.rb +74 -21
  32. data/app/models/concerns/fluentd/setting/registry_loader.rb +38 -0
  33. data/app/models/concerns/fluentd/setting/section_config.rb +52 -0
  34. data/app/models/concerns/fluentd/setting/section_parser.rb +25 -18
  35. data/app/models/fluentd/setting/in_forward.rb +29 -10
  36. data/app/models/fluentd/setting/in_syslog.rb +6 -6
  37. data/app/models/fluentd/setting/out_forward.rb +15 -3
  38. data/app/models/fluentd/setting/out_mongo.rb +0 -11
  39. data/app/models/fluentd/setting/out_s3.rb +17 -3
  40. data/app/models/fluentd/setting/out_tdlog.rb +3 -2
  41. data/app/models/fluentd/setting/section.rb +4 -0
  42. data/app/models/fluentd/setting/type/object.rb +17 -0
  43. data/app/views/fluentd/settings/in_forward/_form.html.haml +5 -3
  44. data/app/views/fluentd/settings/out_forward/_form.html.haml +18 -0
  45. data/app/views/fluentd/settings/out_s3/_form.html.haml +18 -0
  46. data/app/views/shared/settings/_form.html.haml +14 -10
  47. data/app/views/shared/settings/show.html.haml +2 -2
  48. data/app/views/shared/vue/_aws_credential.html.haml +22 -0
  49. data/app/views/shared/vue/_config_field.html.haml +49 -0
  50. data/app/views/shared/vue/_out_forward_setting.html.haml +13 -0
  51. data/app/views/shared/vue/_out_s3_setting.html.haml +19 -0
  52. data/app/views/shared/vue/_owned_plugin_form.html.haml +8 -48
  53. data/app/views/shared/vue/_transport_config.html.haml +20 -0
  54. data/app/views/shared/vue/_transport_section.html.haml +30 -0
  55. data/config/initializers/dig.rb +8 -0
  56. data/config/initializers/dummy_logger.rb +1 -0
  57. data/config/initializers/types.rb +2 -1
  58. data/config/locales/translation_ja.yml +2 -2
  59. data/fluentd-ui.gemspec +1 -0
  60. data/gemfiles/ruby2.2.gemfile +28 -0
  61. data/lib/dummy_logger.rb +13 -0
  62. data/lib/fluentd-ui/version.rb +1 -1
  63. data/package.json +5 -0
  64. data/spec/features/fluentd/setting/in_forward_spec.rb +1 -2
  65. data/spec/features/fluentd/setting/in_http_spec.rb +1 -2
  66. data/spec/features/fluentd/setting/in_monitor_agent_spec.rb +1 -2
  67. data/spec/features/fluentd/setting/out_forward_spec.rb +3 -5
  68. data/spec/features/fluentd/setting/out_stdout_spec.rb +1 -2
  69. data/spec/features/out_elasticsearch_spec.rb +3 -3
  70. data/spec/features/out_forward_spec.rb +6 -5
  71. data/spec/features/out_tdlog_spec.rb +3 -2
  72. data/spec/features/shared_examples/configurable_daemon_settings.rb +2 -2
  73. data/spec/features/source_and_output_spec.rb +2 -0
  74. data/spec/models/fluentd/setting/in_forward_spec.rb +65 -9
  75. data/spec/models/fluentd/setting/in_syslog_spec.rb +12 -7
  76. data/spec/models/fluentd/setting/in_tail_spec.rb +6 -0
  77. data/spec/models/fluentd/setting/out_mongo_spec.rb +2 -3
  78. data/spec/models/fluentd/setting/out_tdlog_spec.rb +1 -2
  79. data/spec/spec_helper.rb +5 -0
  80. data/yarn.lock +466 -12
  81. metadata +90 -39
  82. data/app/helpers/settings_helper.rb +0 -144
  83. data/app/models/concerns/fluentd/setting/section_validator.rb +0 -21
  84. data/app/views/fluentd/settings/_form.html.haml +0 -43
@@ -1,40 +1,41 @@
1
- 'use strict';
2
- import 'lodash/lodash';
1
+ /* global _ */
2
+ "use strict";
3
+ import "lodash/lodash";
3
4
  $(document).ready(()=> {
4
- var $firstSetting = $('.js-nested-column.js-multiple:first');
5
+ var $firstSetting = $(".js-nested-column.js-multiple:first");
5
6
 
6
7
  if ($firstSetting.length === 0) {
7
8
  return;
8
9
  }
9
10
 
10
11
  var counter = 0;
11
- $('.js-append', $firstSetting).on('click', function(ev){
12
+ $(".js-append", $firstSetting).on("click", function(ev){
12
13
  ev.preventDefault();
13
14
  var $new = $firstSetting.clone(true);
14
15
  counter++;
15
16
 
16
- var fields = $('input,select,textarea', $new);
17
+ var fields = $("input,select,textarea", $new);
17
18
  _.each(fields, function(elm){
18
19
  elm.name = elm.name.replace("0", counter);
19
20
  });
20
- $('label', $new).each(function(_, label){
21
+ $("label", $new).each(function(_, label){
21
22
  var $label = $(label);
22
- $label.attr('for', $label.attr('for').replace("0", counter));
23
+ $label.attr("for", $label.attr("for").replace("0", counter));
23
24
  });
24
25
 
25
- $('.js-remove', $new).show();
26
- $('.js-append', $new).hide();
26
+ $(".js-remove", $new).show();
27
+ $(".js-append", $new).hide();
27
28
  $new.appendTo($firstSetting.parent());
28
29
  });
29
30
 
30
- $('.js-remove').on('click', function(ev){
31
+ $(".js-remove").on("click", function(ev){
31
32
  ev.preventDefault();
32
- $(this).closest('.js-nested-column').remove();
33
+ $(this).closest(".js-nested-column").remove();
33
34
  });
34
35
 
35
- var $allSettings = $('.js-nested-column.js-multiple');
36
- $('.js-append', $allSettings).hide();
37
- $('.js-remove', $allSettings).show();
38
- $('.js-append', $firstSetting).show();
39
- $('.js-remove', $firstSetting).hide();
36
+ var $allSettings = $(".js-nested-column.js-multiple");
37
+ $(".js-append", $allSettings).hide();
38
+ $(".js-remove", $allSettings).show();
39
+ $(".js-append", $firstSetting).show();
40
+ $(".js-remove", $firstSetting).hide();
40
41
  });
@@ -1,13 +1,22 @@
1
- const POLLING_INTERVAL = 3 * 1000
2
- const POLLING_URL = "/polling/alerts"
1
+ const POLLING_INTERVAL = 3 * 1000;
2
+ const POLLING_URL = "/polling/alerts";
3
3
 
4
4
  $(document).ready(()=> {
5
- let alert = new Vue({
5
+ new Vue({
6
6
  el: "#vue-notification",
7
7
  data: {
8
8
  "alerts": []
9
9
  },
10
10
 
11
+ computed: {
12
+ alertsCount: {
13
+ get: function(){ return this.alerts.length; }
14
+ },
15
+ hasAlerts: {
16
+ get: function(){ return this.alertsCount > 0; }
17
+ }
18
+ },
19
+
11
20
  created: function(){
12
21
  let timer;
13
22
  let self = this;
@@ -30,25 +39,16 @@ $(document).ready(()=> {
30
39
  }
31
40
  });
32
41
  };
33
- window.addEventListener('focus', function(ev){
42
+ window.addEventListener("focus", function(_event){
34
43
  currentInterval = POLLING_INTERVAL;
35
44
  timer = setTimeout(fetch, currentInterval);
36
45
  }, false);
37
- window.addEventListener('blur', function(ev){
46
+ window.addEventListener("blur", function(_event){
38
47
  clearTimeout(timer);
39
48
  }, false);
40
49
  fetch();
41
50
  },
42
51
 
43
- computed: {
44
- alertsCount: {
45
- get: function(){ return this.alerts.length; }
46
- },
47
- hasAlerts: {
48
- get: function(){ return this.alertsCount > 0; }
49
- }
50
- },
51
-
52
52
  methods: {
53
53
  fetchAlertsData: function() {
54
54
  return new Promise(function(resolve, reject) {
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ import TransportConfig from "./transport_config";
4
+ import OwnedPluginForm from "./owned_plugin_form";
5
+
6
+ $(document).ready(() => {
7
+ new Vue({
8
+ el: "#out-forward-setting",
9
+ components: {
10
+ "transport-config": TransportConfig,
11
+ "owned-plugin-form": OwnedPluginForm,
12
+ }
13
+ });
14
+ });
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+
3
+ import OwnedPluginForm from "./owned_plugin_form";
4
+ import AwsCredential from "./aws_credential";
5
+
6
+ $(document).ready(() => {
7
+ new Vue({
8
+ el: "#out-s3-setting",
9
+ components: {
10
+ "owned-plugin-form": OwnedPluginForm,
11
+ "aws-credential": AwsCredential
12
+ }
13
+ });
14
+ });
@@ -1,16 +1,21 @@
1
- 'use strict'
1
+ /* global _ */
2
+ "use strict";
2
3
 
3
- import ParserMultilineForm from './parser_multiline_form'
4
+ import "lodash/lodash";
5
+ import ParserMultilineForm from "./parser_multiline_form";
6
+ import ConfigField from "./config_field";
4
7
 
5
8
  const OwnedPluginForm = {
6
9
  template: "#vue-owned-plugin-form",
7
10
  components: {
8
11
  "parser-multiline-form": ParserMultilineForm,
12
+ "config-field": ConfigField
9
13
  },
10
14
  props: [
11
15
  "id",
12
16
  "optionsJson",
13
17
  "initialPluginName",
18
+ "initialParamsJson",
14
19
  "pluginType",
15
20
  "pluginLabel"
16
21
  ],
@@ -18,46 +23,56 @@ const OwnedPluginForm = {
18
23
  return {
19
24
  pluginName: "",
20
25
  options: [],
26
+ initialParams: {},
21
27
  commonOptions: [],
22
28
  advancedOptions: [],
23
29
  expression: null,
24
30
  timeFormat: null,
25
31
  unwatchExpression: null,
26
32
  unwatchTimeFormat: null
27
- }
33
+ };
28
34
  },
29
35
 
30
36
  computed: {
31
37
  token: function() {
32
- return Rails.csrfToken()
38
+ return Rails.csrfToken();
33
39
  }
34
40
  },
35
41
 
36
42
  mounted: function() {
37
- this.options = JSON.parse(this.optionsJson)
38
- this.pluginName = this.initialPluginName
43
+ this.options = JSON.parse(this.optionsJson);
44
+ this.initialParams = JSON.parse(this.initialParamsJson || "{}");
45
+ this.pluginName = this.initialPluginName;
39
46
  this.$on("hook:updated", () => {
40
- console.log("hook:updated")
41
- $("[data-toggle=tooltip]").tooltip("dispose")
42
- $("[data-toggle=tooltip]").tooltip("enable")
43
- })
47
+ console.log("hook:updated");
48
+ this.$nextTick(() => {
49
+ $("[data-toggle=tooltip]").tooltip("dispose");
50
+ $("[data-toggle=tooltip]").tooltip("enable");
51
+ });
52
+ });
44
53
  this.$once("data-loaded", () => {
45
- this.updateSection()
46
- })
47
- this.$emit("data-loaded")
54
+ this.updateSection();
55
+ });
56
+ this.$emit("data-loaded");
48
57
  },
49
58
 
50
59
  methods: {
51
60
  onChange: function() {
52
- this.updateSection()
61
+ this.updateSection();
53
62
  if (this.pluginType === "parse") {
54
- this.$emit("change-plugin-name", this.pluginName)
63
+ this.$emit("change-plugin-name", this.pluginName);
55
64
  }
56
65
  },
57
66
 
58
67
  onChangeFormats: function(data) {
59
- console.log("ownedPluginForm:onChangeFormats", data)
60
- this.$emit("change-formats", data)
68
+ console.log("ownedPluginForm:onChangeFormats", data);
69
+ this.$emit("change-formats", data);
70
+ },
71
+
72
+ onChangeParseConfig: function(data) {
73
+ console.log("ownedPluginForm:onChangeParseConfig", data);
74
+ this.expression = data.expression;
75
+ this.timeFormat = data.timeFormat;
61
76
  },
62
77
 
63
78
  updateSection: function() {
@@ -65,77 +80,65 @@ const OwnedPluginForm = {
65
80
  method: "GET",
66
81
  url: "/api/config_definitions",
67
82
  headers: {
68
- 'X-CSRF-Token': this.token
83
+ "X-CSRF-Token": this.token
69
84
  },
70
85
  data: {
71
86
  type: this.pluginType,
72
87
  name: this.pluginName
73
88
  }
74
89
  }).then((data) => {
75
- this.commonOptions = data.commonOptions
76
- let foundExpression = false
77
- let foundTimeFormat = false
90
+ this.commonOptions = data.commonOptions;
91
+ let foundExpression = false;
92
+ let foundTimeFormat = false;
78
93
  _.each(this.commonOptions, (option) => {
79
94
  if (option.name === "expression") {
80
- foundExpression = true
81
- this.expression = option.default
95
+ foundExpression = true;
96
+ this.expression = option.default;
82
97
  this.unwatchExpression = this.$watch("expression", (newValue, oldValue) => {
83
- console.log(newValue)
98
+ console.log(newValue);
84
99
  this.$emit("change-parse-config", {
85
100
  "expression": this.expression,
86
101
  "time_format": this.timeFormat
87
- })
88
- })
102
+ });
103
+ });
89
104
  }
90
105
  if (option.name === "time_format") {
91
- foundTimeFormat = true
92
- this.timeFormat = option.default
106
+ foundTimeFormat = true;
107
+ this.timeFormat = option.default;
108
+ console.log(this.timeFormat);
93
109
  this.unwatchTimeFormat = this.$watch("timeFormat", (newValue, oldValue) => {
94
- console.log({"watch time_format": newValue})
110
+ console.log({"watch time_format": newValue});
95
111
  this.$emit("change-parse-config", {
96
112
  "expression": this.expression,
97
113
  "time_format": this.timeFormat
98
- })
99
- })
114
+ });
115
+ });
100
116
  }
101
117
 
102
118
  if (!foundExpression && this.unwatchExpression) {
103
- this.expression = null
104
- this.unwatchExpression()
105
- this.unwatchExpression = null
119
+ this.expression = null;
120
+ this.unwatchExpression();
121
+ this.unwatchExpression = null;
106
122
  }
107
123
  if (!foundTimeFormat && this.unwatchTimeFormat) {
108
- this.timeFormat = null
109
- this.unwatchTimeFormat()
110
- this.unwatchTimeFormat = null
124
+ this.timeFormat = null;
125
+ this.unwatchTimeFormat();
126
+ this.unwatchTimeFormat = null;
111
127
  }
112
- })
113
- })
128
+ });
129
+ });
114
130
  },
115
131
 
116
132
  selectId: function(pluginType) {
117
- return `setting_${pluginType}_type`
133
+ return `setting_${pluginType}_type`;
118
134
  },
119
135
  selectClass: function(pluginType) {
120
- return `${pluginType} form-control`
136
+ return `${pluginType} form-control`;
121
137
  },
122
138
  selectName: function(pluginType) {
123
- return `setting[${pluginType}_type]`
124
- },
125
- inputId: function(pluginType, option) {
126
- return `setting_${pluginType}_0__${option.name}`
127
- },
128
- inputName: function(pluginType, option) {
129
- return `setting[${pluginType}[0]][${option.name}]`
130
- },
131
- checked: function(checked) {
132
- if (checked === true || checked === "true") {
133
- return "checked"
134
- } else {
135
- return ""
136
- }
139
+ return `setting[${pluginType}_type]`;
137
140
  }
138
141
  }
139
- }
142
+ };
140
143
 
141
- export { OwnedPluginForm as default }
144
+ export { OwnedPluginForm as default };
@@ -1,5 +1,6 @@
1
- 'use strict'
2
- import 'lodash/lodash'
1
+ /* global _ */
2
+ "use strict";
3
+ import "lodash/lodash";
3
4
  const ParserMultilineForm = {
4
5
  template: "#vue-parser-multiline-form",
5
6
  props: [
@@ -12,40 +13,40 @@ const ParserMultilineForm = {
12
13
  formatFirstline: "",
13
14
  formats: "",
14
15
  formatFirstlineDesc: ""
15
- }
16
+ };
16
17
  },
17
18
 
18
19
  watch: {
19
20
  "formatFirstLine": function(newValue, oldValue) {
20
- console.log(`watch formatFirstLine: ${newValue}`)
21
+ console.log(`watch formatFirstLine: ${newValue}`);
21
22
  this.$emit("change-formats", {
22
23
  "format_firstline": this.formatFirstline,
23
24
  "formats": this.formats
24
- })
25
+ });
25
26
  },
26
27
  "formats": function(newValue, oldValue) {
27
- console.log(`watch formats: ${newValue}`)
28
+ console.log(`watch formats: ${newValue}`);
28
29
  this.$emit("change-formats", {
29
30
  "format_firstline": this.formatFirstline,
30
31
  "formats": this.formats
31
- })
32
+ });
32
33
  },
33
34
  "commonOptions": function(newValue, oldValue) {
34
35
  const option = _.find(newValue, (o) => {
35
- return o.name === "format_firstline"
36
- })
37
- this.formatFirstlineDesc = option.desc
36
+ return o.name === "format_firstline";
37
+ });
38
+ this.formatFirstlineDesc = option.desc;
38
39
  }
39
40
  },
40
41
 
41
42
  methods: {
42
43
  textareaId: function(pluginType) {
43
- return `setting_${pluginType}_0__formats`
44
+ return `setting_${pluginType}_0__formats`;
44
45
  },
45
46
  textareaName: function(pluginType) {
46
- return `setting[${pluginType}[0]][formats]`
47
+ return `setting[${pluginType}[0]][formats]`;
47
48
  }
48
49
  }
49
- }
50
+ };
50
51
 
51
- export { ParserMultilineForm as default }
52
+ export { ParserMultilineForm as default };
@@ -1,19 +1,19 @@
1
- 'use strict'
2
- import 'lodash/lodash'
3
- import 'popper.js/dist/popper'
4
- import 'bootstrap/dist/js/bootstrap'
5
- import OwnedPluginForm from './owned_plugin_form'
1
+ "use strict";
2
+ import "lodash/lodash";
3
+ import "popper.js/dist/popper";
4
+ import "bootstrap/dist/js/bootstrap";
5
+ import OwnedPluginForm from "./owned_plugin_form";
6
6
 
7
- window.addEventListener('load', () => {
7
+ window.addEventListener("load", () => {
8
8
  new Vue({
9
- el: '#plugin-setting',
10
- data: () => {
11
- return {}
12
- },
9
+ el: "#plugin-setting",
13
10
  components: {
14
- 'owned-plugin-form': OwnedPluginForm
11
+ "owned-plugin-form": OwnedPluginForm
12
+ },
13
+ data: () => {
14
+ return {};
15
15
  },
16
16
  methods: {
17
17
  }
18
- })
19
- })
18
+ });
19
+ });
@@ -1,10 +1,15 @@
1
+ /* global _ */
2
+ "use strict";
3
+
4
+ import "lodash/lodash";
5
+
1
6
  $(document).ready(() => {
2
7
  const SettingSection = {
3
- template: '#vue-setting-section',
4
- props: ['id', 'content', 'type', 'name', 'arg'],
8
+ template: "#vue-setting-section",
9
+ props: ["id", "content", "type", "name", "arg"],
5
10
  data: function() {
6
11
  return {
7
- mode: 'default',
12
+ mode: "default",
8
13
  processing: false
9
14
  };
10
15
  },
@@ -13,24 +18,24 @@ $(document).ready(() => {
13
18
  },
14
19
  computed: {
15
20
  endpoint: function() {
16
- return '/api/settings/' + this.id;
21
+ return "/api/settings/" + this.id;
17
22
  }
18
23
  },
19
24
  methods: {
20
- onCancel: function(event) {
25
+ onCancel: function(_event) {
21
26
  this.initialState();
22
27
  },
23
- onEdit: function(ev) {
28
+ onEdit: function(_event) {
24
29
  this.mode = "edit";
25
30
  },
26
- onDelete: function(ev) {
31
+ onDelete: function(_event) {
27
32
  if (!confirm("really?")) {
28
33
  return;
29
34
  }
30
35
  this.destroy();
31
36
  },
32
- onSubmit: function(ev) {
33
- const token = document.getElementsByName("csrf-token")[0].getAttribute('content');
37
+ onSubmit: function(_event) {
38
+ const token = document.getElementsByName("csrf-token")[0].getAttribute("content");
34
39
  this.processing = true;
35
40
  this.content = $(`#${this.id} textarea.form-control`)[0].dataset.content;
36
41
  $.ajax({
@@ -42,7 +47,7 @@ $(document).ready(() => {
42
47
  content: this.content
43
48
  },
44
49
  headers: {
45
- 'X-CSRF-Token': token
50
+ "X-CSRF-Token": token
46
51
  }
47
52
  }).then((data)=> {
48
53
  _.each(data, function(v,k){
@@ -55,10 +60,10 @@ $(document).ready(() => {
55
60
  },
56
61
  initialState: function(){
57
62
  this.processing = false;
58
- this.mode = 'default';
63
+ this.mode = "default";
59
64
  },
60
65
  destroy: function(){
61
- const token = document.getElementsByName("csrf-token")[0].getAttribute('content');
66
+ const token = document.getElementsByName("csrf-token")[0].getAttribute("content");
62
67
  $.ajax({
63
68
  url: this.endpoint,
64
69
  method: "POST",
@@ -67,7 +72,7 @@ $(document).ready(() => {
67
72
  id: this.id
68
73
  },
69
74
  headers: {
70
- 'X-CSRF-Token': token
75
+ "X-CSRF-Token": token
71
76
  }
72
77
  }).then(()=> {
73
78
  this.$parent.update();
@@ -78,6 +83,9 @@ $(document).ready(() => {
78
83
 
79
84
  new Vue({
80
85
  el: "#vue-setting",
86
+ components: {
87
+ "setting-section": SettingSection
88
+ },
81
89
  data: function(){
82
90
  return {
83
91
  loaded: false,
@@ -91,10 +99,7 @@ $(document).ready(() => {
91
99
  mounted: function() {
92
100
  this.$nextTick(() => {
93
101
  this.update();
94
- })
95
- },
96
- components: {
97
- 'setting-section': SettingSection
102
+ });
98
103
  },
99
104
  methods: {
100
105
  update: function() {
@@ -119,4 +124,4 @@ $(document).ready(() => {
119
124
  }
120
125
  }
121
126
  });
122
- })
127
+ });