Skip to content

Commit

Permalink
Makefile: add unite-api, Readme: Brought up to date (#4)
Browse files Browse the repository at this point in the history
-
  • Loading branch information
sabudilovskiy authored Apr 2, 2023
1 parent bc5131f commit e4837f9
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ compile_commands.json
cmake-build-*
Testing/
configs/static_config.yaml
Makefile.local
Makefile.local
merged_api.yaml
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ format-cpp:

# Format the sources
.PHONY: format-all
format: format-cpp
format-all: format-cpp
@find tests -name '*.py' -type f | xargs autopep8 -i

# Check format the sources
Expand Down Expand Up @@ -120,6 +120,10 @@ gen:
@find utests -name '*pp' > .gen/unittest.txt
@LC_COLLATE=C sort .gen/unittest.txt -r -o .gen/unittest.txt

.PHONY: unite-api
unite-api:
@python3 srcipts/merge_yaml.py api/api.yaml merged_api.yaml

# Internal hidden targets that are used only in docker environment
--in-docker-start-debug --in-docker-start-release: --in-docker-start-%: install-%
@sed -i 's/config_vars.yaml/config_vars.docker.yaml/g' /home/user/.local/etc/timetable_vsu_backend/static_config.yaml
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ Makefile contains typicaly useful targets for development:
* `make service-start-debug` - builds the service in debug mode and starts it
* `make service-start-release` - builds the service in release mode and starts it
* `make` or `make all` - builds and runs all the tests in release and debug modes
* `make format` - autoformat all the C++ and Python sources
* `make format-all` - autoformat all the C++ and Python sources
* `make format-cpp` - autoformat all the C++ sources
* `make clean-` - cleans the object files
* `make dist-clean` - clean all, including the CMake cached configurations
* `make install` - does a `make build-release` and run install in directory set in environment `PREFIX`
Expand All @@ -32,12 +33,16 @@ Makefile contains typicaly useful targets for development:
* `make docker-start-service-release` - does a `make install-release` and runs service in docker environment
* `make docker-start-service-debug` - does a `make install-debug` and runs service in docker environment
* `make docker-clean-data` - stop docker containers
* `make gen` - regenerate source lists for CMake in .gen
* `make check-format-cpp` - сheck formatting in C++ sources, if something is not formatted, an error will be returned
* `make check-format-all` - сheck formatting in all sources, if something is not formatted, an error will be returned
* `make unite-api` - create united api file(merged_api.yaml)
* `make check-git-status` - сheck if there are changes in files

Edit `Makefile.local` to change the default configuration and build options.


## License

The original template is distributed under the [Apache-2.0 License](https://github.com/userver-framework/userver/blob/develop/LICENSE)
and [CLA](https://github.com/userver-framework/userver/blob/develop/CONTRIBUTING.md). Services based on the template may change
the license and CLA.
and [CLA](https://github.com/userver-framework/userver/blob/develop/CONTRIBUTING.md).
100 changes: 100 additions & 0 deletions scripts/merge_yaml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os
import sys
import yaml

def load_yaml(file_path):
"""Load a YAML file and return its contents as a Python object."""
with open(file_path, 'r', encoding='utf-8', newline='') as f:
return yaml.safe_load(f)


def resolve_refs(content, base_path, resolved_files):
"""Recursively resolve all $ref references in the YAML content."""
if isinstance(content, dict):
for k, v in content.items():
if k == '$ref':
ref_path = os.path.join(base_path, v)
ref_path = os.path.abspath(ref_path)
if ref_path not in resolved_files:
resolved_files[ref_path] = load_yaml(ref_path)
resolve_refs(resolved_files[ref_path], os.path.dirname(ref_path), resolved_files)
content = resolved_files[ref_path]
else:
resolve_refs(v, base_path, resolved_files)
elif isinstance(content, list):
for item in content:
resolve_refs(item, base_path, resolved_files)
return content

def have_refs(yaml):
if isinstance(yaml, dict):
for k, v in yaml.items():
if k == '$ref':
return True
elif have_refs(v):
return True
elif isinstance(yaml, list):
for item in yaml:
if have_refs(item):
return True
return False

def replace_ref(yaml, dir_path, ref_path, ref_src):
if isinstance(yaml, dict):
for k, v in yaml.items():
if isinstance(v, dict):
if len(v) == 1 and '$ref' in v.keys():
if isinstance(v['$ref'], str):
check_path = os.path.abspath(os.path.join(dir_path, v['$ref']))
if check_path == ref_path:
yaml[k] = ref_src
else:
replace_ref(v, dir_path, ref_path, ref_src)
elif isinstance(v, list):
for count, item in enumerate(v):
if isinstance(item, dict) and len(item) == 1 and '$ref' in item.keys():
v[count] = ref_src
else:
replace_ref(v[count], dir_path, ref_path, ref_src)
elif isinstance(yaml, list):
for count, item in enumerate(yaml):
if isinstance(item, dict) and len(item) == 1 and '$ref' in item.keys():
yaml[count] = ref_src
else:
replace_ref(yaml[count], dir_path, ref_path, ref_src)



def create_final_content(refs):
while len(refs) > 1:
old_refs = refs.copy()
for replacer_filepath, replacer_data in old_refs.items():
if not have_refs(replacer_data):
for filepath, data in refs.items():
replace_ref(data, os.path.dirname(filepath), replacer_filepath, replacer_data)
if len(refs) > 1:
refs.pop(replacer_filepath)
return list(refs.values())[0]

def main():
if len(sys.argv) < 3:
print('Usage: python3 merge_yaml.py <input_file> <output_file>')
return

input_path = sys.argv[1]
output_path = sys.argv[2]
input_path = os.path.abspath(input_path)
# Step 1: Collect all files
refs = {}
refs[input_path] = load_yaml(input_path)
resolve_refs(refs[input_path], os.path.dirname(input_path), refs)

# Step 3: Create final_content
yaml.Dumper.ignore_aliases = lambda *args: True
final_content = create_final_content(refs)
with open(output_path, 'w', encoding='utf-8') as result_f:
yaml.dump(final_content, result_f, allow_unicode=True)


if __name__ == '__main__':
main()

0 comments on commit e4837f9

Please sign in to comment.