We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Generator does not add "ignore_above" property for field type: flattened
<!--
GitHub is reserved for bug reports and feature requests; it is not the place
for general questions. If you have a general question, please visit the
discuss forums: https://discuss.elastic.co/tag/ecs-elastic-common-schema.

Please fill in the following details to help us reproduce the bug:
-->

**Description of the problem including expected versus actual behavior**:
Generator does not write the property: `ignore_above` for field type: `flattened`


**Steps to reproduce**:

Create a field in a field set that is flattened with the ignore above key

```yaml

    - name: enrichment_data
      level: custom
      type: flattened
      description: >-
        Flattened object of enrichment data.
      ignore_above: 1024

```

Please include a *minimal* but *complete* recreation of the problem.

1. Created field set ecs file
2. Added field set to subset.yml
3. Generated artifacts
4. Examine generated component template with flattened field.

**Provide logs (if relevant)**:

Generated component template

```json
{
  "_meta": {
    "ecs_version": "8.8.0"
  },
  "template": {
    "mappings": {
      "properties": {
        "enrichment_data": {
          "type": "flattened"
        }
      }
    }
  }
}

```

**Any additional context:**
This key is necessary so elasticsearch does not try to index fields with large values. 

Note: Adding the property after the component template is generated works as a temporary workaround. 

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `scripts/generators/es_template.py`
Content:
```
1 # Licensed to Elasticsearch B.V. under one or more contributor
2 # license agreements. See the NOTICE file distributed with
3 # this work for additional information regarding copyright
4 # ownership. Elasticsearch B.V. licenses this file to you under
5 # the Apache License, Version 2.0 (the "License"); you may
6 # not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # 	http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing,
12 # software distributed under the License is distributed on an
13 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 # KIND, either express or implied.  See the License for the
15 # specific language governing permissions and limitations
16 # under the License.
17 
18 import json
19 import sys
20 from typing import (
21     Dict,
22     List,
23     Optional,
24     Union
25 )
26 
27 from os.path import join
28 
29 from generators import ecs_helpers
30 from _types import (
31     Field,
32     FieldNestedEntry,
33 )
34 
35 # Composable Template
36 
37 
38 def generate(
39     ecs_nested: Dict[str, FieldNestedEntry],
40     ecs_version: str,
41     out_dir: str,
42     mapping_settings_file: str,
43     template_settings_file: str
44 ) -> None:
45     """This generates all artifacts for the composable template approach"""
46     all_component_templates(ecs_nested, ecs_version, out_dir)
47     component_names = component_name_convention(ecs_version, ecs_nested)
48     save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file)
49 
50 
51 def save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file):
52     mappings_section = mapping_settings(mapping_settings_file)
53     template = template_settings(ecs_version, mappings_section, template_settings_file, component_names=component_names)
54 
55     filename = join(out_dir, "elasticsearch/composable/template.json")
56     save_json(filename, template)
57 
58 
59 def all_component_templates(
60     ecs_nested: Dict[str, FieldNestedEntry],
61     ecs_version: str,
62     out_dir: str
63 ) -> None:
64     """Generate one component template per field set"""
65     component_dir: str = join(out_dir, 'elasticsearch/composable/component')
66     ecs_helpers.make_dirs(component_dir)
67 
68     for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():
69         field_mappings = {}
70         for (flat_name, field) in fieldset['fields'].items():
71             name_parts = flat_name.split('.')
72             dict_add_nested(field_mappings, name_parts, entry_for(field))
73 
74         save_component_template(fieldset_name, field['level'], ecs_version, component_dir, field_mappings)
75 
76 
77 def save_component_template(
78     template_name: str,
79     field_level: str,
80     ecs_version: str,
81     out_dir: str,
82     field_mappings: Dict
83 ) -> None:
84     filename: str = join(out_dir, template_name) + ".json"
85     reference_url: str = "https://www.elastic.co/guide/en/ecs/current/ecs-{}.html".format(template_name)
86 
87     template: Dict = {
88         'template': {'mappings': {'properties': field_mappings}},
89         '_meta': {
90             'ecs_version': ecs_version,
91         }
92     }
93 
94     """Only generate a documentation link for ECS fields"""
95     if (field_level != 'custom'):
96         template['_meta']['documentation'] = reference_url
97 
98     save_json(filename, template)
99 
100 
101 def component_name_convention(
102     ecs_version: str,
103     ecs_nested: Dict[str, FieldNestedEntry]
104 ) -> List[str]:
105     version: str = ecs_version.replace('+', '-')
106     names: List[str] = []
107     for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():
108         names.append("ecs_{}_{}".format(version, fieldset_name.lower()))
109     return names
110 
111 
112 def candidate_components(ecs_nested: Dict[str, FieldNestedEntry]) -> Dict[str, FieldNestedEntry]:
113     """Returns same structure as ecs_nested, but skips all field sets with reusable.top_level: False"""
114     components: Dict[str, FieldNestedEntry] = {}
115     for (fieldset_name, fieldset) in ecs_nested.items():
116         if fieldset.get('reusable', None):
117             if not fieldset['reusable']['top_level']:
118                 continue
119         components[fieldset_name] = fieldset
120     return components
121 
122 
123 # Legacy template
124 
125 
126 def generate_legacy(
127     ecs_flat: Dict[str, Field],
128     ecs_version: str,
129     out_dir: str,
130     mapping_settings_file: str,
131     template_settings_file: str
132 ) -> None:
133     """Generate the legacy index template"""
134     field_mappings = {}
135     for flat_name in sorted(ecs_flat):
136         field = ecs_flat[flat_name]
137         name_parts = flat_name.split('.')
138         dict_add_nested(field_mappings, name_parts, entry_for(field))
139 
140     mappings_section: Dict = mapping_settings(mapping_settings_file)
141     mappings_section['properties'] = field_mappings
142 
143     generate_legacy_template_version(ecs_version, mappings_section, out_dir, template_settings_file)
144 
145 
146 def generate_legacy_template_version(
147     ecs_version: str,
148     mappings_section: Dict,
149     out_dir: str,
150     template_settings_file: str
151 ) -> None:
152     ecs_helpers.make_dirs(join(out_dir, 'elasticsearch', "legacy"))
153     template: Dict = template_settings(ecs_version, mappings_section, template_settings_file, is_legacy=True)
154 
155     filename: str = join(out_dir, "elasticsearch/legacy/template.json")
156     save_json(filename, template)
157 
158 
159 # Common helpers
160 
161 
162 def dict_add_nested(
163     dct: Dict,
164     name_parts: List[str],
165     value: Dict
166 ) -> None:
167     current_nesting: str = name_parts[0]
168     rest_name_parts: List[str] = name_parts[1:]
169     if len(rest_name_parts) > 0:
170         dct.setdefault(current_nesting, {})
171         dct[current_nesting].setdefault('properties', {})
172 
173         dict_add_nested(
174             dct[current_nesting]['properties'],
175             rest_name_parts,
176             value)
177 
178     else:
179         if current_nesting in dct and 'type' in value and 'object' == value['type']:
180             return
181         dct[current_nesting] = value
182 
183 
184 def entry_for(field: Field) -> Dict:
185     field_entry: Dict = {'type': field['type']}
186     try:
187         if field['type'] == 'object' or field['type'] == 'nested':
188             if 'enabled' in field and not field['enabled']:
189                 ecs_helpers.dict_copy_existing_keys(field, field_entry, ['enabled'])
190         # the index field is only valid for field types that are not object and nested
191         elif 'index' in field and not field['index']:
192             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['index', 'doc_values'])
193 
194         if field['type'] == 'keyword':
195             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above'])
196         elif field['type'] == 'constant_keyword':
197             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['value'])
198         elif field['type'] == 'text':
199             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['norms'])
200         elif field['type'] == 'alias':
201             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['path'])
202         elif field['type'] == 'scaled_float':
203             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['scaling_factor'])
204 
205         if 'multi_fields' in field:
206             field_entry['fields'] = {}
207             for mf in field['multi_fields']:
208                 mf_type = mf['type']
209                 mf_entry = {'type': mf_type}
210                 if mf_type == 'keyword':
211                     ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['normalizer', 'ignore_above'])
212                 elif mf_type == 'text':
213                     ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['norms', 'analyzer'])
214                 if 'parameters' in mf:
215                     mf_entry.update(mf['parameters'])
216                 field_entry['fields'][mf['name']] = mf_entry
217 
218         if 'parameters' in field:
219             field_entry.update(field['parameters'])
220 
221     except KeyError as ex:
222         print("Exception {} occurred for field {}".format(ex, field))
223         raise ex
224     return field_entry
225 
226 
227 def mapping_settings(mapping_settings_file: str) -> Dict:
228     if mapping_settings_file:
229         with open(mapping_settings_file) as f:
230             mappings = json.load(f)
231     else:
232         mappings = default_mapping_settings()
233     return mappings
234 
235 
236 def template_settings(
237     ecs_version: str,
238     mappings_section: Dict,
239     template_settings_file: Union[str, None],
240     is_legacy: Optional[bool] = False,
241     component_names: Optional[List[str]] = None
242 ) -> Dict:
243     if template_settings_file:
244         with open(template_settings_file) as f:
245             template = json.load(f)
246     else:
247         if is_legacy:
248             template = default_legacy_template_settings(ecs_version)
249         else:
250             template = default_template_settings(ecs_version)
251 
252     finalize_template(template, ecs_version, is_legacy, mappings_section, component_names)
253 
254     return template
255 
256 
257 def finalize_template(
258     template: Dict,
259     ecs_version: str,
260     is_legacy: bool,
261     mappings_section: Dict,
262     component_names: List[str]
263 ) -> None:
264     if is_legacy:
265         if mappings_section:
266             template['mappings'] = mappings_section
267 
268             # _meta can't be at template root in legacy templates, so moving back to mappings section
269             # if present
270             if '_meta' in template:
271                 mappings_section['_meta'] = template.pop('_meta')
272 
273     else:
274         template['template']['mappings'] = mappings_section
275         template['composed_of'] = component_names
276         template['_meta'] = {
277             "ecs_version": ecs_version,
278             "description": "Sample composable template that includes all ECS fields"
279         }
280 
281 
282 def save_json(file: str, data: Dict) -> None:
283     open_mode = "wb"
284     if sys.version_info >= (3, 0):
285         open_mode = "w"
286     with open(file, open_mode) as jsonfile:
287         json.dump(data, jsonfile, indent=2, sort_keys=True)
288         jsonfile.write('\n')
289 
290 
291 def default_template_settings(ecs_version: str) -> Dict:
292     return {
293         "index_patterns": ["try-ecs-*"],
294         "_meta": {
295             "ecs_version": ecs_version,
296             "description": "Sample composable template that includes all ECS fields"
297         },
298         "priority": 1,  # Very low, as this is a sample template
299         "template": {
300             "settings": {
301                 "index": {
302                     "codec": "best_compression",
303                     "mapping": {
304                         "total_fields": {
305                             "limit": 2000
306                         }
307                     }
308                 }
309             },
310         }
311     }
312 
313 
314 def default_legacy_template_settings(ecs_version: str) -> Dict:
315     return {
316         "index_patterns": ["try-ecs-*"],
317         "_meta": {"version": ecs_version},
318         "order": 1,
319         "settings": {
320             "index": {
321                 "mapping": {
322                     "total_fields": {
323                         "limit": 10000
324                     }
325                 },
326                 "refresh_interval": "5s"
327             }
328         }
329     }
330 
331 
332 def default_mapping_settings() -> Dict:
333     return {
334         "date_detection": False,
335         "dynamic_templates": [
336             {
337                 "strings_as_keyword": {
338                     "mapping": {
339                         "ignore_above": 1024,
340                         "type": "keyword"
341                     },
342                     "match_mapping_type": "string"
343                 }
344             }
345         ]
346     }
347
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/scripts/generators/es_template.py b/scripts/generators/es_template.py
--- a/scripts/generators/es_template.py
+++ b/scripts/generators/es_template.py
@@ -191,7 +191,7 @@
         elif 'index' in field and not field['index']:
             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['index', 'doc_values'])
 
-        if field['type'] == 'keyword':
+        if field['type'] == 'keyword' or field['type'] == 'flattened':
             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above'])
         elif field['type'] == 'constant_keyword':
             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['value'])

{"golden_diff": "diff --git a/scripts/generators/es_template.py b/scripts/generators/es_template.py\n--- a/scripts/generators/es_template.py\n+++ b/scripts/generators/es_template.py\n@@ -191,7 +191,7 @@\n         elif 'index' in field and not field['index']:\n             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['index', 'doc_values'])\n \n-        if field['type'] == 'keyword':\n+        if field['type'] == 'keyword' or field['type'] == 'flattened':\n             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above'])\n         elif field['type'] == 'constant_keyword':\n             ecs_helpers.dict_copy_existing_keys(field, field_entry, ['value'])\n", "issue": "Generator does not add \"ignore_above\" property for field type: flattened\n<!--\r\nGitHub is reserved for bug reports and feature requests; it is not the place\r\nfor general questions. If you have a general question, please visit the\r\ndiscuss forums: https://discuss.elastic.co/tag/ecs-elastic-common-schema.\r\n\r\nPlease fill in the following details to help us reproduce the bug:\r\n-->\r\n\r\n**Description of the problem including expected versus actual behavior**:\r\nGenerator does not write the property: `ignore_above` for field type: `flattened`\r\n\r\n\r\n**Steps to reproduce**:\r\n\r\nCreate a field in a field set that is flattened with the ignore above key\r\n\r\n```yaml\r\n\r\n    - name: enrichment_data\r\n      level: custom\r\n      type: flattened\r\n      description: >-\r\n        Flattened object of enrichment data.\r\n      ignore_above: 1024\r\n\r\n```\r\n\r\nPlease include a *minimal* but *complete* recreation of the problem.\r\n\r\n1. Created field set ecs file\r\n2. Added field set to subset.yml\r\n3. Generated artifacts\r\n4. Examine generated component template with flattened field.\r\n\r\n**Provide logs (if relevant)**:\r\n\r\nGenerated component template\r\n\r\n```json\r\n{\r\n  \"_meta\": {\r\n    \"ecs_version\": \"8.8.0\"\r\n  },\r\n  \"template\": {\r\n    \"mappings\": {\r\n      \"properties\": {\r\n        \"enrichment_data\": {\r\n          \"type\": \"flattened\"\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n```\r\n\r\n**Any additional context:**\r\nThis key is necessary so elasticsearch does not try to index fields with large values. \r\n\r\nNote: Adding the property after the component template is generated works as a temporary workaround. \n", "before_files": [{"content": "# Licensed to Elasticsearch B.V. under one or more contributor\n# license agreements. See the NOTICE file distributed with\n# this work for additional information regarding copyright\n# ownership. Elasticsearch B.V. licenses this file to you under\n# the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# \thttp://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport json\nimport sys\nfrom typing import (\n    Dict,\n    List,\n    Optional,\n    Union\n)\n\nfrom os.path import join\n\nfrom generators import ecs_helpers\nfrom _types import (\n    Field,\n    FieldNestedEntry,\n)\n\n# Composable Template\n\n\ndef generate(\n    ecs_nested: Dict[str, FieldNestedEntry],\n    ecs_version: str,\n    out_dir: str,\n    mapping_settings_file: str,\n    template_settings_file: str\n) -> None:\n    \"\"\"This generates all artifacts for the composable template approach\"\"\"\n    all_component_templates(ecs_nested, ecs_version, out_dir)\n    component_names = component_name_convention(ecs_version, ecs_nested)\n    save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file)\n\n\ndef save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file):\n    mappings_section = mapping_settings(mapping_settings_file)\n    template = template_settings(ecs_version, mappings_section, template_settings_file, component_names=component_names)\n\n    filename = join(out_dir, \"elasticsearch/composable/template.json\")\n    save_json(filename, template)\n\n\ndef all_component_templates(\n    ecs_nested: Dict[str, FieldNestedEntry],\n    ecs_version: str,\n    out_dir: str\n) -> None:\n    \"\"\"Generate one component template per field set\"\"\"\n    component_dir: str = join(out_dir, 'elasticsearch/composable/component')\n    ecs_helpers.make_dirs(component_dir)\n\n    for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():\n        field_mappings = {}\n        for (flat_name, field) in fieldset['fields'].items():\n            name_parts = flat_name.split('.')\n            dict_add_nested(field_mappings, name_parts, entry_for(field))\n\n        save_component_template(fieldset_name, field['level'], ecs_version, component_dir, field_mappings)\n\n\ndef save_component_template(\n    template_name: str,\n    field_level: str,\n    ecs_version: str,\n    out_dir: str,\n    field_mappings: Dict\n) -> None:\n    filename: str = join(out_dir, template_name) + \".json\"\n    reference_url: str = \"https://www.elastic.co/guide/en/ecs/current/ecs-{}.html\".format(template_name)\n\n    template: Dict = {\n        'template': {'mappings': {'properties': field_mappings}},\n        '_meta': {\n            'ecs_version': ecs_version,\n        }\n    }\n\n    \"\"\"Only generate a documentation link for ECS fields\"\"\"\n    if (field_level != 'custom'):\n        template['_meta']['documentation'] = reference_url\n\n    save_json(filename, template)\n\n\ndef component_name_convention(\n    ecs_version: str,\n    ecs_nested: Dict[str, FieldNestedEntry]\n) -> List[str]:\n    version: str = ecs_version.replace('+', '-')\n    names: List[str] = []\n    for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():\n        names.append(\"ecs_{}_{}\".format(version, fieldset_name.lower()))\n    return names\n\n\ndef candidate_components(ecs_nested: Dict[str, FieldNestedEntry]) -> Dict[str, FieldNestedEntry]:\n    \"\"\"Returns same structure as ecs_nested, but skips all field sets with reusable.top_level: False\"\"\"\n    components: Dict[str, FieldNestedEntry] = {}\n    for (fieldset_name, fieldset) in ecs_nested.items():\n        if fieldset.get('reusable', None):\n            if not fieldset['reusable']['top_level']:\n                continue\n        components[fieldset_name] = fieldset\n    return components\n\n\n# Legacy template\n\n\ndef generate_legacy(\n    ecs_flat: Dict[str, Field],\n    ecs_version: str,\n    out_dir: str,\n    mapping_settings_file: str,\n    template_settings_file: str\n) -> None:\n    \"\"\"Generate the legacy index template\"\"\"\n    field_mappings = {}\n    for flat_name in sorted(ecs_flat):\n        field = ecs_flat[flat_name]\n        name_parts = flat_name.split('.')\n        dict_add_nested(field_mappings, name_parts, entry_for(field))\n\n    mappings_section: Dict = mapping_settings(mapping_settings_file)\n    mappings_section['properties'] = field_mappings\n\n    generate_legacy_template_version(ecs_version, mappings_section, out_dir, template_settings_file)\n\n\ndef generate_legacy_template_version(\n    ecs_version: str,\n    mappings_section: Dict,\n    out_dir: str,\n    template_settings_file: str\n) -> None:\n    ecs_helpers.make_dirs(join(out_dir, 'elasticsearch', \"legacy\"))\n    template: Dict = template_settings(ecs_version, mappings_section, template_settings_file, is_legacy=True)\n\n    filename: str = join(out_dir, \"elasticsearch/legacy/template.json\")\n    save_json(filename, template)\n\n\n# Common helpers\n\n\ndef dict_add_nested(\n    dct: Dict,\n    name_parts: List[str],\n    value: Dict\n) -> None:\n    current_nesting: str = name_parts[0]\n    rest_name_parts: List[str] = name_parts[1:]\n    if len(rest_name_parts) > 0:\n        dct.setdefault(current_nesting, {})\n        dct[current_nesting].setdefault('properties', {})\n\n        dict_add_nested(\n            dct[current_nesting]['properties'],\n            rest_name_parts,\n            value)\n\n    else:\n        if current_nesting in dct and 'type' in value and 'object' == value['type']:\n            return\n        dct[current_nesting] = value\n\n\ndef entry_for(field: Field) -> Dict:\n    field_entry: Dict = {'type': field['type']}\n    try:\n        if field['type'] == 'object' or field['type'] == 'nested':\n            if 'enabled' in field and not field['enabled']:\n                ecs_helpers.dict_copy_existing_keys(field, field_entry, ['enabled'])\n        # the index field is only valid for field types that are not object and nested\n        elif 'index' in field and not field['index']:\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['index', 'doc_values'])\n\n        if field['type'] == 'keyword':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above'])\n        elif field['type'] == 'constant_keyword':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['value'])\n        elif field['type'] == 'text':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['norms'])\n        elif field['type'] == 'alias':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['path'])\n        elif field['type'] == 'scaled_float':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['scaling_factor'])\n\n        if 'multi_fields' in field:\n            field_entry['fields'] = {}\n            for mf in field['multi_fields']:\n                mf_type = mf['type']\n                mf_entry = {'type': mf_type}\n                if mf_type == 'keyword':\n                    ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['normalizer', 'ignore_above'])\n                elif mf_type == 'text':\n                    ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['norms', 'analyzer'])\n                if 'parameters' in mf:\n                    mf_entry.update(mf['parameters'])\n                field_entry['fields'][mf['name']] = mf_entry\n\n        if 'parameters' in field:\n            field_entry.update(field['parameters'])\n\n    except KeyError as ex:\n        print(\"Exception {} occurred for field {}\".format(ex, field))\n        raise ex\n    return field_entry\n\n\ndef mapping_settings(mapping_settings_file: str) -> Dict:\n    if mapping_settings_file:\n        with open(mapping_settings_file) as f:\n            mappings = json.load(f)\n    else:\n        mappings = default_mapping_settings()\n    return mappings\n\n\ndef template_settings(\n    ecs_version: str,\n    mappings_section: Dict,\n    template_settings_file: Union[str, None],\n    is_legacy: Optional[bool] = False,\n    component_names: Optional[List[str]] = None\n) -> Dict:\n    if template_settings_file:\n        with open(template_settings_file) as f:\n            template = json.load(f)\n    else:\n        if is_legacy:\n            template = default_legacy_template_settings(ecs_version)\n        else:\n            template = default_template_settings(ecs_version)\n\n    finalize_template(template, ecs_version, is_legacy, mappings_section, component_names)\n\n    return template\n\n\ndef finalize_template(\n    template: Dict,\n    ecs_version: str,\n    is_legacy: bool,\n    mappings_section: Dict,\n    component_names: List[str]\n) -> None:\n    if is_legacy:\n        if mappings_section:\n            template['mappings'] = mappings_section\n\n            # _meta can't be at template root in legacy templates, so moving back to mappings section\n            # if present\n            if '_meta' in template:\n                mappings_section['_meta'] = template.pop('_meta')\n\n    else:\n        template['template']['mappings'] = mappings_section\n        template['composed_of'] = component_names\n        template['_meta'] = {\n            \"ecs_version\": ecs_version,\n            \"description\": \"Sample composable template that includes all ECS fields\"\n        }\n\n\ndef save_json(file: str, data: Dict) -> None:\n    open_mode = \"wb\"\n    if sys.version_info >= (3, 0):\n        open_mode = \"w\"\n    with open(file, open_mode) as jsonfile:\n        json.dump(data, jsonfile, indent=2, sort_keys=True)\n        jsonfile.write('\\n')\n\n\ndef default_template_settings(ecs_version: str) -> Dict:\n    return {\n        \"index_patterns\": [\"try-ecs-*\"],\n        \"_meta\": {\n            \"ecs_version\": ecs_version,\n            \"description\": \"Sample composable template that includes all ECS fields\"\n        },\n        \"priority\": 1,  # Very low, as this is a sample template\n        \"template\": {\n            \"settings\": {\n                \"index\": {\n                    \"codec\": \"best_compression\",\n                    \"mapping\": {\n                        \"total_fields\": {\n                            \"limit\": 2000\n                        }\n                    }\n                }\n            },\n        }\n    }\n\n\ndef default_legacy_template_settings(ecs_version: str) -> Dict:\n    return {\n        \"index_patterns\": [\"try-ecs-*\"],\n        \"_meta\": {\"version\": ecs_version},\n        \"order\": 1,\n        \"settings\": {\n            \"index\": {\n                \"mapping\": {\n                    \"total_fields\": {\n                        \"limit\": 10000\n                    }\n                },\n                \"refresh_interval\": \"5s\"\n            }\n        }\n    }\n\n\ndef default_mapping_settings() -> Dict:\n    return {\n        \"date_detection\": False,\n        \"dynamic_templates\": [\n            {\n                \"strings_as_keyword\": {\n                    \"mapping\": {\n                        \"ignore_above\": 1024,\n                        \"type\": \"keyword\"\n                    },\n                    \"match_mapping_type\": \"string\"\n                }\n            }\n        ]\n    }\n", "path": "scripts/generators/es_template.py"}], "after_files": [{"content": "# Licensed to Elasticsearch B.V. under one or more contributor\n# license agreements. See the NOTICE file distributed with\n# this work for additional information regarding copyright\n# ownership. Elasticsearch B.V. licenses this file to you under\n# the Apache License, Version 2.0 (the \"License\"); you may\n# not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# \thttp://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport json\nimport sys\nfrom typing import (\n    Dict,\n    List,\n    Optional,\n    Union\n)\n\nfrom os.path import join\n\nfrom generators import ecs_helpers\nfrom _types import (\n    Field,\n    FieldNestedEntry,\n)\n\n# Composable Template\n\n\ndef generate(\n    ecs_nested: Dict[str, FieldNestedEntry],\n    ecs_version: str,\n    out_dir: str,\n    mapping_settings_file: str,\n    template_settings_file: str\n) -> None:\n    \"\"\"This generates all artifacts for the composable template approach\"\"\"\n    all_component_templates(ecs_nested, ecs_version, out_dir)\n    component_names = component_name_convention(ecs_version, ecs_nested)\n    save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file)\n\n\ndef save_composable_template(ecs_version, component_names, out_dir, mapping_settings_file, template_settings_file):\n    mappings_section = mapping_settings(mapping_settings_file)\n    template = template_settings(ecs_version, mappings_section, template_settings_file, component_names=component_names)\n\n    filename = join(out_dir, \"elasticsearch/composable/template.json\")\n    save_json(filename, template)\n\n\ndef all_component_templates(\n    ecs_nested: Dict[str, FieldNestedEntry],\n    ecs_version: str,\n    out_dir: str\n) -> None:\n    \"\"\"Generate one component template per field set\"\"\"\n    component_dir: str = join(out_dir, 'elasticsearch/composable/component')\n    ecs_helpers.make_dirs(component_dir)\n\n    for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():\n        field_mappings = {}\n        for (flat_name, field) in fieldset['fields'].items():\n            name_parts = flat_name.split('.')\n            dict_add_nested(field_mappings, name_parts, entry_for(field))\n\n        save_component_template(fieldset_name, field['level'], ecs_version, component_dir, field_mappings)\n\n\ndef save_component_template(\n    template_name: str,\n    field_level: str,\n    ecs_version: str,\n    out_dir: str,\n    field_mappings: Dict\n) -> None:\n    filename: str = join(out_dir, template_name) + \".json\"\n    reference_url: str = \"https://www.elastic.co/guide/en/ecs/current/ecs-{}.html\".format(template_name)\n\n    template: Dict = {\n        'template': {'mappings': {'properties': field_mappings}},\n        '_meta': {\n            'ecs_version': ecs_version,\n        }\n    }\n\n    \"\"\"Only generate a documentation link for ECS fields\"\"\"\n    if (field_level != 'custom'):\n        template['_meta']['documentation'] = reference_url\n\n    save_json(filename, template)\n\n\ndef component_name_convention(\n    ecs_version: str,\n    ecs_nested: Dict[str, FieldNestedEntry]\n) -> List[str]:\n    version: str = ecs_version.replace('+', '-')\n    names: List[str] = []\n    for (fieldset_name, fieldset) in candidate_components(ecs_nested).items():\n        names.append(\"ecs_{}_{}\".format(version, fieldset_name.lower()))\n    return names\n\n\ndef candidate_components(ecs_nested: Dict[str, FieldNestedEntry]) -> Dict[str, FieldNestedEntry]:\n    \"\"\"Returns same structure as ecs_nested, but skips all field sets with reusable.top_level: False\"\"\"\n    components: Dict[str, FieldNestedEntry] = {}\n    for (fieldset_name, fieldset) in ecs_nested.items():\n        if fieldset.get('reusable', None):\n            if not fieldset['reusable']['top_level']:\n                continue\n        components[fieldset_name] = fieldset\n    return components\n\n\n# Legacy template\n\n\ndef generate_legacy(\n    ecs_flat: Dict[str, Field],\n    ecs_version: str,\n    out_dir: str,\n    mapping_settings_file: str,\n    template_settings_file: str\n) -> None:\n    \"\"\"Generate the legacy index template\"\"\"\n    field_mappings = {}\n    for flat_name in sorted(ecs_flat):\n        field = ecs_flat[flat_name]\n        name_parts = flat_name.split('.')\n        dict_add_nested(field_mappings, name_parts, entry_for(field))\n\n    mappings_section: Dict = mapping_settings(mapping_settings_file)\n    mappings_section['properties'] = field_mappings\n\n    generate_legacy_template_version(ecs_version, mappings_section, out_dir, template_settings_file)\n\n\ndef generate_legacy_template_version(\n    ecs_version: str,\n    mappings_section: Dict,\n    out_dir: str,\n    template_settings_file: str\n) -> None:\n    ecs_helpers.make_dirs(join(out_dir, 'elasticsearch', \"legacy\"))\n    template: Dict = template_settings(ecs_version, mappings_section, template_settings_file, is_legacy=True)\n\n    filename: str = join(out_dir, \"elasticsearch/legacy/template.json\")\n    save_json(filename, template)\n\n\n# Common helpers\n\n\ndef dict_add_nested(\n    dct: Dict,\n    name_parts: List[str],\n    value: Dict\n) -> None:\n    current_nesting: str = name_parts[0]\n    rest_name_parts: List[str] = name_parts[1:]\n    if len(rest_name_parts) > 0:\n        dct.setdefault(current_nesting, {})\n        dct[current_nesting].setdefault('properties', {})\n\n        dict_add_nested(\n            dct[current_nesting]['properties'],\n            rest_name_parts,\n            value)\n\n    else:\n        if current_nesting in dct and 'type' in value and 'object' == value['type']:\n            return\n        dct[current_nesting] = value\n\n\ndef entry_for(field: Field) -> Dict:\n    field_entry: Dict = {'type': field['type']}\n    try:\n        if field['type'] == 'object' or field['type'] == 'nested':\n            if 'enabled' in field and not field['enabled']:\n                ecs_helpers.dict_copy_existing_keys(field, field_entry, ['enabled'])\n        # the index field is only valid for field types that are not object and nested\n        elif 'index' in field and not field['index']:\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['index', 'doc_values'])\n\n        if field['type'] == 'keyword' or field['type'] == 'flattened':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['ignore_above'])\n        elif field['type'] == 'constant_keyword':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['value'])\n        elif field['type'] == 'text':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['norms'])\n        elif field['type'] == 'alias':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['path'])\n        elif field['type'] == 'scaled_float':\n            ecs_helpers.dict_copy_existing_keys(field, field_entry, ['scaling_factor'])\n\n        if 'multi_fields' in field:\n            field_entry['fields'] = {}\n            for mf in field['multi_fields']:\n                mf_type = mf['type']\n                mf_entry = {'type': mf_type}\n                if mf_type == 'keyword':\n                    ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['normalizer', 'ignore_above'])\n                elif mf_type == 'text':\n                    ecs_helpers.dict_copy_existing_keys(mf, mf_entry, ['norms', 'analyzer'])\n                if 'parameters' in mf:\n                    mf_entry.update(mf['parameters'])\n                field_entry['fields'][mf['name']] = mf_entry\n\n        if 'parameters' in field:\n            field_entry.update(field['parameters'])\n\n    except KeyError as ex:\n        print(\"Exception {} occurred for field {}\".format(ex, field))\n        raise ex\n    return field_entry\n\n\ndef mapping_settings(mapping_settings_file: str) -> Dict:\n    if mapping_settings_file:\n        with open(mapping_settings_file) as f:\n            mappings = json.load(f)\n    else:\n        mappings = default_mapping_settings()\n    return mappings\n\n\ndef template_settings(\n    ecs_version: str,\n    mappings_section: Dict,\n    template_settings_file: Union[str, None],\n    is_legacy: Optional[bool] = False,\n    component_names: Optional[List[str]] = None\n) -> Dict:\n    if template_settings_file:\n        with open(template_settings_file) as f:\n            template = json.load(f)\n    else:\n        if is_legacy:\n            template = default_legacy_template_settings(ecs_version)\n        else:\n            template = default_template_settings(ecs_version)\n\n    finalize_template(template, ecs_version, is_legacy, mappings_section, component_names)\n\n    return template\n\n\ndef finalize_template(\n    template: Dict,\n    ecs_version: str,\n    is_legacy: bool,\n    mappings_section: Dict,\n    component_names: List[str]\n) -> None:\n    if is_legacy:\n        if mappings_section:\n            template['mappings'] = mappings_section\n\n            # _meta can't be at template root in legacy templates, so moving back to mappings section\n            # if present\n            if '_meta' in template:\n                mappings_section['_meta'] = template.pop('_meta')\n\n    else:\n        template['template']['mappings'] = mappings_section\n        template['composed_of'] = component_names\n        template['_meta'] = {\n            \"ecs_version\": ecs_version,\n            \"description\": \"Sample composable template that includes all ECS fields\"\n        }\n\n\ndef save_json(file: str, data: Dict) -> None:\n    open_mode = \"wb\"\n    if sys.version_info >= (3, 0):\n        open_mode = \"w\"\n    with open(file, open_mode) as jsonfile:\n        json.dump(data, jsonfile, indent=2, sort_keys=True)\n        jsonfile.write('\\n')\n\n\ndef default_template_settings(ecs_version: str) -> Dict:\n    return {\n        \"index_patterns\": [\"try-ecs-*\"],\n        \"_meta\": {\n            \"ecs_version\": ecs_version,\n            \"description\": \"Sample composable template that includes all ECS fields\"\n        },\n        \"priority\": 1,  # Very low, as this is a sample template\n        \"template\": {\n            \"settings\": {\n                \"index\": {\n                    \"codec\": \"best_compression\",\n                    \"mapping\": {\n                        \"total_fields\": {\n                            \"limit\": 2000\n                        }\n                    }\n                }\n            },\n        }\n    }\n\n\ndef default_legacy_template_settings(ecs_version: str) -> Dict:\n    return {\n        \"index_patterns\": [\"try-ecs-*\"],\n        \"_meta\": {\"version\": ecs_version},\n        \"order\": 1,\n        \"settings\": {\n            \"index\": {\n                \"mapping\": {\n                    \"total_fields\": {\n                        \"limit\": 10000\n                    }\n                },\n                \"refresh_interval\": \"5s\"\n            }\n        }\n    }\n\n\ndef default_mapping_settings() -> Dict:\n    return {\n        \"date_detection\": False,\n        \"dynamic_templates\": [\n            {\n                \"strings_as_keyword\": {\n                    \"mapping\": {\n                        \"ignore_above\": 1024,\n                        \"type\": \"keyword\"\n                    },\n                    \"match_mapping_type\": \"string\"\n                }\n            }\n        ]\n    }\n", "path": "scripts/generators/es_template.py"}]}