From 28ea92cf08415ee27a7a19d9171a19e749e48914 Mon Sep 17 00:00:00 2001 From: Valentin Date: Tue, 28 Feb 2023 12:59:34 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BD=D1=8E=D0=B0=D0=BD=D1=81=D0=B8=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/jsonata_query_examples/jsonata_query_example.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jsonata_query_examples/jsonata_query_example.md b/src/jsonata_query_examples/jsonata_query_example.md index 611b43b..c691ec8 100644 --- a/src/jsonata_query_examples/jsonata_query_example.md +++ b/src/jsonata_query_examples/jsonata_query_example.md @@ -327,3 +327,4 @@ functions: $eval($functions.get_resources,{"context": {"ram": 2, "cpu_type": "standart", "cpu": 8, "storage_type": "standart", "storage_size": 5}, "mult": 1, "output_type": "full"}); ) ``` +Также нужно учитывать, что блок кода описанный выше работает только с переданным контекстом и если вам нужно будет получить что-то типа `$.components`, то это нужно делать при помощи переданных параметров `$components := $.components`. \ No newline at end of file From a4c8d9ea6a1578d0cd5976ee3c5cd779d81ad542 Mon Sep 17 00:00:00 2001 From: ValentinKozlov Date: Sat, 15 Apr 2023 14:07:38 +0300 Subject: [PATCH 2/6] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=83=D1=8E=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8?= =?UTF-8?q?=D1=8E=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80=D0=B0=20repositor?= =?UTF-8?q?y=5Fstructure=5Fexample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/repository_structure_example/README.md | 37 +- src/repository_structure_example/_root.yaml | 18 +- .../application_arch/_root.yaml | 5 +- .../application_arch/docs.yaml | 4 +- .../application_arch/reports/_root.yaml | 2 + .../reports/systems_list/_root.yaml | 2 + .../reports/systems_list/docs.yaml | 42 ++ .../reports/systems_list/systems_table.md | 3 + .../systems/1cbit_finance.yaml | 19 + .../application_arch/systems/_root.yaml | 9 +- .../application_arch/systems/allure.yaml | 24 + .../systems/components/_root.yaml | 3 + .../systems/components/sid_components.yaml | 509 ++++++++++++++++++ .../systems/components/srole_components.yaml | 150 ++++++ .../application_arch/systems/crm.yaml | 25 + .../application_arch/systems/grafana_dev.yaml | 23 + .../application_arch/systems/sid.yaml | 23 + .../application_arch/systems/spact.yaml | 25 + .../application_arch/systems/spoll.yaml | 31 ++ .../application_arch/systems/srole.yaml | 26 + .../artefacts/_root.yaml | 4 +- .../artefacts/common/_root.yaml | 2 + .../artefacts/common/pal1_landscape.yaml | 15 + .../artefacts/frog_paradise/_root.yaml | 2 + .../frog_paradise/pal1_landscape.yaml | 13 + .../dictionaries/_root.yaml | 3 +- .../dictionaries/dictionaries_model.yaml | 78 --- .../documentation/_root.yaml | 5 + .../documentation/docs.yaml | 17 + .../documentation/glossary/_root.yaml | 3 + .../documentation/glossary/docs.yaml | 30 ++ .../documentation/glossary/glossary.md | 3 + .../documentation/glossary/glossary.yaml | 18 + .../documentation/images/arch_approach.png | Bin 0 -> 100976 bytes .../documentation/introduction.md | 52 ++ .../documentation/useful_links/_root.yaml | 3 + .../documentation/useful_links/docs.yaml | 25 + .../useful_links/useful_links.md | 5 + .../useful_links/useful_links.yaml | 17 + .../enterprise_arch/tools/dochub/_root.yaml | 2 +- .../tools/dochub/arch_desc_guide.md | 84 +++ ...ochub_menu_model.yaml => dochub_menu.yaml} | 48 -- .../tools/dochub/dochub_menu/dochub_map.mmd | 45 -- .../{ => dochub_versions}/dochub-0.0.20.vsix | Bin .../enterprise_arch/tools/dochub/docs.yaml | 10 +- .../information_arch/_root.yaml | 2 +- .../business_entities/_root.yaml | 2 + .../business_entities/business_entities.yaml | 55 ++ .../information_arch/docs.yaml | 9 - .../information_arch/information_arch.md | 1 - .../metamodels/_root.yaml | 7 + .../metamodels/custom/_root.yaml | 7 + .../custom/business_entities/_root.yaml | 2 + .../business_entities_model.yaml | 119 ++++ .../templates/business_entities_in_systems.md | 3 + .../templates/business_entities_list.md | 3 + .../templates/business_entity_card.md | 16 + .../metamodels/custom/dictionaries/_root.yaml | 2 + .../dictionaries/dictionaries_model.yaml | 78 +++ .../templates}/dictionary_card.md | 0 .../custom/dictionaries/templates}/tree.puml | 0 .../custom}/dochub_menu/_root.yaml | 3 +- .../custom/dochub_menu/dochub_menu_model.yaml | 47 ++ .../dochub_menu/templates}/menu_tree.puml | 0 .../datasets/_root.yaml | 0 .../metamodels/datasets/datasets.yaml | 68 +++ .../metamodels/default/_root.yaml | 4 + .../metamodels/default/components/_root.yaml | 3 + .../components/components_extension.yaml | 71 +++ .../default/components}/forms.yaml | 1 + .../default/functions}/_root.yaml | 0 .../default/functions/functions.yaml | 9 + .../metamodels/default/users/_root.yaml | 2 + .../metamodels/default/users/users.yaml | 14 + .../metamodels/jsonata/_root.yaml | 2 + .../jsonata/functions.yaml | 0 .../metamodels/validators/_root.yaml | 6 + .../metamodels/validators/systems.yaml | 62 +++ .../metamodels/validators/technologies.yaml | 26 + .../settings/_root.yaml | 6 - .../settings/components_extension.yaml | 10 - .../settings/datasets/datasets.yaml | 1 - .../settings/validators/_root.yaml | 4 - .../standards/arch_principles/docs.yaml | 6 - .../standards/arch_principles/introduction.md | 6 - 85 files changed, 1876 insertions(+), 245 deletions(-) create mode 100644 src/repository_structure_example/application_arch/reports/_root.yaml create mode 100644 src/repository_structure_example/application_arch/reports/systems_list/_root.yaml create mode 100644 src/repository_structure_example/application_arch/reports/systems_list/docs.yaml create mode 100644 src/repository_structure_example/application_arch/reports/systems_list/systems_table.md create mode 100644 src/repository_structure_example/application_arch/systems/1cbit_finance.yaml create mode 100644 src/repository_structure_example/application_arch/systems/allure.yaml create mode 100644 src/repository_structure_example/application_arch/systems/components/_root.yaml create mode 100644 src/repository_structure_example/application_arch/systems/components/sid_components.yaml create mode 100644 src/repository_structure_example/application_arch/systems/components/srole_components.yaml create mode 100644 src/repository_structure_example/application_arch/systems/crm.yaml create mode 100644 src/repository_structure_example/application_arch/systems/grafana_dev.yaml create mode 100644 src/repository_structure_example/application_arch/systems/sid.yaml create mode 100644 src/repository_structure_example/application_arch/systems/spact.yaml create mode 100644 src/repository_structure_example/application_arch/systems/spoll.yaml create mode 100644 src/repository_structure_example/application_arch/systems/srole.yaml create mode 100644 src/repository_structure_example/artefacts/common/_root.yaml create mode 100644 src/repository_structure_example/artefacts/common/pal1_landscape.yaml create mode 100644 src/repository_structure_example/artefacts/frog_paradise/_root.yaml create mode 100644 src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml delete mode 100644 src/repository_structure_example/dictionaries/dictionaries_model.yaml create mode 100644 src/repository_structure_example/documentation/_root.yaml create mode 100644 src/repository_structure_example/documentation/docs.yaml create mode 100644 src/repository_structure_example/documentation/glossary/_root.yaml create mode 100644 src/repository_structure_example/documentation/glossary/docs.yaml create mode 100644 src/repository_structure_example/documentation/glossary/glossary.md create mode 100644 src/repository_structure_example/documentation/glossary/glossary.yaml create mode 100644 src/repository_structure_example/documentation/images/arch_approach.png create mode 100644 src/repository_structure_example/documentation/introduction.md create mode 100644 src/repository_structure_example/documentation/useful_links/_root.yaml create mode 100644 src/repository_structure_example/documentation/useful_links/docs.yaml create mode 100644 src/repository_structure_example/documentation/useful_links/useful_links.md create mode 100644 src/repository_structure_example/documentation/useful_links/useful_links.yaml create mode 100644 src/repository_structure_example/enterprise_arch/tools/dochub/arch_desc_guide.md rename src/repository_structure_example/enterprise_arch/tools/dochub/{dochub_menu/dochub_menu_model.yaml => dochub_menu.yaml} (61%) delete mode 100644 src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_map.mmd rename src/repository_structure_example/enterprise_arch/tools/dochub/{ => dochub_versions}/dochub-0.0.20.vsix (100%) create mode 100644 src/repository_structure_example/information_arch/business_entities/_root.yaml create mode 100644 src/repository_structure_example/information_arch/business_entities/business_entities.yaml delete mode 100644 src/repository_structure_example/information_arch/docs.yaml delete mode 100644 src/repository_structure_example/information_arch/information_arch.md create mode 100644 src/repository_structure_example/metamodels/_root.yaml create mode 100644 src/repository_structure_example/metamodels/custom/_root.yaml create mode 100644 src/repository_structure_example/metamodels/custom/business_entities/_root.yaml create mode 100644 src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml create mode 100644 src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_in_systems.md create mode 100644 src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_list.md create mode 100644 src/repository_structure_example/metamodels/custom/business_entities/templates/business_entity_card.md create mode 100644 src/repository_structure_example/metamodels/custom/dictionaries/_root.yaml create mode 100644 src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml rename src/repository_structure_example/{dictionaries => metamodels/custom/dictionaries/templates}/dictionary_card.md (100%) rename src/repository_structure_example/{dictionaries => metamodels/custom/dictionaries/templates}/tree.puml (100%) rename src/repository_structure_example/{enterprise_arch/tools/dochub => metamodels/custom}/dochub_menu/_root.yaml (51%) create mode 100644 src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml rename src/repository_structure_example/{enterprise_arch/tools/dochub/dochub_menu => metamodels/custom/dochub_menu/templates}/menu_tree.puml (100%) rename src/repository_structure_example/{settings => metamodels}/datasets/_root.yaml (100%) create mode 100644 src/repository_structure_example/metamodels/datasets/datasets.yaml create mode 100644 src/repository_structure_example/metamodels/default/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/components/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/components/components_extension.yaml rename src/repository_structure_example/{settings => metamodels/default/components}/forms.yaml (97%) rename src/repository_structure_example/{settings/jsonata => metamodels/default/functions}/_root.yaml (100%) create mode 100644 src/repository_structure_example/metamodels/default/functions/functions.yaml create mode 100644 src/repository_structure_example/metamodels/default/users/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/users/users.yaml create mode 100644 src/repository_structure_example/metamodels/jsonata/_root.yaml rename src/repository_structure_example/{settings => metamodels}/jsonata/functions.yaml (100%) create mode 100644 src/repository_structure_example/metamodels/validators/_root.yaml create mode 100644 src/repository_structure_example/metamodels/validators/systems.yaml create mode 100644 src/repository_structure_example/metamodels/validators/technologies.yaml delete mode 100644 src/repository_structure_example/settings/_root.yaml delete mode 100644 src/repository_structure_example/settings/components_extension.yaml delete mode 100644 src/repository_structure_example/settings/datasets/datasets.yaml delete mode 100644 src/repository_structure_example/settings/validators/_root.yaml delete mode 100644 src/repository_structure_example/standards/arch_principles/introduction.md diff --git a/src/repository_structure_example/README.md b/src/repository_structure_example/README.md index 6448114..bab5b05 100644 --- a/src/repository_structure_example/README.md +++ b/src/repository_structure_example/README.md @@ -1,7 +1,11 @@ -# Пример структуры репозитория для описания архитектуры +# Пример структуры репозитория для описания архитектуры v 2.0 **Цель примера:** Снизить порог вхождения в DocHub за счет внедрения типовых шаблонов структуры репозитория и меню DocHub для управления корпоративной архитектурой. +# Отличия от предыдущей версии +* Основным отличием от предыдущей версии является то, что в этой версии управление метамоделями выделено в отдельную папку **metamodels** с целью разделения архитектурных данных и структуры метамоделей. Планируется, что будет разработано два пайплайна. В случае изменения архитектурных данных, эти данные сразу будет ехать на прод, предварительно проверяя наличие ошибок в валидаторе. В случае изменения метамодели, будет проводиться ревью изменений, а только потом специально обученный человек завезет изменения в прод. +* Второе отличие заключается в том, что пример стал более функциональным. В него был внесено множество реально работающих алгоритмов. + # Суть примера Существует множество различных подходов к описанию архитектуры компании. В нашей компании была выбрана методология TOGAF. При этом мы не пытаемся внедрить классическую методологию TOGAF, мы используем комбинированный подход, выбирая только те инструменты, которые закрывают наиболее критичные риски компании. @@ -18,22 +22,35 @@ ## Файловая структура примера * **application_arch** - в этой папке хранятся все данные, связанные с прикладной архитектурой, кроме контекстов. Контексты мы вынесли в отдельную папку artefacts. Детально про artefacts можно почитать ниже. - * **systems** - в этой папке хранятся данные по системам. Каждую систему мы описываем в своём файле и имя файла всегда соответствует имени системы. Идентификатор системы в общем случае представляет из себя доменное имя в виде: `<имя компании>.<имя бизнес-юнита>.<имя системы>`. Если вдруг у системы нет своего репозитория, то в этом файле мы также описываем runtime компоненты этой системы, что позволяет хранить всю информацию по системе в одном месте. + * **reports** - в этой папке хранятся различные отчеты по системам. + * **systems** - в этой папке хранятся данные по системам. Каждую систему мы описываем в своём файле и имя файла всегда соответствует имени системы. Идентификатор системы в общем случае представляет из себя доменное имя в виде: `<имя компании>.<имя бизнес-юнита>.<имя системы>`. Если вдруг у системы нет своего репозитория, то в этом файле мы также описываем runtime компоненты этой системы, что позволяет хранить всю информацию по системе в одном месте. * **artefacts** - в этой папке мы храним все артефакты по системам, например контексты мы храним в такой структуре: `./artefacts/<имя бизнес-юнита>/<имя контекста>`. Также здесь мы храним различные схемы по системам. + * **common** - в этой папке хранится общий контекст ГК Болото. + * **frog_paradise** - в этой папке хранится контекст БЮ Лягушачий рай. * **business_arch** - в этой папке хранятся все данные, связанные с бизнес-архитектурой. На текущий момент мы не ведем в DocHub бизнес-архитектуру. Возможно, суда будем класть архитектурные скетчи, когда Рома закончит встраивать в DocHub https://excalidraw.com/ * **dictionaries** - вспомогательная папка для хранения справочной информации. Например, при заполнении системы встал вопрос по её статусе, и чтобы не дублировать каждый раз всю информацию по статусу мы сделали справочник статусов и в системе используем только идентификатор, а если нам нужна полная информация, то получаем её при помощи функции $lookup внутри запроса. +* **documentation** - в этой папке хранится общая документация. Мы пытались её размещать в различных папках, но в конце концов приняли решение сделать для неё отдельное место. + * **glossary** - в этой папке хранится глоссарий ГК Болото. + * **useful_links** - в этой папке хранятся полезные линки на внешние ресурсы. Это может быть Confluence, различные обучающие видео на YouTube и т.д. + * **images** - в этой папке хранятся картинки для документации. * **enterprise_arch** - это папка корпоративных архитекторов, сюда мы помещаем всю информацию, которая связана с развитием архитектуры в компании. - * **adr** - в этой папке мы храним реестр архитектурных решений + * **adr** - в этой папке хранится реестр архитектурных решений * **processes** - в этой папке мы храним документацию по различным архитектурным процессам - * **tools** - в этой папке мы храним документацию по инструментам архитектуры - * **dochub** - в этой папке мы храним документацию по DocHub - * **welcome** - в этой папке мы храним документацию, которая выводится при открытии DocHub (welcome page) + * **tools** - в этой папке хранится документация по инструментам архитектуры + * **dochub** - в этой папке хранится документация по DocHub + * **welcome** - в этой папке хранится документация, которая выводится при открытии DocHub (welcome page) * **images** - картинки для настоящей документации * **information_arch** - в этой папке хранятся все данные, связанные с информационной архитектурой. На текущий момент мы в начале пути и сделали пока только прототип по управлению бизнес-сущностями на логическом уровне. -* **settings** - здесь мы храним всякие настройки - * **datasets** - здесь у нас общие запросы - * **jsonata** - здесь у нас общие функции. Пример использования можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/blob/main/src/jsonata_query_examples/jsonata_query_example.md). - * **validators** - здесь мы описываем валидаторы, которые нужно выводить в общее меню проблем. Пример использования можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/tree/main/src/validator_example) + * **business_entities** - в этой папке хранятся данные по бизнес-сущностям. Сама модель переехала в папку metamodels. +* **metamodels** - в этой папке хранятся все метамодели, а также дополнительные наборы данных и инструментов необходимых для разработки метaмоделей и их валидации. + * **custom** - в этой папке хранится описание всех пользовательских матамоделей. + * **business_entities** - в этой папке хранится описание модели по управлению бизнес-сущностями на логическом уровне. + * **dictionaries** - в этой папке хранится матамодель описывающая структуру справочников. + * **dochub_menu** - в этой папке хранится матамодель описывающая целевую структуру меню. + * **datasets** - в этой папке хранятся все общие запросы (datasets). + * **default** - в этой папке хранятся все инструменты для расширения дефолтных метамоделей DocHub. + * **jsonata** - в этой папке хранятся общие функции. Пример использования можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/blob/main/src/jsonata_query_examples/jsonata_query_example.md). + * **validators** - здесь мы описываем валидаторы, которые нужно выводить в общее меню проблем. В самой папке есть два вида примеров, это валидация через schema и валидация через проверку заполнения конкретных параметров. Пример использования для schema можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/tree/main/src/validator_example). * **standards** - это одна из ключевых папок, где мы храним архитектурные принципы и стандарты, которые обязательны к выполнению всеми командами * **arch_principles** - здесь у нас описаны архитектурные принципы и принципы информационной безопасности * **it_platforms** - здесь мы храним архитектурные стандарты по платформам, а также принятые нотации по кодированию diff --git a/src/repository_structure_example/_root.yaml b/src/repository_structure_example/_root.yaml index f56b417..cc9e63c 100644 --- a/src/repository_structure_example/_root.yaml +++ b/src/repository_structure_example/_root.yaml @@ -1,10 +1,16 @@ -imports: - - business_arch/_root.yaml - - information_arch/_root.yaml +imports: - application_arch/_root.yaml - - tech_arch/_root.yaml - artefacts/_root.yaml + - business_arch/_root.yaml - dictionaries/_root.yaml + - documentation/_root.yaml - enterprise_arch/_root.yaml - - settings/_root.yaml - - standards/_root.yaml \ No newline at end of file + - information_arch/_root.yaml + - metamodels/_root.yaml + - standards/_root.yaml + - tech_arch/_root.yaml + + + + + \ No newline at end of file diff --git a/src/repository_structure_example/application_arch/_root.yaml b/src/repository_structure_example/application_arch/_root.yaml index bb6c06a..9fe43f1 100644 --- a/src/repository_structure_example/application_arch/_root.yaml +++ b/src/repository_structure_example/application_arch/_root.yaml @@ -1,3 +1,4 @@ -imports: - - systems/_root.yaml +imports: + - reports/_root.yaml + - systems/_root.yaml - docs.yaml diff --git a/src/repository_structure_example/application_arch/docs.yaml b/src/repository_structure_example/application_arch/docs.yaml index 94b188e..c007e27 100644 --- a/src/repository_structure_example/application_arch/docs.yaml +++ b/src/repository_structure_example/application_arch/docs.yaml @@ -1,8 +1,8 @@ docs: - # Заглушка пока нет описсания по бизнес архитектуре + # Заглушка пока нет описания swamp.application_arch: location: 04. Прикладная архитектура - description: Описание информационной архитектурой + description: Описание прикладной архитектуры type: markdown template: application_arch.md diff --git a/src/repository_structure_example/application_arch/reports/_root.yaml b/src/repository_structure_example/application_arch/reports/_root.yaml new file mode 100644 index 0000000..0ff5221 --- /dev/null +++ b/src/repository_structure_example/application_arch/reports/_root.yaml @@ -0,0 +1,2 @@ +imports: + - systems_list/_root.yaml diff --git a/src/repository_structure_example/application_arch/reports/systems_list/_root.yaml b/src/repository_structure_example/application_arch/reports/systems_list/_root.yaml new file mode 100644 index 0000000..fd65cdd --- /dev/null +++ b/src/repository_structure_example/application_arch/reports/systems_list/_root.yaml @@ -0,0 +1,2 @@ +imports: + - docs.yaml diff --git a/src/repository_structure_example/application_arch/reports/systems_list/docs.yaml b/src/repository_structure_example/application_arch/reports/systems_list/docs.yaml new file mode 100644 index 0000000..bb9c623 --- /dev/null +++ b/src/repository_structure_example/application_arch/reports/systems_list/docs.yaml @@ -0,0 +1,42 @@ +docs: + systems_table_list: + type: table + headers: + - value: title + text: Система + sortable: true + align: left + width: 10% + link: link_to_system + + - value: owner_unit + text: Принадлежность + sortable: true + align: left + width: 10% + + - value: description + text: Описание + sortable: false + align: left + width: 40% + + - value: application_owner + text: Владелец приложения + sortable: true + align: left + width: 10% + + - value: system_entities + text: Бизнес-сущности + link: link_to_system_entities + align: left + width: 10% + + source: swamp.dataset.systems_list + + systems_list: + location: 04. Прикладная архитектура/01. Список систем + type: markdown + source: systems_table.md + diff --git a/src/repository_structure_example/application_arch/reports/systems_list/systems_table.md b/src/repository_structure_example/application_arch/reports/systems_list/systems_table.md new file mode 100644 index 0000000..73335a4 --- /dev/null +++ b/src/repository_structure_example/application_arch/reports/systems_list/systems_table.md @@ -0,0 +1,3 @@ +# Список систем/сервисов ГК Болото + +![Получаем список бизнес-сущностей](@document/systems_table_list) diff --git a/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml b/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml new file mode 100644 index 0000000..e86cd76 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml @@ -0,0 +1,19 @@ +components: + swamp.frog.1cbit_finance: + title: 1С Бит.Финанс + entity: system + short_description: Функциональная подсистема автоматизирующая договорной учет + description: Функциональная подсистема автоматизирующая договорной учет + business_owners: + - Кикимора-болотная + application_owner: Лягушка + critical_level: mission_critical #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + links: + # Cвязи с системами + - id: swamp.crocodile.spact + direction: <-- + - id: swamp.frog.spoll + direction: <-- + - id: swamp.crocodile.crm + direction: <-- diff --git a/src/repository_structure_example/application_arch/systems/_root.yaml b/src/repository_structure_example/application_arch/systems/_root.yaml index f7b741b..c940926 100644 --- a/src/repository_structure_example/application_arch/systems/_root.yaml +++ b/src/repository_structure_example/application_arch/systems/_root.yaml @@ -1,2 +1,7 @@ -imports: - +imports: + - 1cbit_finance.yaml + - allure.yaml + - crm.yaml + - grafana_dev.yaml + - spact.yaml + - spoll.yaml diff --git a/src/repository_structure_example/application_arch/systems/allure.yaml b/src/repository_structure_example/application_arch/systems/allure.yaml new file mode 100644 index 0000000..eb30214 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/allure.yaml @@ -0,0 +1,24 @@ +components: + swamp.hippo.allure: + title: Allure # Название компонента + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + short_description: Фреймворк для создания отчётов автотестов + description: Фреймворк от Яндекса для создания простых и понятных отчётов автотестов + business_owners: + - Не найдены + application_owner: Змей-ползучий + critical_level: administrative #administrative/business_operational/business_critical/mission_critical + system_category: it_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + aspects: # Аспекты, которе реализует компонент + - qa_systems + business_entities: + - bank + - country + - unit + - organization + - contractoraccount + - chekingaccount + links: + # Интеграции между системами одного БЮ + - id: swamp.frog.1cbit_finance + direction: <-- diff --git a/src/repository_structure_example/application_arch/systems/components/_root.yaml b/src/repository_structure_example/application_arch/systems/components/_root.yaml new file mode 100644 index 0000000..a391f7c --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/components/_root.yaml @@ -0,0 +1,3 @@ +imports: + - sid_components.yaml + - srole_components.yaml diff --git a/src/repository_structure_example/application_arch/systems/components/sid_components.yaml b/src/repository_structure_example/application_arch/systems/components/sid_components.yaml new file mode 100644 index 0000000..1eed4b9 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/components/sid_components.yaml @@ -0,0 +1,509 @@ +components: + swamp.crocodile.sid.app: + title: Основное приложение + entity: component + technologies: + - Python + - Django + - Django REST Framework + - Faust + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> + - id: swamp.crocodile.sid.redis_master + direction: --> + # title: Кэширование, хранение сессий и хранилище для очереди сообщений в Celery + - id: swamp.crocodile.sid.celery + direction: --> + - id: swamp.crocodile.sid.crmclientvalid + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + prod.slave: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.celery: + title: Celery + description: Очередь задач + entity: component + technologies: + - Python + - Django + - Celery + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.celery_beat: + title: Celery Beat + description: Планировщик задач Celery + entity: component + technologies: + - Python + - Django + - Celery + links: + - id: swamp.crocodile.sid.celery + direction: --> + title: Задачи, запущенные по расписанию + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.crmclientvalid: + title: Валидатор клиентов CRM + description: + entity: component + technologies: + - Python + - Faust + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.web: + title: NGINX + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.app + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.profile_producer: + title: Profile Kafka Producer + description: Продьюсер профилей пользователей в топик id.profile.export.0 + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> # Что означает стрелочка? Запись или кто инициатор? pg_wal как расценивать + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.consumers: + title: Консьюмеры + entity: component + + swamp.crocodile.sid.consumers.catalog_consumer: + title: Kafka Consumer Catalog + description: Консьюмер получения данных из топиков Catalog + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.consumers.smail_consumer: + title: S.Mail Kafka Consumer + description: Консьмер статусов писем из S.Mail + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.consumers.spass_consumer: + title: S.Pass Kafka Consumer + description: Консьюмер рабочих из S.Pass + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.consumers.zup_consumer: + title: ЗУП Kafka Consumer + description: + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.pgbouncer + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.pgbouncer: + title: PG Bouncer + description: Балансировщик PostgreSQL + entity: component + technologies: + - PGBouncer + links: + - id: swamp.crocodile.sid.db_postgresql + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.sid.db_postgresql: + title: DB + description: + entity: database + technologies: + - PostgreSQL + deployment_units: + prod.main: #.<имя du> + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 10 #Gb + stage.main: + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 10 #Gb + dev.main: + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 10 #Gb + + swamp.crocodile.sid.redis_master: + title: Redis + description: Кэш, хранение сессий, брокер сообщений для Celery + entity: database + technologies: + - Redis + deployment_units: + prod.main: #.<имя du> + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + stage.main: + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + dev.main: + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + + swamp.crocodile.sid.celery_exporter: + title: Celery-exporter + description: + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.redis_master + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + + swamp.crocodile.sid.postgres_exporter: + title: Postgres Exporter + description: + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.sid.db_postgresql + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + dev.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.5 #Gb + + # swamp.crocodile.sid.django_migrate: + # title: + # description: + # entity: component + # technologies: + # - Python + # - Django diff --git a/src/repository_structure_example/application_arch/systems/components/srole_components.yaml b/src/repository_structure_example/application_arch/systems/components/srole_components.yaml new file mode 100644 index 0000000..bcf569b --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/components/srole_components.yaml @@ -0,0 +1,150 @@ +components: + swamp.crocodile.srole.app: + title: Основное приложение + entity: component + technologies: + - Python + - Django + - Django REST Framework + - Faust + links: + - id: swamp.crocodile.srole.db_postgresql + direction: --> + - id: swamp.crocodile.srole.redis_master + direction: --> + - id: swamp.crocodile.srole.celery + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.srole.celery: + title: Celery + description: Очередь задач + entity: component + technologies: + - Python + - Django + - Celery + links: + - id: swamp.crocodile.srole.db_postgresql + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + + swamp.crocodile.srole.web: + title: NGINX + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.srole.app + direction: --> + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 2 #float - количество ядер + ram: 8 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.srole.db_postgresql: + title: DB + description: + entity: database + technologies: + - PostgreSQL + deployment_units: + prod.main: #.<имя du> + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + stage.main: + resource_type: k8s #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 0.1 #Gb + + swamp.crocodile.srole.redis_master: + title: Redis + description: Кэш, хранение сессий, брокер сообщений для Celery + entity: component + technologies: + - Redis + deployment_units: + prod.main: #.<имя du> + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 10 #Gb + stage.main: + resource_type: virtual #k8s/virtual/phisycal + cpu_type: highend #highend/standart/lowend + cpu: 1 #float - количество ядер + ram: 1 #float - количество памяти в Gb + storage_type: standart #highend/standart/lowend/extralowend + storage_size: 10 #Gb + + + swamp.crocodile.srole.celery_exporter: + title: Celery-exporter + description: + entity: component + technologies: + - Python + - Django + links: + - id: swamp.crocodile.srole.redis_master + direction: --> + + swamp.crocodile.srole.postgres_exporter: + title: Postgres Exporter + description: Метрики в Prometheus + entity: component + technologies: + - PostgreSQL + links: + - id: swamp.crocodile.srole.db_postgresql + direction: --> diff --git a/src/repository_structure_example/application_arch/systems/crm.yaml b/src/repository_structure_example/application_arch/systems/crm.yaml new file mode 100644 index 0000000..e926a79 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/crm.yaml @@ -0,0 +1,25 @@ +components: + swamp.crocodile.crm: + title: CRM # Название компонента + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + short_description: CRM-система для продажи недвижимости + description: CRM-система (customer relationship management) — это способ управления взаимоотношениями с клиентами и оптимизации бизнес-процессов + business_owners: + - Татоша + - Какоша + application_owner: Мойдодыр + critical_level: mission_critical #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + aspects: # Аспекты, которе реализует компонент + - sales + business_entities: + - bank1 + - chekingaccount + - contractor + - region + - unit + - agreementtype + links: + # Интеграции между системами одного БЮ + - id: swamp.frog.1cbit_finance + direction: <-- diff --git a/src/repository_structure_example/application_arch/systems/grafana_dev.yaml b/src/repository_structure_example/application_arch/systems/grafana_dev.yaml new file mode 100644 index 0000000..e09971c --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/grafana_dev.yaml @@ -0,0 +1,23 @@ +components: + swamp.hippo.grafana: + title: Grafana + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + description: Свободная программная система визуализации данных, ориентированная на данные систем ИТ-мониторинга. Данный инстанс используется в командах разработки. + short_description: Витрина мониторинга систем и сервисов + business_owners: + - Тараканище + application_owner: Волки + critical_level: administrative #administrative/business_operational/business_critical/mission_critical + system_category: it_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + aspects: # Аспекты, которе реализует компонент + - monitoring + links: + # Интеграции между системами одного БЮ + - id: swamp.frog.1cbit_finance + direction: <-- + - id: swamp.frog.spoll + direction: <-- + - id: swamp.crocodile.crm + direction: <-- + + diff --git a/src/repository_structure_example/application_arch/systems/sid.yaml b/src/repository_structure_example/application_arch/systems/sid.yaml new file mode 100644 index 0000000..f3bffa9 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/sid.yaml @@ -0,0 +1,23 @@ +components: + swamp.crocodile.sid: + title: S.ID # Название компоненты + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + system_status: production + short_description: Общекорпоративный сервис SSO (Single Sign-On) + description: Сервис SSO (Single Sign-On). Реализована возможность авторизации через OAuth 2 и OpenID Connect. + business_owners: + - Крокодил Гена + - Шапокляк + application_owner: Чебурашка + budget_holder: Малыш + architect: Карлсон + critical_level: business_critical #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + technologies: # Используемые технологии + - Django + - Python + - Faust + - PostgreSQL + - Redis + - Kafka + - Nginx diff --git a/src/repository_structure_example/application_arch/systems/spact.yaml b/src/repository_structure_example/application_arch/systems/spact.yaml new file mode 100644 index 0000000..d1473dd --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/spact.yaml @@ -0,0 +1,25 @@ +components: + swamp.crocodile.spact: + title: S.Pact # Название компонента + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + short_description: Сервис для создания и подписания договоров + description: Сервис для создания и подписания договоров + business_owners: + - Вупсень + - Пупсень + application_owner: Корней Корнеич + critical_level: business_critical #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + aspects: # Аспекты, которе реализует компонент + - finance.finance_contract + business_entities: + - draftagreement + - currency + - region + - organization + - contractoraccount + - contract + links: + # Интеграции между системами одного БЮ + - id: swamp.frog.1cbit_finance + direction: <-- diff --git a/src/repository_structure_example/application_arch/systems/spoll.yaml b/src/repository_structure_example/application_arch/systems/spoll.yaml new file mode 100644 index 0000000..ba149cf --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/spoll.yaml @@ -0,0 +1,31 @@ +components: + swamp.frog.spoll: + title: S.POLL # Название компоненты + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + short_description: Сервис формирования опросов + description: Сервис формирования опросов + business_owners: + - Пиявки + application_owner: Лунтик + critical_level: business_operational #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + aspects: # Аспекты, которе реализует компонент + - hr + business_entities: + - bank + - currency + - region + - chekingaccount + - contractoraccount + - contract + links: + # Интеграции между системами разных БЮ + - id: swamp.frog.1cbit_finance + direction: <-- + - id: swamp.crocodile.crm + direction: <-- + - id: swamp.crocodile.spact + direction: <-- + + + diff --git a/src/repository_structure_example/application_arch/systems/srole.yaml b/src/repository_structure_example/application_arch/systems/srole.yaml new file mode 100644 index 0000000..c116180 --- /dev/null +++ b/src/repository_structure_example/application_arch/systems/srole.yaml @@ -0,0 +1,26 @@ +components: + swamp.crocodile.srole: + title: S.Role # Название компоненты + entity: system # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + system_status: production + short_description: Сервис управления ролями на уровне компании + description: Сервис управления ролями на уровне компании + business_owners: + - Крокодил Гена + - Шапокляк + application_owner: Чебурашка + budget_holder: Малыш + architect: Карлсон + critical_level: business_operational #administrative/business_operational/business_critical/mission_critical + system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app + technologies: # Используемые технологии + - Django + - Python + - PostgreSQL + - Redis + - Kafka + - Nginx + links: + # Интеграции между системами одного БЮ + - id: swamp.crocodile + direction: <-- diff --git a/src/repository_structure_example/artefacts/_root.yaml b/src/repository_structure_example/artefacts/_root.yaml index b3f993c..9d0de59 100644 --- a/src/repository_structure_example/artefacts/_root.yaml +++ b/src/repository_structure_example/artefacts/_root.yaml @@ -1 +1,3 @@ -imports: +imports: + - common/_root.yaml + - frog_paradise/_root.yaml diff --git a/src/repository_structure_example/artefacts/common/_root.yaml b/src/repository_structure_example/artefacts/common/_root.yaml new file mode 100644 index 0000000..d8dd40c --- /dev/null +++ b/src/repository_structure_example/artefacts/common/_root.yaml @@ -0,0 +1,2 @@ +imports: + - pal1_landscape.yaml \ No newline at end of file diff --git a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml new file mode 100644 index 0000000..2cd42d7 --- /dev/null +++ b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml @@ -0,0 +1,15 @@ +contexts: # Контексты представления архитектурных компонентов + arch.swamp.common: + title: ГК Болото + location: Верхнеуровневый прикладной ландшафт (ПА-L1)/ГК Болото +# extra-links: true + extra-links: false + uml: + $notation: plantuml # sber C4Model plantuml + $autor: Frog + $version: 0.0.1 + $moment: 20.11.2022 + components: + - swamp.frog.* + - swamp.hippo.* + - swamp.crocodile.* diff --git a/src/repository_structure_example/artefacts/frog_paradise/_root.yaml b/src/repository_structure_example/artefacts/frog_paradise/_root.yaml new file mode 100644 index 0000000..d8dd40c --- /dev/null +++ b/src/repository_structure_example/artefacts/frog_paradise/_root.yaml @@ -0,0 +1,2 @@ +imports: + - pal1_landscape.yaml \ No newline at end of file diff --git a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml new file mode 100644 index 0000000..b1a85c2 --- /dev/null +++ b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml @@ -0,0 +1,13 @@ +contexts: # Контексты представления архитектурных компонентов + arch.swamp.frog_paradise: + title: Верхнеуровневый прикладной ландшафт (ПА-L1) БЮ Лягушачий рай/ + location: Верхнеуровневый прикладной ландшафт (ПА-L1)/Лягушачий рай +# extra-links: true + extra-links: false + uml: + $notation: plantuml # sber C4Model plantuml + $autor: Frog + $version: 0.0.1 + $moment: 20.11.2022 + components: + - swamp.frog.* diff --git a/src/repository_structure_example/dictionaries/_root.yaml b/src/repository_structure_example/dictionaries/_root.yaml index c5993a3..39358ec 100644 --- a/src/repository_structure_example/dictionaries/_root.yaml +++ b/src/repository_structure_example/dictionaries/_root.yaml @@ -1,3 +1,2 @@ -imports: - - dictionaries_model.yaml +imports: - dictionaries.yaml \ No newline at end of file diff --git a/src/repository_structure_example/dictionaries/dictionaries_model.yaml b/src/repository_structure_example/dictionaries/dictionaries_model.yaml deleted file mode 100644 index 0df804e..0000000 --- a/src/repository_structure_example/dictionaries/dictionaries_model.yaml +++ /dev/null @@ -1,78 +0,0 @@ -entities: # Сущности расширенной метамодели - # Бизнес-сущности - dictionaries: # Секция, где будет описываться объекты сущности "взаимодействие". Обязательно. - title: Справочники # Название сущности. Обязательно. - description: > # Описание сущности текст или ссылка на документ. Необязательно. - Справочники для хранения различной статической информации используемой внутри DocHub - menu: > - ( - $dictionaries := $.dictionaries; - $makeLocation := function($id) {( - $arrleft := function($arr ,$count) { - $map($arr, function($v, $i) { - $i <= $count ? $v - }) - }; - $domains := $split($id, "."); - "Документы/08. Справочники/" & $join($map($domains, function($domain, $index) {( - $lookup($dictionaries, $join($arrleft($domains, $index), ".")).title - )}), "/"); - )}; - - $append([{ - "icon": *.icon, /* Получаем иконку */ - "link": "entities/dictionaries/tree", /* Ссылка на форму представления tree (дерево дерево объектов "interactions") */ - "location": "Документы/08. Справочники" /* Расположение в меню */ - }], - [$.dictionaries.$spread().{ - "icon": *.icon, /* Получаем иконку */ - "link": "entities/dictionaries/dictionary_card?id=" & $keys()[0], /* Формируем ссылку на бланк документ */ - "location": $makeLocation($keys()[0]) /* Формируем расположение в меню */ - }][location] - ); - ) - - presentations: - tree: - type: plantuml - template: tree.puml - source: > - ( - $set("rpev-id", undefined); - $arrleft := function($arr ,$count) { - $map($arr, function($v, $i) { - $i <= $count ? $v - }) - }; - $dictionaries := $.dictionaries; - [$dictionaries.$spread().($merge([{"id" : $keys()[0]}, $.*]))^(id).( - $prev_nodes := $split($get("rpev-id"), "."); - $prev_level := $count($prev_nodes); - $curr_nodes := $split(id, "."); - $set("isdiff", false); - $result := $map($curr_nodes, function($v, $i) {( - $set("isdiff", $get("isdiff") or $prev_level = 0 or $prev_level <= $i or $v != $prev_nodes[$i]) ? ( - $id := $join($arrleft($curr_nodes, $i), "."); - $dictionary := $lookup($dictionaries, $id); - { - "id": $id, - "level": $pad("", $i + 2, "*"), - "title": $dictionary ? $dictionary.title : $id, - "link": "/entities/dictionaries/dictionary_card?id=" & $id - } - ); - )}); - $set("rpev-id", id); - $result - )]; - ) - # Выводим карточку выбранного справочника - dictionary_card: - type: markdown - template: dictionary_card.md - source: > - ( - $dictionaries := [$.dictionaries.$spread().$merge([$.*, {"id": $keys($)}])]; - $dictionaries [id=$params.id]; - ) - diff --git a/src/repository_structure_example/documentation/_root.yaml b/src/repository_structure_example/documentation/_root.yaml new file mode 100644 index 0000000..ddedea7 --- /dev/null +++ b/src/repository_structure_example/documentation/_root.yaml @@ -0,0 +1,5 @@ +imports: + - glossary/_root.yaml + - useful_links/_root.yaml + - docs.yaml + diff --git a/src/repository_structure_example/documentation/docs.yaml b/src/repository_structure_example/documentation/docs.yaml new file mode 100644 index 0000000..e5443b6 --- /dev/null +++ b/src/repository_structure_example/documentation/docs.yaml @@ -0,0 +1,17 @@ +docs: + arch_introduction: + location: 09. Документация/01. Введение в архитектуру + description: Введение + type: markdown + source: introduction.md + + glossary: + location: 09. Документация/02. Глоссарий терминов + type: markdown + source: glossary/glossary.md + + useful_links: + location: 09. Документация/03. Полезные ссылки + type: markdown + source: useful_links/useful_links.md + diff --git a/src/repository_structure_example/documentation/glossary/_root.yaml b/src/repository_structure_example/documentation/glossary/_root.yaml new file mode 100644 index 0000000..78a76ad --- /dev/null +++ b/src/repository_structure_example/documentation/glossary/_root.yaml @@ -0,0 +1,3 @@ +imports: + - docs.yaml + - glossary.yaml \ No newline at end of file diff --git a/src/repository_structure_example/documentation/glossary/docs.yaml b/src/repository_structure_example/documentation/glossary/docs.yaml new file mode 100644 index 0000000..bb1d086 --- /dev/null +++ b/src/repository_structure_example/documentation/glossary/docs.yaml @@ -0,0 +1,30 @@ +docs: + table_glossary: + type: table + # location: Глоссарий терминов + headers: + - value: type + text: Тип + sortable: true + align: left + + - value: name + text: Термин + sortable: true + align: left + + - value: shortname + text: Сокращение + sortable: true + align: left + + - value: comment + text: Комментарий + sortable: true + align: left + + source: > + ( + $.glossary.terms^(type, name) + ) + diff --git a/src/repository_structure_example/documentation/glossary/glossary.md b/src/repository_structure_example/documentation/glossary/glossary.md new file mode 100644 index 0000000..0f0c3d7 --- /dev/null +++ b/src/repository_structure_example/documentation/glossary/glossary.md @@ -0,0 +1,3 @@ +# Глосарий терминов ГК Болото + +![Выводим таблицу со списком терминов](@document/table_glossary) diff --git a/src/repository_structure_example/documentation/glossary/glossary.yaml b/src/repository_structure_example/documentation/glossary/glossary.yaml new file mode 100644 index 0000000..966b676 --- /dev/null +++ b/src/repository_structure_example/documentation/glossary/glossary.yaml @@ -0,0 +1,18 @@ +glossary: + terms: + - name: Domain Driven Design + shortname: DDD архитектура + comment: > + Набор принципов и схем, направленных на создание оптимальных систем объектов. + Сводится к созданию программных абстракций, которые называются моделями предметных областей. + В эти модели входит бизнес-логика, устанавливающая связь между реальными условиями области применения продукта и кодом. + type: Архитектура + + - name: The Open Group Architecture Framework + shortname: TOGAF + comment: > + Методология/библиотечный метод описания/подход (framework) для описания архитектуры предприятия, + который предлагает подход для проектирования, планирования, внедрения IT-архитектуры предприятия и управления ей. + type: Архитектура + + diff --git a/src/repository_structure_example/documentation/images/arch_approach.png b/src/repository_structure_example/documentation/images/arch_approach.png new file mode 100644 index 0000000000000000000000000000000000000000..94d97b765d8c133073a31d35c39e67bfc28e5cd1 GIT binary patch literal 100976 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%0A@b|rY3@10zXcahj5S7E478b+k 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%0Ar%nS%5Q>Ke?JdGV)$-tbWwt_63Gq}* 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 eK3KiP+W+#q+Jo*z8nmZ?1Uy~+T-G@yGywnr + ( + $.useful_links^(type) + ) + diff --git a/src/repository_structure_example/documentation/useful_links/useful_links.md b/src/repository_structure_example/documentation/useful_links/useful_links.md new file mode 100644 index 0000000..55372e2 --- /dev/null +++ b/src/repository_structure_example/documentation/useful_links/useful_links.md @@ -0,0 +1,5 @@ +# Список полезных ссылок ГК Болото + +[Добавить](https://gitlab.samoletgroup.ru/-/ide/project/enterprise-architecture/samolet/edit/as-is/-/documentation/useful_links/useful_links.yaml) + +![Выводим таблицу со списком полезных ссылок](@document/useful_links_table) diff --git a/src/repository_structure_example/documentation/useful_links/useful_links.yaml b/src/repository_structure_example/documentation/useful_links/useful_links.yaml new file mode 100644 index 0000000..6c6ad88 --- /dev/null +++ b/src/repository_structure_example/documentation/useful_links/useful_links.yaml @@ -0,0 +1,17 @@ +useful_links: + - name: Официальный сайт DocHub + link: https://dochub.info/main + description: На сайте вы можете найти всю необходимыю информацию по DocHub, при этом нужно учитывать что сайт построен на платформе DocHub, что даёт общее предствление о его устройстве. + section: Архитектура + + - name: Ссылка на репозиторий DocHub в GitHub + link: https://github.com/RabotaRu/DocHub + description: В репозитории вы найдете все исходники и инструкцию для устновки DocHub. + section: Архитектура + + - name: Ссылка на репозиторй с примерами от сообщества + link: https://github.com/rpiontik/DocHubExamples + description: В репозитории вы найдете примеры от участников сообщества развития DocHub. + section: Архитектура + + diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/_root.yaml b/src/repository_structure_example/enterprise_arch/tools/dochub/_root.yaml index 2b254a4..2d95c14 100644 --- a/src/repository_structure_example/enterprise_arch/tools/dochub/_root.yaml +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/_root.yaml @@ -1,3 +1,3 @@ imports: - docs.yaml - - dochub_menu/_root.yaml \ No newline at end of file + - dochub_menu.yaml \ No newline at end of file diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/arch_desc_guide.md b/src/repository_structure_example/enterprise_arch/tools/dochub/arch_desc_guide.md new file mode 100644 index 0000000..bae9937 --- /dev/null +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/arch_desc_guide.md @@ -0,0 +1,84 @@ +# Руководство по описанию прикладной архитектуры ГК Болото + +## Введение + +Согласно [методологии описания корпоративной архитектуры ГК Болото](/docs/arch_introduction) данное руководство касается только описания архитектуры уровней ПА-L1 (общая прикладная архитектура) и ПА-L2 (прикладная архитектура проектов). + + +## Правила описания прикладной архитектуры компании в DocHub + +В данном разделе описаны основные правила описания прикладной архитектуры в DocHub. При этом нужно учитывать, что инструмент DocHub постоянно развивается и часть информации может быть неактуальной. С целью минимизации таких ситуаций описание происходит на достаточно высоком уровне. + + +Помимо указанного выше видео есть [сайт разработчика DocHub](https://dochub.info) где можно посмотреть примеры использования инструмента и подробно ознакомиться с его возможностями. + +### Правила описания прикладной архитектуры компании уровня ПА-L1 + +#### Структура данных для управления системами + +Описание репозитория можно найти [здесь](/docs/dochub_app_repo_guide). + + +#### Правила именования объектов связанных с системами + +1. Все имена, названия и код должны писаться в нотации [snake_case](https://ru.wikipedia.org/wiki/Snake_case) +2. Имя yaml файла описывающего конкретную систему должно соответствовать имени системы внутри идентификатора системы (см. п.3) +3. Идентификатор системы в общем случае представляет из себя доменное имя в виде: +`swamp.<имя бизнес-юнита>.<имя системы>` + + + +### Правила описания прикладной архитектуры компании уровня ПА-L2 + +Для описания run-time компонент системы есть 2 пути: +1. Описать run-time компоненты системы внутри общего репозитория swamp +2. Описать run-time компоненты системы внутри собственного репозитория системы + +С точки зрения конечных данных оба варианта равнозначны и выбор только за вами. + +**Советы по выбору варианта** +1. Если вы один архитектор управляющий несколькими системами, то удобней управлять архитектурой своих приложений в общем репозитории Swamp. +2. Если у вас одна система, которая "живет" в своем репозитории, то возможно вам будет удобнее управлять архитектурой из репозитория системы. +3. Если у вас внешняя система для которой нужно описать run-time компоненты, то описывать их нужно в общем репозитории Swamp + + +### Правила отображения связей между системами + +#### Как понять в какую сторону отобразить стрелку при указании связи систем + +В зависимости от уровня абстракции ситуация может выглядеть по-разному. Например, спускаясь на технический уровень практически в 100% случаев нам придется отобразить двухстороннюю связь между системами. + +В первую очередь, это связанно с тем, что при технической реализации системе-приёмнику необходимо выполнить какое-либо действие или набор действий для того, чтобы получить данные. Поэтому использовать этот уровень абстракции логическом уровне ПА-L1 не имеет физического смысла. + +Для того чтобы корректно отобразить стрелку при указании связи систем мы рекомендуем задать вопрос: "В какую систему приходят конечные бизнес-данные, от которых зависит бизнес-процесс?". + +Ответ на этот вопрос, в большинстве случаев даст вам однозначное понимание какая система отправляет данные, а какая получает их. Например, в примере описанном в разделе ["Как правильно отобразить стрелки, если системы отправляют данные друг другу?"](#как-правильно-отобразить-стрелки-если-системы-отправляют-данные-друг-другу) описана ситуация, когда есть два набора бизнес-данных которые отправляются из S.CEC в 1С Бит.Финанс и наоборот. Т.е. по факту есть два топика в Kafka с разной бизнес-информацией от которой зависит логическая раота каждой из систем. + +#### Как правильно отобразить стрелки, если системы отправляют данные друг другу? + +В связи с тем, что описывать системы и их взаимосвязи могут разные сотрудники, то при отображении связей между системами можно задублировать их связи. Для решения этой проблемы необходимо пользоваться следующим правилом: + +- В карточке системы должен отображаться только входящий поток данных `direction: <--`. + +Например, у вас есть две системы 1С Бит.Финанс (БЮ Frog) и S.CEC (БЮ Crocodile). Обе эти системы получают данные и отправляют их друг другу. +Согласно вышеописанному правилу при описании системы 1С Бит.Финанс вы должны отобразить только факт получения данных из S.CEC. + +```yaml +components: + swamp.frog.1cbit_finance: + title: 1С Бит.Финанс + links: + - id: swamp.crocodile.sgovernance.scec + direction: <-- # Входящий поток данных в 1С Бит.Финанс из S.CEC +``` + +То же самое вы должны сделать при описании системы S.CEC + +```yaml +components: + swamp.crocodile.sgovernance.scec: + title: S.CEC + links: + - id: swamp.frog.1cbit_finance.1cbit_finance + direction: <-- # Входящий поток данных в S.CEC из 1С Бит.Финанс +``` diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_menu_model.yaml b/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu.yaml similarity index 61% rename from src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_menu_model.yaml rename to src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu.yaml index b94a79d..13dc2ec 100644 --- a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_menu_model.yaml +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu.yaml @@ -1,51 +1,3 @@ -entities: # Сущности расширенной метамодели - # Бизнес-сущности - dochub_menu: # Секция, где будет описываться объекты сущности "взаимодействие". Обязательно. - title: Меню DocHub # Название сущности. Обязательно. - description: > # Описание сущности текст или ссылка на документ. Необязательно. - Структура меню DocHub - menu: > - ( - [{ - "location": "Документы/07. Развитие корп архитектуры/02. Инструменты/01. DocHub/02. Целевая структура меню DocHub", - "link": "entities/dochub_menu/menu_tree" - }] - ) - - presentations: - menu_tree: - type: plantuml - template: menu_tree.puml - source: > - ( - $set("rpev-id", undefined); - $arrleft := function($arr ,$count) { - $map($arr, function($v, $i) { - $i <= $count ? $v - }) - }; - $dochub_menu := $.dochub_menu; - [$dochub_menu.$spread().($merge([{"id" : $keys()[0]}, $.*]))^(order).( - $prev_nodes := $split($get("rpev-id"), "."); - $prev_level := $count($prev_nodes); - $curr_nodes := $split(id, "."); - $set("isdiff", false); - $result := $map($curr_nodes, function($v, $i) {( - $set("isdiff", $get("isdiff") or $prev_level = 0 or $prev_level <= $i or $v != $prev_nodes[$i]) ? ( - $id := $join($arrleft($curr_nodes, $i), "."); - $menu := $lookup($dochub_menu, $id); - { - "id": $id, - "level": $pad("", $i + 2, "*"), - "title": $menu ? $menu.title : $id - } - ); - )}); - $set("rpev-id", id); - $result - )]; - ) - dochub_menu: dochub: title: 0. DocHub diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_map.mmd b/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_map.mmd deleted file mode 100644 index 7d71bf9..0000000 --- a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/dochub_map.mmd +++ /dev/null @@ -1,45 +0,0 @@ -mindmap - root((DocHub)) - (0. Глосарий терминов) - (1. Архитектурные принципы и стандарты) - 1. Архитектурные принципы - 2. Архитектурные стандарты - 1. Стандарты мониторинга - 2. Стандарты кибербезопасности - 3. Стандарты по платформам - 1. DocHub - 2. S платформы - 3. Платформа 1C - 4. Платформа MS Dynamics - 5. Apach Kafka - (2. Architecture decision log) - (3. Информационная архитектура) - 1. Бизнес сущности - 2. Список бизнес сущностей в системах - (4. Развитие корп архитектуры) - 1. Политики и архитектурные процессы - 2. Инструменты - 1. DocHub - 1. Руководство по описанию прикладной архитектуры - 2. Структура системы или сервиса описанного в DocHub - 3. Интеграционная схема выгрузки систем - 4. Примеры запросов JSONata - 2. Storm BPMN - 1. Описание нюансов работы в ГК Самолет - 3. Команды - 4. План развития - (5. Прикладная архитектура) - 1. Список систем - 2. Карта приложений - 3. Верхнеуровневый прикладной ландшафт ПА/L1 - 4. Детальная документация систем ПА/L1 - 5. Детальная архитектуры систем ПА/L2 - 6. Детальная документация систем ПА/L2 - 7. Функционал систем - 8. Graph систем - (6. Технологическая архитектура) - (10. Бизнес архитектура) - 1. Карта процессов - (20. Техрадар) - (90. Структура меню) - (91. Проблемы) diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub-0.0.20.vsix b/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_versions/dochub-0.0.20.vsix similarity index 100% rename from src/repository_structure_example/enterprise_arch/tools/dochub/dochub-0.0.20.vsix rename to src/repository_structure_example/enterprise_arch/tools/dochub/dochub_versions/dochub-0.0.20.vsix diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/docs.yaml b/src/repository_structure_example/enterprise_arch/tools/dochub/docs.yaml index ea63017..1b5e8b0 100644 --- a/src/repository_structure_example/enterprise_arch/tools/dochub/docs.yaml +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/docs.yaml @@ -1,9 +1,15 @@ docs: dochub_app_arch_desc_guide: location: 07. Развитие корп архитектуры/02. Инструменты/01. DocHub/01. Руководство по описанию прикладной архитектуры - description: Руководство по описанию прикладной архитектуры ГК Самолет + description: Руководство по описанию прикладной архитектуры ГК Болото type: markdown # Специально вынесен файл в корень, чтобы архитекторы заходя в репозиторий сразу видели инструкцию - source: ../../../README.md + source: arch_desc_guide.md + dochub_app_repo_guide: + location: 07. Развитие корп архитектуры/02. Инструменты/01. DocHub/02. Описание структуры текущего репозитория + description: Руководство по описанию структуры текущего репозитория + type: markdown + # Специально вынесен файл в корень, чтобы архитекторы заходя в репозиторий сразу видели инструкцию + source: ../../../README.md diff --git a/src/repository_structure_example/information_arch/_root.yaml b/src/repository_structure_example/information_arch/_root.yaml index 0df4554..5fa5301 100644 --- a/src/repository_structure_example/information_arch/_root.yaml +++ b/src/repository_structure_example/information_arch/_root.yaml @@ -1,3 +1,3 @@ imports: - - docs.yaml + - business_entities/_root.yaml diff --git a/src/repository_structure_example/information_arch/business_entities/_root.yaml b/src/repository_structure_example/information_arch/business_entities/_root.yaml new file mode 100644 index 0000000..785f97c --- /dev/null +++ b/src/repository_structure_example/information_arch/business_entities/_root.yaml @@ -0,0 +1,2 @@ +imports: + - business_entities.yaml \ No newline at end of file diff --git a/src/repository_structure_example/information_arch/business_entities/business_entities.yaml b/src/repository_structure_example/information_arch/business_entities/business_entities.yaml new file mode 100644 index 0000000..d52dbe1 --- /dev/null +++ b/src/repository_structure_example/information_arch/business_entities/business_entities.yaml @@ -0,0 +1,55 @@ +business_entities: + bank: + title: Банки + description: pass + parameters: + - name: bank_name + description: Наименование банка + - name: bik + description: БИК банка + + chekingaccount: + title: Расчетные счета + description: pass + + contractor: + title: Контрагенты + description: pass + + contractoraccount: + title: Счета контрагентов + description: pass + + country: + title: Страны + description: pass + + currency: + title: Валюты + description: pass + + organization: + title: Организации + description: pass + + region: + title: Регионы + description: pass + + unit: + title: Единицы измерения + description: pass + + agreementtype: + title: Типы договоров + description: pass + + contract: + title: Договоры + description: pass + + draftagreement: + title: Проекты договоров + description: pass + + diff --git a/src/repository_structure_example/information_arch/docs.yaml b/src/repository_structure_example/information_arch/docs.yaml deleted file mode 100644 index bfbc80c..0000000 --- a/src/repository_structure_example/information_arch/docs.yaml +++ /dev/null @@ -1,9 +0,0 @@ -docs: - # Заглушка пока нет описсания по бизнес архитектуре - swamp.information_arch: - location: 03. Информационная архитектура - description: Описание информационной архитектурой - type: markdown - template: information_arch.md - - diff --git a/src/repository_structure_example/information_arch/information_arch.md b/src/repository_structure_example/information_arch/information_arch.md deleted file mode 100644 index 7b74bb4..0000000 --- a/src/repository_structure_example/information_arch/information_arch.md +++ /dev/null @@ -1 +0,0 @@ -# Информационная архитектура находится в разработке \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/_root.yaml b/src/repository_structure_example/metamodels/_root.yaml new file mode 100644 index 0000000..cf0ff68 --- /dev/null +++ b/src/repository_structure_example/metamodels/_root.yaml @@ -0,0 +1,7 @@ +imports: + - custom/_root.yaml + - default/_root.yaml + - validators/_root.yaml + - datasets/_root.yaml + - jsonata/_root.yaml + diff --git a/src/repository_structure_example/metamodels/custom/_root.yaml b/src/repository_structure_example/metamodels/custom/_root.yaml new file mode 100644 index 0000000..21c36ec --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/_root.yaml @@ -0,0 +1,7 @@ +imports: + - business_entities/_root.yaml + - dictionaries/_root.yaml + - dochub_menu/_root.yaml + + + diff --git a/src/repository_structure_example/metamodels/custom/business_entities/_root.yaml b/src/repository_structure_example/metamodels/custom/business_entities/_root.yaml new file mode 100644 index 0000000..9babeec --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/business_entities/_root.yaml @@ -0,0 +1,2 @@ +imports: + - business_entities_model.yaml diff --git a/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml b/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml new file mode 100644 index 0000000..ba2c745 --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml @@ -0,0 +1,119 @@ +entities: # Сущности расширенной метамодели + # Бизнес-сущности + business_entities: # Секция, где будет описываться объекты сущности "взаимодействие". Обязательно.enti + title: Бизнес-сущности # Название сущности. Обязательно. + description: > # Описание сущности текст или ссылка на документ. Необязательно. + Ключевые бизнес-сущности ГК Болото + menu: > # Генератор раcположения в меню. Запрос или явное описание объета. Необязательно. + ( + [ + { + "icon": *.icon, + "link": "entities/business_entities/business_entities_list", + "location": "Документы/03. Информационная архитектура/01. Бизнес-сущности" + }, + { + "icon": *.icon, + "link": "entities/business_entities/business_entities_in_systems", + "location": "Документы/03. Информационная архитектура/02. Список бизнес-сущностей в системах" + } + ] + ) + presentations: + business_entities_table_list: + type: table + headers: + - value: title + text: Бизнес-сущность + sortable: true + align: left + link: link_to_entity + + - value: id + text: ID в DocHub + sortable: true + align: left + width: 10% + + - value: description + text: Описание бизнес-сущности + sortable: false + align: left + source: swamp.dataset.business_entities_list + + # Выводим список бизнес-сущностей полученных в запросе выше business_entities_list + business_entities_list: + type: markdown + template: templates/business_entities_list.md + + # Выводим карточку выбранной бизнес-сущности + business_entity_card: + type: markdown + template: templates/business_entity_card.md + source: > + ( + $entities := [$.business_entities.$spread().$merge([$.*, {"id": $keys($)}])]; + $entities [id=$params.id]; + ) + + # Получаем список бизнес-сущностей используемых в системах, все это выводим в таблицу + business_entities_table_in_systems: + type: table + headers: + - value: system_title + text: Система + sortable: true + align: left + link: link_to_system + + - value: system_entities + text: Фильтр + link: link_to_system_entities + align: left + width: 10% + + - value: title + text: Бизнес-сущность + sortable: true + align: left + link: link_to_entity + + - value: id + text: ID в DocHub + sortable: true + align: left + + source: swamp.dataset.business_entities_in_systems + + # Выводим писок бизнес-сущностей используемых в системах полученных в запросе выше business_entities.in_systems + business_entities_in_systems: + type: markdown + template: templates/business_entities_in_systems.md + + # Наследуемся от таблицы business_entities.in_systems и делаем фильтр по конкретной системе + business_entities_table_in_systems.filtered: # Дочерняя таблица + type: table + headers: + - value: system_title + text: Система + sortable: true + align: left + link: link_to_system + + - value: title + text: Бизнес-сущность + sortable: true + align: left + link: link_to_entity + + - value: id + text: ID в DocHub + sortable: true + align: left + + origin: swamp.dataset.business_entities_in_systems # Базовый источник данных + source: > + ( + $[system_id=$params.system_id] + ) + diff --git a/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_in_systems.md b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_in_systems.md new file mode 100644 index 0000000..585249a --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_in_systems.md @@ -0,0 +1,3 @@ +## Список бизнес-сущностей используемых в системах ГК Болото + +![Получаем список бизнес-сущностей](@entity/business_entities/business_entities_table_in_systems) \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_list.md b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_list.md new file mode 100644 index 0000000..f4a58bf --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entities_list.md @@ -0,0 +1,3 @@ +## Список бизнес-сущностей ГК Болото + +![Получаем список бизнес-сущностей](@entity/business_entities/business_entities_table_list) \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entity_card.md b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entity_card.md new file mode 100644 index 0000000..d30f361 --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/business_entities/templates/business_entity_card.md @@ -0,0 +1,16 @@ +## Карточка бизнес-сущности "{{title}}" + +### Параметры по умолчанию +ID бизнес-сущности: **{{id}}** + +Описание: + +**{{description}}** + +### Параметры + +| Название реквизита | Описание реквизита | +|:-------------------|:-------------------| +{{#parameters}} +| **{{name}}** | {{description}} | +{{/parameters}} diff --git a/src/repository_structure_example/metamodels/custom/dictionaries/_root.yaml b/src/repository_structure_example/metamodels/custom/dictionaries/_root.yaml new file mode 100644 index 0000000..e94662f --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/dictionaries/_root.yaml @@ -0,0 +1,2 @@ +imports: + - dictionaries_model.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml b/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml new file mode 100644 index 0000000..cdc0b30 --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml @@ -0,0 +1,78 @@ +entities: # Сущности расширенной метамодели + # Бизнес-сущности + dictionaries: # Секция, где будет описываться объекты сущности "взаимодействие". Обязательно. + title: Справочники # Название сущности. Обязательно. + description: > # Описание сущности текст или ссылка на документ. Необязательно. + Справочники для хранения различной статической информации используемой внутри DocHub + menu: > + ( + $dictionaries := $.dictionaries; + $makeLocation := function($id) {( + $arrleft := function($arr ,$count) { + $map($arr, function($v, $i) { + $i <= $count ? $v + }) + }; + $domains := $split($id, "."); + "Документы/08. Справочники/" & $join($map($domains, function($domain, $index) {( + $lookup($dictionaries, $join($arrleft($domains, $index), ".")).title + )}), "/"); + )}; + + $append([{ + "icon": *.icon, /* Получаем иконку */ + "link": "entities/dictionaries/tree", /* Ссылка на форму представления tree (дерево дерево объектов "interactions") */ + "location": "Документы/08. Справочники" /* Расположение в меню */ + }], + [$.dictionaries.$spread().{ + "icon": *.icon, /* Получаем иконку */ + "link": "entities/dictionaries/dictionary_card?id=" & $keys()[0], /* Формируем ссылку на бланк документ */ + "location": $makeLocation($keys()[0]) /* Формируем расположение в меню */ + }][location] + ); + ) + + presentations: + tree: + type: plantuml + template: templates/tree.puml + source: > + ( + $set("rpev-id", undefined); + $arrleft := function($arr ,$count) { + $map($arr, function($v, $i) { + $i <= $count ? $v + }) + }; + $dictionaries := $.dictionaries; + [$dictionaries.$spread().($merge([{"id" : $keys()[0]}, $.*]))^(id).( + $prev_nodes := $split($get("rpev-id"), "."); + $prev_level := $count($prev_nodes); + $curr_nodes := $split(id, "."); + $set("isdiff", false); + $result := $map($curr_nodes, function($v, $i) {( + $set("isdiff", $get("isdiff") or $prev_level = 0 or $prev_level <= $i or $v != $prev_nodes[$i]) ? ( + $id := $join($arrleft($curr_nodes, $i), "."); + $dictionary := $lookup($dictionaries, $id); + { + "id": $id, + "level": $pad("", $i + 2, "*"), + "title": $dictionary ? $dictionary.title : $id, + "link": "/entities/dictionaries/dictionary_card?id=" & $id + } + ); + )}); + $set("rpev-id", id); + $result + )]; + ) + # Выводим карточку выбранного справочника + dictionary_card: + type: markdown + template: templates/dictionary_card.md + source: > + ( + $dictionaries := [$.dictionaries.$spread().$merge([$.*, {"id": $keys($)}])]; + $dictionaries [id=$params.id]; + ) + diff --git a/src/repository_structure_example/dictionaries/dictionary_card.md b/src/repository_structure_example/metamodels/custom/dictionaries/templates/dictionary_card.md similarity index 100% rename from src/repository_structure_example/dictionaries/dictionary_card.md rename to src/repository_structure_example/metamodels/custom/dictionaries/templates/dictionary_card.md diff --git a/src/repository_structure_example/dictionaries/tree.puml b/src/repository_structure_example/metamodels/custom/dictionaries/templates/tree.puml similarity index 100% rename from src/repository_structure_example/dictionaries/tree.puml rename to src/repository_structure_example/metamodels/custom/dictionaries/templates/tree.puml diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/_root.yaml b/src/repository_structure_example/metamodels/custom/dochub_menu/_root.yaml similarity index 51% rename from src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/_root.yaml rename to src/repository_structure_example/metamodels/custom/dochub_menu/_root.yaml index f7731fa..565980d 100644 --- a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/_root.yaml +++ b/src/repository_structure_example/metamodels/custom/dochub_menu/_root.yaml @@ -1,3 +1,2 @@ -imports: - # - docs.yaml +imports: - dochub_menu_model.yaml diff --git a/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml b/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml new file mode 100644 index 0000000..56e71b2 --- /dev/null +++ b/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml @@ -0,0 +1,47 @@ +entities: # Сущности расширенной метамодели + # Бизнес-сущности + dochub_menu: # Секция, где будет описываться объекты сущности "взаимодействие". Обязательно. + title: Меню DocHub # Название сущности. Обязательно. + description: > # Описание сущности текст или ссылка на документ. Необязательно. + Структура меню DocHub + menu: > + ( + [{ + "location": "Документы/07. Развитие корп архитектуры/02. Инструменты/01. DocHub/03. Целевая структура меню DocHub", + "link": "entities/dochub_menu/menu_tree" + }] + ) + + presentations: + menu_tree: + type: plantuml + template: templates/menu_tree.puml + source: > + ( + $set("rpev-id", undefined); + $arrleft := function($arr ,$count) { + $map($arr, function($v, $i) { + $i <= $count ? $v + }) + }; + $dochub_menu := $.dochub_menu; + [$dochub_menu.$spread().($merge([{"id" : $keys()[0]}, $.*]))^(order).( + $prev_nodes := $split($get("rpev-id"), "."); + $prev_level := $count($prev_nodes); + $curr_nodes := $split(id, "."); + $set("isdiff", false); + $result := $map($curr_nodes, function($v, $i) {( + $set("isdiff", $get("isdiff") or $prev_level = 0 or $prev_level <= $i or $v != $prev_nodes[$i]) ? ( + $id := $join($arrleft($curr_nodes, $i), "."); + $menu := $lookup($dochub_menu, $id); + { + "id": $id, + "level": $pad("", $i + 2, "*"), + "title": $menu ? $menu.title : $id + } + ); + )}); + $set("rpev-id", id); + $result + )]; + ) diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/menu_tree.puml b/src/repository_structure_example/metamodels/custom/dochub_menu/templates/menu_tree.puml similarity index 100% rename from src/repository_structure_example/enterprise_arch/tools/dochub/dochub_menu/menu_tree.puml rename to src/repository_structure_example/metamodels/custom/dochub_menu/templates/menu_tree.puml diff --git a/src/repository_structure_example/settings/datasets/_root.yaml b/src/repository_structure_example/metamodels/datasets/_root.yaml similarity index 100% rename from src/repository_structure_example/settings/datasets/_root.yaml rename to src/repository_structure_example/metamodels/datasets/_root.yaml diff --git a/src/repository_structure_example/metamodels/datasets/datasets.yaml b/src/repository_structure_example/metamodels/datasets/datasets.yaml new file mode 100644 index 0000000..a2ddf2a --- /dev/null +++ b/src/repository_structure_example/metamodels/datasets/datasets.yaml @@ -0,0 +1,68 @@ +datasets: + # Получаем список систем с линками на карточку системы + swamp.dataset.systems_list: + source: > + ( + $REPO := $; + [$distinct([components.$spread().( + $COMPONENT_ID := $keys()[0]; + $BASE_ID := $split($COMPONENT_ID, ".", 2) ~> $join('.'); + + $systems := $.*.{ + "id": $COMPONENT_ID, + "link_to_system": "/architect/components/" & $COMPONENT_ID, + "title": title, + "entity": entity, + "system_entities": "Ссылка", + "link_to_system_entities": "/entities/business_entities/business_entities_table_in_systems.filtered?system_id=" & $COMPONENT_ID, + "description": description, + "application_owner": application_owner, + "owner_unit": $BASE_ID ? $lookup($REPO.components, $BASE_ID).title : $BASE_ID + }; + $systems[entity="system"]; + )])^(id)] + + ) + + # Получаем список бизнес-сущностей с линками на карточку бизнес-cущности + swamp.dataset.business_entities_list: + source: > + ( + [$.business_entities.$spread().( + $ENTITY_ID := $keys()[0]; + $entities := $.*.{ + "id": $ENTITY_ID, + "title": title, + "description": description, + "link_to_entity": "/entities/business_entities/business_entity_card?id=" & $ENTITY_ID + }; + )]^(title) + ) + + # Cписок бизнес-сущностей в системах с линками на карточку бизнес-cущности + swamp.dataset.business_entities_in_systems: # Идентификатор источника + source: > # JSONata запрос к архитектуре + ( + $MANIFEST := $; + $distinct([$MANIFEST.components.$spread().( + $COMPONENT := $; + $COMPONENT_ID := $keys()[0]; + $business_entities := $distinct($COMPONENT.*.business_entities.( + $ENTITY := $; + $ENTITIES := $lookup($MANIFEST.business_entities, $ENTITY); + $ENTITIES := $merge([$ENTITIES, {"id": $ENTITY}]); + $COMPONENT := $lookup($MANIFEST.components, $COMPONENT_ID); + { + "id": $ENTITIES.id, /* ID бизнес-сущности в DocHub */ + "title": $type($ENTITIES.title)="string" ? $ENTITIES.title : "Ошибка в идентификаторе бизнес-сущности", + "link_to_entity": $type($ENTITIES.title)="string" ? "/entities/business_entities/business_entity_card?id=" & $ENTITIES.id : "", + "system_id": $COMPONENT_ID, + "link_to_system": "/architect/components/" & $COMPONENT_ID, + "system_title": $COMPONENT.title, + "system_entities": "Отбор по системе", + "link_to_system_entities": "/entities/business_entities/business_entities_table_in_systems.filtered?system_id=" & $COMPONENT_ID + } + ) + ); + )])^(system_id) + ) \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/_root.yaml b/src/repository_structure_example/metamodels/default/_root.yaml new file mode 100644 index 0000000..f3ca871 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/_root.yaml @@ -0,0 +1,4 @@ +imports: + - components/_root.yaml + - users/_root.yaml + - functions/_root.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/components/_root.yaml b/src/repository_structure_example/metamodels/default/components/_root.yaml new file mode 100644 index 0000000..db1257f --- /dev/null +++ b/src/repository_structure_example/metamodels/default/components/_root.yaml @@ -0,0 +1,3 @@ +imports: + - forms.yaml + - components_extension.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/components/components_extension.yaml b/src/repository_structure_example/metamodels/default/components/components_extension.yaml new file mode 100644 index 0000000..2edd1a1 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/components/components_extension.yaml @@ -0,0 +1,71 @@ +entities: + components: + # Расширяем презентацию сущности + presentations: + # Встраиваемся в основную перезентацию (т.е. она уже существует) + blank: + # Добавляем собственный виджет + widgets: + outside_links: # Идентификатор виджета + # Название виджета + title: Кто зависит от этого компонента + # Идентификатор презентации, которая будет использоваться в виджете + presentation: outside_links + # В params можно передать дополнительные параметры + # презентации, которые будут доступны в перемнной $params + #params: + # param1: Значение параметра 1 + # param2: Значение параметра 2 + # Расположение + # < - слева + # > - справа + # = - Растянуть на 100% + align: "<" + # Добавляем собственную презентацию + # Будем показывать внешние связи на данный компонент + outside_links: # Идентификатор представления + # Контракт по параметрам представления в формате JSONSchema + params: + type: object + properties: + component: + title: Идентификатор компонента + type: string + required: + - component + # Тип документа шаблона + type: table + # Определяем заголовок таблицы + headers: + - value: id # Идентификатор зависимого компонента + text: Идентификатор # Заголовок колокнки + sortable: true # Производить сортировку по колонке + align: left # Форматирование по горизонтали + width: 40% # Ширина колонки + link: link + - value: title # Наименование зависимого компонента + text: Наименование + sortable: true + align: left # Форматирование по горизонтали + width: 40% + link: link + - value: direction # Направление связи + text: Направление связи + sortable: true + align: center + width: 20% + # Данные для шаблона + source: > + ( + [$distinct(components.$spread().( + $id := $keys()[0]; + $title := *.title; + $links := $distinct($.*.links[id=$params.component].("" & direction)); + $links.{ + "id": $id, + "title": $title, + "direction": $links, + "link": "/architect/components/" & $id + } + ))^(id)] + ) \ No newline at end of file diff --git a/src/repository_structure_example/settings/forms.yaml b/src/repository_structure_example/metamodels/default/components/forms.yaml similarity index 97% rename from src/repository_structure_example/settings/forms.yaml rename to src/repository_structure_example/metamodels/default/components/forms.yaml index b8298cf..989c549 100644 --- a/src/repository_structure_example/settings/forms.yaml +++ b/src/repository_structure_example/metamodels/default/components/forms.yaml @@ -5,6 +5,7 @@ forms: # Формы - component - database - person + - unit fields: # Поля формы description: # Идентификатор поля title: Детальное описание # Название поля diff --git a/src/repository_structure_example/settings/jsonata/_root.yaml b/src/repository_structure_example/metamodels/default/functions/_root.yaml similarity index 100% rename from src/repository_structure_example/settings/jsonata/_root.yaml rename to src/repository_structure_example/metamodels/default/functions/_root.yaml diff --git a/src/repository_structure_example/metamodels/default/functions/functions.yaml b/src/repository_structure_example/metamodels/default/functions/functions.yaml new file mode 100644 index 0000000..c15a0dd --- /dev/null +++ b/src/repository_structure_example/metamodels/default/functions/functions.yaml @@ -0,0 +1,9 @@ +aspects: + qa_systems: + title: QA системы + location: QA системы + + sales: + title: Продажи + location: Продажи + \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/users/_root.yaml b/src/repository_structure_example/metamodels/default/users/_root.yaml new file mode 100644 index 0000000..36926bc --- /dev/null +++ b/src/repository_structure_example/metamodels/default/users/_root.yaml @@ -0,0 +1,2 @@ +imports: + - users.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/users/users.yaml b/src/repository_structure_example/metamodels/default/users/users.yaml new file mode 100644 index 0000000..10bee4a --- /dev/null +++ b/src/repository_structure_example/metamodels/default/users/users.yaml @@ -0,0 +1,14 @@ +components: + # внутренние клиенты Экосистемы + + swamp.frog: + title: Лягушка + entity: person + + swamp.creeping_snake: + title: Змей-ползучий + entity: person # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) + + swamp.Moidodyr: + title: Мойдодыр + entity: person # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) diff --git a/src/repository_structure_example/metamodels/jsonata/_root.yaml b/src/repository_structure_example/metamodels/jsonata/_root.yaml new file mode 100644 index 0000000..4cb01e1 --- /dev/null +++ b/src/repository_structure_example/metamodels/jsonata/_root.yaml @@ -0,0 +1,2 @@ +imports: + - functions.yaml \ No newline at end of file diff --git a/src/repository_structure_example/settings/jsonata/functions.yaml b/src/repository_structure_example/metamodels/jsonata/functions.yaml similarity index 100% rename from src/repository_structure_example/settings/jsonata/functions.yaml rename to src/repository_structure_example/metamodels/jsonata/functions.yaml diff --git a/src/repository_structure_example/metamodels/validators/_root.yaml b/src/repository_structure_example/metamodels/validators/_root.yaml new file mode 100644 index 0000000..a29194e --- /dev/null +++ b/src/repository_structure_example/metamodels/validators/_root.yaml @@ -0,0 +1,6 @@ +imports: + - technologies.yaml + - systems.yaml + + + diff --git a/src/repository_structure_example/metamodels/validators/systems.yaml b/src/repository_structure_example/metamodels/validators/systems.yaml new file mode 100644 index 0000000..e2b6010 --- /dev/null +++ b/src/repository_structure_example/metamodels/validators/systems.yaml @@ -0,0 +1,62 @@ +rules: + validators: + system: # https://dochub.samoletgroup.ru/docs/dochub_system_structure + title: Валидатор систем/сервисов + schema: # JSON Schema + type: object + properties: + # ------------------------ + short_description: + type: string + minLength: 10 + # ------------------------ + critical_level: + enum: + - low + - middle + - high + - critical + # ------------------------ + customer_type: + type: array + minItems: 1 + items: + type: string + minLength: 3 + # ------------------------ + system_category: + type: array + minItems: 1 + items: + type: string + minLength: 5 + # ------------------------ + required: + - short_description + - critical_level + - customer_type + - system_category + + additionalProperties: true + source: > # Источник данных об ошибках + ( + /* Создаем валидатор JSON schema */ + $validator := $jsonschema(rules.validators.system.schema); /* Схему валидата получаем из контекста отклонения (rules.validators.system.schema)*/ + /* Формируем базу для проверки */ + ([([ + components_test.$spread().( /* Сканируем все компоненты */ + $ID := $keys()[0]; + { /* Генерируем массив признаков проблем */ + "isSystem": *.entity = "system", /* Это система */ + "isFitDomain": $boolean($match($ID, /samolet\.*\.*/)), + "id": $ID, /* Запоминаем идентификатор компонента */ + "isvalid": $validator($.*) /* Валидируем компонент по схеме */ + } + ) + ][isFitDomain and isSystem and isvalid != true]).isvalid.{ /* Генерируем отклонения по выявленным нарушениям */ + "uid": %.id & "-" & $replace($replace(instancePath, "/", "-"), "-", "",1), /* Уникальный идентификатор выявленной ошибки */ + "location": "/architect/components/" & %.id, /* Ссылка на расположение объекта ошибки */ + "correction": "Корректно заполните реквизит: "& $replace(instancePath, "/", "",1), /* Рекомендации как исправить проблему */ + "description": message + }]) + ) diff --git a/src/repository_structure_example/metamodels/validators/technologies.yaml b/src/repository_structure_example/metamodels/validators/technologies.yaml new file mode 100644 index 0000000..68aab82 --- /dev/null +++ b/src/repository_structure_example/metamodels/validators/technologies.yaml @@ -0,0 +1,26 @@ +rules: + validators: + samolet.technologies: + title: Контроль технологий + samolet.technologies.empty: + title: У системы не указаны технологии + source: > + ( + $MANIFEST := $; + $matcher := /^.*[world, samolet]\.partners.*$/; + $MANIFEST.components.$spread().( + $COMPONENT_ID := $keys()[0]; + $matcher($COMPONENT_ID) ? '' : $.*.{ + "id": $COMPONENT_ID, + "entity": entity, + "technologies": technologies + } + )[entity="system" and ($not(technologies) or $count(technologies) = 0) or ("TBD" in technologies)].( + { + "uid": "$smlt-technologies-empty-" & id, + "location": "/architect/components/" & id, + "correction": "Укажите технологии системы", + "description": "У каждой системы, разрабатываемой ГК Болото, должен быть указан список технологий" + } + ) + ) diff --git a/src/repository_structure_example/settings/_root.yaml b/src/repository_structure_example/settings/_root.yaml deleted file mode 100644 index 3596cd2..0000000 --- a/src/repository_structure_example/settings/_root.yaml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - forms.yaml - - validators/_root.yaml - - datasets/_root.yaml - - components_extension.yaml - - jsonata/_root.yaml \ No newline at end of file diff --git a/src/repository_structure_example/settings/components_extension.yaml b/src/repository_structure_example/settings/components_extension.yaml deleted file mode 100644 index 3a485f0..0000000 --- a/src/repository_structure_example/settings/components_extension.yaml +++ /dev/null @@ -1,10 +0,0 @@ -# Здкесь делаем новые виджеты для карточки систем -entities: - components: - # Расширяем презентацию сущности - presentations: - # Встраиваемся в основную перезентацию (т.е. она уже существует) - blank: - # Добавляем собственный виджет - widgets: - sys_responsible_employees: # Идентификатор виджета diff --git a/src/repository_structure_example/settings/datasets/datasets.yaml b/src/repository_structure_example/settings/datasets/datasets.yaml deleted file mode 100644 index 3d7ae4c..0000000 --- a/src/repository_structure_example/settings/datasets/datasets.yaml +++ /dev/null @@ -1 +0,0 @@ -datasets: diff --git a/src/repository_structure_example/settings/validators/_root.yaml b/src/repository_structure_example/settings/validators/_root.yaml deleted file mode 100644 index 1876f58..0000000 --- a/src/repository_structure_example/settings/validators/_root.yaml +++ /dev/null @@ -1,4 +0,0 @@ -imports: - - - diff --git a/src/repository_structure_example/standards/arch_principles/docs.yaml b/src/repository_structure_example/standards/arch_principles/docs.yaml index 87c8e92..e7baea2 100644 --- a/src/repository_structure_example/standards/arch_principles/docs.yaml +++ b/src/repository_structure_example/standards/arch_principles/docs.yaml @@ -1,10 +1,4 @@ docs: - arch_introduction: - location: 01. Архитектурные принципы и стандарты/01. Введение в архитектуру - description: Введение - type: markdown - source: introduction.md - arch_principles: location: 01. Архитектурные принципы и стандарты/02. Архитектурные принципы description: Архитектурные принципы diff --git a/src/repository_structure_example/standards/arch_principles/introduction.md b/src/repository_structure_example/standards/arch_principles/introduction.md deleted file mode 100644 index 20abd62..0000000 --- a/src/repository_structure_example/standards/arch_principles/introduction.md +++ /dev/null @@ -1,6 +0,0 @@ -# Введение - -## Что такое архитектура компании[^1] - - -[^1]: Термины «архитектура компании», «корпоративная архитектура», «enterprise architecture» здесь и далее являются синонимами​. From 7b0a5b285e15447e66cbea3aa09de098caf34900 Mon Sep 17 00:00:00 2001 From: ValentinKozlov Date: Thu, 4 May 2023 11:44:41 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D1=8D=D0=BA=D1=81=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application_arch/_root.yaml | 1 + .../systems/1cbit_finance.yaml | 4 +- .../application_arch/systems/spoll.yaml | 4 +- .../users/_root.yaml | 0 .../users/users.yaml | 0 .../artefacts/common/pal1_landscape.yaml | 10 +- .../frog_paradise/pal1_landscape.yaml | 12 +- .../documentation/docs.yaml | 17 + .../documentation/examples/example.html | 2 + .../documentation/examples/test.md | 1 + .../ex_from_max/product.yaml | 54 + .../metamodels/_root.yaml | 2 +- .../business_entities_model.yaml | 4 +- .../dictionaries/dictionaries_model.yaml | 4 +- .../custom/dochub_menu/dochub_menu_model.yaml | 2 +- .../metamodels/default/_root.yaml | 9 +- .../metamodels/default/components/_root.yaml | 3 - .../metamodels/default/entities/_root.yaml | 5 + .../default/entities/aspects/_root.yaml | 0 .../default/entities/components/_root.yaml | 4 + .../components/components_extension.yaml | 0 .../entities/components/components_model.yaml | 6 + .../{ => entities}/components/forms.yaml | 0 .../default/entities/contexts/_root.yaml | 5 + .../default/entities/contexts/base.yaml | 192 ++ .../entities/contexts/c4_context copy.yaml | 309 +++ .../default/entities/contexts/c4_context.yaml | 200 ++ .../default/entities/contexts/plantuml.yaml | 208 ++ .../default/entities/contexts/smartants.yaml | 165 ++ .../contexts/templates/container.puml | 1985 +++++++++++++++++ .../entities/contexts/templates/context.puml | 1847 +++++++++++++++ .../entities/contexts/templates/template.puml | 188 ++ .../default/entities/documents/_root.yaml | 3 + .../default/entities/documents/base.yaml | 95 + .../default/entities/documents/blank.yaml | 28 + .../metamodels/default/rules/_root.yaml | 2 + .../metamodels/default/rules/validators.yaml | 144 ++ .../default/samples/01_components.yaml | 23 + .../default/samples/02_contexts.yaml | 23 + .../default/samples/03_documents.yaml | 10 + .../metamodels/default/samples/_root.yaml | 4 + .../metamodels/default/samples/test.puml | 17 + 42 files changed, 5564 insertions(+), 28 deletions(-) rename src/repository_structure_example/{metamodels/default => application_arch}/users/_root.yaml (100%) rename src/repository_structure_example/{metamodels/default => application_arch}/users/users.yaml (100%) create mode 100644 src/repository_structure_example/documentation/examples/example.html create mode 100644 src/repository_structure_example/documentation/examples/test.md create mode 100644 src/repository_structure_example/ex_from_max/product.yaml delete mode 100644 src/repository_structure_example/metamodels/default/components/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/aspects/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/components/_root.yaml rename src/repository_structure_example/metamodels/default/{ => entities}/components/components_extension.yaml (100%) create mode 100644 src/repository_structure_example/metamodels/default/entities/components/components_model.yaml rename src/repository_structure_example/metamodels/default/{ => entities}/components/forms.yaml (100%) create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/base.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/c4_context.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/smartants.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/templates/context.puml create mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml create mode 100644 src/repository_structure_example/metamodels/default/entities/documents/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/documents/base.yaml create mode 100644 src/repository_structure_example/metamodels/default/entities/documents/blank.yaml create mode 100644 src/repository_structure_example/metamodels/default/rules/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/rules/validators.yaml create mode 100644 src/repository_structure_example/metamodels/default/samples/01_components.yaml create mode 100644 src/repository_structure_example/metamodels/default/samples/02_contexts.yaml create mode 100644 src/repository_structure_example/metamodels/default/samples/03_documents.yaml create mode 100644 src/repository_structure_example/metamodels/default/samples/_root.yaml create mode 100644 src/repository_structure_example/metamodels/default/samples/test.puml diff --git a/src/repository_structure_example/application_arch/_root.yaml b/src/repository_structure_example/application_arch/_root.yaml index 9fe43f1..2d4c6b1 100644 --- a/src/repository_structure_example/application_arch/_root.yaml +++ b/src/repository_structure_example/application_arch/_root.yaml @@ -1,4 +1,5 @@ imports: - reports/_root.yaml - systems/_root.yaml + - users/_root.yaml - docs.yaml diff --git a/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml b/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml index e86cd76..335554f 100644 --- a/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml +++ b/src/repository_structure_example/application_arch/systems/1cbit_finance.yaml @@ -11,8 +11,8 @@ components: system_category: business_app #channel_app/business_app/ext_business_app/it_app/ext_it_app links: # Cвязи с системами - - id: swamp.crocodile.spact - direction: <-- + # - id: swamp.crocodile.spact + # direction: <-- - id: swamp.frog.spoll direction: <-- - id: swamp.crocodile.crm diff --git a/src/repository_structure_example/application_arch/systems/spoll.yaml b/src/repository_structure_example/application_arch/systems/spoll.yaml index ba149cf..44995da 100644 --- a/src/repository_structure_example/application_arch/systems/spoll.yaml +++ b/src/repository_structure_example/application_arch/systems/spoll.yaml @@ -24,8 +24,8 @@ components: direction: <-- - id: swamp.crocodile.crm direction: <-- - - id: swamp.crocodile.spact - direction: <-- + # - id: swamp.crocodile.spact + # direction: <-- diff --git a/src/repository_structure_example/metamodels/default/users/_root.yaml b/src/repository_structure_example/application_arch/users/_root.yaml similarity index 100% rename from src/repository_structure_example/metamodels/default/users/_root.yaml rename to src/repository_structure_example/application_arch/users/_root.yaml diff --git a/src/repository_structure_example/metamodels/default/users/users.yaml b/src/repository_structure_example/application_arch/users/users.yaml similarity index 100% rename from src/repository_structure_example/metamodels/default/users/users.yaml rename to src/repository_structure_example/application_arch/users/users.yaml diff --git a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml index 2cd42d7..0d33218 100644 --- a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml @@ -1,13 +1,13 @@ contexts: # Контексты представления архитектурных компонентов arch.swamp.common: title: ГК Болото - location: Верхнеуровневый прикладной ландшафт (ПА-L1)/ГК Болото -# extra-links: true - extra-links: false + location: ГК Болото + extra-links: true + # extra-links: false uml: - $notation: plantuml # sber C4Model plantuml + # $notation: plantuml # sber C4Model plantuml $autor: Frog - $version: 0.0.1 + $version: '0.0.1' $moment: 20.11.2022 components: - swamp.frog.* diff --git a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml index b1a85c2..58a31b4 100644 --- a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml @@ -1,13 +1,13 @@ contexts: # Контексты представления архитектурных компонентов arch.swamp.frog_paradise: title: Верхнеуровневый прикладной ландшафт (ПА-L1) БЮ Лягушачий рай/ - location: Верхнеуровневый прикладной ландшафт (ПА-L1)/Лягушачий рай + location: Лягушачий рай # extra-links: true extra-links: false - uml: - $notation: plantuml # sber C4Model plantuml - $autor: Frog - $version: 0.0.1 - $moment: 20.11.2022 + # uml: + # $notation: plantuml # sber C4Model plantuml + # $autor: Frog + # $version: 0.0.1 + # $moment: 20.11.2022 components: - swamp.frog.* diff --git a/src/repository_structure_example/documentation/docs.yaml b/src/repository_structure_example/documentation/docs.yaml index e5443b6..8a0d9da 100644 --- a/src/repository_structure_example/documentation/docs.yaml +++ b/src/repository_structure_example/documentation/docs.yaml @@ -15,3 +15,20 @@ docs: type: markdown source: useful_links/useful_links.md + + plugins.frog: + location: 09. Документация/Плагины/1. Мой плагин + type: test + params: + param1: Значение параметра 1 + param2: Значение параметра 2 + + source: examples/test.md" + + dochub.plugins.example: + location: 09. Документация/Плагины/2. Пример Ромы + type: html + headers: + head1: + type: test + source: examples/example.html \ No newline at end of file diff --git a/src/repository_structure_example/documentation/examples/example.html b/src/repository_structure_example/documentation/examples/example.html new file mode 100644 index 0000000..22d70f6 --- /dev/null +++ b/src/repository_structure_example/documentation/examples/example.html @@ -0,0 +1,2 @@ +

Привет!

+

Это простейший пример HTML-документа, который выводится с использованием плагина DocHub.

diff --git a/src/repository_structure_example/documentation/examples/test.md b/src/repository_structure_example/documentation/examples/test.md new file mode 100644 index 0000000..36033b3 --- /dev/null +++ b/src/repository_structure_example/documentation/examples/test.md @@ -0,0 +1 @@ +Привет 2 \ No newline at end of file diff --git a/src/repository_structure_example/ex_from_max/product.yaml b/src/repository_structure_example/ex_from_max/product.yaml new file mode 100644 index 0000000..f843403 --- /dev/null +++ b/src/repository_structure_example/ex_from_max/product.yaml @@ -0,0 +1,54 @@ +contexts: + arch.products.details: + source: arch.levels.l1 + api: + tags: + product: + $bgColor: LimeGreen + fetchTitle: > + ( + $lookup(manifest.products, componentId).title + ) + fetchComponents: > + ( + $p_id := $params.componentId; + $manifest := $params.manifest; + $product := $append([], $manifest.components.$spread().{ + "id": $keys()[0], + "component": $.* + }.( + $exists(component.products) and $p_id in component.products ? ( + id + ) + )); + $merge($manifest.components.$spread().{ + "id": $keys()[0], + "component": $.* + }[id in $distinct($append($append($manifest.components.$spread().{ + "c_id": $keys()[0], + "component": $.* + }.( + $exists(component.entity) and component.entity in ["system", "database", "actor"] ? ( + component.links[id in $product] ? ( + c_id + ) + ) + ), + $product.( + $lookup($manifest.components, $).links.( + $.id + ) + ) + ), $product))].( + { + id : id in $product ? ( + $merge([ + component, + { "tags" : ["product"]} + ]) + ) : (component) + } + )) + ) + + diff --git a/src/repository_structure_example/metamodels/_root.yaml b/src/repository_structure_example/metamodels/_root.yaml index cf0ff68..36a80e4 100644 --- a/src/repository_structure_example/metamodels/_root.yaml +++ b/src/repository_structure_example/metamodels/_root.yaml @@ -1,6 +1,6 @@ imports: - custom/_root.yaml - - default/_root.yaml + # - default/_root.yaml - validators/_root.yaml - datasets/_root.yaml - jsonata/_root.yaml diff --git a/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml b/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml index ba2c745..4043aa7 100644 --- a/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml +++ b/src/repository_structure_example/metamodels/custom/business_entities/business_entities_model.yaml @@ -10,12 +10,12 @@ entities: # Сущности расширенной метамодели { "icon": *.icon, "link": "entities/business_entities/business_entities_list", - "location": "Документы/03. Информационная архитектура/01. Бизнес-сущности" + "location": "03. Информационная архитектура/01. Бизнес-сущности" }, { "icon": *.icon, "link": "entities/business_entities/business_entities_in_systems", - "location": "Документы/03. Информационная архитектура/02. Список бизнес-сущностей в системах" + "location": "03. Информационная архитектура/02. Список бизнес-сущностей в системах" } ] ) diff --git a/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml b/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml index cdc0b30..87fa487 100644 --- a/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml +++ b/src/repository_structure_example/metamodels/custom/dictionaries/dictionaries_model.yaml @@ -14,7 +14,7 @@ entities: # Сущности расширенной метамодели }) }; $domains := $split($id, "."); - "Документы/08. Справочники/" & $join($map($domains, function($domain, $index) {( + "08. Справочники/" & $join($map($domains, function($domain, $index) {( $lookup($dictionaries, $join($arrleft($domains, $index), ".")).title )}), "/"); )}; @@ -22,7 +22,7 @@ entities: # Сущности расширенной метамодели $append([{ "icon": *.icon, /* Получаем иконку */ "link": "entities/dictionaries/tree", /* Ссылка на форму представления tree (дерево дерево объектов "interactions") */ - "location": "Документы/08. Справочники" /* Расположение в меню */ + "location": "08. Справочники" /* Расположение в меню */ }], [$.dictionaries.$spread().{ "icon": *.icon, /* Получаем иконку */ diff --git a/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml b/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml index 56e71b2..87881ce 100644 --- a/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml +++ b/src/repository_structure_example/metamodels/custom/dochub_menu/dochub_menu_model.yaml @@ -7,7 +7,7 @@ entities: # Сущности расширенной метамодели menu: > ( [{ - "location": "Документы/07. Развитие корп архитектуры/02. Инструменты/01. DocHub/03. Целевая структура меню DocHub", + "location": "07. Развитие корп архитектуры/02. Инструменты/01. DocHub/03. Целевая структура меню DocHub", "link": "entities/dochub_menu/menu_tree" }] ) diff --git a/src/repository_structure_example/metamodels/default/_root.yaml b/src/repository_structure_example/metamodels/default/_root.yaml index f3ca871..28a2f4f 100644 --- a/src/repository_structure_example/metamodels/default/_root.yaml +++ b/src/repository_structure_example/metamodels/default/_root.yaml @@ -1,4 +1,5 @@ -imports: - - components/_root.yaml - - users/_root.yaml - - functions/_root.yaml \ No newline at end of file +imports: + - functions/_root.yaml + - entities/_root.yaml + - rules/_root.yaml + # - samples/_root.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/components/_root.yaml b/src/repository_structure_example/metamodels/default/components/_root.yaml deleted file mode 100644 index db1257f..0000000 --- a/src/repository_structure_example/metamodels/default/components/_root.yaml +++ /dev/null @@ -1,3 +0,0 @@ -imports: - - forms.yaml - - components_extension.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/_root.yaml b/src/repository_structure_example/metamodels/default/entities/_root.yaml new file mode 100644 index 0000000..9646190 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/_root.yaml @@ -0,0 +1,5 @@ +imports: + - components/_root.yaml + - aspects/_root.yaml + - contexts/_root.yaml + - documents/_root.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/aspects/_root.yaml b/src/repository_structure_example/metamodels/default/entities/aspects/_root.yaml new file mode 100644 index 0000000..e69de29 diff --git a/src/repository_structure_example/metamodels/default/entities/components/_root.yaml b/src/repository_structure_example/metamodels/default/entities/components/_root.yaml new file mode 100644 index 0000000..a57a9cf --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/components/_root.yaml @@ -0,0 +1,4 @@ +imports: + - forms.yaml + - components_extension.yaml + - components_model.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/components/components_extension.yaml b/src/repository_structure_example/metamodels/default/entities/components/components_extension.yaml similarity index 100% rename from src/repository_structure_example/metamodels/default/components/components_extension.yaml rename to src/repository_structure_example/metamodels/default/entities/components/components_extension.yaml diff --git a/src/repository_structure_example/metamodels/default/entities/components/components_model.yaml b/src/repository_structure_example/metamodels/default/entities/components/components_model.yaml new file mode 100644 index 0000000..45c899d --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/components/components_model.yaml @@ -0,0 +1,6 @@ +entities: + components: + title: Архитектурный компонент + description: > + Компоненты являются базовыми сущностями DocHub. + На их основе автоматически генерируются диаграммы связей. diff --git a/src/repository_structure_example/metamodels/default/components/forms.yaml b/src/repository_structure_example/metamodels/default/entities/components/forms.yaml similarity index 100% rename from src/repository_structure_example/metamodels/default/components/forms.yaml rename to src/repository_structure_example/metamodels/default/entities/components/forms.yaml diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/_root.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/_root.yaml new file mode 100644 index 0000000..6779139 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/_root.yaml @@ -0,0 +1,5 @@ +imports: + - base.yaml + - plantuml.yaml + - smartants.yaml + - c4_context.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml new file mode 100644 index 0000000..ab42524 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml @@ -0,0 +1,192 @@ +# Базовое описание сущности "Contexts" +entities: + contexts: + title: Контексты + description: > + Представлять указанные архитектурные компоненты в виде диаграмм. + schema: + type: object + patternProperties: + "^[a-zA-Z][a-zA-Z0-9_-]*(\\.[a-zA-Z][a-zA-Z0-9_-]*)*$": + type: object + properties: + title: + title: Название контекста + type: string + location: + title: Место расположения в меню + type: string + icon: + title: Иконка в меню + type: string + components: + title: Комппоненты контекста + type: array + items: + type: string + title: Идентификатор компонента или маска + pattern: "^[a-zA-Z][a-zA-Z0-9\\_]*(\\.([a-zA-Z][a-zA-Z0-9\\_]*|\\*))*$" + extra-links: + title: Отображать компоненты ближайших связей + type: boolean + presentation: + title: Принудительно определяет презентацию + type: string + enum: + - plantuml + - smartants + uml: + title: Кастомизация контекста + oneOf: + - type: string + title: Путь к файлу PlantUML + - type: object + title: Дополнительные параметры + properties: + $before: + title: Код встраиваемый до сгенерированного + type: string + $after: + title: Код встраиваемый после сгенерированного + type: string + api: + type: object + title: Переопределяемые API + properties: + fetchComponents: + type: string + title: Возвращает список компонентов в кормате ключ-значение + fetchLinks: + type: string + title: Возвращает список связей компонентов + source: + title: Источник данный (запрос или dataset) + type: string + anyOf: + - type: string + title: Идентификатор dataset + pattern: "^[a-zA-Z][a-zA-Z0-9\_]*(\\.[a-zA-Z][a-zA-Z0-9\\_]*)*$" + - type: string + title: JSONata запрос + pattern: "\\s*\\((.|\\s)*\\)\\s*" + required: + - title + - components + additionalProperties: false + # Определяет пути к объектам сущности + routes: + context: "/:id" # Путь к контексту + menu: > + ( + $presentation := entities.contexts.config.defaultPresentation; + $append([ + { + "title": "Контексты", + "location": '04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)', + "icon": 'location_searching' + } + ], + contexts.$spread().( + /* Если указана явно презентация, используем ее */ + $presentation := $.*.presentation ? *.presentation : $presentation; + *.location ? { + "location": "04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)/" & *.location, + "link": "entities/contexts/" & $presentation & "?dh-context-id=" & $keys()[0] + } + ) + ); + ) + # Предопределенные конфигурационные параметры для генерации контекстов + config: + # Идентификатор презентации по умолчанию + defaultPresentation: plantuml # plantuml / smartants / c4_context + # Общие API функции для генерирования контекста + api: + # Возввращает компоненты входящие в контекст + # Входящие параметры: + # manifest - данные архитектуры + # contextId - идентификатор контекста + # extra-links - признак необходимости отразить окружение + # componentId - идентификатор компонента для контекста SELF + fetchComponents: > + ( + /* Обрабатываем параметры */ + $params := $; + $manifest := $params.manifest; + $context := $lookup($params.manifest.contexts, contextId); + + /* Определяем необходимость показывать ближайшие связи */ + $isExtraLinks := $params.componentId ? true : $params."extra-links"; + + /* Определяем, какие компоненты покажем в контексте */ + $showComponents := $params.componentId + /* Если контекст под определенный компонент, ограничиваемся им. */ + ? [$params.componentId] + /* Если нет берем все компоненты указанные в контексте */ + : $context.components; + + /* Если в контексте переопределена функция получения компонентов, используем ее*/ + $context.api.fetchComponents ? ( + $eval($context.api.fetchComponents, $params) + ) : ( + /* Получаем все компоненты входящие в контекст */ + $components := $merge($showComponents.( + $mask := $; + $manifest.components.$spread().( + $componentId := $keys()[0]; + $wcard($componentId, $mask) ? $ + ) + )); + + /* Добавляем окружение, если это нужно */ + $merge([$components, $isExtraLinks ? ( + $components.*.links.( + { + id: $lookup($manifest.components, id) + } + ); + ) : {}]); + ) + ) + # На основании списка компонентов генерирует массиа областей которые они затрагивают + # Входящие параметры: + # components - список компонентов в формате fetchComponents + fetchAreas: > + ( + $distinct(components.$spread().( + $componentId := $keys()[0]; + $domains := $componentId.$split("."); + $limit := $count($domains) - 1; + $areaId := $map($domains, function($v, $i) {( + $join($map($domains, function($sv, $si) { + $si <= $i ? $sv + }), ".") + )}); + ))^($); + ) + # Генерирует список отображаемых связей + # Входящие параметры: + # components - список компонентов в формате fetchComponents + fetchLinks: > + ( + /* Обрабатываем параметры */ + $components := components; + $distinct( + $components.$spread().( + $from := $keys()[0]; + $direction := $.*.direction ? $.*.direction : "--"; + $.*.links[$lookup($components, id)].( + $title := title ? title : contract; + $title := contract ? ("[[/docs/" & contract & " " & $title & "]]") : $title; + { + "from": $from, + "to": (id ? id : "undefined"), + "title": $title, + "contract": contract, + "direction": direction ? direction : "--" + } + ) + ) + ); + ) + diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml new file mode 100644 index 0000000..aee6e4a --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml @@ -0,0 +1,309 @@ +entities: + contexts: + menu: > + ( + $presentation := entities.contexts.config.defaultPresentation; + $append([ + { + "title": "Контексты", + "location": '04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)', + "icon": 'location_searching' + } + ], + contexts.$spread().( + /* Если указана явно презентация, используем ее */ + $presentation := $.*.presentation ? *.presentation : $presentation; + *.location ? { + "location": "04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)/" & *.location, + "link": "entities/contexts/" & $presentation & "?dh-context-id=" & $keys()[0] + } + ) + ); + ) + config: + defaultPresentation: c4_context + renderCore: elk + api: + c4_context: + mapping: + system: System + database: SystemDb + person: Person + actor: Person + # Возввращает компоненты входящие в контекст + # Входящие параметры: + # manifest - данные архитектуры + # contextId - идентификатор контекста + # extra-links - признак необходимости отразить окружение + # componentId - идентификатор компонента для контекста SELF + fetchComponents: > + ( + /* Обрабатываем параметры */ + $params := $; + $manifest := $params.manifest; + $context := $lookup($params.manifest.contexts, contextId); + + /* Определяем необходимость показывать ближайшие связи */ + $isExtraLinks := $params.componentId ? true : $params."extra-links"; + + /* Определяем, какие компоненты покажем в контексте */ + $showComponents := $params.componentId + /* Если контекст под определенный компонент, ограничиваемся им. */ + ? [$params.componentId] + /* Если нет берем все компоненты указанные в контексте */ + : $context.components; + + /* Если в контексте переопределена функция получения компонентов, используем ее*/ + $context.api.fetchComponents ? ( + $eval($context.api.fetchComponents, $params) + ) : ( + /* Получаем все компоненты входящие в контекст */ + $components := $merge($showComponents.( + $mask := $; + $manifest.components.$spread().( + $componentId := $keys()[0]; + $wcard($componentId, $mask) ? $ + ) + )); + + /* Добавляем окружение, если это нужно */ + $merge([$components, $isExtraLinks ? ( + $components.*.links.( + { + id: $lookup($manifest.components, id) + } + ); + ) : {}]); + ) + ) + # На основании списка компонентов генерирует массиа областей которые они затрагивают + # Входящие параметры: + # components - список компонентов в формате fetchComponents + fetchAreas: > + ( + $distinct(components.$spread().( + $componentId := $keys()[0]; + $domains := $componentId.$split("."); + $limit := $count($domains) - 1; + $areaId := $map($domains, function($v, $i) {( + $join($map($domains, function($sv, $si) { + $si <= $i ? $sv + }), ".") + )}); + ))^($); + ) + # Генерирует список отображаемых связей + # Входящие параметры: + # components - список компонентов в формате fetchComponents + fetchLinks: > + ( + /* Обрабатываем параметры */ + $components := components; + $distinct( + $components.$spread().( + $from := $keys()[0]; + $direction := $.*.direction ? $.*.direction : "--"; + $.*.links[$lookup($components, id)].( + $title := title ? title : contract; + $title := contract ? ("[[/docs/" & contract & " " & $title & "]]") : $title; + { + "from": $from, + "to": (id ? id : "undefined"), + "title": $title, + "contract": contract, + "direction": direction ? direction : "--" + } + ) + ) + ); + ) + + fetchTitle: > + ( + context.title ? context.title : componentId + ) + + presentations: + c4_context: + title: Представление в C4 Context PlantUML + params: + title: Требуемые параметры для презентации + type: object + properties: + "dh-context-id": + title: Идентификатор контекста + type: string + pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ + required: + - dh-context-id + type: plantuml + template: templates/context.puml + $constructor: > # Переносим необходимую информацию из контекста в презентацию + ( + $id := $params."dh-context-id"; + $context := $lookup(contexts, $id); + $prototype := entities.contexts.presentations.c4_context; + + /* Преобразует относительные пути к файлам в прямые*/ + $toDirectRes := function($value) { + $substring($value, 0, 4) = "res:" ? $value : "res://contexts/" & $id & "#" & $value + }; + + /* Если явно указан puml файл, просто рендерим его */ + $substring($context.uml, -5) = ".puml" ? ( + { + "type": "plantuml", + "source": $toDirectRes($context.uml) + } + ) : ( + $result := $context.source ? ($merge([$prototype, { + "origin": { "_source": $context.source, "_origin": "($)" } + }])) : $prototype; + + /* Если в контексте переопределен шаблон, используем его по прямой ссылке */ + $result := $context.template + ? $merge([$result, { "template": $toDirectRes($context.template) }]) + : $result; + ) + ) + source: > + ( + $id := $params."dh-context-id"; + /* Получаем доступ к оригинальным данным */ + $manifest := _origin ? _origin : $; + /* Получаем контекст */ + $context := $lookup($manifest.contexts, $id); + /* Если в контексте задан источник, берем его за основу */ + $manifest := _source ? _source : $; + + $isExtraLinks := $not($string($context."extra-links") = "false"); + + /* Получаем коллекцию дефолтных вспомогательных функций */ + $defFunctions := $manifest.entities.contexts.api; + + /* Получаем коллекцию дефолтных параметров */ + $defConfig := $manifest.entities.contexts.config; + + /* Определяем движок рендеринга */ + $renderCore := $lookup({ + "elk": "!pragma layout elk", + "smetana": "!pragma layout smetana" + }, $defConfig.renderCore); + + $fetchTitle := $exists($context.api) and $exists($context.api.fetchTitle) ? + $context.api.fetchTitle : $defFunctions.fetchTitle; + $title := $eval($fetchTitle, { + "manifest": $manifest, + "context": $context, + "contextId": $id, + "componentId": $params.componentId + }); + /* Формируем заголовок */ + $header := "title: " & $title & "\n"; + + $tags := $exists($context.api) and $exists($context.api.tags) ? ( + $join( + $context.api.tags.$spread().{ + "id": $keys()[0], + "value": $.*}.( + "AddElementTag(" & id & "," & + $join(value.$spread().{ + "key": $keys()[0], + "value": $.* + }.( + key & "=" & value + ), ",") & ")\n" + ) + , "\n") + ) : (""); + + /* Получаем все компоненты входящие в контекст */ + $components := $eval($defFunctions.fetchComponents, { + "manifest": $manifest, + "contextId": $id, + "extra-links": $isExtraLinks, + "componentId": $params.componentId + }); + + /* Генерируем области */ + $areas := $eval($defFunctions.fetchAreas, { + "components": $components + }); + + /* Генерируем PlantUML диаграмму компонентов */ + $elements := ( + $join($map($areas, function($domain, $index) {( + $result := ""; + $component := $lookup($components, $domain); + $component := $not($exists($component)) ? ($lookup($manifest.components, $domain)) : $component; + $context := $lookup($manifest.contexts, $domain); + + /* Определяем является ли элемент областью */ + $isAreaBegin := $areas[$index + 1].$substring(0, $domain.$length() + 1) = $domain & "."; + + $result := $result & ($isAreaBegin ? ( + $title := $component.title ? $component.title : ( + $context.title ? $context.title : $domain + ); + "Boundary" + & ($component.external ? "_Ext" : "") + & "(" + & $domain & "," + & $title + & ") {\n"; + ) : ""); + + /* Если домен является компонентом, выводим его на диаграмму */ + $result := $result & ( + $not($isAreaBegin) and $component ? ( + /* Открываем секцию компонента */ + $entity := $lookup($defFunctions.c4_context.mapping, $component.entity ? $component.entity : "system"); + $entity := $not($exists($entity))? "System": $entity; + $entity + & ($component.external ? "_Ext" : "") + & "(" + & $domain + & "," + & $component.title + & "," + & "$link=" & "/architect/components/" & $domain + & ($exists($component.tags) ? ( + ", $tags=" & $join($component.tags, ",") + )) + & ")\n"; + ): ""; + ); + + /* Определяем, что область нужно закрыть*/ + $result & ( + $count($split($domain, ".")) > 1 + and ($count($split($domain, ".")) > $count($split($areas[$index + 1], ".")) + ) ? "}\n" : ""); + )})); + ); + /* Получаем список связей */ + $links := $eval($defFunctions.fetchLinks, { + "components": $components + }); + + /* Генерируем код связей */ + $linksCode := $join($links.( + "Rel(" + &from + &"," + &to + &",\"" + &title + &"\", " + &"$link=\"" + &link + &"\")\n" + ), ""); + + /* Готовим данные для передачи в шаблон */ + { + "renderCore": $renderCore, + "presentation": $defConfig.defaultPresentation, + "code": $header & $tags & $elements & $linksCode + } + ) diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/c4_context.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/c4_context.yaml new file mode 100644 index 0000000..1ac9530 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/c4_context.yaml @@ -0,0 +1,200 @@ +entities: + contexts: + config: + renderCore: elk + api: + c4_context: + mapping: + system: System + database: SystemDb + person: Person + actor: Person + fetchTitle: > + ( + context.title ? context.title : componentId + ) + + presentations: + c4_context: + title: Представление в C4 Context PlantUML + params: + title: Требуемые параметры для презентации + type: object + properties: + "dh-context-id": + title: Идентификатор контекста + type: string + pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ + required: + - dh-context-id + type: plantuml + template: templates/context.puml + $constructor: > # Переносим необходимую информацию из контекста в презентацию + ( + $id := $params."dh-context-id"; + $context := $lookup(contexts, $id); + $prototype := entities.contexts.presentations.c4_context; + + /* Преобразует относительные пути к файлам в прямые*/ + $toDirectRes := function($value) { + $substring($value, 0, 4) = "res:" ? $value : "res://contexts/" & $id & "#" & $value + }; + + /* Если явно указан puml файл, просто рендерим его */ + $substring($context.uml, -5) = ".puml" ? ( + { + "type": "plantuml", + "source": $toDirectRes($context.uml) + } + ) : ( + $result := $context.source ? ($merge([$prototype, { + "origin": { "_source": $context.source, "_origin": "($)" } + }])) : $prototype; + + /* Если в контексте переопределен шаблон, используем его по прямой ссылке */ + $result := $context.template + ? $merge([$result, { "template": $toDirectRes($context.template) }]) + : $result; + ) + ) + source: > + ( + $id := $params."dh-context-id"; + /* Получаем доступ к оригинальным данным */ + $manifest := _origin ? _origin : $; + /* Получаем контекст */ + $context := $lookup($manifest.contexts, $id); + /* Если в контексте задан источник, берем его за основу */ + $manifest := _source ? _source : $; + + $isExtraLinks := $not($string($context."extra-links") = "false"); + + /* Получаем коллекцию дефолтных вспомогательных функций */ + $defFunctions := $manifest.entities.contexts.api; + + /* Получаем коллекцию дефолтных параметров */ + $defConfig := $manifest.entities.contexts.config; + + /* Определяем движок рендеринга */ + $renderCore := $lookup({ + "elk": "!pragma layout elk", + "smetana": "!pragma layout smetana" + }, $defConfig.renderCore); + + $fetchTitle := $exists($context.api) and $exists($context.api.fetchTitle) ? + $context.api.fetchTitle : $defFunctions.fetchTitle; + $title := $eval($fetchTitle, { + "manifest": $manifest, + "context": $context, + "contextId": $id, + "componentId": $params.componentId + }); + /* Формируем заголовок */ + $header := "title: " & $title & "\n"; + + $tags := $exists($context.api) and $exists($context.api.tags) ? ( + $join( + $context.api.tags.$spread().{ + "id": $keys()[0], + "value": $.*}.( + "AddElementTag(" & id & "," & + $join(value.$spread().{ + "key": $keys()[0], + "value": $.* + }.( + key & "=" & value + ), ",") & ")\n" + ) + , "\n") + ) : (""); + + /* Получаем все компоненты входящие в контекст */ + $components := $eval($defFunctions.fetchComponents, { + "manifest": $manifest, + "contextId": $id, + "extra-links": $isExtraLinks, + "componentId": $params.componentId + }); + + /* Генерируем области */ + $areas := $eval($defFunctions.fetchAreas, { + "components": $components + }); + + /* Генерируем PlantUML диаграмму компонентов */ + $elements := ( + $join($map($areas, function($domain, $index) {( + $result := ""; + $component := $lookup($components, $domain); + $component := $not($exists($component)) ? ($lookup($manifest.components, $domain)) : $component; + $context := $lookup($manifest.contexts, $domain); + + /* Определяем является ли элемент областью */ + $isAreaBegin := $areas[$index + 1].$substring(0, $domain.$length() + 1) = $domain & "."; + + $result := $result & ($isAreaBegin ? ( + $title := $component.title ? $component.title : ( + $context.title ? $context.title : $domain + ); + "Boundary" + & ($component.external ? "_Ext" : "") + & "(" + & $domain & "," + & $title + & ") {\n"; + ) : ""); + + /* Если домен является компонентом, выводим его на диаграмму */ + $result := $result & ( + $not($isAreaBegin) and $component ? ( + /* Открываем секцию компонента */ + $entity := $lookup($defFunctions.c4_context.mapping, $component.entity ? $component.entity : "system"); + $entity := $not($exists($entity))? "System": $entity; + $entity + & ($component.external ? "_Ext" : "") + & "(" + & $domain + & "," + & $component.title + & "," + & "$link=" & "/architect/components/" & $domain + & ($exists($component.tags) ? ( + ", $tags=" & $join($component.tags, ",") + )) + & ")\n"; + ): ""; + ); + + /* Определяем, что область нужно закрыть*/ + $result & ( + $count($split($domain, ".")) > 1 + and ($count($split($domain, ".")) > $count($split($areas[$index + 1], ".")) + ) ? "}\n" : ""); + )})); + ); + /* Получаем список связей */ + $links := $eval($defFunctions.fetchLinks, { + "components": $components + }); + + /* Генерируем код связей */ + $linksCode := $join($links.( + "Rel(" + &from + &"," + &to + &",\"" + &title + &"\", " + &"$link=\"" + &link + &"\")\n" + ), ""); + + /* Готовим данные для передачи в шаблон */ + { + "renderCore": $renderCore, + "presentation": $defConfig.defaultPresentation, + "code": $header & $tags & $elements & $linksCode + } + ) diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml new file mode 100644 index 0000000..ac07499 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml @@ -0,0 +1,208 @@ +# Расширение сущности "Contexts" для генерации PlantUML диаграмм +entities: + contexts: + # Предопределенные конфигурационные параметры для генерации PlantUML + config: + # Какой движок будет использоваться для рендера + renderCore: elk # elk / smetana / graphviz + # API функции, используемые для генерации PlantUML + api: + # Генерирует на данных о компонентах и областях PlantUML диаграмму компонентов + # Входящие параметры: + # manifest - данные архитектуры + # components - список компонентов в формате fetchComponents + # areas - список компонентов в формате fetchAreas + makePumlComponentDiagram: > + ( + /* Обрабатываем параметры */ + $areas := areas; + $components := components; + $manifest := manifest; + + $join($map($areas, function($domain, $index) {( + $result := ""; + $component := $lookup($components, $domain); + $context := $lookup($manifest.contexts, $domain); + + /* Определяем является ли элемент областью */ + $isAreaBegin := $areas[$index + 1].$substring(0, $domain.$length() + 1) = $domain & "."; + + $result := $result & ($isAreaBegin ? ( + $title := $component.title ? $component.title : ( + $context.title ? $context.title : $domain + ); + "$Region(" + & $domain + & ",\"[[/architect/components/" + & $domain + & " " & $title + & "]]\", ) {\n"; + ) : ""); + + /* Если домен является компонентом, выводим его на диаграмму */ + $result := $result & ( + $not($isAreaBegin) and $component ? ( + /* Открываем секцию компонента */ + $entity := $component.entity ? $component.entity : "component"; + $result := "$Entity(\"" + & $entity + & "\", \"" + & "[[/architect/components/" & $domain & " " & $component.title & "]]" + & "\", " + & $domain + & ", )\n"; + + /* Добавляем аспекты */ + $result := $result & $join($component.aspects.( + $aspect := $lookup($manifest.aspects, $); + $title := $aspect ? $aspect.title : $; + "$EntityAspect(\"" + & $entity + & "\",\"[[/architect/aspects/" + & $ + & " " + & $title + & "]]\")\n" + )); + + /* Если компонент является контекстом добавляем дрилдаун */ + $context ? + $result := $result & "$EntityExpand(\"" + & $entity + & "\", " + & $domain + & ")\n"; + + /* Закрываем секцию компонента */ + $result := $result & + "$EntityEnd(\"" + & $entity + & "\")\n"; + ): ""; + ); + + /* Определяем, что область нужно закрыть*/ + $result & ( + $count($split($domain, ".")) > 1 + and ($count($split($domain, ".")) > $count($split($areas[$index + 1], ".")) + ) ? "}\n" : ""); + )})); + ) + # Генерирует PlantUML код связей для диаграммы компонентов + # Входящие параметры: + # links - список связей в формате fetchLinks + makePumlComponentsLinks: > + ( + $join(links.( + from & " " & direction & " " & to & (title ? ": " & title : "") & "\n" + )) + ) + # Представления контекстов в PlantUML + presentations: + plantuml: + title: Представление в PlantUML + params: + title: Требуемые параметры для презентации + type: object + properties: + "dh-context-id": + title: Идентификатор контекста + type: string + pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ + required: + - dh-context-id + type: plantuml + $constructor: > # Переносим необходимую информацию из контекста в презентацию + ( + $id := $params."dh-context-id"; + $context := $lookup(contexts, $id); + $prototype := entities.contexts.presentations.plantuml; + + /* Преобразует относительные пути к файлам в прямые*/ + $toDirectRes := function($value) { + $substring($value, 0, 4) = "res:" ? $value : "res://contexts/" & $id & "#" & $value + }; + + /* Если явно указан puml файл, просто рендерим его */ + $substring($context.uml, -5) = ".puml" ? ( + { + "type": "plantuml", + "source": $toDirectRes($context.uml) + } + ) : ( + $result := $context.source ? ($merge([$prototype, { + "origin": { "_source": $context.source, "_origin": "($)" } + }])) : $prototype; + + /* Если в контексте переопределен шаблон, используем его по прямой ссылке */ + $result := $context.template + ? $merge([$result, { "template": $toDirectRes($context.template) }]) + : $result; + ) + ) + template: templates/template.puml + # template: templates/context.puml + source: > + ( + $id := $params."dh-context-id"; + /* Получаем доступ к оригинальным данным */ + $manifest := _origin ? _origin : $; + /* Получаем контекст */ + $context := $lookup($manifest.contexts, $id); + /* Если в контексте задан источник, берем его за основу */ + $manifest := _source ? _source : $; + + $isExtraLinks := $not($string($context."extra-links") = "false"); + + /* Получаем коллекцию дефолтных вспомогательных функций */ + $defFunctions := $manifest.entities.contexts.api; + + /* Получаем коллекцию дефолтных параметров */ + $defConfig := $manifest.entities.contexts.config; + + /* Определяем движок рендеринга */ + $renderCore := $lookup({ + "elk": "!pragma layout elk", + "smetana": "!pragma layout smetana" + }, $defConfig.renderCore); + + /* Формируем заголовок */ + $header := "$Header(\"" & ($context.title ? $context.title : $id ) & "\", , , )\n"; + + /* Получаем все компоненты входящие в контекст */ + $components := $eval($defFunctions.fetchComponents, { + "manifest": $manifest, + "contextId": $id, + "extra-links": $isExtraLinks, + "componentId": $params.componentId + }); + + /* Генерируем области */ + $areas := $eval($defFunctions.fetchAreas, { + "components": $components + }); + + /* Генерируем PlantUML диаграмму компонентов */ + $elements := $eval($defFunctions.makePumlComponentDiagram, { + "manifest": $manifest, + "areas": $areas, + "components": $components + }); + + /* Получаем список связей */ + $links := $eval($defFunctions.fetchLinks, { + "components": $components + }); + + /* Генерируем код связей */ + $linksCode := $eval($defFunctions.makePumlComponentsLinks, { + "links": $links + }); + + /* Готовим данные для передачи в шаблон */ + { + "renderCore": $renderCore, + "presentation": $defConfig.defaultPresentation, + "code": $header & $elements & $linksCode + } + ) diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/smartants.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/smartants.yaml new file mode 100644 index 0000000..824fca9 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/smartants.yaml @@ -0,0 +1,165 @@ +# Расширение сущности "Contexts" для генерации SmartAnts диаграмм +entities: + contexts: + # Предопределенные конфигурационные параметры для генерации контекстов + config: + asRowHeight: 14 # Высота строки аспекта + asPadding: 8 # Отступы + asFontWidth: 7 # Ширина символов для расчета ширины строк + asFontHeight: 12 # Высота символов + # API презентаций SmartAnts + api: + # Генерирует ноды для диаграммы + # Входящие параметры: + # manifest - данные архитектуры + # components - архитектурные компоненты + makeSANodes: > + ( + $merge(components.$spread().( + { + $keys()[0]: { + "title": $.*.title, + "symbol": + /* Если это актор, то используем встроенный символ */ + $.*.entity = "actor" ? "user" : ( + /* Если спекты не указаны, выводится стандартный символ системы */ + $count($.*.aspects) = 0 + ? "system" + /* Иначе используем сгенерированные символы */ + : "symbol-" & $keys()[0] + ) + } + } + )) + ) + + # Генерирует символ для элементов диаграммы + # Входящие параметры: + # manifest - данные архитектуры + # componentId - идентификатор компонента + makeSASymbol: > + ( + /* Обрабатываем входящие параметры */ + $manifest := manifest; + $component := $lookup($manifest.components, componentId); + $defConfig := $manifest.entities.contexts.config; + + /* Получаем высоту строки аспекта */ + $rowHeight := $defConfig.asRowHeight; + /* Получаем отступы */ + $padding := $defConfig.asPadding; + /* Получаем ширину символа */ + $fontWidth := $defConfig.asFontWidth; + /* Получаем ширину символа */ + $fontHeight := $defConfig.asFontHeight; + + /* Определяем количество аспектов для вычисления высоты символа */ + $aspectCount := $count($component.aspects); + + /* Формируем массив аспектов */ + $aspects := $component.aspects.( + $aspect := $lookup($manifest.aspects, $); + { + "id" : $, + "title": $aspect.title ? $aspect.title : $ + } + ); + + /* Определяем максимальную длину текста для вычисления ширины символа */ + $maxTitle := $max($aspects.($length(title))); + + /* Вычисляем размеры символа */ + $width := $maxTitle ? $maxTitle * $fontWidth + $padding * 2 : 64; /* Умножаем на ширину символа */ + $height := ($aspectCount + 1) * $rowHeight + $padding * 2; /* Умножаем на высоту строки */ + + /* Генерируем символ */ + { + "symbol-" & componentId: + "" + /* Создаем контейнер */ + & "" + /* Заполняем аспектами */ + & $join($map($aspects, function($v, $i) {( + "" + & "" & $v.title & "" + & "" + )})) + & "" + } + ) + # Генерирует библиотеку символов + # Входящие параметры: + # manifest - данные архитектуры + # components - компоненты выводимые на диаграмму + makeSASymbolLibrary: > + ( + /* Обрабатываем входящие параметры */ + $manifest := manifest; + $merge(components.$spread().( + $eval($manifest.entities.contexts.api.makeSASymbol, { + "manifest": $manifest, + "componentId": $keys()[0] + }) + )); + ) + # Представления контекстов в PlantUML + presentations: + smartants: + title: Представление в SmartAnts + params: + title: Требуемые параметры для презентации + type: object + properties: + "dh-context-id": + title: Идентификатор контекста + type: string + pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ + required: + - dh-context-id + type: smartants + source: > + ( + $id := $params."dh-context-id"; + $manifest := $; + $context := $lookup($manifest.contexts, $id); + $isExtraLinks := $not($string($context."extra-links") = "false"); + + /* Получаем коллекцию дефолтных вспомогательных функций */ + $defFunctions := $manifest.entities.contexts.api; + + /* Получаем все компоненты входящие в контекст */ + $components := $eval($defFunctions.fetchComponents, { + "manifest": $manifest, + "contextId": $id, + "extra-links": $isExtraLinks, + "componentId": $params.componentId + }); + + /* Строим из компонентов ноды */ + $nodes := $eval($defFunctions.makeSANodes, { + "manifest": $manifest, + "components": $components + }); + + /* Создаем библиотеку символов */ + $symbols := $eval($defFunctions.makeSASymbolLibrary, { + "manifest": $manifest, + "components": $components + }); + + /* Получаем список связей */ + $links := $eval($defFunctions.fetchLinks, { + "components": $components + }).{ + "from": from, + "to": to, + "style": direction, + "title": title + }; + /* Готовим данные для передачи в шаблон */ + { + "nodes": $nodes, + "links": $links, + "symbols": $symbols + } + ) diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml b/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml new file mode 100644 index 0000000..bce1ae5 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml @@ -0,0 +1,1985 @@ +@startuml + +' C4-PlantUML + +'Version +' ################################## +!function C4Version() + ' 2 spaces and ' are used as unique marker, that the release scripts makes the correct version update + !$c4Version = "2.6.0beta1" + !return $c4Version +!end function + +!procedure C4VersionDetails() +rectangle C4VersionDetailsArea <> [ +| PlantUML | **%version()** | +| C4-PlantUML | **C4Version()** | +] +!end procedure + +' Colors +' ################################## + +!global $ELEMENT_FONT_COLOR = "#FFFFFF" + +!global $ARROW_COLOR = "#666666" + +!global $BOUNDARY_COLOR = "#444444" +!global $BOUNDARY_BG_COLOR = "transparent" + +!global $LEGEND_FONT_COLOR = "#FFFFFF" +!global $LEGEND_TITLE_COLOR = "#000000" +' %darken(darkkhaki,50), #khaki +!global $LEGEND_DARK_COLOR = "#66622E" +!global $LEGEND_LIGHT_COLOR = "#khaki" + +!global $SKETCH_BG_COLOR = "#EEEBDC" +!global $SKETCH_FONT_COLOR = "" +!global $SKETCH_WARNING_COLOR = "red" +!global $SKETCH_FONT_NAME = "Comic Sans MS" + +' Labels +' ################################## + +!global $LEGEND_SHADOW_TEXT = "shadow" +!global $LEGEND_NO_SHADOW_TEXT = "no shadow" +!global $LEGEND_NO_FONT_BG_TEXT = "last text and back color" +!global $LEGEND_NO_FONT_TEXT = "last text color" +!global $LEGEND_NO_BG_TEXT = "last back color" +!global $LEGEND_NO_LINE_TEXT = "last line color" +!global $LEGEND_ROUNDED_BOX = "rounded box" +!global $LEGEND_EIGHT_SIDED = "eight sided" +!global $LEGEND_DOTTED_LINE = "dotted" +!global $LEGEND_DASHED_LINE = "dashed" +!global $LEGEND_BOLD_LINE = "bold" +!global $LEGEND_BOUNDARY = "boundary" +!global $LEGEND_DASHED_BOUNDARY = "dashed" +' ignore transparent atm, that the legend is smaller +'!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed, transparent" +!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed" + +!global $SKETCH_FOOTER_WARNING = "Warning:" +!global $SKETCH_FOOTER_TEXT = "Created for discussion, needs to be validated" + +' Styling +' ################################## + +!global $TECHN_FONT_SIZE = 12 +!global $ROUNDED_BOX_SIZE = 25 +!global $EIGHT_SIDED_SIZE = 18 + +!global $LEGEND_DETAILS_SMALL_SIZE = 10 +!global $LEGEND_DETAILS_NORMAL_SIZE = 14 +!global $LEGEND_DETAILS_SIZE = $LEGEND_DETAILS_SMALL_SIZE + +!global $ROUNDED_BOX = "roundedBox" +!global $EIGHT_SIDED = "eightSided" + +!global $DOTTED_LINE = "dotted" +!global $DASHED_LINE = "dashed" +!global $BOLD_LINE = "bold" + +!global $LEGEND_DETAILS_NONE = "none" +!global $LEGEND_DETAILS_NORMAL = "normal" +!global $LEGEND_DETAILS_SMALL = "small" + +skinparam defaultTextAlignment center + +skinparam wrapWidth 200 +skinparam maxMessageSize 150 + +skinparam LegendBorderColor transparent +skinparam LegendBackgroundColor transparent +skinparam LegendFontColor $LEGEND_FONT_COLOR + +skinparam shadowing<> false +' #00000000 is transparent +skinparam rectangle<> { + backgroundcolor #00000000 + bordercolor #00000000 +} + +skinparam rectangle { + StereotypeFontSize 12 + shadowing false +} + +skinparam database { + StereotypeFontSize 12 + shadowing false +} + +skinparam queue { + StereotypeFontSize 12 + shadowing false +} + +skinparam arrow { + Color $ARROW_COLOR + FontColor $ARROW_COLOR + FontSize 12 +} + +skinparam person { + StereotypeFontSize 12 + shadowing false +} + +skinparam actor { + StereotypeFontSize 12 + shadowing false + style awesome +} + +' Some boundary skinparams have to be set as package skinparams too (PlantUML uses internal packages) +' UpdateBoundaryStyle() called in boundary section below +skinparam rectangle<> { + Shadowing false + StereotypeFontSize 6 + StereotypeFontColor $BOUNDARY_BG_COLOR + BorderStyle dashed +} + +skinparam package { + StereotypeFontSize 6 + StereotypeFontColor $BOUNDARY_BG_COLOR + FontStyle plain + BackgroundColor $BOUNDARY_BG_COLOR +} + +' Legend and Tags +' ################################## +!global $tagDefaultLegend = "" +!global $tagCustomLegend = "" + +' rel specific +!unquoted function $toStereos($tags) + !if (%strlen($tags) == 0) + !return '' + !endif + !$stereos = '' + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$stereos = $stereos + '<<' + $tag + '>>' +%set_variable_value("$" + $tag + "_LineLegend", %true()) + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$stereos = $stereos + '<<' + $tags + '>>' +%set_variable_value("$" + $tags + "_LineLegend", %true()) + !endif + !return $stereos +!endfunction + +' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag +!unquoted function $toRelArg($arg, $tags, $varPostfix) + !if ($arg > "") + !return $arg + !endif + + !if (%strlen($tags) == 0) + !return $arg + !endif + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$newArg = %get_variable_value("$" + $tag + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$newArg = %get_variable_value("$" + $tags + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !endif + !return $arg +!endfunction + +' element specific (unused are hidden based on mask) +!unquoted function $toStereos($elementType, $tags) + !if (%strlen($tags) == 0) + !$stereos = '<<' + $elementType + '>>' +%set_variable_value("$" + $elementType + "Legend", %true()) + !return $stereos + !endif + !$stereos = '' + !$mask = $resetMask() + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$stereos = $stereos + '<<' + $tag + '>>' + !$mergedMask = $combineMaskWithTag($mask, $tag) + !if ($mergedMask != $mask) +%set_variable_value("$" + $tag + "Legend", %true()) + !$mask = $mergedMask + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$stereos = $stereos + '<<' + $tags + '>>' + !$mergedMask = $combineMaskWithTag($mask, $tags) + !if ($mergedMask != $mask) +%set_variable_value("$" + $tags + "Legend", %true()) + !$mask = $mergedMask + !endif + !endif + ' has to be last, otherwise PlantUML overwrites all tag specific skinparams + !$stereos = $stereos + '<<' + $elementType + '>>' + !$mergedMask = $combineMaskWithTag($mask, $elementType) + !if ($mergedMask != $mask) +%set_variable_value("$" + $elementType + "Legend", %true()) + !$mask = $mergedMask + !endif + !return $stereos +!endfunction + +' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag +!unquoted function $toElementArg($arg, $tags, $varPostfix, $elementType) + !if ($arg > "") + !return $arg + !endif + + !if (%strlen($tags) == 0) + !$newArg = %get_variable_value("$" + $elementType + $varPostfix) + !if ($newArg > "") + !return $newArg + !else + !return $arg + !endif + !endif + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$newArg = %get_variable_value("$" + $tag + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$newArg = %get_variable_value("$" + $tags + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$newArg = %get_variable_value("$" + $elementType + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !endif + !return $arg +!endfunction + +' if $value is empty try to load it via variable, optional can it store the calculated value +!function $restoreEmpty($elementType, $property, $value, $store) + !$var = "$" + $elementType + "Restore" + $property + !if ($value == "") + !$value = %get_variable_value($var) + !elseif ($store) + %set_variable_value($var, $value) + !endif + !return $value +!endfunction + +' clear the restore property +!function $clearRestore($elementType, $property) + !$var = "$" + $elementType + "Restore" + $property + %set_variable_value($var, "") + !return "" +!endfunction + +!function $elementTagSkinparams($element, $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !$elementSkin = "skinparam " + $element + "<<" + $tagStereo + ">> {" + %newline() + !if ($fontColor != "") + !if (%strpos($tagStereo, "boundary") < 0) + !$elementSkin = $elementSkin + " StereotypeFontColor " + $fontColor + %newline() + !endif + !$elementSkin = $elementSkin + " FontColor " + $fontColor + %newline() + !endif + !if ($bgColor != "") + !$elementSkin = $elementSkin + " BackgroundColor " + $bgColor + %newline() + !endif + !if ($borderColor != "") + !$elementSkin = $elementSkin + " BorderColor " + $borderColor+ %newline() + !endif + !if ($shadowing == "true") + !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "true" + %newline() + !endif + !if ($shadowing == "false") + !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "false" + %newline() + !endif + ' only rectangle supports shape(d corners), define both skinparam that overlays are working + !if ($shape != "" && $element == "rectangle") + !if ($shape == $ROUNDED_BOX) + !$elementSkin = $elementSkin + " RoundCorner " + $ROUNDED_BOX_SIZE+ %newline() + !$elementSkin = $elementSkin + " DiagonalCorner " + "0" + %newline() + !elseif ($shape == $EIGHT_SIDED) + !$elementSkin = $elementSkin + " RoundCorner " + "0" + %newline() + !$elementSkin = $elementSkin + " DiagonalCorner " + $EIGHT_SIDED_SIZE+ %newline() + !endif + !endif + !$elementSkin = $elementSkin + "}" + %newline() + !return $elementSkin +!endfunction + +!unquoted procedure $defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + ' only rectangle supports shape(d corners) + !$tagSkin = $elementTagSkinparams("rectangle", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !$tagSkin = $tagSkin + $elementTagSkinparams("database", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + !$tagSkin = $tagSkin + $elementTagSkinparams("queue", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + ' plantuml.jar bug - actor have to be after person + !$tagSkin = $tagSkin + $elementTagSkinparams("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + ' actor has style awesome, therefore $fontColor is ignored and text uses $bgColor too + !$tagSkin = $tagSkin + $elementTagSkinparams("actor", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "") + !if (%strpos($tagStereo, "boundary") >= 0 && $bgColor != "") + !$tagSkin = $tagSkin + "skinparam package<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() + !$tagSkin = $tagSkin + "skinparam rectangle<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() + !endif +$tagSkin +!endprocedure + +' arrow colors cannot start with # (legend background has to start with #) +!function $colorWithoutHash($c) + !if (%substr($c, 0, 1) == "#") + !$c = %substr($c,1) + !endif + !return $c +!endfunction + +!unquoted procedure $defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) + !$elementSkin = "skinparam arrow<<" + $tagStereo + ">> {" + %newline() + !if ($lineColor != "") || ($textColor != "") || ($lineStyle != "") + !$elementSkin = $elementSkin + " Color " + !if ($lineColor != "") + !$elementSkin = $elementSkin + $colorWithoutHash($lineColor) + !endif + !if ($textColor != "") + !$elementSkin = $elementSkin + ";text:" + $colorWithoutHash($textColor) + !endif + !if ($lineStyle != "") + !$elementSkin = $elementSkin + ";line." + $lineStyle + !endif + !$elementSkin = $elementSkin + %newline() + !endif + !if ($lineThickness != "") + !$elementSkin = $elementSkin + " thickness " + $lineThickness + %newline() + !endif + !$elementSkin = $elementSkin + "}" + %newline() +$elementSkin +!endprocedure + +' %is_dark() requires PlantUML version >= 1.2021.6 +!if (%function_exists("%is_dark")) + !$PlantUMLSupportsDynamicLegendColor = %true() +!else + !$PlantUMLSupportsDynamicLegendColor = %false() + !log "dynamic undefined legend colors" requires PlantUML version >= 1.2021.6, therefore only static assigned colors are used +!endif + +!unquoted function $contrastLegend($color) + !if (%is_dark($color)) + !$value = $LEGEND_LIGHT_COLOR + !else + !$value = $LEGEND_DARK_COLOR + !endif + !return $value +!endfunction + +!unquoted function $flatLegend($color) + !if (%is_dark($color)) + !$value = $LEGEND_DARK_COLOR + !else + !$value = $LEGEND_LIGHT_COLOR + !endif + !return $value +!endfunction + +' legend background has to start with # +!function $colorWithHash($c) + !if (%substr($c, 0, 1) != "#") + !$c = "#" + $c + !endif + !return $c +!endfunction + +!function $addMaskFlag($mask, $attr) + !if ($attr == "") + !$mask = $mask + "0" + !else + !$mask = $mask + "1" + !endif + !return $mask +!endfunction + +!function $orFlags($flag1, $flag2) + !if ($flag1 == "0" && $flag2 == "0") + !return "0" + !endif + !return "1" +!endfunction + +!function $tagLegendMask($bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) + !$mask = "" + !$mask = $addMaskFlag($mask, $bgColor) + !$mask = $addMaskFlag($mask, $fontColor) + !$mask = $addMaskFlag($mask, $borderColor) + !$mask = $addMaskFlag($mask, $shadowing) + !$mask = $addMaskFlag($mask, $shape) + !$mask = $addMaskFlag($mask, $sprite) + !return $mask +!endfunction + +!function $resetMask() + !return "000000" +!endfunction + +!function $combineMasks($mask1, $mask2) + !$mask = "" + !$mask = $mask + $orFlags(%substr($mask1, 0, 1), %substr($mask2, 0, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 1, 1), %substr($mask2, 1, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 2, 1), %substr($mask2, 2, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 3, 1), %substr($mask2, 3, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 4, 1), %substr($mask2, 4, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 5, 1), %substr($mask2, 5, 1)) + !return $mask +!endfunction + +!function $combineMaskWithTag($mask1, $tag) + !$mask2 = %get_variable_value("$" + $tag+ "LegendMask") + !if ($mask2 == "") + ' !log combineMaskWithTag $mask1, $tag, ... only $mask1 + !return $mask1 + !endif + + ' !log combineMaskWithTag $mask1, $tag, $mask2 ... $combineMasks($mask1, $mask2) + !return $combineMasks($mask1, $mask2) +!endfunction + +' element symbols typically 4 times too big in legend +!function $smallVersionSprite($sprite) + ' ,scale= ... has to be first (...,color=black,scale=0.25... is invalid too) + !if (%strpos($sprite, "=") < 0) + !if (%substr($sprite, 0, 4) == "img:") + !$smallSprite = $sprite + "{scale=0.25}" + !else + !$smallSprite = $sprite + ",scale=0.25" + !endif + !else + !$smallSprite = $sprite + !endif + !return $smallSprite +!endfunction + +' format sprite that it can be used in diagram +!function $getSprite($sprite) + ' if it starts with & it's a OpenIconic, details see https://useiconic.com/open/ + ' if it starts with img: it's an image, details see https://plantuml.com/creole + !if (%substr($sprite, 0, 1) != "&" && %substr($sprite, 0, 4) != "img:") + !$formatted = "<$" + $sprite + ">" + !else + !$formatted = "<" + $sprite + ">" + !endif + !return $formatted +!endfunction + +!function $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + !$bg = $bgColor + !$fo = $fontColor + !$bo = $borderColor + + !if ($fo == "") + !if ($bg != "") +!if ($PlantUMLSupportsDynamicLegendColor) + !$fo = $contrastLegend($bg) +!else + !$fo = $LEGEND_DARK_COLOR +!endif + !else + !if ($bo == "") + !$fo = $LEGEND_DARK_COLOR + !$bg = $LEGEND_LIGHT_COLOR + !else +!if ($PlantUMLSupportsDynamicLegendColor) + !$fo = $flatLegend($bo) + !$bg = $contrastLegend($bo) +!else + !$fo = $LEGEND_DARK_COLOR + !$bg = $LEGEND_LIGHT_COLOR +!endif + !endif + !endif + !else + !if ($bg == "") +!if ($PlantUMLSupportsDynamicLegendColor) + !$bg = $contrastLegend($fo) +!else + !$bg = $LEGEND_LIGHT_COLOR +!endif + !endif + !endif + + !if ($bo == "") + !$bo = $bg + !endif + + !$tagEntry = "|" + !$tagDetails = "(" + !$tagEntry = $tagEntry + "<" + $colorWithHash($bg) +">" + ' ..white rectangle + !$tagEntry = $tagEntry + " " + !$tagEntry = $tagEntry + "" + !if ($legendSprite != "") + !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " + !endif + !if ($legendText == "") + !if ($tagStereo == "boundary") + !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " + !else + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " + !endif + !elseif (%strpos($tagStereo, "boundary") >= 0) + ' if contains/ends with _boundary remove _boundary and add "boundary (dashed)" + !$pos = %strpos($tagStereo, "_boundary") + !if ($pos > 0) + !$tagEntry = $tagEntry + " " + %substr($tagStereo, 0 ,$pos) + !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " + !else + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " + !endif + !endif + !else + !$tagEntry = $tagEntry + " " + $tagStereo + " " + !endif + !if ($shadowing == "true") + !$tagDetails = $tagDetails + $LEGEND_SHADOW_TEXT + ", " + !endif + !if ($shadowing == "false") + !$tagDetails = $tagDetails + $LEGEND_NO_SHADOW_TEXT + ", " + !endif + !if ($shape == $ROUNDED_BOX) + !$tagDetails = $tagDetails + $LEGEND_ROUNDED_BOX + ", " + !endif + !if ($shape == $EIGHT_SIDED) + !$tagDetails = $tagDetails + $LEGEND_EIGHT_SIDED + ", " + !endif + !if ($fontColor == "" && $bgColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_BG_TEXT + ", " + !else + !if ($fontColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " + !endif + !if ($bgColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_BG_TEXT + ", " + !endif + !endif + !if ($tagDetails=="(") + !$tagDetails = "" + !else + !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) + !$tagDetails = $tagDetails + ")" + !endif + !else + !$brPos = %strpos($legendText, "\n") + !if ($brPos > 0) + !$tagEntry = $tagEntry + %substr($legendText, 0, $brPos) + " " + !$details = %substr($legendText, $brPos + 2) + !if ($details=="") + !$tagDetails = "" + !else + !$tagDetails = $tagDetails + $details + ")" + !endif + !else + !$tagEntry = $tagEntry + " " + $legendText + " " + !$tagDetails = "" + !endif + !endif + + !$tagDetails = $tagDetails + " " + !$tagDetails = $tagDetails + "|" +%set_variable_value("$" + $tagStereo + "LegendEntry", $tagEntry) +%set_variable_value("$" + $tagStereo + "LegendDetails", $tagDetails) + !return $tagEntry +!endfunction + +!function $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) + !$tc = $textColor + !$lc = $lineColor + + !if ($tc == "") + !if ($PlantUMLSupportsDynamicLegendColor) + !$tc = $flatLegend($ARROW_COLOR) + !else + !$tc = $LEGEND_DARK_COLOR + !endif + !endif + !if ($lc == "") + !if ($PlantUMLSupportsDynamicLegendColor) + !$lc = $flatLegend($ARROW_COLOR) + !else + !$lc = $LEGEND_DARK_COLOR + !endif + !endif + + !$tagEntry = "|" + !$tagDetails = "(" + ' ..white line + !$tagEntry = $tagEntry + " " + !$tagEntry = $tagEntry + "" + !if ($legendSprite != "") + !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " + !endif + !if ($legendText == "") + !$tagEntry = $tagEntry + " " + $tagStereo + " " + !if ($textColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " + !endif + !if ($lineColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_LINE_TEXT + ", " + !endif + !if ($lineStyle != "") + !if ($lineStyle == $DOTTED_LINE) + !$tagDetails = $tagDetails + $LEGEND_DOTTED_LINE + ", " + !elseif ($lineStyle == $DASHED_LINE) + !$tagDetails = $tagDetails + $LEGEND_DASHED_LINE + ", " + !elseif ($lineStyle == $BOLD_LINE) + !$tagDetails = $tagDetails + $LEGEND_BOLD_LINE + ", " + !else + !$tagDetails = $tagDetails + $lineStyle + ", " + !endif + !endif + !if ($lineThickness != "") + !$tagDetails = $tagDetails + "thickness " + $lineThickness + ") " + !endif + !if ($tagDetails=="(") + !$tagDetails = "" + !else + !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) + !$tagDetails = $tagDetails + ")" + !endif + !else + !$brPos = %strpos($legendText, "\n") + !if ($brPos > 0) + !$tagEntry = $tagEntry + " " + %substr($legendText, 0, $brPos) + " " + !$details = %substr($legendText, $brPos + 2) + !if ($details=="") + !$tagDetails = "" + !else + !$tagDetails = $tagDetails + $details + ")" + !endif + !else + !$tagEntry = $tagEntry + " " + $legendText + " " + !$tagDetails = "" + !endif + !endif + + !$tagDetails = $tagDetails + " " + !$tagDetails = $tagDetails + "|" +%set_variable_value("$" + $tagStereo + "_LineLegendEntry", $tagEntry) +%set_variable_value("$" + $tagStereo + "_LineLegendDetails", $tagDetails) + !return $tagEntry +!endfunction + +!unquoted procedure $addTagToLegend($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $legendText="", $legendSprite="") +'' if a combined element tag is defined (e.g. "v1.0&v1.1") then it is typically a merged color, +'' like a new $fontColor="#fdae61" therefore it should be added to the legend +'' and the & combined tags will be not removed +' !if (%strpos($tagStereo, "&") < 0) + !$dummyAlreadyVariables = $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + !$tagCustomLegend = $tagCustomLegend + $tagStereo + "\n" + !$tagMask = $tagLegendMask( $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) +%set_variable_value("$" + $tagStereo + "LegendMask", $tagMask) +' !endif +!endprocedure + +!unquoted procedure $addRelTagToLegend($tagStereo, $textColor="", $lineColor="", $lineStyle="", $legendText="", $legendSprite="", $lineThickness="") +'' Arrows have a bug with stereotype/skinparams and cannot combine text colors of one stereotype +'' and the line color of another stereotype. Therefore the text color of one tag and the line color +'' of another tag have to be combined via a "workaround" tag ("v1.0&v1.1"). +'' This workaround tag could be theoretically removed in the legend but after that there would +'' be an inconsistency between the element tags and the rel tags and therefore +'' & combined workaround tags are not removed too (and in unlikely cases the color itself could be changed) +' !if (%strpos($tagStereo, "&") < 0) + !$dummyAlreadyVariables = $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) + !$tagCustomLegend = $tagCustomLegend + $tagStereo + "_Line\n" +' !endif +!endprocedure + +!procedure $showActiveLegendEntries($allDefined) + !$brPos = %strpos($allDefined, "\n") + !while ($brPos >= 0) + !$tagStereo = %substr($allDefined, 0, $brPos) + !$allDefined = %substr($allDefined, $brPos+2) + !$brPos = %strpos($allDefined, "\n") + !if (%variable_exists("$" + $tagStereo + "Legend")) + ' is part of legendDetails + !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") + !$partSize = "" + !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") + !$line = $part1 + $partSize + $part2 +$line + !endif + !endwhile + !if (%strlen($allDefined) > 0) + !$tagStereo = $allDefined + !if (%variable_exists("$" + $tagStereo + "Legend")) + ' is part of legendDetails + !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") + !$partSize = "" + !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") + !$line = $part1 + $partSize + $part2 +$line + !endif + !endif +!endprocedure + +!function RoundedBoxShape() +!return $ROUNDED_BOX +!endfunction + +!function EightSidedShape() +!return $EIGHT_SIDED +!endfunction + +!function DottedLine() +!return $DOTTED_LINE +!endfunction + +!function DashedLine() +!return $DASHED_LINE +!endfunction + +!function BoldLine() +!return $BOLD_LINE +!endfunction + +' used by new defined tags +!unquoted procedure AddElementTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") +$defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !if ($sprite!="") +%set_variable_value("$" + $tagStereo + "ElementTagSprite", $sprite) + !if ($legendSprite == "") + !$legendSprite = $smallVersionSprite($sprite) + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $tagStereo + "ElementTagTechn", $techn) + !endif +$addTagToLegend($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) +!endprocedure + +!unquoted procedure $addElementTagInclReuse($elementName, $tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") + !$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) + !$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) + !$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) + !$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) + !$shape=$restoreEmpty($elementName, "shape", $shape, %true()) + !$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) + !$techn=$restoreEmpty($elementName, "techn", $techn, %true()) + ' new style should has its own legend text + ' !$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) + !$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) + + AddElementTag($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) +!endprocedure + +' used by new defined rel tags +!unquoted procedure AddRelTag($tagStereo, $textColor="", $lineColor="", $lineStyle="", $sprite="", $techn="", $legendText="", $legendSprite="", $lineThickness="") +$defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) + !if ($sprite != "") +%set_variable_value("$" + $tagStereo + "RelTagSprite", $sprite) + !if ($legendSprite == "") + ' relation symbols typically 1:1 no additional scale required + !$legendSprite = $sprite + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $tagStereo + "RelTagTechn", $techn) + !endif +$addRelTagToLegend($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) +!endprocedure + +' update the style of existing elements like person, ... +!unquoted procedure UpdateElementStyle($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") +!$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) +!$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) +!$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) +!$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) +!$shape=$restoreEmpty($elementName, "shape", $shape, %true()) +!$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) +!$techn=$restoreEmpty($elementName, "techn", $techn, %true()) +!$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) +!$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) +$defineSkinparams($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !if ($sprite != "") +%set_variable_value("$" + $elementName + "ElementTagSprite", $sprite) + !if ($legendSprite == "") + !$legendSprite = $smallVersionSprite($sprite) + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $elementName + "ElementTagTechn", $techn) + !endif + !$dummyAlreadyVariables = $setTagLegendVariables($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + ' default tags sets at least bgColor and fontColor + !$tagMask = $tagLegendMask("CHANGED", "CHANGED", $borderColor, $shadowing, $shape, $sprite) +%set_variable_value("$" + $elementName + "LegendMask", $tagMask) +!endprocedure + +/' @deprecated in favor of UpdateElementStyle '/ +!unquoted procedure UpdateSkinparamsAndLegendEntry($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="") +UpdateElementStyle($elementName, $bgColor, $fontColor, $borderColor, $shadowing) +!endprocedure + +' update the style of default relation, it has to set both properties (combined statement not working) +!unquoted procedure UpdateRelStyle($textColor, $lineColor) + !$elementSkin = "skinparam arrow {" + %newline() + !$elementSkin = $elementSkin + " Color " + $lineColor + %newline() + !$elementSkin = $elementSkin + " FontColor " + $textColor + %newline() + !$elementSkin = $elementSkin + "}" + %newline() +$elementSkin +!endprocedure + +' tags/stereotypes have to be delimited with \n +!unquoted procedure SetDefaultLegendEntries($tagStereoEntries) + !$tagDefaultLegend = $tagStereoEntries +!endprocedure + +' Links +' ################################## + +!function $getLink($link) + !if ($link != "") + !return "[[" + $link + "]]" + !else + !return "" + !endif +!endfunction + +' Line breaks +' ################################## + +' PlantUML supports no DETERMINISTIC/automatic line breaks of "PlantUML line" (C4 Relashionships) +' therefore Rel...() implements an automatic line break based on spaces (like in all other objects). +' If a $type contains \n then these are used (and no automatic space based line breaks are done) +' $REL_TECHN_MAX_CHAR_WIDTH defines the automatic line break position +!global $REL_TECHN_MAX_CHAR_WIDTH = 35 +!global $REL_DESCR_MAX_CHAR_WIDTH = 32 + +!unquoted function $breakText($text, $usedNewLine, $widthStr="-1") +!$width = %intval($widthStr) +!$multiLine = "" +!if (%strpos($text, "\n") >= 0) + !while (%strpos($text, "\n") >= 0) + !$brPos = %strpos($text, "\n") + !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine + !$text = %substr($text, $brPos+2) + !endwhile +!else + !while ($width>0 && %strlen($text) > $width) + !$brPos = $width + !while ($brPos > 0 && %substr($text, $brPos, 1) != ' ') + !$brPos = $brPos - 1 + !endwhile + + !if ($brPos < 1) + !$brPos = %strpos($text, " ") + !else + !endif + + !if ($brPos > 0) + !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine + !$text = %substr($text, $brPos + 1) + !else + !$multiLine = $multiLine+ $text + !$text = "" + !endif + !endwhile +!endif +!if (%strlen($text) > 0) + !$multiLine = $multiLine + $text +!endif +!return $multiLine +!endfunction + +!unquoted function $breakLabel($text) +!$usedNewLine = "\n== " +!$multiLine = $breakText($text, $usedNewLine) +!return $multiLine +!endfunction + +!unquoted function $breakDescr($text, $widthStr) + !$usedNewLine = "\n" + !return $breakText($text, $usedNewLine, $widthStr) +!endfunction + +' $breakTechn() supports //...//; $breakNode() in C4_Deployment supports no //....// +!unquoted function $breakTechn($text, $widthStr) + !$usedNewLine = '//\n//' + !return $breakText($text, $usedNewLine, $widthStr) +!endfunction + +' Element base layout +' ################################## + +!function $getElementBase($label, $techn, $descr, $sprite) + !$element = "" + !if ($sprite != "") + !$element = $element + $getSprite($sprite) + !if ($label != "") + !$element = $element + '\n' + !endif + !endif + !if ($label != "") + !$element = $element + '== ' + $breakLabel($label) + !else + !$element = $element + '.' + !endif + !if ($techn != "") + !$element = $element + '\n//[' + $breakTechn($techn, '-1') + ']//' + !endif + !if ($descr != "") + !$element = $element + '\n\n' + $descr + !endif + !return $element +!endfunction + +' Element properties +' ################################## + +' collect all defined properties as table rows +!global $propTable = "" +!global $propTableCaption = "" +!global $propColCaption = "=" + +!unquoted function SetPropertyHeader($col1Name, $col2Name, $col3Name = "", $col4Name = "") + !$propColCaption = "" + !$propTableCaption = "|= " + $col1Name + " |= " + $col2Name + " |" + !if ($col3Name != "") + !$propTableCaption = $propTableCaption + "= " + $col3Name + " |" + !endif + !if ($col4Name != "") + !$propTableCaption = $propTableCaption + "= " + $col4Name + " |" + !endif + !return "" +!endfunction + +!unquoted function WithoutPropertyHeader() + !$propTableCaption = "" + !$propColCaption = "=" + !return "" +!endfunction + +!unquoted function AddProperty($col1, $col2, $col3 = "", $col4 = "") + !if ($propTable == "") + !if ($propTableCaption != "") + !$propTable = $propTableCaption + "\n" + !endif + !else + !$propTable = $propTable + "\n" + !endif + !$propTable = $propTable + "| " + $col1 + " |" + $propColCaption + " " + $col2 + " |" + !if ($col3 != "") + !$propTable = $propTable + " " + $col3 + " |" + !endif + !if ($col4 != "") + !$propTable = $propTable + " " + $col4 + " |" + !endif + !return "" +!endfunction + +!unquoted function $getProps($alignedNL = "\n") + !if ($propTable != "") + !$retTable = $alignedNL + $propTable + !$propTable = "" + !return $retTable + !endif + !return "" +!endfunction + +!unquoted function $getProps_L() + !return $getProps("\l") +!endfunction + +!unquoted function $getProps_R() + !return $getProps("\r") +!endfunction + +SetPropertyHeader("Property","Value") + +' Layout +' ################################## + +!function $getLegendDetailsSize($detailsFormat) + !if $detailsFormat == $LEGEND_DETAILS_NONE + !$size = 0 + !elseif $detailsFormat == $LEGEND_DETAILS_SMALL + !$size = $LEGEND_DETAILS_SMALL_SIZE + !else + !$size = $LEGEND_DETAILS_NORMAL_SIZE + !endif + !return $size +!endfunction + +!procedure $getHideStereotype($hideStereotype) +!if ($hideStereotype == "true") +hide stereotype +!endif +!endprocedure + +!procedure $getLegendTable($detailsFormat) +!global $LEGEND_DETAILS_SIZE = $getLegendDetailsSize($detailsFormat) +<#00000000,#00000000>|**Legend** | +$showActiveLegendEntries($tagDefaultLegend) +$showActiveLegendEntries($tagCustomLegend) +!endprocedure + +!procedure $getLegendArea($areaAlias, $hideStereotype, $details) +$getHideStereotype($hideStereotype) +rectangle $areaAlias<> [ +$getLegendTable($details) +] +!endprocedure + +!procedure HIDE_STEREOTYPE() +hide stereotype +!endprocedure + +!unquoted procedure SET_SKETCH_STYLE($bgColor="_dont_change_", $fontColor="_dont_change_", $warningColor="_dont_change_", $fontName="_dont_change_", $footerWarning="_dont_change_", $footerText="_dont_change_") +!if $bgColor != "_dont_change_" + !global $SKETCH_BG_COLOR = $bgColor +!endif +!if $fontColor != "_dont_change_" + !global $SKETCH_FONT_COLOR = $fontColor +!endif +!if $warningColor != "_dont_change_" + !global $SKETCH_WARNING_COLOR = $warningColor +!endif +!if $fontName != "_dont_change_" + !global $SKETCH_FONT_NAME = $fontName +!endif +!if $footerWarning != "_dont_change_" + !global $SKETCH_FOOTER_WARNING = $footerWarning +!endif +!if $footerText != "_dont_change_" + !global $SKETCH_FOOTER_TEXT = $footerText +!endif +!endprocedure + +!procedure LAYOUT_AS_SKETCH() + skinparam handwritten true +!if $SKETCH_BG_COLOR > "" + skinparam backgroundColor $SKETCH_BG_COLOR +!endif +!if $SKETCH_FONT_COLOR > "" + skinparam footer { + FontColor $SKETCH_FONT_COLOR + } + !if $ARROW_COLOR == "#666666" + !global $ARROW_COLOR = $SKETCH_FONT_COLOR + skinparam arrow { + Color $ARROW_COLOR + FontColor $ARROW_COLOR + } + !endif + !if $BOUNDARY_COLOR == "#444444" + !global $BOUNDARY_COLOR = $SKETCH_FONT_COLOR + skinparam rectangle<> { + FontColor $BOUNDARY_COLOR + BorderColor $BOUNDARY_COLOR + } + !endif +!endif +!if $SKETCH_FONT_NAMES > "" + skinparam defaultFontName $SKETCH_FONT_NAME +!endif +!if $SKETCH_FOOTER_WARNING > "" || $SKETCH_FOOTER_TEXT > "" + !$line = "footer "+ $SKETCH_FOOTER_WARNING + " " + $SKETCH_FOOTER_TEXT + $line +!endif +!endprocedure + +!global $fix_direction=%false() + +!function $down($start,$end) +!if ($fix_direction) +!return $start+"RIGHT"+$end +!else +!return $start+"DOWN"+$end +!endif +!endfunction + +!function $up($start,$end) +!if ($fix_direction) +!return $start+"LEFT"+$end +!else +!return $start+"UP"+$end +!endif +!endfunction + +!function $left($start,$end) +!if ($fix_direction) +!return $start+"UP"+$end +!else +!return $start+"LEFT"+$end +!endif +!endfunction + +!function $right($start,$end) +!if ($fix_direction) +!return $start+"DOWN"+$end +!else +!return $start+"RIGHT"+$end +!endif +!endfunction + +!procedure LAYOUT_TOP_DOWN() +!global $fix_direction=%false() +top to bottom direction +!endprocedure + +!procedure LAYOUT_LEFT_RIGHT() +!global $fix_direction = %false() +left to right direction +!endprocedure + +!procedure LAYOUT_LANDSCAPE() +!global $fix_direction = %true() +left to right direction +!endprocedure + +' legend details can displayed as Normal(), Small(), None() +!function None() +!return $LEGEND_DETAILS_NONE +!endfunction + +!function Normal() +!return $LEGEND_DETAILS_NORMAL +!endfunction + +!function Small() +!return $LEGEND_DETAILS_SMALL +!endfunction + +' has to be last call in diagram +!unquoted procedure SHOW_LEGEND($hideStereotype="true", $details=Small()) +$getHideStereotype($hideStereotype) +legend right +$getLegendTable($details) +endlegend +!endprocedure + +/' @deprecated in favor of SHOW_LEGEND '/ +!unquoted procedure SHOW_DYNAMIC_LEGEND($hideStereotype="true") +SHOW_LEGEND($hideStereotype) +!endprocedure + +' legend is reserved and cannot be uses as alias of SHOW_FLOATING_LEGEND() therefore +' LEGEND() is introduced. It returns the default name of the floating alias "floating_legend_alias" +' and can be used in the Lay_Distance() calls +!function LEGEND() +!return "floating_legend_alias" +!endfunction + +' enables that legend can be located in drawing area of the diagram. It has to be last call in diagram followed by Lay_Distance() +!unquoted procedure SHOW_FLOATING_LEGEND($alias=LEGEND(), $hideStereotype="true", $details=Normal()) +$getLegendArea($alias, $hideStereotype, $details) +!endprocedure + +' Boundaries +' ################################## + +!unquoted procedure UpdateBoundaryStyle($elementName="", $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") + !if ($elementName != "") + !$elementBoundary = $elementName + '_boundary' + UpdateElementStyle($elementBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") + !else + UpdateElementStyle("boundary", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") + ' simulate color inheritance + UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Enterprise", "") + UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "System", "") + UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Container", "") + !endif +!endprocedure + +!unquoted procedure AddBoundaryTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") + !$tagBoundary = $tagStereo + '_boundary' + AddElementTag($tagBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") +!endprocedure + +' add _boundary to all tags that short tag version can be used +!unquoted function $addBoundaryPostfix($tags) + !if (%strlen($tags) == 0) + !return '' + !endif + !$boundaryTags = '' + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$boundaryTags = $boundaryTags + $tag + '_boundary+' + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$boundaryTags = $boundaryTags + $tags + '_boundary' + !endif + !return $boundaryTags +!endfunction + +!function $getBoundary($label, $type) + !if ($type == "") + !return '== ' + $breakLabel($label) + !endif + !if (type != "") + !return '== ' + $breakLabel($label) + '\n[' + $type + ']' + !endif +!endfunction + +!unquoted procedure Boundary($alias, $label, $type="", $tags="", $link="") +!$boundaryTags = $addBoundaryPostfix($tags) +' nodes $type reuses $techn definition of $boundaryTags +!$type=$toElementArg($type, $boundaryTags, "ElementTagTechn", "boundary") +rectangle "$getBoundary($label, $type)" $toStereos("boundary", $boundaryTags) as $alias $getLink($link) +!endprocedure + +' Boundary Styling +UpdateBoundaryStyle("", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR) + +' Relationship +' ################################## + +!function $getRel($direction, $alias1, $alias2, $label, $techn, $descr, $sprite, $tags, $link) + !$sprite = $toRelArg($sprite, $tags, "RelTagSprite") + !$techn = $toRelArg($techn, $tags, "RelTagTechn") + !$rel = $alias1 + ' ' + $direction + ' ' + $alias2 + !if ($tags != "") + !$rel = $rel + ' ' + $toStereos($tags) + !endif + !$rel = $rel + ' : ' + !if ($link != "") + !$rel = $rel + '**[[' + $link + ' ' + !endif + !if ($sprite != "") + !$rel = $rel + $getSprite($sprite) + !if ($label != "") + !$rel = $rel + ' ' + !endif + !endif + !if ($link != "") + !$usedNewLine = ']]**\n**[[' + $link + ' ' + ' if sprite and label is empty than the link url is shown (otherwise link cannot be activated at all) + !$rel = $rel + $breakText($label, $usedNewLine) + ']]**' + !else + !if ($label != "") + !$usedNewLine = '**\n**' + !$rel = $rel + '**' + $breakText($label, $usedNewLine) + '**' + !else + !$rel = $rel + '.' + !endif + !endif + !if ($techn != "") + ' line break is not deterministic, calculate it + !$rel = $rel + '\n//[' + $breakTechn($techn, $REL_TECHN_MAX_CHAR_WIDTH) + ']//' + !endif + !if ($descr != "") + ' line break is not deterministic, calculate it + !$rel = $rel + '\n\n' + $breakDescr($descr, $REL_DESCR_MAX_CHAR_WIDTH) + !endif + !$prop = $getProps() + !if ($prop != "") + ' reuse table + !$rel = $rel + $prop + !endif + !return $rel +!endfunction + +!unquoted procedure Rel_($alias1, $alias2, $label, $direction) +$getRel($direction, $alias1, $alias2, $label, "", "", "", "", "") +!endprocedure +!unquoted procedure Rel_($alias1, $alias2, $label, $techn, $direction) +$getRel($direction, $alias1, $alias2, $label, $techn, "", "", "", "") +!endprocedure + +!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<--", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<-", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +' Layout Helpers +' ################################## + +!function $getHiddenLine($distance) + !return '-[hidden]' + %substr('------------', 0, %intval($distance) + 1) +!endfunction + +!unquoted procedure Lay_D($from, $to) +$from -[hidden]D- $to +!endprocedure +!unquoted procedure Lay_Down($from, $to) +$from -[hidden]D- $to +!endprocedure + +!unquoted procedure Lay_U($from, $to) +$from -[hidden]U- $to +!endprocedure +!unquoted procedure Lay_Up($from, $to) +$from -[hidden]U- $to +!endprocedure + +!unquoted procedure Lay_R($from, $to) +$from -[hidden]R- $to +!endprocedure +!unquoted procedure Lay_Right($from, $to) +$from -[hidden]R- $to +!endprocedure + +!unquoted procedure Lay_L($from, $to) +$from -[hidden]L- $to +!endprocedure +!unquoted procedure Lay_Left($from, $to) +$from -[hidden]L- $to +!endprocedure + +' PlantUML bug: lines which does "not match" with the orientation/direction of the diagram +' uses the same length therefore the method offers no direction at all. +' If a direction is required the Lay_...() methods can be used +!unquoted procedure Lay_Distance($from, $to, $distance="0") +$from $getHiddenLine($distance) $to +!endprocedure + +!global $PERSON_BG_COLOR = "#08427B" +!global $PERSON_BORDER_COLOR = "#073B6F" +!global $EXTERNAL_PERSON_BG_COLOR = "#686868" +!global $EXTERNAL_PERSON_BORDER_COLOR = "#8A8A8A" +!global $SYSTEM_BG_COLOR = "#1168BD" +!global $SYSTEM_BORDER_COLOR = "#3C7FC0" +!global $EXTERNAL_SYSTEM_BG_COLOR = "#999999" +!global $EXTERNAL_SYSTEM_BORDER_COLOR = "#8A8A8A" + +' Styling +' ################################## + +UpdateElementStyle("person", $PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $PERSON_BORDER_COLOR) +UpdateElementStyle("external_person", $EXTERNAL_PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_PERSON_BORDER_COLOR) +UpdateElementStyle("system", $SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $SYSTEM_BORDER_COLOR) +UpdateElementStyle("external_system", $EXTERNAL_SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_SYSTEM_BORDER_COLOR) + +UpdateBoundaryStyle("enterprise", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="Enterprise") +UpdateBoundaryStyle("system", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="System") + +' shortcuts with default colors +!unquoted procedure AddPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddExternalPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("external_person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddExternalSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("external_system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure + +!unquoted procedure UpdateEnterpriseBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="Enterprise", $legendText="") + UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) +!endprocedure +!unquoted procedure UpdateSystemBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="System", $legendText="") + UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) +!endprocedure + +' Sprites +' ################################## + +sprite $person [48x48/16] { +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +0000000000000000000049BCCA7200000000000000000000 +0000000000000000006EFFFFFFFFB3000000000000000000 +00000000000000001CFFFFFFFFFFFF700000000000000000 +0000000000000001EFFFFFFFFFFFFFF80000000000000000 +000000000000000CFFFFFFFFFFFFFFFF6000000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 +0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 +0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 +00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +000000000000000BFFFFFFFFFFFFFFFF5000000000000000 +0000000000000001DFFFFFFFFFFFFFF70000000000000000 +00000000000000000BFFFFFFFFFFFF500000000000000000 +0000000000000000005DFFFFFFFFA1000000000000000000 +0000000000000000000037ABB96100000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000025788300000000005886410000000000000 +000000000007DFFFFFFD9643347BFFFFFFFB400000000000 +0000000004EFFFFFFFFFFFFFFFFFFFFFFFFFFB1000000000 +000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD200000000 +00000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE10000000 +0000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0000000 +000000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5000000 +000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000 +000009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF200000 +00000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF600000 +00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 +00000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF700000 +000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 +0000008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3000000 +000000014555555555555555555555555555555300000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +} + +sprite $person2 [48x48/16] { +0000000000000000000049BCCA7200000000000000000000 +0000000000000000006EFFFFFFFFB3000000000000000000 +00000000000000001CFFFFFFFFFFFF700000000000000000 +0000000000000001EFFFFFFFFFFFFFF80000000000000000 +000000000000000CFFFFFFFFFFFFFFFF6000000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 +0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 +0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 +00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +000000000000000BFFFFFFFFFFFFFFFF5000000000000000 +0000000000000001DFFFFFFFFFFFFFF70000000000000000 +00000000000000000BFFFFFFFFFFFF500000000000000000 +0000000000000000005DFFFFFFFFA1000000000000000000 +0000000000000000000037ABB96100000000000000000000 +000000000002578888300000000005888864100000000000 +0000000007DFFFFFFFFD9643347BFFFFFFFFFB4000000000 +00000004EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB10000000 +0000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2000000 +000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 +00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +0000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50000 +0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0000 +0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2000 +000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 +000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA000 +000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 +0009FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF2000 +0003FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFD0000 +0000BFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF50000 +00003FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFB00000 +000006FFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFE100000 +0000007FFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFD2000000 +00000004EFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFB10000000 +0000000007DF8FFFFFFFFFFFFFFFFFFFFFF8FB4000000000 +000000000002578888888888888888888864100000000000 +} + +sprite $robot [48x48/16] { +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000005BFFFFFFFFFFFFFFFFFFFFFE9100000000000 +0000000000AFFFFFFFFFFFFFFFFFFFFFFFFFE30000000000 +0000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFE1000000000 +000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000000000 +000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000699405FFFFFFC427FFFFFFFFFC427FFFFFFE009982000 +008FFF705FFFFFE10006FFFFFFFE00007FFFFFE00FFFF100 +00CFFF705FFFFFA00001FFFFFFF900002FFFFFE00FFFF500 +00DFFF705FFFFFB00002FFFFFFFA00003FFFFFE00FFFF500 +00DFFF705FFFFFF4000AFFFFFFFF3000BFFFFFE00FFFF500 +00DFFF705FFFFFFFA8DFFFFFFFFFFA8DFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00CFFF705FFFFFF87777777777777777CFFFFFE00FFFF500 +008FFF705FFFFFF100000000000000009FFFFFE00FFFF100 +000699405FFFFFF76666666666666666CFFFFFE009982000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 +000000000EFFFFFFFFFFFFFFFFFFFFFFFFFFFF7000000000 +0000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFD0000000000 +00000000004CFFFFFFFFFFFFFFFFFFFFFFFF910000000000 +000000000000011111111111111111111110000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +} + +sprite $robot2 [48x48/16] { +000000000000000088888888888888880000000000000000 +000000000000000AFFFFFFFFFFFFFFFFA000000000000000 +00000000000000CFFFFFFFFFFFFFFFFFFC00000000000000 +00000000000004EFFFFFFFFFFFFFFFFFFE40000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFFA0000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000004CFFFFFFFFFFFFFFFFFFC40000000000000 +000000488888848CFFFFFFFFFFFFFFFFC848888884000000 +00000CFFFFFFFFC888888888888888888CFFFFFFFFC00000 +00008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000 +0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0000CFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFC0000 +00008FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF80000 +00000CFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFC00000 +000000488887578888888888888888888864688884000000 +000000000000000000000000000000000000000000000000 +} + +' Layout +' ################################## + +SetDefaultLegendEntries("person\nsystem\nexternal_person\nexternal_system\nenterprise_boundary\nsystem_boundary\nboundary") + +!procedure LAYOUT_WITH_LEGEND() +hide stereotype +legend right +|**Legend** | +|<$PERSON_BG_COLOR> person | +|<$SYSTEM_BG_COLOR> system| +|<$EXTERNAL_PERSON_BG_COLOR> external person | +|<$EXTERNAL_SYSTEM_BG_COLOR> external system | +endlegend +!endprocedure + +!global $defaultPersonSprite = "person" +!$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) +UpdateElementStyle("person") +' workaround of plantuml.jar bug - person overwrites external_person setting +!$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) +UpdateElementStyle("external_person") +!global $portraitPerson = "false" + +!procedure $clearPersonRestore() + !$dummy = $clearRestore("person", "sprite") + !$dummy = $clearRestore("person", "legendSprite") + %set_variable_value("$" + "person" + "ElementTagSprite", "") + UpdateElementStyle("person") + ' workaround of plantuml.jar bug - person overwrites external_person setting + !$dummy = $clearRestore("external_person", "sprite") + !$dummy = $clearRestore("external_person", "legendSprite") + %set_variable_value("$" + "external_person" + "ElementTagSprite", "") + UpdateElementStyle("external_person") +!endprocedure + +!procedure HIDE_PERSON_SPRITE() + !$defaultPersonSprite = "" + !$portraitPerson = "false" + $clearPersonRestore() +!endprocedure + +!unquoted procedure SHOW_PERSON_SPRITE($sprite="") + !if ($sprite == "") + !$defaultPersonSprite = "person" + !else + !$defaultPersonSprite = $sprite + !endif + !$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) + UpdateElementStyle("person") + ' workaround of plantuml.jar bug - person overwrites external_person setting + !$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) + UpdateElementStyle("external_person") + !$portraitPerson = "false" +!endprocedure + +!unquoted procedure SHOW_PERSON_PORTRAIT() + !$defaultPersonSprite = "" + !$portraitPerson = "portrait" + $clearPersonRestore() +!endprocedure + +!unquoted procedure SHOW_PERSON_OUTLINE() + !$defaultPersonSprite = "" + !$portraitPerson = "outline" + $clearPersonRestore() +!endprocedure + +' Elements +' ################################## + +!function $getPerson($label, $descr, $sprite) + !if ($sprite == "") && ($defaultPersonSprite != "") + !$sprite = $defaultPersonSprite + !endif + !return $getElementBase($label, "", $descr, $sprite) +!endfunction + +!function $getSystem($label, $descr, $sprite) + !return $getElementBase($label, "", $descr, $sprite) +!endfunction + +!unquoted procedure Person($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "person") +!if ($portraitPerson == "portrait") && ($sprite == "") +actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!elseif ($portraitPerson == "outline") && ($sprite == "") +person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!else +rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!endif +!endprocedure + +!unquoted procedure Person_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_person") +!if ($portraitPerson == "portrait") && ($sprite == "") +actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!elseif ($portraitPerson == "outline") && ($sprite == "") +person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!else +rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!endif +!endprocedure + +!unquoted procedure System($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure System_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemDb($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemQueue($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemDb_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemQueue_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +' Boundaries +' ################################## + +!unquoted procedure Enterprise_Boundary($alias, $label, $tags="", $link="") + !if ($tags != "") + !$allTags = $tags + '+enterprise' + !else + !$allTags = 'enterprise' + !endif + ' $type defined via $tag style + Boundary($alias, $label, "", $allTags, $link) +!endprocedure + +!unquoted procedure System_Boundary($alias, $label, $tags="", $link="") + !if ($tags != "") + !$allTags = $tags + '+system' + !else + !$allTags = 'system' + !endif + ' $type defined via $tag style + Boundary($alias, $label, "", $allTags, $link) +!endprocedure + +allow_mixing + +'Общее позиционирование +skinparam { + wrapWidth 200 + maxMessageSize 200 + defaultfontname arial + roundCorner 10 + linetype ortho + shadowing false + HyperLinkColor #?black:white + HyperLinkUnderline false +} +hide circle + + +skinparam arrow { + color black + thickness 1 +} + +skinparam note { + bordercolor #4e4948 + backgroundcolor pink +} + +' Scope: A single software system. +' Primary elements: Containers within the software system in scope. +' Supporting elements: People and software systems directly connected to the containers. +' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff. + +' Colors +' ################################## + +!$CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR +!$CONTAINER_BG_COLOR ?= "#438DD5" +!$CONTAINER_BORDER_COLOR ?= "#3C7FC0" + +!$CONTAINER_BOUNDARY_COLOR ?= $BOUNDARY_COLOR +!$CONTAINER_BOUNDARY_BG_COLOR ?= $BOUNDARY_BG_COLOR +!$CONTAINER_BOUNDARY_BORDER_STYLE ?= $BOUNDARY_BORDER_STYLE + +!$EXTERNAL_CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR +!$EXTERNAL_CONTAINER_BG_COLOR ?= "#B3B3B3" +!$EXTERNAL_CONTAINER_BORDER_COLOR ?= "#A6A6A6" + +' Styling +' ################################## +UpdateElementStyle("container", $CONTAINER_BG_COLOR, $CONTAINER_FONT_COLOR, $CONTAINER_BORDER_COLOR) +UpdateElementStyle("external_container", $EXTERNAL_CONTAINER_BG_COLOR, $EXTERNAL_CONTAINER_FONT_COLOR, $EXTERNAL_CONTAINER_BORDER_COLOR) + +UpdateBoundaryStyle("container", $bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $type="Container") + +' shortcuts with default colors +!unquoted procedure AddContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") + $addElementTagInclReuse("container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddExternalContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") + $addElementTagInclReuse("external_container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) +!endprocedure + +!unquoted procedure UpdateContainerBoundaryStyle($bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $shadowing="", $shape="", $type="Container", $legendText="") + UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) +!endprocedure + +' Layout +' ################################## + +SetDefaultLegendEntries("person\nsystem\ncontainer\nexternal_person\nexternal_system\nexternal_container\nenterprise_boundary\nsystem_boundary\ncontainer_boundary\nboundary") + +!procedure LAYOUT_WITH_LEGEND() +hide stereotype +legend right +|**Legend** | +|<$PERSON_BG_COLOR> person | +|<$SYSTEM_BG_COLOR> system | +|<$CONTAINER_BG_COLOR> container | +|<$EXTERNAL_PERSON_BG_COLOR> external person | +|<$EXTERNAL_SYSTEM_BG_COLOR> external system | +|<$EXTERNAL_CONTAINER_BG_COLOR> external container | +endlegend +!endprocedure + +' Elements +' ################################## + +!function $getContainer($label, $techn, $descr, $sprite) + !return $getElementBase($label, $techn, $descr, $sprite) +!endfunction + +!unquoted procedure Container($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") +rectangle "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure ContainerDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") +database "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure ContainerQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") +queue "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure Container_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") +rectangle "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure ContainerDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") +database "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure ContainerQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") +!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") +queue "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) +!endprocedure + +' Boundaries +' ################################## + +!unquoted procedure Container_Boundary($alias, $label, $tags="", $link="") + !if ($tags != "") + !$allTags = $tags + '+container' + !else + !$allTags = 'container' + !endif + ' $type defined via $tag style + Boundary($alias, $label, "", $allTags, $link) +!endprocedure + +{{&renderCore}} + +{{&code}} + +@enduml diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/templates/context.puml b/src/repository_structure_example/metamodels/default/entities/contexts/templates/context.puml new file mode 100644 index 0000000..5488dd5 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/templates/context.puml @@ -0,0 +1,1847 @@ +@startuml +' C4-PlantUML + +'Version +' ################################## +!function C4Version() + ' 2 spaces and ' are used as unique marker, that the release scripts makes the correct version update + !$c4Version = "2.6.0beta1" + !return $c4Version +!end function + +!procedure C4VersionDetails() +rectangle C4VersionDetailsArea <> [ +| PlantUML | **%version()** | +| C4-PlantUML | **C4Version()** | +] +!end procedure + +' Colors +' ################################## + +!global $ELEMENT_FONT_COLOR = "#FFFFFF" + +!global $ARROW_COLOR = "#666666" + +!global $BOUNDARY_COLOR = "#444444" +!global $BOUNDARY_BG_COLOR = "transparent" + +!global $LEGEND_FONT_COLOR = "#FFFFFF" +!global $LEGEND_TITLE_COLOR = "#000000" +' %darken(darkkhaki,50), #khaki +!global $LEGEND_DARK_COLOR = "#66622E" +!global $LEGEND_LIGHT_COLOR = "#khaki" + +!global $SKETCH_BG_COLOR = "#EEEBDC" +!global $SKETCH_FONT_COLOR = "" +!global $SKETCH_WARNING_COLOR = "red" +!global $SKETCH_FONT_NAME = "Comic Sans MS" + +' Labels +' ################################## + +!global $LEGEND_SHADOW_TEXT = "shadow" +!global $LEGEND_NO_SHADOW_TEXT = "no shadow" +!global $LEGEND_NO_FONT_BG_TEXT = "last text and back color" +!global $LEGEND_NO_FONT_TEXT = "last text color" +!global $LEGEND_NO_BG_TEXT = "last back color" +!global $LEGEND_NO_LINE_TEXT = "last line color" +!global $LEGEND_ROUNDED_BOX = "rounded box" +!global $LEGEND_EIGHT_SIDED = "eight sided" +!global $LEGEND_DOTTED_LINE = "dotted" +!global $LEGEND_DASHED_LINE = "dashed" +!global $LEGEND_BOLD_LINE = "bold" +!global $LEGEND_BOUNDARY = "boundary" +!global $LEGEND_DASHED_BOUNDARY = "dashed" +' ignore transparent atm, that the legend is smaller +'!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed, transparent" +!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed" + +!global $SKETCH_FOOTER_WARNING = "Warning:" +!global $SKETCH_FOOTER_TEXT = "Created for discussion, needs to be validated" + +' Styling +' ################################## + +!global $TECHN_FONT_SIZE = 12 +!global $ROUNDED_BOX_SIZE = 25 +!global $EIGHT_SIDED_SIZE = 18 + +!global $LEGEND_DETAILS_SMALL_SIZE = 10 +!global $LEGEND_DETAILS_NORMAL_SIZE = 14 +!global $LEGEND_DETAILS_SIZE = $LEGEND_DETAILS_SMALL_SIZE + +!global $ROUNDED_BOX = "roundedBox" +!global $EIGHT_SIDED = "eightSided" + +!global $DOTTED_LINE = "dotted" +!global $DASHED_LINE = "dashed" +!global $BOLD_LINE = "bold" + +!global $LEGEND_DETAILS_NONE = "none" +!global $LEGEND_DETAILS_NORMAL = "normal" +!global $LEGEND_DETAILS_SMALL = "small" + +skinparam defaultTextAlignment center + +skinparam wrapWidth 200 +skinparam maxMessageSize 150 + +skinparam LegendBorderColor transparent +skinparam LegendBackgroundColor transparent +skinparam LegendFontColor $LEGEND_FONT_COLOR + +skinparam shadowing<> false +' #00000000 is transparent +skinparam rectangle<> { + backgroundcolor #00000000 + bordercolor #00000000 +} + +skinparam rectangle { + StereotypeFontSize 12 + shadowing false +} + +skinparam database { + StereotypeFontSize 12 + shadowing false +} + +skinparam queue { + StereotypeFontSize 12 + shadowing false +} + +skinparam arrow { + Color $ARROW_COLOR + FontColor $ARROW_COLOR + FontSize 12 +} + +skinparam person { + StereotypeFontSize 12 + shadowing false +} + +skinparam actor { + StereotypeFontSize 12 + shadowing false + style awesome +} + +' Some boundary skinparams have to be set as package skinparams too (PlantUML uses internal packages) +' UpdateBoundaryStyle() called in boundary section below +skinparam rectangle<> { + Shadowing false + StereotypeFontSize 6 + StereotypeFontColor $BOUNDARY_BG_COLOR + BorderStyle dashed +} + +skinparam package { + StereotypeFontSize 6 + StereotypeFontColor $BOUNDARY_BG_COLOR + FontStyle plain + BackgroundColor $BOUNDARY_BG_COLOR +} + +' Legend and Tags +' ################################## +!global $tagDefaultLegend = "" +!global $tagCustomLegend = "" + +' rel specific +!unquoted function $toStereos($tags) + !if (%strlen($tags) == 0) + !return '' + !endif + !$stereos = '' + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$stereos = $stereos + '<<' + $tag + '>>' +%set_variable_value("$" + $tag + "_LineLegend", %true()) + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$stereos = $stereos + '<<' + $tags + '>>' +%set_variable_value("$" + $tags + "_LineLegend", %true()) + !endif + !return $stereos +!endfunction + +' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag +!unquoted function $toRelArg($arg, $tags, $varPostfix) + !if ($arg > "") + !return $arg + !endif + + !if (%strlen($tags) == 0) + !return $arg + !endif + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$newArg = %get_variable_value("$" + $tag + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$newArg = %get_variable_value("$" + $tags + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !endif + !return $arg +!endfunction + +' element specific (unused are hidden based on mask) +!unquoted function $toStereos($elementType, $tags) + !if (%strlen($tags) == 0) + !$stereos = '<<' + $elementType + '>>' +%set_variable_value("$" + $elementType + "Legend", %true()) + !return $stereos + !endif + !$stereos = '' + !$mask = $resetMask() + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$stereos = $stereos + '<<' + $tag + '>>' + !$mergedMask = $combineMaskWithTag($mask, $tag) + !if ($mergedMask != $mask) +%set_variable_value("$" + $tag + "Legend", %true()) + !$mask = $mergedMask + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$stereos = $stereos + '<<' + $tags + '>>' + !$mergedMask = $combineMaskWithTag($mask, $tags) + !if ($mergedMask != $mask) +%set_variable_value("$" + $tags + "Legend", %true()) + !$mask = $mergedMask + !endif + !endif + ' has to be last, otherwise PlantUML overwrites all tag specific skinparams + !$stereos = $stereos + '<<' + $elementType + '>>' + !$mergedMask = $combineMaskWithTag($mask, $elementType) + !if ($mergedMask != $mask) +%set_variable_value("$" + $elementType + "Legend", %true()) + !$mask = $mergedMask + !endif + !return $stereos +!endfunction + +' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag +!unquoted function $toElementArg($arg, $tags, $varPostfix, $elementType) + !if ($arg > "") + !return $arg + !endif + + !if (%strlen($tags) == 0) + !$newArg = %get_variable_value("$" + $elementType + $varPostfix) + !if ($newArg > "") + !return $newArg + !else + !return $arg + !endif + !endif + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$newArg = %get_variable_value("$" + $tag + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$newArg = %get_variable_value("$" + $tags + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !$newArg = %get_variable_value("$" + $elementType + $varPostfix) + !if ($newArg > "") + !return $newArg + !endif + !endif + !return $arg +!endfunction + +' if $value is empty try to load it via variable, optional can it store the calculated value +!function $restoreEmpty($elementType, $property, $value, $store) + !$var = "$" + $elementType + "Restore" + $property + !if ($value == "") + !$value = %get_variable_value($var) + !elseif ($store) + %set_variable_value($var, $value) + !endif + !return $value +!endfunction + +' clear the restore property +!function $clearRestore($elementType, $property) + !$var = "$" + $elementType + "Restore" + $property + %set_variable_value($var, "") + !return "" +!endfunction + +!function $elementTagSkinparams($element, $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !$elementSkin = "skinparam " + $element + "<<" + $tagStereo + ">> {" + %newline() + !if ($fontColor != "") + !if (%strpos($tagStereo, "boundary") < 0) + !$elementSkin = $elementSkin + " StereotypeFontColor " + $fontColor + %newline() + !endif + !$elementSkin = $elementSkin + " FontColor " + $fontColor + %newline() + !endif + !if ($bgColor != "") + !$elementSkin = $elementSkin + " BackgroundColor " + $bgColor + %newline() + !endif + !if ($borderColor != "") + !$elementSkin = $elementSkin + " BorderColor " + $borderColor+ %newline() + !endif + !if ($shadowing == "true") + !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "true" + %newline() + !endif + !if ($shadowing == "false") + !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "false" + %newline() + !endif + ' only rectangle supports shape(d corners), define both skinparam that overlays are working + !if ($shape != "" && $element == "rectangle") + !if ($shape == $ROUNDED_BOX) + !$elementSkin = $elementSkin + " RoundCorner " + $ROUNDED_BOX_SIZE+ %newline() + !$elementSkin = $elementSkin + " DiagonalCorner " + "0" + %newline() + !elseif ($shape == $EIGHT_SIDED) + !$elementSkin = $elementSkin + " RoundCorner " + "0" + %newline() + !$elementSkin = $elementSkin + " DiagonalCorner " + $EIGHT_SIDED_SIZE+ %newline() + !endif + !endif + !$elementSkin = $elementSkin + "}" + %newline() + !return $elementSkin +!endfunction + +!unquoted procedure $defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + ' only rectangle supports shape(d corners) + !$tagSkin = $elementTagSkinparams("rectangle", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !$tagSkin = $tagSkin + $elementTagSkinparams("database", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + !$tagSkin = $tagSkin + $elementTagSkinparams("queue", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + ' plantuml.jar bug - actor have to be after person + !$tagSkin = $tagSkin + $elementTagSkinparams("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") + ' actor has style awesome, therefore $fontColor is ignored and text uses $bgColor too + !$tagSkin = $tagSkin + $elementTagSkinparams("actor", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "") + !if (%strpos($tagStereo, "boundary") >= 0 && $bgColor != "") + !$tagSkin = $tagSkin + "skinparam package<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() + !$tagSkin = $tagSkin + "skinparam rectangle<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() + !endif +$tagSkin +!endprocedure + +' arrow colors cannot start with # (legend background has to start with #) +!function $colorWithoutHash($c) + !if (%substr($c, 0, 1) == "#") + !$c = %substr($c,1) + !endif + !return $c +!endfunction + +!unquoted procedure $defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) + !$elementSkin = "skinparam arrow<<" + $tagStereo + ">> {" + %newline() + !if ($lineColor != "") || ($textColor != "") || ($lineStyle != "") + !$elementSkin = $elementSkin + " Color " + !if ($lineColor != "") + !$elementSkin = $elementSkin + $colorWithoutHash($lineColor) + !endif + !if ($textColor != "") + !$elementSkin = $elementSkin + ";text:" + $colorWithoutHash($textColor) + !endif + !if ($lineStyle != "") + !$elementSkin = $elementSkin + ";line." + $lineStyle + !endif + !$elementSkin = $elementSkin + %newline() + !endif + !if ($lineThickness != "") + !$elementSkin = $elementSkin + " thickness " + $lineThickness + %newline() + !endif + !$elementSkin = $elementSkin + "}" + %newline() +$elementSkin +!endprocedure + +' %is_dark() requires PlantUML version >= 1.2021.6 +!if (%function_exists("%is_dark")) + !$PlantUMLSupportsDynamicLegendColor = %true() +!else + !$PlantUMLSupportsDynamicLegendColor = %false() + !log "dynamic undefined legend colors" requires PlantUML version >= 1.2021.6, therefore only static assigned colors are used +!endif + +!unquoted function $contrastLegend($color) + !if (%is_dark($color)) + !$value = $LEGEND_LIGHT_COLOR + !else + !$value = $LEGEND_DARK_COLOR + !endif + !return $value +!endfunction + +!unquoted function $flatLegend($color) + !if (%is_dark($color)) + !$value = $LEGEND_DARK_COLOR + !else + !$value = $LEGEND_LIGHT_COLOR + !endif + !return $value +!endfunction + +' legend background has to start with # +!function $colorWithHash($c) + !if (%substr($c, 0, 1) != "#") + !$c = "#" + $c + !endif + !return $c +!endfunction + +!function $addMaskFlag($mask, $attr) + !if ($attr == "") + !$mask = $mask + "0" + !else + !$mask = $mask + "1" + !endif + !return $mask +!endfunction + +!function $orFlags($flag1, $flag2) + !if ($flag1 == "0" && $flag2 == "0") + !return "0" + !endif + !return "1" +!endfunction + +!function $tagLegendMask($bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) + !$mask = "" + !$mask = $addMaskFlag($mask, $bgColor) + !$mask = $addMaskFlag($mask, $fontColor) + !$mask = $addMaskFlag($mask, $borderColor) + !$mask = $addMaskFlag($mask, $shadowing) + !$mask = $addMaskFlag($mask, $shape) + !$mask = $addMaskFlag($mask, $sprite) + !return $mask +!endfunction + +!function $resetMask() + !return "000000" +!endfunction + +!function $combineMasks($mask1, $mask2) + !$mask = "" + !$mask = $mask + $orFlags(%substr($mask1, 0, 1), %substr($mask2, 0, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 1, 1), %substr($mask2, 1, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 2, 1), %substr($mask2, 2, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 3, 1), %substr($mask2, 3, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 4, 1), %substr($mask2, 4, 1)) + !$mask = $mask + $orFlags(%substr($mask1, 5, 1), %substr($mask2, 5, 1)) + !return $mask +!endfunction + +!function $combineMaskWithTag($mask1, $tag) + !$mask2 = %get_variable_value("$" + $tag+ "LegendMask") + !if ($mask2 == "") + ' !log combineMaskWithTag $mask1, $tag, ... only $mask1 + !return $mask1 + !endif + + ' !log combineMaskWithTag $mask1, $tag, $mask2 ... $combineMasks($mask1, $mask2) + !return $combineMasks($mask1, $mask2) +!endfunction + +' element symbols typically 4 times too big in legend +!function $smallVersionSprite($sprite) + ' ,scale= ... has to be first (...,color=black,scale=0.25... is invalid too) + !if (%strpos($sprite, "=") < 0) + !if (%substr($sprite, 0, 4) == "img:") + !$smallSprite = $sprite + "{scale=0.25}" + !else + !$smallSprite = $sprite + ",scale=0.25" + !endif + !else + !$smallSprite = $sprite + !endif + !return $smallSprite +!endfunction + +' format sprite that it can be used in diagram +!function $getSprite($sprite) + ' if it starts with & it's a OpenIconic, details see https://useiconic.com/open/ + ' if it starts with img: it's an image, details see https://plantuml.com/creole + !if (%substr($sprite, 0, 1) != "&" && %substr($sprite, 0, 4) != "img:") + !$formatted = "<$" + $sprite + ">" + !else + !$formatted = "<" + $sprite + ">" + !endif + !return $formatted +!endfunction + +!function $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + !$bg = $bgColor + !$fo = $fontColor + !$bo = $borderColor + + !if ($fo == "") + !if ($bg != "") +!if ($PlantUMLSupportsDynamicLegendColor) + !$fo = $contrastLegend($bg) +!else + !$fo = $LEGEND_DARK_COLOR +!endif + !else + !if ($bo == "") + !$fo = $LEGEND_DARK_COLOR + !$bg = $LEGEND_LIGHT_COLOR + !else +!if ($PlantUMLSupportsDynamicLegendColor) + !$fo = $flatLegend($bo) + !$bg = $contrastLegend($bo) +!else + !$fo = $LEGEND_DARK_COLOR + !$bg = $LEGEND_LIGHT_COLOR +!endif + !endif + !endif + !else + !if ($bg == "") +!if ($PlantUMLSupportsDynamicLegendColor) + !$bg = $contrastLegend($fo) +!else + !$bg = $LEGEND_LIGHT_COLOR +!endif + !endif + !endif + + !if ($bo == "") + !$bo = $bg + !endif + + !$tagEntry = "|" + !$tagDetails = "(" + !$tagEntry = $tagEntry + "<" + $colorWithHash($bg) +">" + ' ..white rectangle + !$tagEntry = $tagEntry + " " + !$tagEntry = $tagEntry + "" + !if ($legendSprite != "") + !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " + !endif + !if ($legendText == "") + !if ($tagStereo == "boundary") + !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " + !else + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " + !endif + !elseif (%strpos($tagStereo, "boundary") >= 0) + ' if contains/ends with _boundary remove _boundary and add "boundary (dashed)" + !$pos = %strpos($tagStereo, "_boundary") + !if ($pos > 0) + !$tagEntry = $tagEntry + " " + %substr($tagStereo, 0 ,$pos) + !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " + !else + !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " + !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " + !endif + !endif + !else + !$tagEntry = $tagEntry + " " + $tagStereo + " " + !endif + !if ($shadowing == "true") + !$tagDetails = $tagDetails + $LEGEND_SHADOW_TEXT + ", " + !endif + !if ($shadowing == "false") + !$tagDetails = $tagDetails + $LEGEND_NO_SHADOW_TEXT + ", " + !endif + !if ($shape == $ROUNDED_BOX) + !$tagDetails = $tagDetails + $LEGEND_ROUNDED_BOX + ", " + !endif + !if ($shape == $EIGHT_SIDED) + !$tagDetails = $tagDetails + $LEGEND_EIGHT_SIDED + ", " + !endif + !if ($fontColor == "" && $bgColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_BG_TEXT + ", " + !else + !if ($fontColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " + !endif + !if ($bgColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_BG_TEXT + ", " + !endif + !endif + !if ($tagDetails=="(") + !$tagDetails = "" + !else + !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) + !$tagDetails = $tagDetails + ")" + !endif + !else + !$brPos = %strpos($legendText, "\n") + !if ($brPos > 0) + !$tagEntry = $tagEntry + %substr($legendText, 0, $brPos) + " " + !$details = %substr($legendText, $brPos + 2) + !if ($details=="") + !$tagDetails = "" + !else + !$tagDetails = $tagDetails + $details + ")" + !endif + !else + !$tagEntry = $tagEntry + " " + $legendText + " " + !$tagDetails = "" + !endif + !endif + + !$tagDetails = $tagDetails + " " + !$tagDetails = $tagDetails + "|" +%set_variable_value("$" + $tagStereo + "LegendEntry", $tagEntry) +%set_variable_value("$" + $tagStereo + "LegendDetails", $tagDetails) + !return $tagEntry +!endfunction + +!function $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) + !$tc = $textColor + !$lc = $lineColor + + !if ($tc == "") + !if ($PlantUMLSupportsDynamicLegendColor) + !$tc = $flatLegend($ARROW_COLOR) + !else + !$tc = $LEGEND_DARK_COLOR + !endif + !endif + !if ($lc == "") + !if ($PlantUMLSupportsDynamicLegendColor) + !$lc = $flatLegend($ARROW_COLOR) + !else + !$lc = $LEGEND_DARK_COLOR + !endif + !endif + + !$tagEntry = "|" + !$tagDetails = "(" + ' ..white line + !$tagEntry = $tagEntry + " " + !$tagEntry = $tagEntry + "" + !if ($legendSprite != "") + !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " + !endif + !if ($legendText == "") + !$tagEntry = $tagEntry + " " + $tagStereo + " " + !if ($textColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " + !endif + !if ($lineColor == "") + !$tagDetails = $tagDetails + $LEGEND_NO_LINE_TEXT + ", " + !endif + !if ($lineStyle != "") + !if ($lineStyle == $DOTTED_LINE) + !$tagDetails = $tagDetails + $LEGEND_DOTTED_LINE + ", " + !elseif ($lineStyle == $DASHED_LINE) + !$tagDetails = $tagDetails + $LEGEND_DASHED_LINE + ", " + !elseif ($lineStyle == $BOLD_LINE) + !$tagDetails = $tagDetails + $LEGEND_BOLD_LINE + ", " + !else + !$tagDetails = $tagDetails + $lineStyle + ", " + !endif + !endif + !if ($lineThickness != "") + !$tagDetails = $tagDetails + "thickness " + $lineThickness + ") " + !endif + !if ($tagDetails=="(") + !$tagDetails = "" + !else + !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) + !$tagDetails = $tagDetails + ")" + !endif + !else + !$brPos = %strpos($legendText, "\n") + !if ($brPos > 0) + !$tagEntry = $tagEntry + " " + %substr($legendText, 0, $brPos) + " " + !$details = %substr($legendText, $brPos + 2) + !if ($details=="") + !$tagDetails = "" + !else + !$tagDetails = $tagDetails + $details + ")" + !endif + !else + !$tagEntry = $tagEntry + " " + $legendText + " " + !$tagDetails = "" + !endif + !endif + + !$tagDetails = $tagDetails + " " + !$tagDetails = $tagDetails + "|" +%set_variable_value("$" + $tagStereo + "_LineLegendEntry", $tagEntry) +%set_variable_value("$" + $tagStereo + "_LineLegendDetails", $tagDetails) + !return $tagEntry +!endfunction + +!unquoted procedure $addTagToLegend($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $legendText="", $legendSprite="") +'' if a combined element tag is defined (e.g. "v1.0&v1.1") then it is typically a merged color, +'' like a new $fontColor="#fdae61" therefore it should be added to the legend +'' and the & combined tags will be not removed +' !if (%strpos($tagStereo, "&") < 0) + !$dummyAlreadyVariables = $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + !$tagCustomLegend = $tagCustomLegend + $tagStereo + "\n" + !$tagMask = $tagLegendMask( $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) +%set_variable_value("$" + $tagStereo + "LegendMask", $tagMask) +' !endif +!endprocedure + +!unquoted procedure $addRelTagToLegend($tagStereo, $textColor="", $lineColor="", $lineStyle="", $legendText="", $legendSprite="", $lineThickness="") +'' Arrows have a bug with stereotype/skinparams and cannot combine text colors of one stereotype +'' and the line color of another stereotype. Therefore the text color of one tag and the line color +'' of another tag have to be combined via a "workaround" tag ("v1.0&v1.1"). +'' This workaround tag could be theoretically removed in the legend but after that there would +'' be an inconsistency between the element tags and the rel tags and therefore +'' & combined workaround tags are not removed too (and in unlikely cases the color itself could be changed) +' !if (%strpos($tagStereo, "&") < 0) + !$dummyAlreadyVariables = $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) + !$tagCustomLegend = $tagCustomLegend + $tagStereo + "_Line\n" +' !endif +!endprocedure + +!procedure $showActiveLegendEntries($allDefined) + !$brPos = %strpos($allDefined, "\n") + !while ($brPos >= 0) + !$tagStereo = %substr($allDefined, 0, $brPos) + !$allDefined = %substr($allDefined, $brPos+2) + !$brPos = %strpos($allDefined, "\n") + !if (%variable_exists("$" + $tagStereo + "Legend")) + ' is part of legendDetails + !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") + !$partSize = "" + !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") + !$line = $part1 + $partSize + $part2 +$line + !endif + !endwhile + !if (%strlen($allDefined) > 0) + !$tagStereo = $allDefined + !if (%variable_exists("$" + $tagStereo + "Legend")) + ' is part of legendDetails + !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") + !$partSize = "" + !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") + !$line = $part1 + $partSize + $part2 +$line + !endif + !endif +!endprocedure + +!function RoundedBoxShape() +!return $ROUNDED_BOX +!endfunction + +!function EightSidedShape() +!return $EIGHT_SIDED +!endfunction + +!function DottedLine() +!return $DOTTED_LINE +!endfunction + +!function DashedLine() +!return $DASHED_LINE +!endfunction + +!function BoldLine() +!return $BOLD_LINE +!endfunction + +' used by new defined tags +!unquoted procedure AddElementTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") +$defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !if ($sprite!="") +%set_variable_value("$" + $tagStereo + "ElementTagSprite", $sprite) + !if ($legendSprite == "") + !$legendSprite = $smallVersionSprite($sprite) + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $tagStereo + "ElementTagTechn", $techn) + !endif +$addTagToLegend($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) +!endprocedure + +!unquoted procedure $addElementTagInclReuse($elementName, $tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") + !$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) + !$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) + !$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) + !$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) + !$shape=$restoreEmpty($elementName, "shape", $shape, %true()) + !$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) + !$techn=$restoreEmpty($elementName, "techn", $techn, %true()) + ' new style should has its own legend text + ' !$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) + !$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) + + AddElementTag($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) +!endprocedure + +' used by new defined rel tags +!unquoted procedure AddRelTag($tagStereo, $textColor="", $lineColor="", $lineStyle="", $sprite="", $techn="", $legendText="", $legendSprite="", $lineThickness="") +$defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) + !if ($sprite != "") +%set_variable_value("$" + $tagStereo + "RelTagSprite", $sprite) + !if ($legendSprite == "") + ' relation symbols typically 1:1 no additional scale required + !$legendSprite = $sprite + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $tagStereo + "RelTagTechn", $techn) + !endif +$addRelTagToLegend($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) +!endprocedure + +' update the style of existing elements like person, ... +!unquoted procedure UpdateElementStyle($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") +!$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) +!$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) +!$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) +!$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) +!$shape=$restoreEmpty($elementName, "shape", $shape, %true()) +!$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) +!$techn=$restoreEmpty($elementName, "techn", $techn, %true()) +!$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) +!$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) +$defineSkinparams($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape) + !if ($sprite != "") +%set_variable_value("$" + $elementName + "ElementTagSprite", $sprite) + !if ($legendSprite == "") + !$legendSprite = $smallVersionSprite($sprite) + !endif + !endif + !if ($techn != "") +%set_variable_value("$" + $elementName + "ElementTagTechn", $techn) + !endif + !$dummyAlreadyVariables = $setTagLegendVariables($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) + ' default tags sets at least bgColor and fontColor + !$tagMask = $tagLegendMask("CHANGED", "CHANGED", $borderColor, $shadowing, $shape, $sprite) +%set_variable_value("$" + $elementName + "LegendMask", $tagMask) +!endprocedure + +/' @deprecated in favor of UpdateElementStyle '/ +!unquoted procedure UpdateSkinparamsAndLegendEntry($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="") +UpdateElementStyle($elementName, $bgColor, $fontColor, $borderColor, $shadowing) +!endprocedure + +' update the style of default relation, it has to set both properties (combined statement not working) +!unquoted procedure UpdateRelStyle($textColor, $lineColor) + !$elementSkin = "skinparam arrow {" + %newline() + !$elementSkin = $elementSkin + " Color " + $lineColor + %newline() + !$elementSkin = $elementSkin + " FontColor " + $textColor + %newline() + !$elementSkin = $elementSkin + "}" + %newline() +$elementSkin +!endprocedure + +' tags/stereotypes have to be delimited with \n +!unquoted procedure SetDefaultLegendEntries($tagStereoEntries) + !$tagDefaultLegend = $tagStereoEntries +!endprocedure + +' Links +' ################################## + +!function $getLink($link) + !if ($link != "") + !return "[[" + $link + "]]" + !else + !return "" + !endif +!endfunction + +' Line breaks +' ################################## + +' PlantUML supports no DETERMINISTIC/automatic line breaks of "PlantUML line" (C4 Relashionships) +' therefore Rel...() implements an automatic line break based on spaces (like in all other objects). +' If a $type contains \n then these are used (and no automatic space based line breaks are done) +' $REL_TECHN_MAX_CHAR_WIDTH defines the automatic line break position +!global $REL_TECHN_MAX_CHAR_WIDTH = 35 +!global $REL_DESCR_MAX_CHAR_WIDTH = 32 + +!unquoted function $breakText($text, $usedNewLine, $widthStr="-1") +!$width = %intval($widthStr) +!$multiLine = "" +!if (%strpos($text, "\n") >= 0) + !while (%strpos($text, "\n") >= 0) + !$brPos = %strpos($text, "\n") + !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine + !$text = %substr($text, $brPos+2) + !endwhile +!else + !while ($width>0 && %strlen($text) > $width) + !$brPos = $width + !while ($brPos > 0 && %substr($text, $brPos, 1) != ' ') + !$brPos = $brPos - 1 + !endwhile + + !if ($brPos < 1) + !$brPos = %strpos($text, " ") + !else + !endif + + !if ($brPos > 0) + !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine + !$text = %substr($text, $brPos + 1) + !else + !$multiLine = $multiLine+ $text + !$text = "" + !endif + !endwhile +!endif +!if (%strlen($text) > 0) + !$multiLine = $multiLine + $text +!endif +!return $multiLine +!endfunction + +!unquoted function $breakLabel($text) +!$usedNewLine = "\n== " +!$multiLine = $breakText($text, $usedNewLine) +!return $multiLine +!endfunction + +!unquoted function $breakDescr($text, $widthStr) + !$usedNewLine = "\n" + !return $breakText($text, $usedNewLine, $widthStr) +!endfunction + +' $breakTechn() supports //...//; $breakNode() in C4_Deployment supports no //....// +!unquoted function $breakTechn($text, $widthStr) + !$usedNewLine = '//\n//' + !return $breakText($text, $usedNewLine, $widthStr) +!endfunction + +' Element base layout +' ################################## + +!function $getElementBase($label, $techn, $descr, $sprite) + !$element = "" + !if ($sprite != "") + !$element = $element + $getSprite($sprite) + !if ($label != "") + !$element = $element + '\n' + !endif + !endif + !if ($label != "") + !$element = $element + '== ' + $breakLabel($label) + !else + !$element = $element + '.' + !endif + !if ($techn != "") + !$element = $element + '\n//[' + $breakTechn($techn, '-1') + ']//' + !endif + !if ($descr != "") + !$element = $element + '\n\n' + $descr + !endif + !return $element +!endfunction + +' Element properties +' ################################## + +' collect all defined properties as table rows +!global $propTable = "" +!global $propTableCaption = "" +!global $propColCaption = "=" + +!unquoted function SetPropertyHeader($col1Name, $col2Name, $col3Name = "", $col4Name = "") + !$propColCaption = "" + !$propTableCaption = "|= " + $col1Name + " |= " + $col2Name + " |" + !if ($col3Name != "") + !$propTableCaption = $propTableCaption + "= " + $col3Name + " |" + !endif + !if ($col4Name != "") + !$propTableCaption = $propTableCaption + "= " + $col4Name + " |" + !endif + !return "" +!endfunction + +!unquoted function WithoutPropertyHeader() + !$propTableCaption = "" + !$propColCaption = "=" + !return "" +!endfunction + +!unquoted function AddProperty($col1, $col2, $col3 = "", $col4 = "") + !if ($propTable == "") + !if ($propTableCaption != "") + !$propTable = $propTableCaption + "\n" + !endif + !else + !$propTable = $propTable + "\n" + !endif + !$propTable = $propTable + "| " + $col1 + " |" + $propColCaption + " " + $col2 + " |" + !if ($col3 != "") + !$propTable = $propTable + " " + $col3 + " |" + !endif + !if ($col4 != "") + !$propTable = $propTable + " " + $col4 + " |" + !endif + !return "" +!endfunction + +!unquoted function $getProps($alignedNL = "\n") + !if ($propTable != "") + !$retTable = $alignedNL + $propTable + !$propTable = "" + !return $retTable + !endif + !return "" +!endfunction + +!unquoted function $getProps_L() + !return $getProps("\l") +!endfunction + +!unquoted function $getProps_R() + !return $getProps("\r") +!endfunction + +SetPropertyHeader("Property","Value") + +' Layout +' ################################## + +!function $getLegendDetailsSize($detailsFormat) + !if $detailsFormat == $LEGEND_DETAILS_NONE + !$size = 0 + !elseif $detailsFormat == $LEGEND_DETAILS_SMALL + !$size = $LEGEND_DETAILS_SMALL_SIZE + !else + !$size = $LEGEND_DETAILS_NORMAL_SIZE + !endif + !return $size +!endfunction + +!procedure $getHideStereotype($hideStereotype) +!if ($hideStereotype == "true") +hide stereotype +!endif +!endprocedure + +!procedure $getLegendTable($detailsFormat) +!global $LEGEND_DETAILS_SIZE = $getLegendDetailsSize($detailsFormat) +<#00000000,#00000000>|**Legend** | +$showActiveLegendEntries($tagDefaultLegend) +$showActiveLegendEntries($tagCustomLegend) +!endprocedure + +!procedure $getLegendArea($areaAlias, $hideStereotype, $details) +$getHideStereotype($hideStereotype) +rectangle $areaAlias<> [ +$getLegendTable($details) +] +!endprocedure + +!procedure HIDE_STEREOTYPE() +hide stereotype +!endprocedure + +!unquoted procedure SET_SKETCH_STYLE($bgColor="_dont_change_", $fontColor="_dont_change_", $warningColor="_dont_change_", $fontName="_dont_change_", $footerWarning="_dont_change_", $footerText="_dont_change_") +!if $bgColor != "_dont_change_" + !global $SKETCH_BG_COLOR = $bgColor +!endif +!if $fontColor != "_dont_change_" + !global $SKETCH_FONT_COLOR = $fontColor +!endif +!if $warningColor != "_dont_change_" + !global $SKETCH_WARNING_COLOR = $warningColor +!endif +!if $fontName != "_dont_change_" + !global $SKETCH_FONT_NAME = $fontName +!endif +!if $footerWarning != "_dont_change_" + !global $SKETCH_FOOTER_WARNING = $footerWarning +!endif +!if $footerText != "_dont_change_" + !global $SKETCH_FOOTER_TEXT = $footerText +!endif +!endprocedure + +!procedure LAYOUT_AS_SKETCH() + skinparam handwritten true +!if $SKETCH_BG_COLOR > "" + skinparam backgroundColor $SKETCH_BG_COLOR +!endif +!if $SKETCH_FONT_COLOR > "" + skinparam footer { + FontColor $SKETCH_FONT_COLOR + } + !if $ARROW_COLOR == "#666666" + !global $ARROW_COLOR = $SKETCH_FONT_COLOR + skinparam arrow { + Color $ARROW_COLOR + FontColor $ARROW_COLOR + } + !endif + !if $BOUNDARY_COLOR == "#444444" + !global $BOUNDARY_COLOR = $SKETCH_FONT_COLOR + skinparam rectangle<> { + FontColor $BOUNDARY_COLOR + BorderColor $BOUNDARY_COLOR + } + !endif +!endif +!if $SKETCH_FONT_NAMES > "" + skinparam defaultFontName $SKETCH_FONT_NAME +!endif +!if $SKETCH_FOOTER_WARNING > "" || $SKETCH_FOOTER_TEXT > "" + !$line = "footer "+ $SKETCH_FOOTER_WARNING + " " + $SKETCH_FOOTER_TEXT + $line +!endif +!endprocedure + +!global $fix_direction=%false() + +!function $down($start,$end) +!if ($fix_direction) +!return $start+"RIGHT"+$end +!else +!return $start+"DOWN"+$end +!endif +!endfunction + +!function $up($start,$end) +!if ($fix_direction) +!return $start+"LEFT"+$end +!else +!return $start+"UP"+$end +!endif +!endfunction + +!function $left($start,$end) +!if ($fix_direction) +!return $start+"UP"+$end +!else +!return $start+"LEFT"+$end +!endif +!endfunction + +!function $right($start,$end) +!if ($fix_direction) +!return $start+"DOWN"+$end +!else +!return $start+"RIGHT"+$end +!endif +!endfunction + +!procedure LAYOUT_TOP_DOWN() +!global $fix_direction=%false() +top to bottom direction +!endprocedure + +!procedure LAYOUT_LEFT_RIGHT() +!global $fix_direction = %false() +left to right direction +!endprocedure + +!procedure LAYOUT_LANDSCAPE() +!global $fix_direction = %true() +left to right direction +!endprocedure + +' legend details can displayed as Normal(), Small(), None() +!function None() +!return $LEGEND_DETAILS_NONE +!endfunction + +!function Normal() +!return $LEGEND_DETAILS_NORMAL +!endfunction + +!function Small() +!return $LEGEND_DETAILS_SMALL +!endfunction + +' has to be last call in diagram +!unquoted procedure SHOW_LEGEND($hideStereotype="true", $details=Small()) +$getHideStereotype($hideStereotype) +legend right +$getLegendTable($details) +endlegend +!endprocedure + +/' @deprecated in favor of SHOW_LEGEND '/ +!unquoted procedure SHOW_DYNAMIC_LEGEND($hideStereotype="true") +SHOW_LEGEND($hideStereotype) +!endprocedure + +' legend is reserved and cannot be uses as alias of SHOW_FLOATING_LEGEND() therefore +' LEGEND() is introduced. It returns the default name of the floating alias "floating_legend_alias" +' and can be used in the Lay_Distance() calls +!function LEGEND() +!return "floating_legend_alias" +!endfunction + +' enables that legend can be located in drawing area of the diagram. It has to be last call in diagram followed by Lay_Distance() +!unquoted procedure SHOW_FLOATING_LEGEND($alias=LEGEND(), $hideStereotype="true", $details=Normal()) +$getLegendArea($alias, $hideStereotype, $details) +!endprocedure + +' Boundaries +' ################################## + +!unquoted procedure UpdateBoundaryStyle($elementName="", $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") + !if ($elementName != "") + !$elementBoundary = $elementName + '_boundary' + UpdateElementStyle($elementBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") + !else + UpdateElementStyle("boundary", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") + ' simulate color inheritance + UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Enterprise", "") + UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "System", "") + UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Container", "") + !endif +!endprocedure + +!unquoted procedure AddBoundaryTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") + !$tagBoundary = $tagStereo + '_boundary' + AddElementTag($tagBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") +!endprocedure + +' add _boundary to all tags that short tag version can be used +!unquoted function $addBoundaryPostfix($tags) + !if (%strlen($tags) == 0) + !return '' + !endif + !$boundaryTags = '' + !$brPos = %strpos($tags, "+") + !while ($brPos >= 0) + !$tag = %substr($tags, 0, $brPos) + !$boundaryTags = $boundaryTags + $tag + '_boundary+' + !$tags = %substr($tags, $brPos+1) + !$brPos = %strpos($tags, "+") + !endwhile + !if (%strlen($tags) > 0) + !$boundaryTags = $boundaryTags + $tags + '_boundary' + !endif + !return $boundaryTags +!endfunction + +!function $getBoundary($label, $type) + !if ($type == "") + !return '== ' + $breakLabel($label) + !endif + !if (type != "") + !return '== ' + $breakLabel($label) + '\n[' + $type + ']' + !endif +!endfunction + +!unquoted procedure Boundary($alias, $label, $type="", $tags="", $link="") +!$boundaryTags = $addBoundaryPostfix($tags) +' nodes $type reuses $techn definition of $boundaryTags +!$type=$toElementArg($type, $boundaryTags, "ElementTagTechn", "boundary") +rectangle "$getBoundary($label, $type)" $toStereos("boundary", $boundaryTags) as $alias $getLink($link) +!endprocedure + +' Boundary Styling +UpdateBoundaryStyle("", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR) + +' Relationship +' ################################## + +!function $getRel($direction, $alias1, $alias2, $label, $techn, $descr, $sprite, $tags, $link) + !$sprite = $toRelArg($sprite, $tags, "RelTagSprite") + !$techn = $toRelArg($techn, $tags, "RelTagTechn") + !$rel = $alias1 + ' ' + $direction + ' ' + $alias2 + !if ($tags != "") + !$rel = $rel + ' ' + $toStereos($tags) + !endif + !$rel = $rel + ' : ' + !if ($link != "") + !$rel = $rel + '**[[' + $link + ' ' + !endif + !if ($sprite != "") + !$rel = $rel + $getSprite($sprite) + !if ($label != "") + !$rel = $rel + ' ' + !endif + !endif + !if ($link != "") + !$usedNewLine = ']]**\n**[[' + $link + ' ' + ' if sprite and label is empty than the link url is shown (otherwise link cannot be activated at all) + !$rel = $rel + $breakText($label, $usedNewLine) + ']]**' + !else + !if ($label != "") + !$usedNewLine = '**\n**' + !$rel = $rel + '**' + $breakText($label, $usedNewLine) + '**' + !else + !$rel = $rel + '.' + !endif + !endif + !if ($techn != "") + ' line break is not deterministic, calculate it + !$rel = $rel + '\n//[' + $breakTechn($techn, $REL_TECHN_MAX_CHAR_WIDTH) + ']//' + !endif + !if ($descr != "") + ' line break is not deterministic, calculate it + !$rel = $rel + '\n\n' + $breakDescr($descr, $REL_DESCR_MAX_CHAR_WIDTH) + !endif + !$prop = $getProps() + !if ($prop != "") + ' reuse table + !$rel = $rel + $prop + !endif + !return $rel +!endfunction + +!unquoted procedure Rel_($alias1, $alias2, $label, $direction) +$getRel($direction, $alias1, $alias2, $label, "", "", "", "", "") +!endprocedure +!unquoted procedure Rel_($alias1, $alias2, $label, $techn, $direction) +$getRel($direction, $alias1, $alias2, $label, $techn, "", "", "", "") +!endprocedure + +!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<--", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel("<<-", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +!unquoted procedure BiRel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure +!unquoted procedure BiRel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") +$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) +!endprocedure + +' Layout Helpers +' ################################## + +!function $getHiddenLine($distance) + !return '-[hidden]' + %substr('------------', 0, %intval($distance) + 1) +!endfunction + +!unquoted procedure Lay_D($from, $to) +$from -[hidden]D- $to +!endprocedure +!unquoted procedure Lay_Down($from, $to) +$from -[hidden]D- $to +!endprocedure + +!unquoted procedure Lay_U($from, $to) +$from -[hidden]U- $to +!endprocedure +!unquoted procedure Lay_Up($from, $to) +$from -[hidden]U- $to +!endprocedure + +!unquoted procedure Lay_R($from, $to) +$from -[hidden]R- $to +!endprocedure +!unquoted procedure Lay_Right($from, $to) +$from -[hidden]R- $to +!endprocedure + +!unquoted procedure Lay_L($from, $to) +$from -[hidden]L- $to +!endprocedure +!unquoted procedure Lay_Left($from, $to) +$from -[hidden]L- $to +!endprocedure + +' PlantUML bug: lines which does "not match" with the orientation/direction of the diagram +' uses the same length therefore the method offers no direction at all. +' If a direction is required the Lay_...() methods can be used +!unquoted procedure Lay_Distance($from, $to, $distance="0") +$from $getHiddenLine($distance) $to +!endprocedure + +!global $PERSON_BG_COLOR = "#08427B" +!global $PERSON_BORDER_COLOR = "#073B6F" +!global $EXTERNAL_PERSON_BG_COLOR = "#686868" +!global $EXTERNAL_PERSON_BORDER_COLOR = "#8A8A8A" +!global $SYSTEM_BG_COLOR = "#1168BD" +!global $SYSTEM_BORDER_COLOR = "#3C7FC0" +!global $EXTERNAL_SYSTEM_BG_COLOR = "#999999" +!global $EXTERNAL_SYSTEM_BORDER_COLOR = "#8A8A8A" + +' Styling +' ################################## + +UpdateElementStyle("person", $PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $PERSON_BORDER_COLOR) +UpdateElementStyle("external_person", $EXTERNAL_PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_PERSON_BORDER_COLOR) +UpdateElementStyle("system", $SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $SYSTEM_BORDER_COLOR) +UpdateElementStyle("external_system", $EXTERNAL_SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_SYSTEM_BORDER_COLOR) + +UpdateBoundaryStyle("enterprise", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="Enterprise") +UpdateBoundaryStyle("system", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="System") + +' shortcuts with default colors +!unquoted procedure AddPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddExternalPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("external_person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure +!unquoted procedure AddExternalSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") + $addElementTagInclReuse("external_system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) +!endprocedure + +!unquoted procedure UpdateEnterpriseBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="Enterprise", $legendText="") + UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) +!endprocedure +!unquoted procedure UpdateSystemBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="System", $legendText="") + UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) +!endprocedure + +' Sprites +' ################################## + +sprite $person [48x48/16] { +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +0000000000000000000049BCCA7200000000000000000000 +0000000000000000006EFFFFFFFFB3000000000000000000 +00000000000000001CFFFFFFFFFFFF700000000000000000 +0000000000000001EFFFFFFFFFFFFFF80000000000000000 +000000000000000CFFFFFFFFFFFFFFFF6000000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 +0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 +0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 +00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +000000000000000BFFFFFFFFFFFFFFFF5000000000000000 +0000000000000001DFFFFFFFFFFFFFF70000000000000000 +00000000000000000BFFFFFFFFFFFF500000000000000000 +0000000000000000005DFFFFFFFFA1000000000000000000 +0000000000000000000037ABB96100000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000025788300000000005886410000000000000 +000000000007DFFFFFFD9643347BFFFFFFFB400000000000 +0000000004EFFFFFFFFFFFFFFFFFFFFFFFFFFB1000000000 +000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD200000000 +00000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE10000000 +0000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0000000 +000000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5000000 +000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000 +000009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF200000 +00000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF600000 +00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 +00000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF700000 +000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 +0000008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3000000 +000000014555555555555555555555555555555300000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +} + +sprite $person2 [48x48/16] { +0000000000000000000049BCCA7200000000000000000000 +0000000000000000006EFFFFFFFFB3000000000000000000 +00000000000000001CFFFFFFFFFFFF700000000000000000 +0000000000000001EFFFFFFFFFFFFFF80000000000000000 +000000000000000CFFFFFFFFFFFFFFFF6000000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 +0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 +0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 +0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 +00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 +00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 +000000000000007FFFFFFFFFFFFFFFFFF100000000000000 +000000000000000BFFFFFFFFFFFFFFFF5000000000000000 +0000000000000001DFFFFFFFFFFFFFF70000000000000000 +00000000000000000BFFFFFFFFFFFF500000000000000000 +0000000000000000005DFFFFFFFFA1000000000000000000 +0000000000000000000037ABB96100000000000000000000 +000000000002578888300000000005888864100000000000 +0000000007DFFFFFFFFD9643347BFFFFFFFFFB4000000000 +00000004EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB10000000 +0000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2000000 +000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 +00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 +0000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50000 +0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0000 +0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2000 +000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 +000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 +001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA000 +000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 +0009FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF2000 +0003FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFD0000 +0000BFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF50000 +00003FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFB00000 +000006FFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFE100000 +0000007FFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFD2000000 +00000004EFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFB10000000 +0000000007DF8FFFFFFFFFFFFFFFFFFFFFF8FB4000000000 +000000000002578888888888888888888864100000000000 +} + +sprite $robot [48x48/16] { +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000005BFFFFFFFFFFFFFFFFFFFFFE9100000000000 +0000000000AFFFFFFFFFFFFFFFFFFFFFFFFFE30000000000 +0000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFE1000000000 +000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000000000 +000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000699405FFFFFFC427FFFFFFFFFC427FFFFFFE009982000 +008FFF705FFFFFE10006FFFFFFFE00007FFFFFE00FFFF100 +00CFFF705FFFFFA00001FFFFFFF900002FFFFFE00FFFF500 +00DFFF705FFFFFB00002FFFFFFFA00003FFFFFE00FFFF500 +00DFFF705FFFFFF4000AFFFFFFFF3000BFFFFFE00FFFF500 +00DFFF705FFFFFFFA8DFFFFFFFFFFA8DFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 +00CFFF705FFFFFF87777777777777777CFFFFFE00FFFF500 +008FFF705FFFFFF100000000000000009FFFFFE00FFFF100 +000699405FFFFFF76666666666666666CFFFFFE009982000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 +000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 +000000000EFFFFFFFFFFFFFFFFFFFFFFFFFFFF7000000000 +0000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFD0000000000 +00000000004CFFFFFFFFFFFFFFFFFFFFFFFF910000000000 +000000000000011111111111111111111110000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000 +} + +sprite $robot2 [48x48/16] { +000000000000000088888888888888880000000000000000 +000000000000000AFFFFFFFFFFFFFFFFA000000000000000 +00000000000000CFFFFFFFFFFFFFFFFFFC00000000000000 +00000000000004EFFFFFFFFFFFFFFFFFFE40000000000000 +0000000000000AFFFFFFFFFFFFFFFFFFFFA0000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 +00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 +00000000000004CFFFFFFFFFFFFFFFFFFC40000000000000 +000000488888848CFFFFFFFFFFFFFFFFC848888884000000 +00000CFFFFFFFFC888888888888888888CFFFFFFFFC00000 +00008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000 +0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 +0000CFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFC0000 +00008FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF80000 +00000CFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFC00000 +000000488887578888888888888888888864688884000000 +000000000000000000000000000000000000000000000000 +} + +' Layout +' ################################## + +SetDefaultLegendEntries("person\nsystem\nexternal_person\nexternal_system\nenterprise_boundary\nsystem_boundary\nboundary") + +!procedure LAYOUT_WITH_LEGEND() +hide stereotype +legend right +|**Legend** | +|<$PERSON_BG_COLOR> person | +|<$SYSTEM_BG_COLOR> system| +|<$EXTERNAL_PERSON_BG_COLOR> external person | +|<$EXTERNAL_SYSTEM_BG_COLOR> external system | +endlegend +!endprocedure + +!global $defaultPersonSprite = "person" +!$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) +UpdateElementStyle("person") +' workaround of plantuml.jar bug - person overwrites external_person setting +!$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) +UpdateElementStyle("external_person") +!global $portraitPerson = "false" + +!procedure $clearPersonRestore() + !$dummy = $clearRestore("person", "sprite") + !$dummy = $clearRestore("person", "legendSprite") + %set_variable_value("$" + "person" + "ElementTagSprite", "") + UpdateElementStyle("person") + ' workaround of plantuml.jar bug - person overwrites external_person setting + !$dummy = $clearRestore("external_person", "sprite") + !$dummy = $clearRestore("external_person", "legendSprite") + %set_variable_value("$" + "external_person" + "ElementTagSprite", "") + UpdateElementStyle("external_person") +!endprocedure + +!procedure HIDE_PERSON_SPRITE() + !$defaultPersonSprite = "" + !$portraitPerson = "false" + $clearPersonRestore() +!endprocedure + +!unquoted procedure SHOW_PERSON_SPRITE($sprite="") + !if ($sprite == "") + !$defaultPersonSprite = "person" + !else + !$defaultPersonSprite = $sprite + !endif + !$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) + UpdateElementStyle("person") + ' workaround of plantuml.jar bug - person overwrites external_person setting + !$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) + UpdateElementStyle("external_person") + !$portraitPerson = "false" +!endprocedure + +!unquoted procedure SHOW_PERSON_PORTRAIT() + !$defaultPersonSprite = "" + !$portraitPerson = "portrait" + $clearPersonRestore() +!endprocedure + +!unquoted procedure SHOW_PERSON_OUTLINE() + !$defaultPersonSprite = "" + !$portraitPerson = "outline" + $clearPersonRestore() +!endprocedure + +' Elements +' ################################## + +!function $getPerson($label, $descr, $sprite) + !if ($sprite == "") && ($defaultPersonSprite != "") + !$sprite = $defaultPersonSprite + !endif + !return $getElementBase($label, "", $descr, $sprite) +!endfunction + +!function $getSystem($label, $descr, $sprite) + !return $getElementBase($label, "", $descr, $sprite) +!endfunction + +!unquoted procedure Person($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "person") +!if ($portraitPerson == "portrait") && ($sprite == "") +actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!elseif ($portraitPerson == "outline") && ($sprite == "") +person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!else +rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) +!endif +!endprocedure + +!unquoted procedure Person_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_person") +!if ($portraitPerson == "portrait") && ($sprite == "") +actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!elseif ($portraitPerson == "outline") && ($sprite == "") +person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!else +rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) +!endif +!endprocedure + +!unquoted procedure System($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure System_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemDb($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemQueue($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") +queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemDb_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +!unquoted procedure SystemQueue_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") +!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") +queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) +!endprocedure + +' Boundaries +' ################################## + +!unquoted procedure Enterprise_Boundary($alias, $label, $tags="", $link="") + !if ($tags != "") + !$allTags = $tags + '+enterprise' + !else + !$allTags = 'enterprise' + !endif + ' $type defined via $tag style + Boundary($alias, $label, "", $allTags, $link) +!endprocedure + +!unquoted procedure System_Boundary($alias, $label, $tags="", $link="") + !if ($tags != "") + !$allTags = $tags + '+system' + !else + !$allTags = 'system' + !endif + ' $type defined via $tag style + Boundary($alias, $label, "", $allTags, $link) +!endprocedure + +skinparam hyperlinkUnderline false +skinparam hyperlinkColor ?#000000:#FFFFFFFF +{{&renderCore}} + +{{&code}} + +@enduml diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml b/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml new file mode 100644 index 0000000..ab6122e --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml @@ -0,0 +1,188 @@ +@startuml +skinparam useBetaStyle true +allow_mixing +{{renderCore}} +skinparam { + HyperLinkColor #000 + wrapWidth 200 + maxMessageSize 200 + 'hide stereotype + defaultfontname Roboto, sans-serif + roundCorner 10 + 'linetype ortho + 'linetype polyline + nodesep 70 + ranksep 70 + shadowing false + padding 2 +} + + + +!unquoted procedure $Region($alias, $label, $type) + !if ($type) + rectangle "$label" <<$type>> as $alias + !else + rectangle "$label" as $alias + !endif +!endprocedure + +!unquoted procedure $Header($Title="Header", $Authors="Name", $Version="0.1", $Date="01.01.1999") + header + !if ($Authors) + Authors: $Authors + !endif + !if ($Version) + Version: $Version + !endif + !if ($Date) + Date: $Date + !endif + + endheader + + hide empty members + + title $Title +!endprocedure + + +'Стили Person +skinparam component<> { + backgroundColor Transparent + borderColor Transparent + shadowing false + hide stereotype + borderthickness 1 + FontSize 14 + FontStyle Bold +} + +!unquoted procedure $join_start() + %set_variable_value("$join_append", 0) +!endprocedure + +!unquoted function $join_append() + !$val = %get_variable_value("$join_append") + !if $val == 0 + %set_variable_value("$join_append", 1) + !endif + !return $val +!endfunction + +!unquoted procedure $join_end() + %set_variable_value("$join_append", 0) +!endprocedure + +!unquoted procedure $Entity($entity, $ACName, $id, $ACType) + $join_start() + !if ($entity == component) + component $id[ + $ACName + ==== + !elseif ($entity == system) + component $id[ + $ACName + ==== + !elseif ($entity == actor || $entity == person) + actor $id [ + ..===$ACName.. + ] + !else + $entity $id [ + $ACName + ==== + !endif +!endprocedure + +!unquoted procedure $EntityEnd($entity) + !if ($entity == component) + ] + !elseif ($entity == system) + ] + !elseif ($entity == actor || $entity == person) +'nothing + !else + ] + !endif + $join_end() +!endprocedure + +!unquoted procedure $EntityAspect($entity, $prop) + !if ($entity == component) + !$val = $join_append() + !if ($val != 0) + --- + !endif + * $prop + !elseif ($entity == system) + * $prop + !elseif ($entity == actor || $entity == person) +'nothing + !else + * $prop + !endif +!endprocedure + +!unquoted procedure $EntityExpand($entity, $ID) + !if ($entity == component) + --- + [[/entities/contexts/{{presentation}}?dh-context-id=$ID ≫≫]] + !elseif ($entity == system) + --- + [[/entities/contexts/{{presentation}}?dh-context-id=$ID ≫≫]] + !elseif ($entity == actor || $entity == person) +'nothing + !else +'nothing + !endif +!endprocedure + +left to right direction + +{{&code}} + +@enduml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/documents/_root.yaml b/src/repository_structure_example/metamodels/default/entities/documents/_root.yaml new file mode 100644 index 0000000..31b5b33 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/documents/_root.yaml @@ -0,0 +1,3 @@ +imports: + - base.yaml + - blank.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/documents/base.yaml b/src/repository_structure_example/metamodels/default/entities/documents/base.yaml new file mode 100644 index 0000000..cd42832 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/documents/base.yaml @@ -0,0 +1,95 @@ +entities: + docs: + title: Документы + # Строим меню по объектам документа + menu: > + ( + $append([ + { + "title": 'Документы', + "location": 'Документы', + "expand": true, + "icon": 'description' + } + ], + docs.$spread().( + *.location ? { + "location": *.location, + "link": "entities/docs/blank?dh-doc-id=" & $keys()[0] + } + ) + ) + ) + # Схема данных объекта "Документ" + schema: + type: object + patternProperties: + "^[a-zA-Z][a-zA-Z0-9_-]*(\\.[a-zA-Z][a-zA-Z0-9_-]*)*$": + oneOf: + - type: object + properties: + title: + title: Название документа + type: string + location: + title: Место расположения в меню + type: string + icon: + title: Иконка в меню + type: string + type: + title: Тип документа + type: string + subjects: + title: Принадлежит объектам + type: array + items: + type: string + source: + title: Источник данный (запрос или dataset) + type: string + oneOf: + - type: string + title: Идентификатор dataset + pattern: "^[a-zA-Z][a-zA-Z0-9\_]*(\\.[a-zA-Z][a-zA-Z0-9\\_]*)*$" + - type: string + title: JSONata запрос + pattern: "\\s*\\((.|\\s)*\\)\\s*" + required: + - source + - type: object + properties: + title: + title: Название документа + type: string + location: + title: Место расположения в меню + type: string + icon: + title: Иконка в меню + type: string + type: + title: Тип документа + type: string + subjects: + title: Принадлежит объектам + type: array + items: + type: string + template: + title: Путь к файлу шаблона + type: string + source: + title: Данные для шаблона + type: string + oneOf: + - type: string + title: Идентификатор dataset + pattern: "^[a-zA-Z][a-zA-Z0-9\_]*(\\.[a-zA-Z][a-zA-Z0-9\\_]*)*$" + - type: string + title: JSONata запрос + pattern: "\\s*\\((.|\\s)*\\)\\s*" + required: + - template + - source + additionalProperties: false diff --git a/src/repository_structure_example/metamodels/default/entities/documents/blank.yaml b/src/repository_structure_example/metamodels/default/entities/documents/blank.yaml new file mode 100644 index 0000000..3adbf15 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/entities/documents/blank.yaml @@ -0,0 +1,28 @@ +# Расширение сущности "Contexts" для генерации PlantUML диаграмм +entities: + docs: + # Основное представление документов + presentations: + blank: + title: Представление документа + params: + title: Требуемые параметры для презентации + type: object + properties: + "dh-doc-id": + title: Идентификатор документа + type: string + pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ + required: + - dh-doc-id + $constructor: > + ( + /* Получаем объект документа */ + $doc := $lookup(docs, $params."dh-doc-id"); + /* Для рендера берем за основу объек документа */ + $merge([$doc, ( + { + "$base": "/docs/" & $params."dh-doc-id" + } + )]) + ) diff --git a/src/repository_structure_example/metamodels/default/rules/_root.yaml b/src/repository_structure_example/metamodels/default/rules/_root.yaml new file mode 100644 index 0000000..1f2e67e --- /dev/null +++ b/src/repository_structure_example/metamodels/default/rules/_root.yaml @@ -0,0 +1,2 @@ +imports: + - validators.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/rules/validators.yaml b/src/repository_structure_example/metamodels/default/rules/validators.yaml new file mode 100644 index 0000000..d01b0e6 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/rules/validators.yaml @@ -0,0 +1,144 @@ +# Правила контроля целостности DocHub +rules: + validators: + dochub: + title: DocHub + dochub.metamodel: + title: Контроль ядра метамодели + dochub.metamodel.contexts: + title: Контроль целостности контекстов + dochub.metamodel.contexts.no_found_component: + title: Несуществующие компоненты + source: > + ([( + $MANIFEST := $; + contexts.$spread().( + $CONTEXT_ID := $keys()[0]; + *.components. + { + "contextID": $CONTEXT_ID, + "componentID": $ + }; + )[$ and $not($substring(componentID, -2) = ".*") and $not($exists($lookup($MANIFEST.components, componentID)))] + ).{ + "uid": "$dh-mm-nfc-" & contextID & "-" & componentID, + "correction": "Определите компонент или удалите ссылку на него в контексте", + "description": "Возможно компонент [" & componentID & "] не описан или описан в подключаемой + кодовой базе архитектуры, которая сейчас недоступна.", + "location": "/architect/contexts/" & contextID + }]) + dochub.metamodel.contexts.outof: + title: Компоненты вне контекста + source: > + ( + $MANIFEST := $; + $USED := $distinct(contexts.*.components); + [components.$spread().( + $ID := $keys()[0]; + { + "id" : $ID, + "mask" : $USED[$wcard($ID, $)] + } + )[$not($exists(mask))].{ + "uid": "$dh-mm-ofb-" & id, + "correction": "Добавьте компонент в контекст", + "description": "Предполагается, что компонент не включенный в контекст не учтен.", + "location": "/architect/components/" & id + }] + ) + dochub.metamodel.components: + title: Контроль целостности компонентов + dochub.metamodel.components.no_found_aspect: + title: Аспект не определен + source: > + ([( + $MANIFEST := $; + components.$spread().( + $COMPONENT_ID := $keys()[0]; + *.aspects. + { + "componentID": $COMPONENT_ID, + "aspectID": $ + }; + )[$ and $not($exists($lookup($MANIFEST.aspects, aspectID)))] + ).{ + "uid": "$dh-mm-nfa-" & contextID & "-" & componentID, + "correction": "Определите аспект или удалите ссылку на него", + "description": "Возможно аспект [" & aspectID & "] не описан или описан в подключаемой + кодовой базе архитектуры, которая сейчас недоступна.", + "location": "/architect/spects/" & aspectID + }]) + dochub.metamodel.components.no_define_parent: + title: Компонент верхнего уровня не определен + source: > + ( + $ens := function($id) { + ( + $ids := $split($id, "."); + $join($map($ids, function($v, $i, $a) { + $i < $count($ids) - 1 ? $v : undefined + }), ".") + ) + }; + + $MANIFEST := $; + + [[$distinct(components.$spread().( + $ens($keys()[0]) + ))[$not($exists($lookup($MANIFEST.components, $)))]].{ + "uid": "$dh-mm-cmp-ndp-" & $, + "correction": "Опишите компонент с идентификатором [" & $ & "]", + "description": "Предполагается, что для всех уровней компоненты определены." + }] + ) + dochub.metamodel.components.links: + title: Связь компонентов + dochub.metamodel.components.links.nofound: + title: Связь с несуществующими компонентами + source: > + ( + $MANIFEST := $; + [[components.$spread().( + $ID := $keys()[0]; + $.*.links.{ + "ownerID": $ID, + "linkID": id + } + )[$ and $not($exists($lookup($MANIFEST.components, linkID)))]].{ + "uid": "$dh-mm-cmp-lnk-nf-" & ownerID & "-" & linkID, + "correction": "Определите компонент [" & linkID & "] или удалите ссылку на него", + "description": "Возможно компонент [" & linkID & "] не описан или описан в подключаемой + кодовой базе архитектуры, которая сейчас недоступна.", + "location": "/architect/components/" & ownerID + }] + ) + dochub.metamodel.aspects: + title: Контроль целостности аспектов + dochub.metamodel.aspects.no_define_parent: + title: Аспект верхнего уровня не определен + source: > + ( + $ens := function($id) { + ( + $ids := $split($id, "."); + $join($map($ids, function($v, $i, $a) { + $i < $count($ids) - 1 ? $v : undefined + }), ".") + ) + }; + + $MANIFEST := $; + + [[$distinct(aspects.$spread().( + $ens($keys()[0]) + ))[$not($exists($lookup($MANIFEST.aspects, $)))]].{ + "uid": "$dh-mm-apt-ndp-" & $, + "correction": "Опишите аспект с идентификатором [" & $ & "]", + "description": "Предполагается, что для всех уровней аспекты определены." + }] + ) + + + + + diff --git a/src/repository_structure_example/metamodels/default/samples/01_components.yaml b/src/repository_structure_example/metamodels/default/samples/01_components.yaml new file mode 100644 index 0000000..40e7827 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/samples/01_components.yaml @@ -0,0 +1,23 @@ +components: + component1: + title: Component1 + entity: component + links: + - id: component2 + title: Связь 1 + direction: <-> + component2: + title: Component2 + entity: component + links: + - id: component3 + title: Связь 1 + direction: <-> + component3: + title: Component3 + entity: component + links: + - id: component1 + title: Связь 1 + direction: <-> + diff --git a/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml b/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml new file mode 100644 index 0000000..cf912fb --- /dev/null +++ b/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml @@ -0,0 +1,23 @@ +# Базовое описание сущности "Contexts" +contexts: + test: + location: Проба/Тест + title: Проба + extra-links: false + template: test.puml + components: + - dochub.front + api: + fetchComponents: > + ( + { + "test.c1": { + "title": "test", + "entity": "component" + }, + "test.c2": { + "title": "test", + "entity": "component" + } + } + ) diff --git a/src/repository_structure_example/metamodels/default/samples/03_documents.yaml b/src/repository_structure_example/metamodels/default/samples/03_documents.yaml new file mode 100644 index 0000000..67e5eb1 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/samples/03_documents.yaml @@ -0,0 +1,10 @@ +docs: + test: + location: test + type: plantuml + source: > + ( + $ + ) + template: test.puml + diff --git a/src/repository_structure_example/metamodels/default/samples/_root.yaml b/src/repository_structure_example/metamodels/default/samples/_root.yaml new file mode 100644 index 0000000..f252652 --- /dev/null +++ b/src/repository_structure_example/metamodels/default/samples/_root.yaml @@ -0,0 +1,4 @@ +imports: + - 01_components.yaml + - 02_contexts.yaml + - 03_documents.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/samples/test.puml b/src/repository_structure_example/metamodels/default/samples/test.puml new file mode 100644 index 0000000..9224f4d --- /dev/null +++ b/src/repository_structure_example/metamodels/default/samples/test.puml @@ -0,0 +1,17 @@ +@startuml + participant Participant as Foo + actor Actor as Foo1 + boundary Boundary as Foo2 + control Control as Foo3 + entity Entity as Foo4 + database Database as Foo5 + collections Collections as Foo6 + queue Queue as Foo7 + Foo -> Foo1 : To actor + Foo -> Foo2 : To boundary + Foo -> Foo3 : To control + Foo -> Foo4 : To entity + Foo -> Foo5 : To database + Foo -> Foo6 : To collections + Foo -> Foo7 : To queue +@enduml \ No newline at end of file From 470c8a8014da5e8957f7c46ef57c79d2845626fd Mon Sep 17 00:00:00 2001 From: ValentinKozlov Date: Thu, 4 May 2023 18:06:26 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D1=8D=D0=BA=D1=81=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application_arch/systems/grafana_dev.yaml | 1 + .../application_arch/systems/sid.yaml | 1 + .../application_arch/systems/spact.yaml | 3 +- .../application_arch/systems/spoll.yaml | 4 +- .../application_arch/systems/srole.yaml | 3 +- .../artefacts/common/pal1_landscape.yaml | 11 +- .../frog_paradise/pal1_landscape.yaml | 2 +- .../default/entities/contexts/plantuml.yaml | 2 +- .../contexts/templates/container.puml | 1985 ----------------- .../entities/contexts/templates/template.puml | 6 +- 10 files changed, 18 insertions(+), 2000 deletions(-) delete mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml diff --git a/src/repository_structure_example/application_arch/systems/grafana_dev.yaml b/src/repository_structure_example/application_arch/systems/grafana_dev.yaml index e09971c..5ef3568 100644 --- a/src/repository_structure_example/application_arch/systems/grafana_dev.yaml +++ b/src/repository_structure_example/application_arch/systems/grafana_dev.yaml @@ -19,5 +19,6 @@ components: direction: <-- - id: swamp.crocodile.crm direction: <-- + color: Red diff --git a/src/repository_structure_example/application_arch/systems/sid.yaml b/src/repository_structure_example/application_arch/systems/sid.yaml index f3bffa9..7d2caa8 100644 --- a/src/repository_structure_example/application_arch/systems/sid.yaml +++ b/src/repository_structure_example/application_arch/systems/sid.yaml @@ -21,3 +21,4 @@ components: - Redis - Kafka - Nginx + diff --git a/src/repository_structure_example/application_arch/systems/spact.yaml b/src/repository_structure_example/application_arch/systems/spact.yaml index d1473dd..6cb81c5 100644 --- a/src/repository_structure_example/application_arch/systems/spact.yaml +++ b/src/repository_structure_example/application_arch/systems/spact.yaml @@ -22,4 +22,5 @@ components: links: # Интеграции между системами одного БЮ - id: swamp.frog.1cbit_finance - direction: <-- + direction: <-- + color: Yellow diff --git a/src/repository_structure_example/application_arch/systems/spoll.yaml b/src/repository_structure_example/application_arch/systems/spoll.yaml index 44995da..ba149cf 100644 --- a/src/repository_structure_example/application_arch/systems/spoll.yaml +++ b/src/repository_structure_example/application_arch/systems/spoll.yaml @@ -24,8 +24,8 @@ components: direction: <-- - id: swamp.crocodile.crm direction: <-- - # - id: swamp.crocodile.spact - # direction: <-- + - id: swamp.crocodile.spact + direction: <-- diff --git a/src/repository_structure_example/application_arch/systems/srole.yaml b/src/repository_structure_example/application_arch/systems/srole.yaml index c116180..7c07c93 100644 --- a/src/repository_structure_example/application_arch/systems/srole.yaml +++ b/src/repository_structure_example/application_arch/systems/srole.yaml @@ -20,7 +20,6 @@ components: - Redis - Kafka - Nginx - links: - # Интеграции между системами одного БЮ + links: - id: swamp.crocodile direction: <-- diff --git a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml index 0d33218..64c73f3 100644 --- a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml @@ -4,12 +4,13 @@ contexts: # Контексты представления архитектур location: ГК Болото extra-links: true # extra-links: false - uml: - # $notation: plantuml # sber C4Model plantuml - $autor: Frog - $version: '0.0.1' - $moment: 20.11.2022 + # uml: + # $notation: plantuml # sber C4Model plantuml + # $autor: Frog + # $version: '0.0.1' + # $moment: 20.11.2022 components: - swamp.frog.* - swamp.hippo.* - swamp.crocodile.* + - swamp.crocodile.sid diff --git a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml index 58a31b4..d3c165f 100644 --- a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml @@ -10,4 +10,4 @@ contexts: # Контексты представления архитектур # $version: 0.0.1 # $moment: 20.11.2022 components: - - swamp.frog.* + - swamp.frog.* \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml index ac07499..05295e8 100644 --- a/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml +++ b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml @@ -50,7 +50,7 @@ entities: & "[[/architect/components/" & $domain & " " & $component.title & "]]" & "\", " & $domain - & ", )\n"; + & ", ,"& ($exists($component.color) ? ("#" & $component.color) : ("")) &")\n"; /* Добавляем аспекты */ $result := $result & $join($component.aspects.( diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml b/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml deleted file mode 100644 index bce1ae5..0000000 --- a/src/repository_structure_example/metamodels/default/entities/contexts/templates/container.puml +++ /dev/null @@ -1,1985 +0,0 @@ -@startuml - -' C4-PlantUML - -'Version -' ################################## -!function C4Version() - ' 2 spaces and ' are used as unique marker, that the release scripts makes the correct version update - !$c4Version = "2.6.0beta1" - !return $c4Version -!end function - -!procedure C4VersionDetails() -rectangle C4VersionDetailsArea <> [ -| PlantUML | **%version()** | -| C4-PlantUML | **C4Version()** | -] -!end procedure - -' Colors -' ################################## - -!global $ELEMENT_FONT_COLOR = "#FFFFFF" - -!global $ARROW_COLOR = "#666666" - -!global $BOUNDARY_COLOR = "#444444" -!global $BOUNDARY_BG_COLOR = "transparent" - -!global $LEGEND_FONT_COLOR = "#FFFFFF" -!global $LEGEND_TITLE_COLOR = "#000000" -' %darken(darkkhaki,50), #khaki -!global $LEGEND_DARK_COLOR = "#66622E" -!global $LEGEND_LIGHT_COLOR = "#khaki" - -!global $SKETCH_BG_COLOR = "#EEEBDC" -!global $SKETCH_FONT_COLOR = "" -!global $SKETCH_WARNING_COLOR = "red" -!global $SKETCH_FONT_NAME = "Comic Sans MS" - -' Labels -' ################################## - -!global $LEGEND_SHADOW_TEXT = "shadow" -!global $LEGEND_NO_SHADOW_TEXT = "no shadow" -!global $LEGEND_NO_FONT_BG_TEXT = "last text and back color" -!global $LEGEND_NO_FONT_TEXT = "last text color" -!global $LEGEND_NO_BG_TEXT = "last back color" -!global $LEGEND_NO_LINE_TEXT = "last line color" -!global $LEGEND_ROUNDED_BOX = "rounded box" -!global $LEGEND_EIGHT_SIDED = "eight sided" -!global $LEGEND_DOTTED_LINE = "dotted" -!global $LEGEND_DASHED_LINE = "dashed" -!global $LEGEND_BOLD_LINE = "bold" -!global $LEGEND_BOUNDARY = "boundary" -!global $LEGEND_DASHED_BOUNDARY = "dashed" -' ignore transparent atm, that the legend is smaller -'!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed, transparent" -!global $LEGEND_DASHED_TRANSPARENT_BOUNDARY = "dashed" - -!global $SKETCH_FOOTER_WARNING = "Warning:" -!global $SKETCH_FOOTER_TEXT = "Created for discussion, needs to be validated" - -' Styling -' ################################## - -!global $TECHN_FONT_SIZE = 12 -!global $ROUNDED_BOX_SIZE = 25 -!global $EIGHT_SIDED_SIZE = 18 - -!global $LEGEND_DETAILS_SMALL_SIZE = 10 -!global $LEGEND_DETAILS_NORMAL_SIZE = 14 -!global $LEGEND_DETAILS_SIZE = $LEGEND_DETAILS_SMALL_SIZE - -!global $ROUNDED_BOX = "roundedBox" -!global $EIGHT_SIDED = "eightSided" - -!global $DOTTED_LINE = "dotted" -!global $DASHED_LINE = "dashed" -!global $BOLD_LINE = "bold" - -!global $LEGEND_DETAILS_NONE = "none" -!global $LEGEND_DETAILS_NORMAL = "normal" -!global $LEGEND_DETAILS_SMALL = "small" - -skinparam defaultTextAlignment center - -skinparam wrapWidth 200 -skinparam maxMessageSize 150 - -skinparam LegendBorderColor transparent -skinparam LegendBackgroundColor transparent -skinparam LegendFontColor $LEGEND_FONT_COLOR - -skinparam shadowing<> false -' #00000000 is transparent -skinparam rectangle<> { - backgroundcolor #00000000 - bordercolor #00000000 -} - -skinparam rectangle { - StereotypeFontSize 12 - shadowing false -} - -skinparam database { - StereotypeFontSize 12 - shadowing false -} - -skinparam queue { - StereotypeFontSize 12 - shadowing false -} - -skinparam arrow { - Color $ARROW_COLOR - FontColor $ARROW_COLOR - FontSize 12 -} - -skinparam person { - StereotypeFontSize 12 - shadowing false -} - -skinparam actor { - StereotypeFontSize 12 - shadowing false - style awesome -} - -' Some boundary skinparams have to be set as package skinparams too (PlantUML uses internal packages) -' UpdateBoundaryStyle() called in boundary section below -skinparam rectangle<> { - Shadowing false - StereotypeFontSize 6 - StereotypeFontColor $BOUNDARY_BG_COLOR - BorderStyle dashed -} - -skinparam package { - StereotypeFontSize 6 - StereotypeFontColor $BOUNDARY_BG_COLOR - FontStyle plain - BackgroundColor $BOUNDARY_BG_COLOR -} - -' Legend and Tags -' ################################## -!global $tagDefaultLegend = "" -!global $tagCustomLegend = "" - -' rel specific -!unquoted function $toStereos($tags) - !if (%strlen($tags) == 0) - !return '' - !endif - !$stereos = '' - !$brPos = %strpos($tags, "+") - !while ($brPos >= 0) - !$tag = %substr($tags, 0, $brPos) - !$stereos = $stereos + '<<' + $tag + '>>' -%set_variable_value("$" + $tag + "_LineLegend", %true()) - !$tags = %substr($tags, $brPos+1) - !$brPos = %strpos($tags, "+") - !endwhile - !if (%strlen($tags) > 0) - !$stereos = $stereos + '<<' + $tags + '>>' -%set_variable_value("$" + $tags + "_LineLegend", %true()) - !endif - !return $stereos -!endfunction - -' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag -!unquoted function $toRelArg($arg, $tags, $varPostfix) - !if ($arg > "") - !return $arg - !endif - - !if (%strlen($tags) == 0) - !return $arg - !endif - !$brPos = %strpos($tags, "+") - !while ($brPos >= 0) - !$tag = %substr($tags, 0, $brPos) - !$newArg = %get_variable_value("$" + $tag + $varPostfix) - !if ($newArg > "") - !return $newArg - !endif - !$tags = %substr($tags, $brPos+1) - !$brPos = %strpos($tags, "+") - !endwhile - !if (%strlen($tags) > 0) - !$newArg = %get_variable_value("$" + $tags + $varPostfix) - !if ($newArg > "") - !return $newArg - !endif - !endif - !return $arg -!endfunction - -' element specific (unused are hidden based on mask) -!unquoted function $toStereos($elementType, $tags) - !if (%strlen($tags) == 0) - !$stereos = '<<' + $elementType + '>>' -%set_variable_value("$" + $elementType + "Legend", %true()) - !return $stereos - !endif - !$stereos = '' - !$mask = $resetMask() - !$brPos = %strpos($tags, "+") - !while ($brPos >= 0) - !$tag = %substr($tags, 0, $brPos) - !$stereos = $stereos + '<<' + $tag + '>>' - !$mergedMask = $combineMaskWithTag($mask, $tag) - !if ($mergedMask != $mask) -%set_variable_value("$" + $tag + "Legend", %true()) - !$mask = $mergedMask - !endif - !$tags = %substr($tags, $brPos+1) - !$brPos = %strpos($tags, "+") - !endwhile - !if (%strlen($tags) > 0) - !$stereos = $stereos + '<<' + $tags + '>>' - !$mergedMask = $combineMaskWithTag($mask, $tags) - !if ($mergedMask != $mask) -%set_variable_value("$" + $tags + "Legend", %true()) - !$mask = $mergedMask - !endif - !endif - ' has to be last, otherwise PlantUML overwrites all tag specific skinparams - !$stereos = $stereos + '<<' + $elementType + '>>' - !$mergedMask = $combineMaskWithTag($mask, $elementType) - !if ($mergedMask != $mask) -%set_variable_value("$" + $elementType + "Legend", %true()) - !$mask = $mergedMask - !endif - !return $stereos -!endfunction - -' if $sprite/$techn is an empty argument, try to calculate it via the defined $tag -!unquoted function $toElementArg($arg, $tags, $varPostfix, $elementType) - !if ($arg > "") - !return $arg - !endif - - !if (%strlen($tags) == 0) - !$newArg = %get_variable_value("$" + $elementType + $varPostfix) - !if ($newArg > "") - !return $newArg - !else - !return $arg - !endif - !endif - !$brPos = %strpos($tags, "+") - !while ($brPos >= 0) - !$tag = %substr($tags, 0, $brPos) - !$newArg = %get_variable_value("$" + $tag + $varPostfix) - !if ($newArg > "") - !return $newArg - !endif - !$tags = %substr($tags, $brPos+1) - !$brPos = %strpos($tags, "+") - !endwhile - !if (%strlen($tags) > 0) - !$newArg = %get_variable_value("$" + $tags + $varPostfix) - !if ($newArg > "") - !return $newArg - !endif - !$newArg = %get_variable_value("$" + $elementType + $varPostfix) - !if ($newArg > "") - !return $newArg - !endif - !endif - !return $arg -!endfunction - -' if $value is empty try to load it via variable, optional can it store the calculated value -!function $restoreEmpty($elementType, $property, $value, $store) - !$var = "$" + $elementType + "Restore" + $property - !if ($value == "") - !$value = %get_variable_value($var) - !elseif ($store) - %set_variable_value($var, $value) - !endif - !return $value -!endfunction - -' clear the restore property -!function $clearRestore($elementType, $property) - !$var = "$" + $elementType + "Restore" + $property - %set_variable_value($var, "") - !return "" -!endfunction - -!function $elementTagSkinparams($element, $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) - !$elementSkin = "skinparam " + $element + "<<" + $tagStereo + ">> {" + %newline() - !if ($fontColor != "") - !if (%strpos($tagStereo, "boundary") < 0) - !$elementSkin = $elementSkin + " StereotypeFontColor " + $fontColor + %newline() - !endif - !$elementSkin = $elementSkin + " FontColor " + $fontColor + %newline() - !endif - !if ($bgColor != "") - !$elementSkin = $elementSkin + " BackgroundColor " + $bgColor + %newline() - !endif - !if ($borderColor != "") - !$elementSkin = $elementSkin + " BorderColor " + $borderColor+ %newline() - !endif - !if ($shadowing == "true") - !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "true" + %newline() - !endif - !if ($shadowing == "false") - !$elementSkin = $elementSkin + " Shadowing<<" + $tagStereo + ">> " + "false" + %newline() - !endif - ' only rectangle supports shape(d corners), define both skinparam that overlays are working - !if ($shape != "" && $element == "rectangle") - !if ($shape == $ROUNDED_BOX) - !$elementSkin = $elementSkin + " RoundCorner " + $ROUNDED_BOX_SIZE+ %newline() - !$elementSkin = $elementSkin + " DiagonalCorner " + "0" + %newline() - !elseif ($shape == $EIGHT_SIDED) - !$elementSkin = $elementSkin + " RoundCorner " + "0" + %newline() - !$elementSkin = $elementSkin + " DiagonalCorner " + $EIGHT_SIDED_SIZE+ %newline() - !endif - !endif - !$elementSkin = $elementSkin + "}" + %newline() - !return $elementSkin -!endfunction - -!unquoted procedure $defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) - ' only rectangle supports shape(d corners) - !$tagSkin = $elementTagSkinparams("rectangle", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) - !$tagSkin = $tagSkin + $elementTagSkinparams("database", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") - !$tagSkin = $tagSkin + $elementTagSkinparams("queue", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") - ' plantuml.jar bug - actor have to be after person - !$tagSkin = $tagSkin + $elementTagSkinparams("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, "") - ' actor has style awesome, therefore $fontColor is ignored and text uses $bgColor too - !$tagSkin = $tagSkin + $elementTagSkinparams("actor", $tagStereo, $bgColor, $bgColor, $borderColor, $shadowing, "") - !if (%strpos($tagStereo, "boundary") >= 0 && $bgColor != "") - !$tagSkin = $tagSkin + "skinparam package<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() - !$tagSkin = $tagSkin + "skinparam rectangle<<" + $tagStereo + ">>StereotypeFontColor " + $bgColor + %newline() - !endif -$tagSkin -!endprocedure - -' arrow colors cannot start with # (legend background has to start with #) -!function $colorWithoutHash($c) - !if (%substr($c, 0, 1) == "#") - !$c = %substr($c,1) - !endif - !return $c -!endfunction - -!unquoted procedure $defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) - !$elementSkin = "skinparam arrow<<" + $tagStereo + ">> {" + %newline() - !if ($lineColor != "") || ($textColor != "") || ($lineStyle != "") - !$elementSkin = $elementSkin + " Color " - !if ($lineColor != "") - !$elementSkin = $elementSkin + $colorWithoutHash($lineColor) - !endif - !if ($textColor != "") - !$elementSkin = $elementSkin + ";text:" + $colorWithoutHash($textColor) - !endif - !if ($lineStyle != "") - !$elementSkin = $elementSkin + ";line." + $lineStyle - !endif - !$elementSkin = $elementSkin + %newline() - !endif - !if ($lineThickness != "") - !$elementSkin = $elementSkin + " thickness " + $lineThickness + %newline() - !endif - !$elementSkin = $elementSkin + "}" + %newline() -$elementSkin -!endprocedure - -' %is_dark() requires PlantUML version >= 1.2021.6 -!if (%function_exists("%is_dark")) - !$PlantUMLSupportsDynamicLegendColor = %true() -!else - !$PlantUMLSupportsDynamicLegendColor = %false() - !log "dynamic undefined legend colors" requires PlantUML version >= 1.2021.6, therefore only static assigned colors are used -!endif - -!unquoted function $contrastLegend($color) - !if (%is_dark($color)) - !$value = $LEGEND_LIGHT_COLOR - !else - !$value = $LEGEND_DARK_COLOR - !endif - !return $value -!endfunction - -!unquoted function $flatLegend($color) - !if (%is_dark($color)) - !$value = $LEGEND_DARK_COLOR - !else - !$value = $LEGEND_LIGHT_COLOR - !endif - !return $value -!endfunction - -' legend background has to start with # -!function $colorWithHash($c) - !if (%substr($c, 0, 1) != "#") - !$c = "#" + $c - !endif - !return $c -!endfunction - -!function $addMaskFlag($mask, $attr) - !if ($attr == "") - !$mask = $mask + "0" - !else - !$mask = $mask + "1" - !endif - !return $mask -!endfunction - -!function $orFlags($flag1, $flag2) - !if ($flag1 == "0" && $flag2 == "0") - !return "0" - !endif - !return "1" -!endfunction - -!function $tagLegendMask($bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) - !$mask = "" - !$mask = $addMaskFlag($mask, $bgColor) - !$mask = $addMaskFlag($mask, $fontColor) - !$mask = $addMaskFlag($mask, $borderColor) - !$mask = $addMaskFlag($mask, $shadowing) - !$mask = $addMaskFlag($mask, $shape) - !$mask = $addMaskFlag($mask, $sprite) - !return $mask -!endfunction - -!function $resetMask() - !return "000000" -!endfunction - -!function $combineMasks($mask1, $mask2) - !$mask = "" - !$mask = $mask + $orFlags(%substr($mask1, 0, 1), %substr($mask2, 0, 1)) - !$mask = $mask + $orFlags(%substr($mask1, 1, 1), %substr($mask2, 1, 1)) - !$mask = $mask + $orFlags(%substr($mask1, 2, 1), %substr($mask2, 2, 1)) - !$mask = $mask + $orFlags(%substr($mask1, 3, 1), %substr($mask2, 3, 1)) - !$mask = $mask + $orFlags(%substr($mask1, 4, 1), %substr($mask2, 4, 1)) - !$mask = $mask + $orFlags(%substr($mask1, 5, 1), %substr($mask2, 5, 1)) - !return $mask -!endfunction - -!function $combineMaskWithTag($mask1, $tag) - !$mask2 = %get_variable_value("$" + $tag+ "LegendMask") - !if ($mask2 == "") - ' !log combineMaskWithTag $mask1, $tag, ... only $mask1 - !return $mask1 - !endif - - ' !log combineMaskWithTag $mask1, $tag, $mask2 ... $combineMasks($mask1, $mask2) - !return $combineMasks($mask1, $mask2) -!endfunction - -' element symbols typically 4 times too big in legend -!function $smallVersionSprite($sprite) - ' ,scale= ... has to be first (...,color=black,scale=0.25... is invalid too) - !if (%strpos($sprite, "=") < 0) - !if (%substr($sprite, 0, 4) == "img:") - !$smallSprite = $sprite + "{scale=0.25}" - !else - !$smallSprite = $sprite + ",scale=0.25" - !endif - !else - !$smallSprite = $sprite - !endif - !return $smallSprite -!endfunction - -' format sprite that it can be used in diagram -!function $getSprite($sprite) - ' if it starts with & it's a OpenIconic, details see https://useiconic.com/open/ - ' if it starts with img: it's an image, details see https://plantuml.com/creole - !if (%substr($sprite, 0, 1) != "&" && %substr($sprite, 0, 4) != "img:") - !$formatted = "<$" + $sprite + ">" - !else - !$formatted = "<" + $sprite + ">" - !endif - !return $formatted -!endfunction - -!function $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) - !$bg = $bgColor - !$fo = $fontColor - !$bo = $borderColor - - !if ($fo == "") - !if ($bg != "") -!if ($PlantUMLSupportsDynamicLegendColor) - !$fo = $contrastLegend($bg) -!else - !$fo = $LEGEND_DARK_COLOR -!endif - !else - !if ($bo == "") - !$fo = $LEGEND_DARK_COLOR - !$bg = $LEGEND_LIGHT_COLOR - !else -!if ($PlantUMLSupportsDynamicLegendColor) - !$fo = $flatLegend($bo) - !$bg = $contrastLegend($bo) -!else - !$fo = $LEGEND_DARK_COLOR - !$bg = $LEGEND_LIGHT_COLOR -!endif - !endif - !endif - !else - !if ($bg == "") -!if ($PlantUMLSupportsDynamicLegendColor) - !$bg = $contrastLegend($fo) -!else - !$bg = $LEGEND_LIGHT_COLOR -!endif - !endif - !endif - - !if ($bo == "") - !$bo = $bg - !endif - - !$tagEntry = "|" - !$tagDetails = "(" - !$tagEntry = $tagEntry + "<" + $colorWithHash($bg) +">" - ' ..white rectangle - !$tagEntry = $tagEntry + " " - !$tagEntry = $tagEntry + "" - !if ($legendSprite != "") - !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " - !endif - !if ($legendText == "") - !if ($tagStereo == "boundary") - !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") - !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " - !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " - !else - !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " - !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " - !endif - !elseif (%strpos($tagStereo, "boundary") >= 0) - ' if contains/ends with _boundary remove _boundary and add "boundary (dashed)" - !$pos = %strpos($tagStereo, "_boundary") - !if ($pos > 0) - !$tagEntry = $tagEntry + " " + %substr($tagStereo, 0 ,$pos) - !if ($bgColor == "#00000000" || %lower($bgColor) == "transparent") - !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " - !$tagDetails = $tagDetails + $LEGEND_DASHED_TRANSPARENT_BOUNDARY + ", " - !else - !$tagEntry = $tagEntry + " " + $LEGEND_BOUNDARY + " " - !$tagDetails = $tagDetails + $LEGEND_DASHED_BOUNDARY + ", " - !endif - !endif - !else - !$tagEntry = $tagEntry + " " + $tagStereo + " " - !endif - !if ($shadowing == "true") - !$tagDetails = $tagDetails + $LEGEND_SHADOW_TEXT + ", " - !endif - !if ($shadowing == "false") - !$tagDetails = $tagDetails + $LEGEND_NO_SHADOW_TEXT + ", " - !endif - !if ($shape == $ROUNDED_BOX) - !$tagDetails = $tagDetails + $LEGEND_ROUNDED_BOX + ", " - !endif - !if ($shape == $EIGHT_SIDED) - !$tagDetails = $tagDetails + $LEGEND_EIGHT_SIDED + ", " - !endif - !if ($fontColor == "" && $bgColor == "") - !$tagDetails = $tagDetails + $LEGEND_NO_FONT_BG_TEXT + ", " - !else - !if ($fontColor == "") - !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " - !endif - !if ($bgColor == "") - !$tagDetails = $tagDetails + $LEGEND_NO_BG_TEXT + ", " - !endif - !endif - !if ($tagDetails=="(") - !$tagDetails = "" - !else - !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) - !$tagDetails = $tagDetails + ")" - !endif - !else - !$brPos = %strpos($legendText, "\n") - !if ($brPos > 0) - !$tagEntry = $tagEntry + %substr($legendText, 0, $brPos) + " " - !$details = %substr($legendText, $brPos + 2) - !if ($details=="") - !$tagDetails = "" - !else - !$tagDetails = $tagDetails + $details + ")" - !endif - !else - !$tagEntry = $tagEntry + " " + $legendText + " " - !$tagDetails = "" - !endif - !endif - - !$tagDetails = $tagDetails + " " - !$tagDetails = $tagDetails + "|" -%set_variable_value("$" + $tagStereo + "LegendEntry", $tagEntry) -%set_variable_value("$" + $tagStereo + "LegendDetails", $tagDetails) - !return $tagEntry -!endfunction - -!function $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) - !$tc = $textColor - !$lc = $lineColor - - !if ($tc == "") - !if ($PlantUMLSupportsDynamicLegendColor) - !$tc = $flatLegend($ARROW_COLOR) - !else - !$tc = $LEGEND_DARK_COLOR - !endif - !endif - !if ($lc == "") - !if ($PlantUMLSupportsDynamicLegendColor) - !$lc = $flatLegend($ARROW_COLOR) - !else - !$lc = $LEGEND_DARK_COLOR - !endif - !endif - - !$tagEntry = "|" - !$tagDetails = "(" - ' ..white line - !$tagEntry = $tagEntry + " " - !$tagEntry = $tagEntry + "" - !if ($legendSprite != "") - !$tagEntry = $tagEntry + $getSprite($legendSprite) + " " - !endif - !if ($legendText == "") - !$tagEntry = $tagEntry + " " + $tagStereo + " " - !if ($textColor == "") - !$tagDetails = $tagDetails + $LEGEND_NO_FONT_TEXT + ", " - !endif - !if ($lineColor == "") - !$tagDetails = $tagDetails + $LEGEND_NO_LINE_TEXT + ", " - !endif - !if ($lineStyle != "") - !if ($lineStyle == $DOTTED_LINE) - !$tagDetails = $tagDetails + $LEGEND_DOTTED_LINE + ", " - !elseif ($lineStyle == $DASHED_LINE) - !$tagDetails = $tagDetails + $LEGEND_DASHED_LINE + ", " - !elseif ($lineStyle == $BOLD_LINE) - !$tagDetails = $tagDetails + $LEGEND_BOLD_LINE + ", " - !else - !$tagDetails = $tagDetails + $lineStyle + ", " - !endif - !endif - !if ($lineThickness != "") - !$tagDetails = $tagDetails + "thickness " + $lineThickness + ") " - !endif - !if ($tagDetails=="(") - !$tagDetails = "" - !else - !$tagDetails = %substr($tagDetails, 0, %strlen($tagDetails)-2) - !$tagDetails = $tagDetails + ")" - !endif - !else - !$brPos = %strpos($legendText, "\n") - !if ($brPos > 0) - !$tagEntry = $tagEntry + " " + %substr($legendText, 0, $brPos) + " " - !$details = %substr($legendText, $brPos + 2) - !if ($details=="") - !$tagDetails = "" - !else - !$tagDetails = $tagDetails + $details + ")" - !endif - !else - !$tagEntry = $tagEntry + " " + $legendText + " " - !$tagDetails = "" - !endif - !endif - - !$tagDetails = $tagDetails + " " - !$tagDetails = $tagDetails + "|" -%set_variable_value("$" + $tagStereo + "_LineLegendEntry", $tagEntry) -%set_variable_value("$" + $tagStereo + "_LineLegendDetails", $tagDetails) - !return $tagEntry -!endfunction - -!unquoted procedure $addTagToLegend($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $legendText="", $legendSprite="") -'' if a combined element tag is defined (e.g. "v1.0&v1.1") then it is typically a merged color, -'' like a new $fontColor="#fdae61" therefore it should be added to the legend -'' and the & combined tags will be not removed -' !if (%strpos($tagStereo, "&") < 0) - !$dummyAlreadyVariables = $setTagLegendVariables($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) - !$tagCustomLegend = $tagCustomLegend + $tagStereo + "\n" - !$tagMask = $tagLegendMask( $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite) -%set_variable_value("$" + $tagStereo + "LegendMask", $tagMask) -' !endif -!endprocedure - -!unquoted procedure $addRelTagToLegend($tagStereo, $textColor="", $lineColor="", $lineStyle="", $legendText="", $legendSprite="", $lineThickness="") -'' Arrows have a bug with stereotype/skinparams and cannot combine text colors of one stereotype -'' and the line color of another stereotype. Therefore the text color of one tag and the line color -'' of another tag have to be combined via a "workaround" tag ("v1.0&v1.1"). -'' This workaround tag could be theoretically removed in the legend but after that there would -'' be an inconsistency between the element tags and the rel tags and therefore -'' & combined workaround tags are not removed too (and in unlikely cases the color itself could be changed) -' !if (%strpos($tagStereo, "&") < 0) - !$dummyAlreadyVariables = $setTagRelLegendVariables($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) - !$tagCustomLegend = $tagCustomLegend + $tagStereo + "_Line\n" -' !endif -!endprocedure - -!procedure $showActiveLegendEntries($allDefined) - !$brPos = %strpos($allDefined, "\n") - !while ($brPos >= 0) - !$tagStereo = %substr($allDefined, 0, $brPos) - !$allDefined = %substr($allDefined, $brPos+2) - !$brPos = %strpos($allDefined, "\n") - !if (%variable_exists("$" + $tagStereo + "Legend")) - ' is part of legendDetails - !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") - !$partSize = "" - !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") - !$line = $part1 + $partSize + $part2 -$line - !endif - !endwhile - !if (%strlen($allDefined) > 0) - !$tagStereo = $allDefined - !if (%variable_exists("$" + $tagStereo + "Legend")) - ' is part of legendDetails - !$part1 = %get_variable_value("$" + $tagStereo + "LegendEntry") - !$partSize = "" - !$part2 = %get_variable_value("$" + $tagStereo + "LegendDetails") - !$line = $part1 + $partSize + $part2 -$line - !endif - !endif -!endprocedure - -!function RoundedBoxShape() -!return $ROUNDED_BOX -!endfunction - -!function EightSidedShape() -!return $EIGHT_SIDED -!endfunction - -!function DottedLine() -!return $DOTTED_LINE -!endfunction - -!function DashedLine() -!return $DASHED_LINE -!endfunction - -!function BoldLine() -!return $BOLD_LINE -!endfunction - -' used by new defined tags -!unquoted procedure AddElementTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") -$defineSkinparams($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape) - !if ($sprite!="") -%set_variable_value("$" + $tagStereo + "ElementTagSprite", $sprite) - !if ($legendSprite == "") - !$legendSprite = $smallVersionSprite($sprite) - !endif - !endif - !if ($techn != "") -%set_variable_value("$" + $tagStereo + "ElementTagTechn", $techn) - !endif -$addTagToLegend($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) -!endprocedure - -!unquoted procedure $addElementTagInclReuse($elementName, $tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") - !$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) - !$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) - !$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) - !$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) - !$shape=$restoreEmpty($elementName, "shape", $shape, %true()) - !$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) - !$techn=$restoreEmpty($elementName, "techn", $techn, %true()) - ' new style should has its own legend text - ' !$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) - !$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) - - AddElementTag($tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) -!endprocedure - -' used by new defined rel tags -!unquoted procedure AddRelTag($tagStereo, $textColor="", $lineColor="", $lineStyle="", $sprite="", $techn="", $legendText="", $legendSprite="", $lineThickness="") -$defineRelSkinparams($tagStereo, $textColor, $lineColor, $lineStyle, $lineThickness) - !if ($sprite != "") -%set_variable_value("$" + $tagStereo + "RelTagSprite", $sprite) - !if ($legendSprite == "") - ' relation symbols typically 1:1 no additional scale required - !$legendSprite = $sprite - !endif - !endif - !if ($techn != "") -%set_variable_value("$" + $tagStereo + "RelTagTechn", $techn) - !endif -$addRelTagToLegend($tagStereo, $textColor, $lineColor, $lineStyle, $legendText, $legendSprite, $lineThickness) -!endprocedure - -' update the style of existing elements like person, ... -!unquoted procedure UpdateElementStyle($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") -!$bgColor=$restoreEmpty($elementName, "bgColor", $bgColor, %true()) -!$fontColor=$restoreEmpty($elementName, "fontColor", $fontColor, %true()) -!$borderColor=$restoreEmpty($elementName, "borderColor", $borderColor, %true()) -!$shadowing=$restoreEmpty($elementName, "shadowing", $shadowing, %true()) -!$shape=$restoreEmpty($elementName, "shape", $shape, %true()) -!$sprite=$restoreEmpty($elementName, "sprite", $sprite, %true()) -!$techn=$restoreEmpty($elementName, "techn", $techn, %true()) -!$legendText=$restoreEmpty($elementName, "legendText", $legendText, %true()) -!$legendSprite=$restoreEmpty($elementName, "legendSprite", $legendSprite, %true()) -$defineSkinparams($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape) - !if ($sprite != "") -%set_variable_value("$" + $elementName + "ElementTagSprite", $sprite) - !if ($legendSprite == "") - !$legendSprite = $smallVersionSprite($sprite) - !endif - !endif - !if ($techn != "") -%set_variable_value("$" + $elementName + "ElementTagTechn", $techn) - !endif - !$dummyAlreadyVariables = $setTagLegendVariables($elementName, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $legendText, $legendSprite) - ' default tags sets at least bgColor and fontColor - !$tagMask = $tagLegendMask("CHANGED", "CHANGED", $borderColor, $shadowing, $shape, $sprite) -%set_variable_value("$" + $elementName + "LegendMask", $tagMask) -!endprocedure - -/' @deprecated in favor of UpdateElementStyle '/ -!unquoted procedure UpdateSkinparamsAndLegendEntry($elementName, $bgColor="", $fontColor="", $borderColor="", $shadowing="") -UpdateElementStyle($elementName, $bgColor, $fontColor, $borderColor, $shadowing) -!endprocedure - -' update the style of default relation, it has to set both properties (combined statement not working) -!unquoted procedure UpdateRelStyle($textColor, $lineColor) - !$elementSkin = "skinparam arrow {" + %newline() - !$elementSkin = $elementSkin + " Color " + $lineColor + %newline() - !$elementSkin = $elementSkin + " FontColor " + $textColor + %newline() - !$elementSkin = $elementSkin + "}" + %newline() -$elementSkin -!endprocedure - -' tags/stereotypes have to be delimited with \n -!unquoted procedure SetDefaultLegendEntries($tagStereoEntries) - !$tagDefaultLegend = $tagStereoEntries -!endprocedure - -' Links -' ################################## - -!function $getLink($link) - !if ($link != "") - !return "[[" + $link + "]]" - !else - !return "" - !endif -!endfunction - -' Line breaks -' ################################## - -' PlantUML supports no DETERMINISTIC/automatic line breaks of "PlantUML line" (C4 Relashionships) -' therefore Rel...() implements an automatic line break based on spaces (like in all other objects). -' If a $type contains \n then these are used (and no automatic space based line breaks are done) -' $REL_TECHN_MAX_CHAR_WIDTH defines the automatic line break position -!global $REL_TECHN_MAX_CHAR_WIDTH = 35 -!global $REL_DESCR_MAX_CHAR_WIDTH = 32 - -!unquoted function $breakText($text, $usedNewLine, $widthStr="-1") -!$width = %intval($widthStr) -!$multiLine = "" -!if (%strpos($text, "\n") >= 0) - !while (%strpos($text, "\n") >= 0) - !$brPos = %strpos($text, "\n") - !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine - !$text = %substr($text, $brPos+2) - !endwhile -!else - !while ($width>0 && %strlen($text) > $width) - !$brPos = $width - !while ($brPos > 0 && %substr($text, $brPos, 1) != ' ') - !$brPos = $brPos - 1 - !endwhile - - !if ($brPos < 1) - !$brPos = %strpos($text, " ") - !else - !endif - - !if ($brPos > 0) - !$multiLine = $multiLine + %substr($text, 0, $brPos) + $usedNewLine - !$text = %substr($text, $brPos + 1) - !else - !$multiLine = $multiLine+ $text - !$text = "" - !endif - !endwhile -!endif -!if (%strlen($text) > 0) - !$multiLine = $multiLine + $text -!endif -!return $multiLine -!endfunction - -!unquoted function $breakLabel($text) -!$usedNewLine = "\n== " -!$multiLine = $breakText($text, $usedNewLine) -!return $multiLine -!endfunction - -!unquoted function $breakDescr($text, $widthStr) - !$usedNewLine = "\n" - !return $breakText($text, $usedNewLine, $widthStr) -!endfunction - -' $breakTechn() supports //...//; $breakNode() in C4_Deployment supports no //....// -!unquoted function $breakTechn($text, $widthStr) - !$usedNewLine = '//\n//' - !return $breakText($text, $usedNewLine, $widthStr) -!endfunction - -' Element base layout -' ################################## - -!function $getElementBase($label, $techn, $descr, $sprite) - !$element = "" - !if ($sprite != "") - !$element = $element + $getSprite($sprite) - !if ($label != "") - !$element = $element + '\n' - !endif - !endif - !if ($label != "") - !$element = $element + '== ' + $breakLabel($label) - !else - !$element = $element + '.' - !endif - !if ($techn != "") - !$element = $element + '\n//[' + $breakTechn($techn, '-1') + ']//' - !endif - !if ($descr != "") - !$element = $element + '\n\n' + $descr - !endif - !return $element -!endfunction - -' Element properties -' ################################## - -' collect all defined properties as table rows -!global $propTable = "" -!global $propTableCaption = "" -!global $propColCaption = "=" - -!unquoted function SetPropertyHeader($col1Name, $col2Name, $col3Name = "", $col4Name = "") - !$propColCaption = "" - !$propTableCaption = "|= " + $col1Name + " |= " + $col2Name + " |" - !if ($col3Name != "") - !$propTableCaption = $propTableCaption + "= " + $col3Name + " |" - !endif - !if ($col4Name != "") - !$propTableCaption = $propTableCaption + "= " + $col4Name + " |" - !endif - !return "" -!endfunction - -!unquoted function WithoutPropertyHeader() - !$propTableCaption = "" - !$propColCaption = "=" - !return "" -!endfunction - -!unquoted function AddProperty($col1, $col2, $col3 = "", $col4 = "") - !if ($propTable == "") - !if ($propTableCaption != "") - !$propTable = $propTableCaption + "\n" - !endif - !else - !$propTable = $propTable + "\n" - !endif - !$propTable = $propTable + "| " + $col1 + " |" + $propColCaption + " " + $col2 + " |" - !if ($col3 != "") - !$propTable = $propTable + " " + $col3 + " |" - !endif - !if ($col4 != "") - !$propTable = $propTable + " " + $col4 + " |" - !endif - !return "" -!endfunction - -!unquoted function $getProps($alignedNL = "\n") - !if ($propTable != "") - !$retTable = $alignedNL + $propTable - !$propTable = "" - !return $retTable - !endif - !return "" -!endfunction - -!unquoted function $getProps_L() - !return $getProps("\l") -!endfunction - -!unquoted function $getProps_R() - !return $getProps("\r") -!endfunction - -SetPropertyHeader("Property","Value") - -' Layout -' ################################## - -!function $getLegendDetailsSize($detailsFormat) - !if $detailsFormat == $LEGEND_DETAILS_NONE - !$size = 0 - !elseif $detailsFormat == $LEGEND_DETAILS_SMALL - !$size = $LEGEND_DETAILS_SMALL_SIZE - !else - !$size = $LEGEND_DETAILS_NORMAL_SIZE - !endif - !return $size -!endfunction - -!procedure $getHideStereotype($hideStereotype) -!if ($hideStereotype == "true") -hide stereotype -!endif -!endprocedure - -!procedure $getLegendTable($detailsFormat) -!global $LEGEND_DETAILS_SIZE = $getLegendDetailsSize($detailsFormat) -<#00000000,#00000000>|**Legend** | -$showActiveLegendEntries($tagDefaultLegend) -$showActiveLegendEntries($tagCustomLegend) -!endprocedure - -!procedure $getLegendArea($areaAlias, $hideStereotype, $details) -$getHideStereotype($hideStereotype) -rectangle $areaAlias<> [ -$getLegendTable($details) -] -!endprocedure - -!procedure HIDE_STEREOTYPE() -hide stereotype -!endprocedure - -!unquoted procedure SET_SKETCH_STYLE($bgColor="_dont_change_", $fontColor="_dont_change_", $warningColor="_dont_change_", $fontName="_dont_change_", $footerWarning="_dont_change_", $footerText="_dont_change_") -!if $bgColor != "_dont_change_" - !global $SKETCH_BG_COLOR = $bgColor -!endif -!if $fontColor != "_dont_change_" - !global $SKETCH_FONT_COLOR = $fontColor -!endif -!if $warningColor != "_dont_change_" - !global $SKETCH_WARNING_COLOR = $warningColor -!endif -!if $fontName != "_dont_change_" - !global $SKETCH_FONT_NAME = $fontName -!endif -!if $footerWarning != "_dont_change_" - !global $SKETCH_FOOTER_WARNING = $footerWarning -!endif -!if $footerText != "_dont_change_" - !global $SKETCH_FOOTER_TEXT = $footerText -!endif -!endprocedure - -!procedure LAYOUT_AS_SKETCH() - skinparam handwritten true -!if $SKETCH_BG_COLOR > "" - skinparam backgroundColor $SKETCH_BG_COLOR -!endif -!if $SKETCH_FONT_COLOR > "" - skinparam footer { - FontColor $SKETCH_FONT_COLOR - } - !if $ARROW_COLOR == "#666666" - !global $ARROW_COLOR = $SKETCH_FONT_COLOR - skinparam arrow { - Color $ARROW_COLOR - FontColor $ARROW_COLOR - } - !endif - !if $BOUNDARY_COLOR == "#444444" - !global $BOUNDARY_COLOR = $SKETCH_FONT_COLOR - skinparam rectangle<> { - FontColor $BOUNDARY_COLOR - BorderColor $BOUNDARY_COLOR - } - !endif -!endif -!if $SKETCH_FONT_NAMES > "" - skinparam defaultFontName $SKETCH_FONT_NAME -!endif -!if $SKETCH_FOOTER_WARNING > "" || $SKETCH_FOOTER_TEXT > "" - !$line = "footer "+ $SKETCH_FOOTER_WARNING + " " + $SKETCH_FOOTER_TEXT - $line -!endif -!endprocedure - -!global $fix_direction=%false() - -!function $down($start,$end) -!if ($fix_direction) -!return $start+"RIGHT"+$end -!else -!return $start+"DOWN"+$end -!endif -!endfunction - -!function $up($start,$end) -!if ($fix_direction) -!return $start+"LEFT"+$end -!else -!return $start+"UP"+$end -!endif -!endfunction - -!function $left($start,$end) -!if ($fix_direction) -!return $start+"UP"+$end -!else -!return $start+"LEFT"+$end -!endif -!endfunction - -!function $right($start,$end) -!if ($fix_direction) -!return $start+"DOWN"+$end -!else -!return $start+"RIGHT"+$end -!endif -!endfunction - -!procedure LAYOUT_TOP_DOWN() -!global $fix_direction=%false() -top to bottom direction -!endprocedure - -!procedure LAYOUT_LEFT_RIGHT() -!global $fix_direction = %false() -left to right direction -!endprocedure - -!procedure LAYOUT_LANDSCAPE() -!global $fix_direction = %true() -left to right direction -!endprocedure - -' legend details can displayed as Normal(), Small(), None() -!function None() -!return $LEGEND_DETAILS_NONE -!endfunction - -!function Normal() -!return $LEGEND_DETAILS_NORMAL -!endfunction - -!function Small() -!return $LEGEND_DETAILS_SMALL -!endfunction - -' has to be last call in diagram -!unquoted procedure SHOW_LEGEND($hideStereotype="true", $details=Small()) -$getHideStereotype($hideStereotype) -legend right -$getLegendTable($details) -endlegend -!endprocedure - -/' @deprecated in favor of SHOW_LEGEND '/ -!unquoted procedure SHOW_DYNAMIC_LEGEND($hideStereotype="true") -SHOW_LEGEND($hideStereotype) -!endprocedure - -' legend is reserved and cannot be uses as alias of SHOW_FLOATING_LEGEND() therefore -' LEGEND() is introduced. It returns the default name of the floating alias "floating_legend_alias" -' and can be used in the Lay_Distance() calls -!function LEGEND() -!return "floating_legend_alias" -!endfunction - -' enables that legend can be located in drawing area of the diagram. It has to be last call in diagram followed by Lay_Distance() -!unquoted procedure SHOW_FLOATING_LEGEND($alias=LEGEND(), $hideStereotype="true", $details=Normal()) -$getLegendArea($alias, $hideStereotype, $details) -!endprocedure - -' Boundaries -' ################################## - -!unquoted procedure UpdateBoundaryStyle($elementName="", $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") - !if ($elementName != "") - !$elementBoundary = $elementName + '_boundary' - UpdateElementStyle($elementBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") - !else - UpdateElementStyle("boundary", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") - ' simulate color inheritance - UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Enterprise", "") - UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "System", "") - UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, "Container", "") - !endif -!endprocedure - -!unquoted procedure AddBoundaryTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $type="", $legendText="") - !$tagBoundary = $tagStereo + '_boundary' - AddElementTag($tagBoundary, $bgColor, $fontColor, $borderColor, $shadowing, $shape, "", $type, $legendText, "") -!endprocedure - -' add _boundary to all tags that short tag version can be used -!unquoted function $addBoundaryPostfix($tags) - !if (%strlen($tags) == 0) - !return '' - !endif - !$boundaryTags = '' - !$brPos = %strpos($tags, "+") - !while ($brPos >= 0) - !$tag = %substr($tags, 0, $brPos) - !$boundaryTags = $boundaryTags + $tag + '_boundary+' - !$tags = %substr($tags, $brPos+1) - !$brPos = %strpos($tags, "+") - !endwhile - !if (%strlen($tags) > 0) - !$boundaryTags = $boundaryTags + $tags + '_boundary' - !endif - !return $boundaryTags -!endfunction - -!function $getBoundary($label, $type) - !if ($type == "") - !return '== ' + $breakLabel($label) - !endif - !if (type != "") - !return '== ' + $breakLabel($label) + '\n[' + $type + ']' - !endif -!endfunction - -!unquoted procedure Boundary($alias, $label, $type="", $tags="", $link="") -!$boundaryTags = $addBoundaryPostfix($tags) -' nodes $type reuses $techn definition of $boundaryTags -!$type=$toElementArg($type, $boundaryTags, "ElementTagTechn", "boundary") -rectangle "$getBoundary($label, $type)" $toStereos("boundary", $boundaryTags) as $alias $getLink($link) -!endprocedure - -' Boundary Styling -UpdateBoundaryStyle("", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR) - -' Relationship -' ################################## - -!function $getRel($direction, $alias1, $alias2, $label, $techn, $descr, $sprite, $tags, $link) - !$sprite = $toRelArg($sprite, $tags, "RelTagSprite") - !$techn = $toRelArg($techn, $tags, "RelTagTechn") - !$rel = $alias1 + ' ' + $direction + ' ' + $alias2 - !if ($tags != "") - !$rel = $rel + ' ' + $toStereos($tags) - !endif - !$rel = $rel + ' : ' - !if ($link != "") - !$rel = $rel + '**[[' + $link + ' ' - !endif - !if ($sprite != "") - !$rel = $rel + $getSprite($sprite) - !if ($label != "") - !$rel = $rel + ' ' - !endif - !endif - !if ($link != "") - !$usedNewLine = ']]**\n**[[' + $link + ' ' - ' if sprite and label is empty than the link url is shown (otherwise link cannot be activated at all) - !$rel = $rel + $breakText($label, $usedNewLine) + ']]**' - !else - !if ($label != "") - !$usedNewLine = '**\n**' - !$rel = $rel + '**' + $breakText($label, $usedNewLine) + '**' - !else - !$rel = $rel + '.' - !endif - !endif - !if ($techn != "") - ' line break is not deterministic, calculate it - !$rel = $rel + '\n//[' + $breakTechn($techn, $REL_TECHN_MAX_CHAR_WIDTH) + ']//' - !endif - !if ($descr != "") - ' line break is not deterministic, calculate it - !$rel = $rel + '\n\n' + $breakDescr($descr, $REL_DESCR_MAX_CHAR_WIDTH) - !endif - !$prop = $getProps() - !if ($prop != "") - ' reuse table - !$rel = $rel + $prop - !endif - !return $rel -!endfunction - -!unquoted procedure Rel_($alias1, $alias2, $label, $direction) -$getRel($direction, $alias1, $alias2, $label, "", "", "", "", "") -!endprocedure -!unquoted procedure Rel_($alias1, $alias2, $label, $techn, $direction) -$getRel($direction, $alias1, $alias2, $label, $techn, "", "", "", "") -!endprocedure - -!unquoted procedure Rel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("<<-->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_Back($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("<<--", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("<<->>", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_Back_Neighbor($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel("<<-", $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure Rel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($down("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel_D($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure BiRel_Down($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($down("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure Rel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($up("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel_U($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure BiRel_Up($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($up("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure Rel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($left("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel_L($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure BiRel_Left($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($left("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure Rel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure Rel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($right("-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -!unquoted procedure BiRel_R($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure -!unquoted procedure BiRel_Right($from, $to, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -$getRel($right("<<-","->>"), $from, $to, $label, $techn, $descr, $sprite, $tags, $link) -!endprocedure - -' Layout Helpers -' ################################## - -!function $getHiddenLine($distance) - !return '-[hidden]' + %substr('------------', 0, %intval($distance) + 1) -!endfunction - -!unquoted procedure Lay_D($from, $to) -$from -[hidden]D- $to -!endprocedure -!unquoted procedure Lay_Down($from, $to) -$from -[hidden]D- $to -!endprocedure - -!unquoted procedure Lay_U($from, $to) -$from -[hidden]U- $to -!endprocedure -!unquoted procedure Lay_Up($from, $to) -$from -[hidden]U- $to -!endprocedure - -!unquoted procedure Lay_R($from, $to) -$from -[hidden]R- $to -!endprocedure -!unquoted procedure Lay_Right($from, $to) -$from -[hidden]R- $to -!endprocedure - -!unquoted procedure Lay_L($from, $to) -$from -[hidden]L- $to -!endprocedure -!unquoted procedure Lay_Left($from, $to) -$from -[hidden]L- $to -!endprocedure - -' PlantUML bug: lines which does "not match" with the orientation/direction of the diagram -' uses the same length therefore the method offers no direction at all. -' If a direction is required the Lay_...() methods can be used -!unquoted procedure Lay_Distance($from, $to, $distance="0") -$from $getHiddenLine($distance) $to -!endprocedure - -!global $PERSON_BG_COLOR = "#08427B" -!global $PERSON_BORDER_COLOR = "#073B6F" -!global $EXTERNAL_PERSON_BG_COLOR = "#686868" -!global $EXTERNAL_PERSON_BORDER_COLOR = "#8A8A8A" -!global $SYSTEM_BG_COLOR = "#1168BD" -!global $SYSTEM_BORDER_COLOR = "#3C7FC0" -!global $EXTERNAL_SYSTEM_BG_COLOR = "#999999" -!global $EXTERNAL_SYSTEM_BORDER_COLOR = "#8A8A8A" - -' Styling -' ################################## - -UpdateElementStyle("person", $PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $PERSON_BORDER_COLOR) -UpdateElementStyle("external_person", $EXTERNAL_PERSON_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_PERSON_BORDER_COLOR) -UpdateElementStyle("system", $SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $SYSTEM_BORDER_COLOR) -UpdateElementStyle("external_system", $EXTERNAL_SYSTEM_BG_COLOR, $ELEMENT_FONT_COLOR, $EXTERNAL_SYSTEM_BORDER_COLOR) - -UpdateBoundaryStyle("enterprise", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="Enterprise") -UpdateBoundaryStyle("system", $bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $type="System") - -' shortcuts with default colors -!unquoted procedure AddPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") - $addElementTagInclReuse("person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) -!endprocedure -!unquoted procedure AddExternalPersonTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") - $addElementTagInclReuse("external_person", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) -!endprocedure -!unquoted procedure AddSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") - $addElementTagInclReuse("system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) -!endprocedure -!unquoted procedure AddExternalSystemTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $legendText="", $legendSprite="") - $addElementTagInclReuse("external_system", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, "", $legendText, $legendSprite) -!endprocedure - -!unquoted procedure UpdateEnterpriseBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="Enterprise", $legendText="") - UpdateBoundaryStyle("enterprise", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) -!endprocedure -!unquoted procedure UpdateSystemBoundaryStyle($bgColor=$BOUNDARY_BG_COLOR, $fontColor=$BOUNDARY_COLOR, $borderColor=$BOUNDARY_COLOR, $shadowing="", $shape="", $type="System", $legendText="") - UpdateBoundaryStyle("system", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) -!endprocedure - -' Sprites -' ################################## - -sprite $person [48x48/16] { -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -0000000000000000000049BCCA7200000000000000000000 -0000000000000000006EFFFFFFFFB3000000000000000000 -00000000000000001CFFFFFFFFFFFF700000000000000000 -0000000000000001EFFFFFFFFFFFFFF80000000000000000 -000000000000000CFFFFFFFFFFFFFFFF6000000000000000 -000000000000007FFFFFFFFFFFFFFFFFF100000000000000 -00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 -00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 -0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 -0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 -0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 -0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 -0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 -0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 -00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 -00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 -000000000000007FFFFFFFFFFFFFFFFFF100000000000000 -000000000000000BFFFFFFFFFFFFFFFF5000000000000000 -0000000000000001DFFFFFFFFFFFFFF70000000000000000 -00000000000000000BFFFFFFFFFFFF500000000000000000 -0000000000000000005DFFFFFFFFA1000000000000000000 -0000000000000000000037ABB96100000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000025788300000000005886410000000000000 -000000000007DFFFFFFD9643347BFFFFFFFB400000000000 -0000000004EFFFFFFFFFFFFFFFFFFFFFFFFFFB1000000000 -000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD200000000 -00000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE10000000 -0000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0000000 -000000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5000000 -000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000 -000009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF200000 -00000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF600000 -00000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF800000 -00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 -00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 -00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 -00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 -00001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA00000 -00000EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF700000 -000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 -0000008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD3000000 -000000014555555555555555555555555555555300000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -} - -sprite $person2 [48x48/16] { -0000000000000000000049BCCA7200000000000000000000 -0000000000000000006EFFFFFFFFB3000000000000000000 -00000000000000001CFFFFFFFFFFFF700000000000000000 -0000000000000001EFFFFFFFFFFFFFF80000000000000000 -000000000000000CFFFFFFFFFFFFFFFF6000000000000000 -000000000000007FFFFFFFFFFFFFFFFFF100000000000000 -00000000000001FFFFFFFFFFFFFFFFFFF900000000000000 -00000000000006FFFFFFFFFFFFFFFFFFFF00000000000000 -0000000000000BFFFFFFFFFFFFFFFFFFFF40000000000000 -0000000000000EFFFFFFFFFFFFFFFFFFFF70000000000000 -0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 -0000000000000FFFFFFFFFFFFFFFFFFFFF80000000000000 -0000000000000DFFFFFFFFFFFFFFFFFFFF60000000000000 -0000000000000AFFFFFFFFFFFFFFFFFFFF40000000000000 -00000000000006FFFFFFFFFFFFFFFFFFFE00000000000000 -00000000000000EFFFFFFFFFFFFFFFFFF800000000000000 -000000000000007FFFFFFFFFFFFFFFFFF100000000000000 -000000000000000BFFFFFFFFFFFFFFFF5000000000000000 -0000000000000001DFFFFFFFFFFFFFF70000000000000000 -00000000000000000BFFFFFFFFFFFF500000000000000000 -0000000000000000005DFFFFFFFFA1000000000000000000 -0000000000000000000037ABB96100000000000000000000 -000000000002578888300000000005888864100000000000 -0000000007DFFFFFFFFD9643347BFFFFFFFFFB4000000000 -00000004EFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB10000000 -0000007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD2000000 -000006FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE100000 -00003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB00000 -0000BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF50000 -0003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD0000 -0009FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2000 -000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 -000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 -001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 -001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB000 -001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA000 -000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -000DFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6000 -0009FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF2000 -0003FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFD0000 -0000BFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF50000 -00003FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFB00000 -000006FFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFE100000 -0000007FFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFD2000000 -00000004EFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFB10000000 -0000000007DF8FFFFFFFFFFFFFFFFFFFFFF8FB4000000000 -000000000002578888888888888888888864100000000000 -} - -sprite $robot [48x48/16] { -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000005BFFFFFFFFFFFFFFFFFFFFFE9100000000000 -0000000000AFFFFFFFFFFFFFFFFFFFFFFFFFE30000000000 -0000000007FFFFFFFFFFFFFFFFFFFFFFFFFFFE1000000000 -000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000000000 -000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 -000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFD000000000 -000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 -000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 -000699405FFFFFFC427FFFFFFFFFC427FFFFFFE009982000 -008FFF705FFFFFE10006FFFFFFFE00007FFFFFE00FFFF100 -00CFFF705FFFFFA00001FFFFFFF900002FFFFFE00FFFF500 -00DFFF705FFFFFB00002FFFFFFFA00003FFFFFE00FFFF500 -00DFFF705FFFFFF4000AFFFFFFFF3000BFFFFFE00FFFF500 -00DFFF705FFFFFFFA8DFFFFFFFFFFA8DFFFFFFE00FFFF500 -00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 -00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 -00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 -00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 -00DFFF705FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE00FFFF500 -00CFFF705FFFFFF87777777777777777CFFFFFE00FFFF500 -008FFF705FFFFFF100000000000000009FFFFFE00FFFF100 -000699405FFFFFF76666666666666666CFFFFFE009982000 -000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 -000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFFFE000000000 -000000004FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000000 -000000000EFFFFFFFFFFFFFFFFFFFFFFFFFFFF7000000000 -0000000005FFFFFFFFFFFFFFFFFFFFFFFFFFFD0000000000 -00000000004CFFFFFFFFFFFFFFFFFFFFFFFF910000000000 -000000000000011111111111111111111110000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000 -} - -sprite $robot2 [48x48/16] { -000000000000000088888888888888880000000000000000 -000000000000000AFFFFFFFFFFFFFFFFA000000000000000 -00000000000000CFFFFFFFFFFFFFFFFFFC00000000000000 -00000000000004EFFFFFFFFFFFFFFFFFFE40000000000000 -0000000000000AFFFFFFFFFFFFFFFFFFFFA0000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000008FF8FFFFFFFFFFFFFFFFFFFF8FF80000000000 -00000000000888FFFFFFFFFFFFFFFFFFFF88800000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000008FFFFFFFFFFFFFFFFFFFF80000000000000 -00000000000004CFFFFFFFFFFFFFFFFFFC40000000000000 -000000488888848CFFFFFFFFFFFFFFFFC848888884000000 -00000CFFFFFFFFC888888888888888888CFFFFFFFFC00000 -00008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF80000 -0000CFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8000 -0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 -0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 -0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 -0008FFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFF8000 -0000CFFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFFC0000 -00008FFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFF80000 -00000CFFFFFF8FFFFFFFFFFFFFFFFFFFFFF8FFFFFFC00000 -000000488887578888888888888888888864688884000000 -000000000000000000000000000000000000000000000000 -} - -' Layout -' ################################## - -SetDefaultLegendEntries("person\nsystem\nexternal_person\nexternal_system\nenterprise_boundary\nsystem_boundary\nboundary") - -!procedure LAYOUT_WITH_LEGEND() -hide stereotype -legend right -|**Legend** | -|<$PERSON_BG_COLOR> person | -|<$SYSTEM_BG_COLOR> system| -|<$EXTERNAL_PERSON_BG_COLOR> external person | -|<$EXTERNAL_SYSTEM_BG_COLOR> external system | -endlegend -!endprocedure - -!global $defaultPersonSprite = "person" -!$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) -UpdateElementStyle("person") -' workaround of plantuml.jar bug - person overwrites external_person setting -!$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) -UpdateElementStyle("external_person") -!global $portraitPerson = "false" - -!procedure $clearPersonRestore() - !$dummy = $clearRestore("person", "sprite") - !$dummy = $clearRestore("person", "legendSprite") - %set_variable_value("$" + "person" + "ElementTagSprite", "") - UpdateElementStyle("person") - ' workaround of plantuml.jar bug - person overwrites external_person setting - !$dummy = $clearRestore("external_person", "sprite") - !$dummy = $clearRestore("external_person", "legendSprite") - %set_variable_value("$" + "external_person" + "ElementTagSprite", "") - UpdateElementStyle("external_person") -!endprocedure - -!procedure HIDE_PERSON_SPRITE() - !$defaultPersonSprite = "" - !$portraitPerson = "false" - $clearPersonRestore() -!endprocedure - -!unquoted procedure SHOW_PERSON_SPRITE($sprite="") - !if ($sprite == "") - !$defaultPersonSprite = "person" - !else - !$defaultPersonSprite = $sprite - !endif - !$dummy = $restoreEmpty("person", "sprite", $defaultPersonSprite, %true()) - UpdateElementStyle("person") - ' workaround of plantuml.jar bug - person overwrites external_person setting - !$dummy = $restoreEmpty("external_person", "sprite", $defaultPersonSprite, %true()) - UpdateElementStyle("external_person") - !$portraitPerson = "false" -!endprocedure - -!unquoted procedure SHOW_PERSON_PORTRAIT() - !$defaultPersonSprite = "" - !$portraitPerson = "portrait" - $clearPersonRestore() -!endprocedure - -!unquoted procedure SHOW_PERSON_OUTLINE() - !$defaultPersonSprite = "" - !$portraitPerson = "outline" - $clearPersonRestore() -!endprocedure - -' Elements -' ################################## - -!function $getPerson($label, $descr, $sprite) - !if ($sprite == "") && ($defaultPersonSprite != "") - !$sprite = $defaultPersonSprite - !endif - !return $getElementBase($label, "", $descr, $sprite) -!endfunction - -!function $getSystem($label, $descr, $sprite) - !return $getElementBase($label, "", $descr, $sprite) -!endfunction - -!unquoted procedure Person($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "person") -!if ($portraitPerson == "portrait") && ($sprite == "") -actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) -!elseif ($portraitPerson == "outline") && ($sprite == "") -person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) -!else -rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("person", $tags) as $alias $getLink($link) -!endif -!endprocedure - -!unquoted procedure Person_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_person") -!if ($portraitPerson == "portrait") && ($sprite == "") -actor "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) -!elseif ($portraitPerson == "outline") && ($sprite == "") -person "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) -!else -rectangle "$getPerson($label, $descr, $sprite)$getProps()" $toStereos("external_person", $tags) as $alias $getLink($link) -!endif -!endprocedure - -!unquoted procedure System($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") -rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure System_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") -rectangle "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure SystemDb($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") -database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure SystemQueue($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "system") -queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("system", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure SystemDb_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") -database "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure SystemQueue_Ext($alias, $label, $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_system") -queue "$getSystem($label, $descr, $sprite)$getProps()" $toStereos("external_system", $tags) as $alias $getLink($link) -!endprocedure - -' Boundaries -' ################################## - -!unquoted procedure Enterprise_Boundary($alias, $label, $tags="", $link="") - !if ($tags != "") - !$allTags = $tags + '+enterprise' - !else - !$allTags = 'enterprise' - !endif - ' $type defined via $tag style - Boundary($alias, $label, "", $allTags, $link) -!endprocedure - -!unquoted procedure System_Boundary($alias, $label, $tags="", $link="") - !if ($tags != "") - !$allTags = $tags + '+system' - !else - !$allTags = 'system' - !endif - ' $type defined via $tag style - Boundary($alias, $label, "", $allTags, $link) -!endprocedure - -allow_mixing - -'Общее позиционирование -skinparam { - wrapWidth 200 - maxMessageSize 200 - defaultfontname arial - roundCorner 10 - linetype ortho - shadowing false - HyperLinkColor #?black:white - HyperLinkUnderline false -} -hide circle - - -skinparam arrow { - color black - thickness 1 -} - -skinparam note { - bordercolor #4e4948 - backgroundcolor pink -} - -' Scope: A single software system. -' Primary elements: Containers within the software system in scope. -' Supporting elements: People and software systems directly connected to the containers. -' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff. - -' Colors -' ################################## - -!$CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR -!$CONTAINER_BG_COLOR ?= "#438DD5" -!$CONTAINER_BORDER_COLOR ?= "#3C7FC0" - -!$CONTAINER_BOUNDARY_COLOR ?= $BOUNDARY_COLOR -!$CONTAINER_BOUNDARY_BG_COLOR ?= $BOUNDARY_BG_COLOR -!$CONTAINER_BOUNDARY_BORDER_STYLE ?= $BOUNDARY_BORDER_STYLE - -!$EXTERNAL_CONTAINER_FONT_COLOR ?= $ELEMENT_FONT_COLOR -!$EXTERNAL_CONTAINER_BG_COLOR ?= "#B3B3B3" -!$EXTERNAL_CONTAINER_BORDER_COLOR ?= "#A6A6A6" - -' Styling -' ################################## -UpdateElementStyle("container", $CONTAINER_BG_COLOR, $CONTAINER_FONT_COLOR, $CONTAINER_BORDER_COLOR) -UpdateElementStyle("external_container", $EXTERNAL_CONTAINER_BG_COLOR, $EXTERNAL_CONTAINER_FONT_COLOR, $EXTERNAL_CONTAINER_BORDER_COLOR) - -UpdateBoundaryStyle("container", $bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $type="Container") - -' shortcuts with default colors -!unquoted procedure AddContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") - $addElementTagInclReuse("container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) -!endprocedure -!unquoted procedure AddExternalContainerTag($tagStereo, $bgColor="", $fontColor="", $borderColor="", $shadowing="", $shape="", $sprite="", $techn="", $legendText="", $legendSprite="") - $addElementTagInclReuse("external_container", $tagStereo, $bgColor, $fontColor, $borderColor, $shadowing, $shape, $sprite, $techn, $legendText, $legendSprite) -!endprocedure - -!unquoted procedure UpdateContainerBoundaryStyle($bgColor=$CONTAINER_BOUNDARY_BG_COLOR, $fontColor=$CONTAINER_BOUNDARY_COLOR, $borderColor=$CONTAINER_BOUNDARY_COLOR, $shadowing="", $shape="", $type="Container", $legendText="") - UpdateBoundaryStyle("container", $bgColor, $fontColor, $borderColor, $shadowing, $shape, $type, $legendText) -!endprocedure - -' Layout -' ################################## - -SetDefaultLegendEntries("person\nsystem\ncontainer\nexternal_person\nexternal_system\nexternal_container\nenterprise_boundary\nsystem_boundary\ncontainer_boundary\nboundary") - -!procedure LAYOUT_WITH_LEGEND() -hide stereotype -legend right -|**Legend** | -|<$PERSON_BG_COLOR> person | -|<$SYSTEM_BG_COLOR> system | -|<$CONTAINER_BG_COLOR> container | -|<$EXTERNAL_PERSON_BG_COLOR> external person | -|<$EXTERNAL_SYSTEM_BG_COLOR> external system | -|<$EXTERNAL_CONTAINER_BG_COLOR> external container | -endlegend -!endprocedure - -' Elements -' ################################## - -!function $getContainer($label, $techn, $descr, $sprite) - !return $getElementBase($label, $techn, $descr, $sprite) -!endfunction - -!unquoted procedure Container($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") -rectangle "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure ContainerDb($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") -database "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure ContainerQueue($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "container") -queue "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("container", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure Container_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") -rectangle "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure ContainerDb_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") -database "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) -!endprocedure - -!unquoted procedure ContainerQueue_Ext($alias, $label, $techn="", $descr="", $sprite="", $tags="", $link="") -!$sprite=$toElementArg($sprite, $tags, "ElementTagSprite", "external_container") -!$techn=$toElementArg($techn, $tags, "ElementTagTechn", "external_container") -queue "$getContainer($label, $techn, $descr, $sprite)$getProps()" $toStereos("external_container", $tags) as $alias $getLink($link) -!endprocedure - -' Boundaries -' ################################## - -!unquoted procedure Container_Boundary($alias, $label, $tags="", $link="") - !if ($tags != "") - !$allTags = $tags + '+container' - !else - !$allTags = 'container' - !endif - ' $type defined via $tag style - Boundary($alias, $label, "", $allTags, $link) -!endprocedure - -{{&renderCore}} - -{{&code}} - -@enduml diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml b/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml index ab6122e..4c562bb 100644 --- a/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml +++ b/src/repository_structure_example/metamodels/default/entities/contexts/templates/template.puml @@ -117,14 +117,14 @@ skinparam component<> { %set_variable_value("$join_append", 0) !endprocedure -!unquoted procedure $Entity($entity, $ACName, $id, $ACType) +!unquoted procedure $Entity($entity, $ACName, $id, $ACType, $Colored = "") $join_start() !if ($entity == component) - component $id[ + component $id $Colored [ $ACName ==== !elseif ($entity == system) - component $id[ + component $id $Colored [ $ACName ==== !elseif ($entity == actor || $entity == person) From 9de72a346931af2ee19b1aa5fde88523cfc06445 Mon Sep 17 00:00:00 2001 From: ValentinKozlov Date: Sun, 14 May 2023 10:23:06 +0300 Subject: [PATCH 5/6] =?UTF-8?q?=D0=9F=D1=80=D0=BE=D0=B4=D0=BE=D0=BB=D0=B6?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=20=D1=8D=D0=BA=D1=81=D0=BF=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82=D1=8B=20=D1=81=20=D0=B2=D1=8B=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D0=BE=D0=BC=20=D0=BC=D0=B0=D1=82=D0=B0=D0=BC=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B8=20=D0=B2=20=D1=81=D0=B2=D0=BE=D1=8E=20?= =?UTF-8?q?=D1=80=D0=B5=D0=BF=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application_arch/systems/_root.yaml | 3 + .../application_arch/users/users.yaml | 4 - .../artefacts/common/pal1_landscape.yaml | 13 +- .../frog_paradise/pal1_landscape.yaml | 10 +- .../business_arch/_root.yaml | 3 +- .../business_arch/units.yaml | 14 + .../ex_from_max/product.yaml | 54 --- .../default/entities/contexts/base.yaml | 9 +- .../entities/contexts/c4_context copy.yaml | 309 ------------------ .../default/entities/contexts/plantuml.yaml | 8 +- .../default/entities/documents/base.yaml | 2 +- 11 files changed, 42 insertions(+), 387 deletions(-) create mode 100644 src/repository_structure_example/business_arch/units.yaml delete mode 100644 src/repository_structure_example/ex_from_max/product.yaml delete mode 100644 src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml diff --git a/src/repository_structure_example/application_arch/systems/_root.yaml b/src/repository_structure_example/application_arch/systems/_root.yaml index c940926..4150c24 100644 --- a/src/repository_structure_example/application_arch/systems/_root.yaml +++ b/src/repository_structure_example/application_arch/systems/_root.yaml @@ -3,5 +3,8 @@ imports: - allure.yaml - crm.yaml - grafana_dev.yaml + - sid.yaml - spact.yaml - spoll.yaml + - srole.yaml + diff --git a/src/repository_structure_example/application_arch/users/users.yaml b/src/repository_structure_example/application_arch/users/users.yaml index 10bee4a..ac01df3 100644 --- a/src/repository_structure_example/application_arch/users/users.yaml +++ b/src/repository_structure_example/application_arch/users/users.yaml @@ -1,10 +1,6 @@ components: # внутренние клиенты Экосистемы - swamp.frog: - title: Лягушка - entity: person - swamp.creeping_snake: title: Змей-ползучий entity: person # Сущность компонента из PlantUML (https://plantuml.com/ru/deployment-diagram) diff --git a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml index 64c73f3..bbad6da 100644 --- a/src/repository_structure_example/artefacts/common/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/common/pal1_landscape.yaml @@ -4,13 +4,12 @@ contexts: # Контексты представления архитектур location: ГК Болото extra-links: true # extra-links: false - # uml: - # $notation: plantuml # sber C4Model plantuml - # $autor: Frog - # $version: '0.0.1' - # $moment: 20.11.2022 + uml: + $notation: plantuml # sber C4Model plantuml + $autor: Frog + $version: '0.0.1' + $moment: 20.11.2022 components: - swamp.frog.* - swamp.hippo.* - - swamp.crocodile.* - - swamp.crocodile.sid + - swamp.crocodile.* diff --git a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml index d3c165f..b01d225 100644 --- a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml @@ -4,10 +4,10 @@ contexts: # Контексты представления архитектур location: Лягушачий рай # extra-links: true extra-links: false - # uml: - # $notation: plantuml # sber C4Model plantuml - # $autor: Frog - # $version: 0.0.1 - # $moment: 20.11.2022 + uml: + $notation: plantuml # sber C4Model plantuml + $autor: Frog + $version: 0.0.1 + $moment: 20.11.2022 components: - swamp.frog.* \ No newline at end of file diff --git a/src/repository_structure_example/business_arch/_root.yaml b/src/repository_structure_example/business_arch/_root.yaml index dad12cc..4448ccb 100644 --- a/src/repository_structure_example/business_arch/_root.yaml +++ b/src/repository_structure_example/business_arch/_root.yaml @@ -1,2 +1,3 @@ imports: - - docs.yaml \ No newline at end of file + - docs.yaml + - units.yaml \ No newline at end of file diff --git a/src/repository_structure_example/business_arch/units.yaml b/src/repository_structure_example/business_arch/units.yaml new file mode 100644 index 0000000..1d5ffaf --- /dev/null +++ b/src/repository_structure_example/business_arch/units.yaml @@ -0,0 +1,14 @@ +components: + swamp: + title: Болото + entity: unit + swamp.hippo: + title: Бизнес-юнит Бегемота + entity: unit + swamp.frog: + title: Бизнес-юнит Лягушки + entity: unit + swamp.crocodile: + title: Бизнес-юнит Крокодила + entity: unit + \ No newline at end of file diff --git a/src/repository_structure_example/ex_from_max/product.yaml b/src/repository_structure_example/ex_from_max/product.yaml deleted file mode 100644 index f843403..0000000 --- a/src/repository_structure_example/ex_from_max/product.yaml +++ /dev/null @@ -1,54 +0,0 @@ -contexts: - arch.products.details: - source: arch.levels.l1 - api: - tags: - product: - $bgColor: LimeGreen - fetchTitle: > - ( - $lookup(manifest.products, componentId).title - ) - fetchComponents: > - ( - $p_id := $params.componentId; - $manifest := $params.manifest; - $product := $append([], $manifest.components.$spread().{ - "id": $keys()[0], - "component": $.* - }.( - $exists(component.products) and $p_id in component.products ? ( - id - ) - )); - $merge($manifest.components.$spread().{ - "id": $keys()[0], - "component": $.* - }[id in $distinct($append($append($manifest.components.$spread().{ - "c_id": $keys()[0], - "component": $.* - }.( - $exists(component.entity) and component.entity in ["system", "database", "actor"] ? ( - component.links[id in $product] ? ( - c_id - ) - ) - ), - $product.( - $lookup($manifest.components, $).links.( - $.id - ) - ) - ), $product))].( - { - id : id in $product ? ( - $merge([ - component, - { "tags" : ["product"]} - ]) - ) : (component) - } - )) - ) - - diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml index ab42524..750bd10 100644 --- a/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml +++ b/src/repository_structure_example/metamodels/default/entities/contexts/base.yaml @@ -113,6 +113,9 @@ entities: /* Обрабатываем параметры */ $params := $; $manifest := $params.manifest; + /* Признак SELF контекста */ + $isSelf := $params.componentId ? true : false; + /* Получаем объект контекста */ $context := $lookup($params.manifest.contexts, contextId); /* Определяем необходимость показывать ближайшие связи */ @@ -134,7 +137,7 @@ entities: $mask := $; $manifest.components.$spread().( $componentId := $keys()[0]; - $wcard($componentId, $mask) ? $ + $result := $wcard($componentId, $mask) ? $ ) )); @@ -142,7 +145,9 @@ entities: $merge([$components, $isExtraLinks ? ( $components.*.links.( { - id: $lookup($manifest.components, id) + id: $isSelf + ? $merge([$lookup($manifest.components, id), { "links": [] }]) + : $lookup($manifest.components, id) } ); ) : {}]); diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml deleted file mode 100644 index aee6e4a..0000000 --- a/src/repository_structure_example/metamodels/default/entities/contexts/c4_context copy.yaml +++ /dev/null @@ -1,309 +0,0 @@ -entities: - contexts: - menu: > - ( - $presentation := entities.contexts.config.defaultPresentation; - $append([ - { - "title": "Контексты", - "location": '04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)', - "icon": 'location_searching' - } - ], - contexts.$spread().( - /* Если указана явно презентация, используем ее */ - $presentation := $.*.presentation ? *.presentation : $presentation; - *.location ? { - "location": "04. Прикладная архитектура/03. Верхнеуровневый прикладной ландшафт (ПА-L1)/" & *.location, - "link": "entities/contexts/" & $presentation & "?dh-context-id=" & $keys()[0] - } - ) - ); - ) - config: - defaultPresentation: c4_context - renderCore: elk - api: - c4_context: - mapping: - system: System - database: SystemDb - person: Person - actor: Person - # Возввращает компоненты входящие в контекст - # Входящие параметры: - # manifest - данные архитектуры - # contextId - идентификатор контекста - # extra-links - признак необходимости отразить окружение - # componentId - идентификатор компонента для контекста SELF - fetchComponents: > - ( - /* Обрабатываем параметры */ - $params := $; - $manifest := $params.manifest; - $context := $lookup($params.manifest.contexts, contextId); - - /* Определяем необходимость показывать ближайшие связи */ - $isExtraLinks := $params.componentId ? true : $params."extra-links"; - - /* Определяем, какие компоненты покажем в контексте */ - $showComponents := $params.componentId - /* Если контекст под определенный компонент, ограничиваемся им. */ - ? [$params.componentId] - /* Если нет берем все компоненты указанные в контексте */ - : $context.components; - - /* Если в контексте переопределена функция получения компонентов, используем ее*/ - $context.api.fetchComponents ? ( - $eval($context.api.fetchComponents, $params) - ) : ( - /* Получаем все компоненты входящие в контекст */ - $components := $merge($showComponents.( - $mask := $; - $manifest.components.$spread().( - $componentId := $keys()[0]; - $wcard($componentId, $mask) ? $ - ) - )); - - /* Добавляем окружение, если это нужно */ - $merge([$components, $isExtraLinks ? ( - $components.*.links.( - { - id: $lookup($manifest.components, id) - } - ); - ) : {}]); - ) - ) - # На основании списка компонентов генерирует массиа областей которые они затрагивают - # Входящие параметры: - # components - список компонентов в формате fetchComponents - fetchAreas: > - ( - $distinct(components.$spread().( - $componentId := $keys()[0]; - $domains := $componentId.$split("."); - $limit := $count($domains) - 1; - $areaId := $map($domains, function($v, $i) {( - $join($map($domains, function($sv, $si) { - $si <= $i ? $sv - }), ".") - )}); - ))^($); - ) - # Генерирует список отображаемых связей - # Входящие параметры: - # components - список компонентов в формате fetchComponents - fetchLinks: > - ( - /* Обрабатываем параметры */ - $components := components; - $distinct( - $components.$spread().( - $from := $keys()[0]; - $direction := $.*.direction ? $.*.direction : "--"; - $.*.links[$lookup($components, id)].( - $title := title ? title : contract; - $title := contract ? ("[[/docs/" & contract & " " & $title & "]]") : $title; - { - "from": $from, - "to": (id ? id : "undefined"), - "title": $title, - "contract": contract, - "direction": direction ? direction : "--" - } - ) - ) - ); - ) - - fetchTitle: > - ( - context.title ? context.title : componentId - ) - - presentations: - c4_context: - title: Представление в C4 Context PlantUML - params: - title: Требуемые параметры для презентации - type: object - properties: - "dh-context-id": - title: Идентификатор контекста - type: string - pattern: ^[a-zA-Z][a-zA-Z0-9_-]*(\.[a-zA-Z][a-zA-Z0-9_-]*)*$ - required: - - dh-context-id - type: plantuml - template: templates/context.puml - $constructor: > # Переносим необходимую информацию из контекста в презентацию - ( - $id := $params."dh-context-id"; - $context := $lookup(contexts, $id); - $prototype := entities.contexts.presentations.c4_context; - - /* Преобразует относительные пути к файлам в прямые*/ - $toDirectRes := function($value) { - $substring($value, 0, 4) = "res:" ? $value : "res://contexts/" & $id & "#" & $value - }; - - /* Если явно указан puml файл, просто рендерим его */ - $substring($context.uml, -5) = ".puml" ? ( - { - "type": "plantuml", - "source": $toDirectRes($context.uml) - } - ) : ( - $result := $context.source ? ($merge([$prototype, { - "origin": { "_source": $context.source, "_origin": "($)" } - }])) : $prototype; - - /* Если в контексте переопределен шаблон, используем его по прямой ссылке */ - $result := $context.template - ? $merge([$result, { "template": $toDirectRes($context.template) }]) - : $result; - ) - ) - source: > - ( - $id := $params."dh-context-id"; - /* Получаем доступ к оригинальным данным */ - $manifest := _origin ? _origin : $; - /* Получаем контекст */ - $context := $lookup($manifest.contexts, $id); - /* Если в контексте задан источник, берем его за основу */ - $manifest := _source ? _source : $; - - $isExtraLinks := $not($string($context."extra-links") = "false"); - - /* Получаем коллекцию дефолтных вспомогательных функций */ - $defFunctions := $manifest.entities.contexts.api; - - /* Получаем коллекцию дефолтных параметров */ - $defConfig := $manifest.entities.contexts.config; - - /* Определяем движок рендеринга */ - $renderCore := $lookup({ - "elk": "!pragma layout elk", - "smetana": "!pragma layout smetana" - }, $defConfig.renderCore); - - $fetchTitle := $exists($context.api) and $exists($context.api.fetchTitle) ? - $context.api.fetchTitle : $defFunctions.fetchTitle; - $title := $eval($fetchTitle, { - "manifest": $manifest, - "context": $context, - "contextId": $id, - "componentId": $params.componentId - }); - /* Формируем заголовок */ - $header := "title: " & $title & "\n"; - - $tags := $exists($context.api) and $exists($context.api.tags) ? ( - $join( - $context.api.tags.$spread().{ - "id": $keys()[0], - "value": $.*}.( - "AddElementTag(" & id & "," & - $join(value.$spread().{ - "key": $keys()[0], - "value": $.* - }.( - key & "=" & value - ), ",") & ")\n" - ) - , "\n") - ) : (""); - - /* Получаем все компоненты входящие в контекст */ - $components := $eval($defFunctions.fetchComponents, { - "manifest": $manifest, - "contextId": $id, - "extra-links": $isExtraLinks, - "componentId": $params.componentId - }); - - /* Генерируем области */ - $areas := $eval($defFunctions.fetchAreas, { - "components": $components - }); - - /* Генерируем PlantUML диаграмму компонентов */ - $elements := ( - $join($map($areas, function($domain, $index) {( - $result := ""; - $component := $lookup($components, $domain); - $component := $not($exists($component)) ? ($lookup($manifest.components, $domain)) : $component; - $context := $lookup($manifest.contexts, $domain); - - /* Определяем является ли элемент областью */ - $isAreaBegin := $areas[$index + 1].$substring(0, $domain.$length() + 1) = $domain & "."; - - $result := $result & ($isAreaBegin ? ( - $title := $component.title ? $component.title : ( - $context.title ? $context.title : $domain - ); - "Boundary" - & ($component.external ? "_Ext" : "") - & "(" - & $domain & "," - & $title - & ") {\n"; - ) : ""); - - /* Если домен является компонентом, выводим его на диаграмму */ - $result := $result & ( - $not($isAreaBegin) and $component ? ( - /* Открываем секцию компонента */ - $entity := $lookup($defFunctions.c4_context.mapping, $component.entity ? $component.entity : "system"); - $entity := $not($exists($entity))? "System": $entity; - $entity - & ($component.external ? "_Ext" : "") - & "(" - & $domain - & "," - & $component.title - & "," - & "$link=" & "/architect/components/" & $domain - & ($exists($component.tags) ? ( - ", $tags=" & $join($component.tags, ",") - )) - & ")\n"; - ): ""; - ); - - /* Определяем, что область нужно закрыть*/ - $result & ( - $count($split($domain, ".")) > 1 - and ($count($split($domain, ".")) > $count($split($areas[$index + 1], ".")) - ) ? "}\n" : ""); - )})); - ); - /* Получаем список связей */ - $links := $eval($defFunctions.fetchLinks, { - "components": $components - }); - - /* Генерируем код связей */ - $linksCode := $join($links.( - "Rel(" - &from - &"," - &to - &",\"" - &title - &"\", " - &"$link=\"" - &link - &"\")\n" - ), ""); - - /* Готовим данные для передачи в шаблон */ - { - "renderCore": $renderCore, - "presentation": $defConfig.defaultPresentation, - "code": $header & $tags & $elements & $linksCode - } - ) diff --git a/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml index 05295e8..4fb634c 100644 --- a/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml +++ b/src/repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml @@ -22,13 +22,14 @@ entities: $join($map($areas, function($domain, $index) {( $result := ""; $component := $lookup($components, $domain); + $originComponent := $lookup($manifest.components, $domain); $context := $lookup($manifest.contexts, $domain); /* Определяем является ли элемент областью */ $isAreaBegin := $areas[$index + 1].$substring(0, $domain.$length() + 1) = $domain & "."; $result := $result & ($isAreaBegin ? ( - $title := $component.title ? $component.title : ( + $title := $originComponent.title ? $originComponent.title : ( $context.title ? $context.title : $domain ); "$Region(" @@ -124,7 +125,7 @@ entities: }; /* Если явно указан puml файл, просто рендерим его */ - $substring($context.uml, -5) = ".puml" ? ( + ($type($context.uml) = "string") and ($substring($context.uml, -5) = ".puml") ? ( { "type": "plantuml", "source": $toDirectRes($context.uml) @@ -141,7 +142,6 @@ entities: ) ) template: templates/template.puml - # template: templates/context.puml source: > ( $id := $params."dh-context-id"; @@ -167,7 +167,7 @@ entities: }, $defConfig.renderCore); /* Формируем заголовок */ - $header := "$Header(\"" & ($context.title ? $context.title : $id ) & "\", , , )\n"; + $header := "$Header(\"" & ($context.title ? $context.title : $id ) & "\", \"" & $context.uml."$autor" & "\", \"" & $context.uml."$version" & "\" , \"" & $context.uml."$moment" & "\")\n"; /* Получаем все компоненты входящие в контекст */ $components := $eval($defFunctions.fetchComponents, { diff --git a/src/repository_structure_example/metamodels/default/entities/documents/base.yaml b/src/repository_structure_example/metamodels/default/entities/documents/base.yaml index cd42832..0cabe76 100644 --- a/src/repository_structure_example/metamodels/default/entities/documents/base.yaml +++ b/src/repository_structure_example/metamodels/default/entities/documents/base.yaml @@ -7,7 +7,7 @@ entities: $append([ { "title": 'Документы', - "location": 'Документы', + /*"location": 'Документы',*/ "expand": true, "icon": 'description' } From e1ddf858b58d40ddae73e082a45b8a36834dd429 Mon Sep 17 00:00:00 2001 From: ValentinKozlov Date: Sun, 14 May 2023 21:54:42 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=20=D0=BF=D1=80=D0=B8=D0=BC=D0=B5=D1=80,=20=D1=81?= =?UTF-8?q?=D0=B4=D0=B5=D0=BB=D0=B0=D0=BD=D0=BE=20=D0=BE=D0=BF=D0=B8=D1=81?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/repository_structure_example/README.md | 97 ++++++- .../application_arch/_root.yaml | 2 + .../application_arch/aspects/_root.yaml | 2 + .../aspects/aspects.yaml} | 0 .../application_arch/systems/spact.yaml | 2 +- .../application_arch/systems/srole.yaml | 2 +- .../units.yaml | 0 .../frog_paradise/pal1_landscape.yaml | 10 +- .../business_arch/_root.yaml | 3 +- .../documentation/examples/example.html | 2 - .../documentation/examples/test.md | 1 - .../enterprise_arch/tools/dochub/.env | 238 ++++++++++++++++++ .../tools/dochub/docker-compose.yml | 14 ++ .../images/c4_context.png | Bin 0 -> 103424 bytes .../images/contexts_switch.png | Bin 0 -> 47564 bytes .../images/plantuml.png | Bin 0 -> 113045 bytes .../images/repo.png | Bin 0 -> 48996 bytes .../metamodels/_root.yaml | 3 +- .../metamodels/default/_root.yaml | 4 +- .../default/entities/contexts/base.yaml | 2 +- .../default/entities/contexts/c4_context.yaml | 5 +- .../metamodels/default/functions/_root.yaml | 2 - .../metamodels/default/rules/_root.yaml | 4 +- .../rules}/systems.yaml | 0 .../rules}/technologies.yaml | 0 .../default/samples/01_components.yaml | 23 -- .../default/samples/02_contexts.yaml | 23 -- .../default/samples/03_documents.yaml | 10 - .../metamodels/default/samples/_root.yaml | 4 - .../metamodels/default/samples/test.puml | 17 -- .../metamodels/validators/_root.yaml | 6 - 31 files changed, 362 insertions(+), 114 deletions(-) create mode 100644 src/repository_structure_example/application_arch/aspects/_root.yaml rename src/repository_structure_example/{metamodels/default/functions/functions.yaml => application_arch/aspects/aspects.yaml} (100%) rename src/repository_structure_example/{business_arch => application_arch}/units.yaml (100%) delete mode 100644 src/repository_structure_example/documentation/examples/example.html delete mode 100644 src/repository_structure_example/documentation/examples/test.md create mode 100644 src/repository_structure_example/enterprise_arch/tools/dochub/.env create mode 100644 src/repository_structure_example/enterprise_arch/tools/dochub/docker-compose.yml create mode 100644 src/repository_structure_example/images/c4_context.png create mode 100644 src/repository_structure_example/images/contexts_switch.png create mode 100644 src/repository_structure_example/images/plantuml.png create mode 100644 src/repository_structure_example/images/repo.png delete mode 100644 src/repository_structure_example/metamodels/default/functions/_root.yaml rename src/repository_structure_example/metamodels/{validators => default/rules}/systems.yaml (100%) rename src/repository_structure_example/metamodels/{validators => default/rules}/technologies.yaml (100%) delete mode 100644 src/repository_structure_example/metamodels/default/samples/01_components.yaml delete mode 100644 src/repository_structure_example/metamodels/default/samples/02_contexts.yaml delete mode 100644 src/repository_structure_example/metamodels/default/samples/03_documents.yaml delete mode 100644 src/repository_structure_example/metamodels/default/samples/_root.yaml delete mode 100644 src/repository_structure_example/metamodels/default/samples/test.puml delete mode 100644 src/repository_structure_example/metamodels/validators/_root.yaml diff --git a/src/repository_structure_example/README.md b/src/repository_structure_example/README.md index bab5b05..bf2fe5c 100644 --- a/src/repository_structure_example/README.md +++ b/src/repository_structure_example/README.md @@ -1,10 +1,17 @@ # Пример структуры репозитория для описания архитектуры v 2.0 -**Цель примера:** Снизить порог вхождения в DocHub за счет внедрения типовых шаблонов структуры репозитория и меню DocHub для управления корпоративной архитектурой. +**Цели примера:** +1. Снизить порог вхождения в DocHub за счет внедрения типовых шаблонов структуры репозитория и меню DocHub для управления корпоративной архитектурой. +2. Показать пример переноса дефолтной метамодели к себе в репозиторий +3. Показать возможность расширения дефолтных контекстов (plantuml,smartants) на примере реализации [с4 модели](https://github.com/plantuml-stdlib/C4-PlantUML) +4. Показать возможность расширения дефолтного контекста plantuml - реализована возможность подкрашивать системы различными цветами + +# Благодарность +Как говорит, один мой хороший друг, дабы не перебивать копирайты, хочу сказать отдельное спасибо Максиму Муратову (https://t.me/maxim_muratov). Собственно Максим и реализовал с4 контекст и помог реализовать возможность красить контексты, а я "просто разместил объяву". # Отличия от предыдущей версии * Основным отличием от предыдущей версии является то, что в этой версии управление метамоделями выделено в отдельную папку **metamodels** с целью разделения архитектурных данных и структуры метамоделей. Планируется, что будет разработано два пайплайна. В случае изменения архитектурных данных, эти данные сразу будет ехать на прод, предварительно проверяя наличие ошибок в валидаторе. В случае изменения метамодели, будет проводиться ревью изменений, а только потом специально обученный человек завезет изменения в прод. -* Второе отличие заключается в том, что пример стал более функциональным. В него был внесено множество реально работающих алгоритмов. +* Второе отличие заключается в том, что пример стал более функциональным. В него было внесено множество реально работающих алгоритмов. # Суть примера Существует множество различных подходов к описанию архитектуры компании. В нашей компании была выбрана методология TOGAF. При этом мы не пытаемся внедрить классическую методологию TOGAF, мы используем комбинированный подход, выбирая только те инструменты, которые закрывают наиболее критичные риски компании. @@ -24,11 +31,13 @@ * **application_arch** - в этой папке хранятся все данные, связанные с прикладной архитектурой, кроме контекстов. Контексты мы вынесли в отдельную папку artefacts. Детально про artefacts можно почитать ниже. * **reports** - в этой папке хранятся различные отчеты по системам. * **systems** - в этой папке хранятся данные по системам. Каждую систему мы описываем в своём файле и имя файла всегда соответствует имени системы. Идентификатор системы в общем случае представляет из себя доменное имя в виде: `<имя компании>.<имя бизнес-юнита>.<имя системы>`. Если вдруг у системы нет своего репозитория, то в этом файле мы также описываем runtime компоненты этой системы, что позволяет хранить всю информацию по системе в одном месте. -* **artefacts** - в этой папке мы храним все артефакты по системам, например контексты мы храним в такой структуре: `./artefacts/<имя бизнес-юнита>/<имя контекста>`. Также здесь мы храним различные схемы по системам. + * **aspects** - в этой папке хранится список аспектов системы. + * **users** - в этой папке хранится список пользователей системы. +* **artefacts** - в этой папке хранятся все артефакты по системам, например контексты мы храним в такой структуре: `./artefacts/<имя бизнес-юнита>/<имя контекста>`. Также здесь мы храним различные схемы по системам. * **common** - в этой папке хранится общий контекст ГК Болото. * **frog_paradise** - в этой папке хранится контекст БЮ Лягушачий рай. -* **business_arch** - в этой папке хранятся все данные, связанные с бизнес-архитектурой. На текущий момент мы не ведем в DocHub бизнес-архитектуру. Возможно, суда будем класть архитектурные скетчи, когда Рома закончит встраивать в DocHub https://excalidraw.com/ -* **dictionaries** - вспомогательная папка для хранения справочной информации. Например, при заполнении системы встал вопрос по её статусе, и чтобы не дублировать каждый раз всю информацию по статусу мы сделали справочник статусов и в системе используем только идентификатор, а если нам нужна полная информация, то получаем её при помощи функции $lookup внутри запроса. +* **business_arch** - в этой папке хранятся все данные, связанные с бизнес-архитектурой. На текущий момент мы не ведем в DocHub бизнес-архитектуру. Возможно, сюда будем класть архитектурные скетчи, когда Рома закончит встраивать в DocHub https://excalidraw.com/ +* **dictionaries** - вспомогательная папка для хранения справочной информации. Например, при заполнении системы встал вопрос по её статусам, и чтобы не дублировать каждый раз всю информацию по статусу мы сделали справочник статусов. В результате в системе мы используем только идентификатор статуса, а если нам нужна полная информация, то получаем её при помощи функции $lookup внутри запроса. * **documentation** - в этой папке хранится общая документация. Мы пытались её размещать в различных папках, но в конце концов приняли решение сделать для неё отдельное место. * **glossary** - в этой папке хранится глоссарий ГК Болото. * **useful_links** - в этой папке хранятся полезные линки на внешние ресурсы. Это может быть Confluence, различные обучающие видео на YouTube и т.д. @@ -48,9 +57,10 @@ * **dictionaries** - в этой папке хранится матамодель описывающая структуру справочников. * **dochub_menu** - в этой папке хранится матамодель описывающая целевую структуру меню. * **datasets** - в этой папке хранятся все общие запросы (datasets). - * **default** - в этой папке хранятся все инструменты для расширения дефолтных метамоделей DocHub. + * **default** - в эту папку была перенесена метамодель DocHub заданная по умолчанию. Также папке хранятся все инструменты для расширения дефолтных метамоделей DocHub. + * **entities** - в этой папке хранится метамодель DocHub заданная по умолчанию. + * **rules** - в этой папке хранится матамодель штатных валидаторов ядра, а также кастомных расширений. В самой папке есть два вида примеров, это валидация через schema и валидация через проверку заполнения конкретных параметров. Пример использования для schema можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/tree/main/src/validator_example). * **jsonata** - в этой папке хранятся общие функции. Пример использования можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/blob/main/src/jsonata_query_examples/jsonata_query_example.md). - * **validators** - здесь мы описываем валидаторы, которые нужно выводить в общее меню проблем. В самой папке есть два вида примеров, это валидация через schema и валидация через проверку заполнения конкретных параметров. Пример использования для schema можно посмотреть [здесь](https://github.com/rpiontik/DocHubExamples/tree/main/src/validator_example). * **standards** - это одна из ключевых папок, где мы храним архитектурные принципы и стандарты, которые обязательны к выполнению всеми командами * **arch_principles** - здесь у нас описаны архитектурные принципы и принципы информационной безопасности * **it_platforms** - здесь мы храним архитектурные стандарты по платформам, а также принятые нотации по кодированию @@ -65,7 +75,7 @@ Такой подход позволяет очень быстро переструктурировать папки репозитория, а также избегать множественного импорта при подключении репозитория как подмодуль. ## Пример целевого меню -Мы не дождались пока Рома сделает кастомный вариант меню, поэтому в web версии переделали его по себя. Ниже наша версия такого меню: +Ниже наша версия такого меню: ![Menu](./images/menu_example.jpg) @@ -75,3 +85,74 @@ ## Использование Скачайте себе пример и "потыкайте" каждый пункт меню, думаю найдете что-нибудь интересное :-). + +# Дополнения к примеру + +## Перенос метамодели DocHub к себе в репозиторий +Так как у нас не получилось в файле .env задать относительный путь к репозиторию, а копировать репозиторий не хотелось, то мы решили эту проблему создав символьную ссылку на наш репозиторий. +1. Перейдите в папку `cd dochub/public` +2. Выполните команду `ln -s ../../<путь к вашему репозиторию> workspace` +3. Проверьте, что после выполнения команды у вас появился каталог `workspace` в папке `public` +![Repo](./images/repo.png) +4. Настройте файл `.env` согласно примера `./repository_structure_example/enterprise_arch/tools/dochub/.env` +5. Запустите веб версию DocHub и проверьте что все работает + +## Контекст в формате c4 model + +Для реализации контекст в формате c4 model было сделано несколько изменений в метамодели DocHub: + +1. В каталог `./repository_structure_example/metamodels/default/entities/contexts/templates/` был добавлен шаблон `context.puml`. По факту это адаптированный файл из репозитория https://github.com/plantuml-stdlib/C4-PlantUML +2. В каталог `./repository_structure_example/metamodels/default/entities/contexts/` был добавлен файл `c4_context.yaml` реализующий логику формирования формирования System Context diagram ([в терминах с4](https://c4model.com/)) +3. В файл `./repository_structure_example/metamodels/default/entities/contexts/base.yaml` был добавлен вариант по умолчанию для отображения контекстов c4 `defaultPresentation: c4_context # plantuml / smartants / c4_context`. + +Пример +![C4 Model](./images/c4_context.png) + + +**Нюансы** + +Если вы используете общественный сервер PlantUML, то вы не сможете отобразить контекст в формате c4, так как схемы получаются большие, а у общественного сервера есть ограничения по объёму запросов. Поэтом для экспериментов я рекомендую поднять свой локальный сервер. Для этого просто скопируйте себе в отдельную папку файл `./repository_structure_example/enterprise_arch/tools/dochub/docker-compose.yml` и выполните команду `docker compose up` и по 9000 порту у вас будет доступен локальный сервер PlantUML. Именно этот сервер указан в примере файла `.env. + + +## Как раскрасить системы в контексте +Иногда хочется раскрашивать системы в контексте в зависимости от каких либо параметров системы. Данный пример показывает как это можно реализовать. + +Для упрощения примера в существующую метамодель системы был добавлен параметр `color`. Реализацию можно посмотреть в файле `./repository_structure_example/application_arch/systems/spact.yaml`. +Далее параметр color был добавлен в файл формирования стандартного контекста в формате plantuml `./repository_structure_example/metamodels/default/entities/contexts/plantuml.yaml` +``` +$result := "$Entity(\"" + & $entity + & "\", \"" + & "[[/architect/components/" & $domain & " " & $component.title & "]]" + & "\", " + & $domain + & ", ,"& ($exists($component.color) ? ("#" & $component.color) : ("")) &")\n"; +``` +Последним действием был изменен шаблон `./repository_structure_example/metamodels/default/entities/contexts/templates/template.puml`. Внутри шаблона был доработана процедура $Entity. +``` +!unquoted procedure $Entity($entity, $ACName, $id, $ACType, $Colored = "") + $join_start() + !if ($entity == component) + component $id $Colored [ + $ACName + ==== + !elseif ($entity == system) + component $id $Colored [ + $ACName + ==== + !elseif ($entity == actor || $entity == person) + actor $id [ + ..===$ACName.. + ] + !else + $entity $id [ + $ACName + ==== + !endif +!endprocedure +``` +После нескольких манипуляций мы получили раскрашенный контекст: +![Цветной контекст](./images/plantuml.png) + +Не забываем, что контексты в новой метамодели можно переключать по правой кнопке мыши: +![Переключение контекста](./images/contexts_switch.png) \ No newline at end of file diff --git a/src/repository_structure_example/application_arch/_root.yaml b/src/repository_structure_example/application_arch/_root.yaml index 2d4c6b1..9c547dc 100644 --- a/src/repository_structure_example/application_arch/_root.yaml +++ b/src/repository_structure_example/application_arch/_root.yaml @@ -2,4 +2,6 @@ imports: - reports/_root.yaml - systems/_root.yaml - users/_root.yaml + - aspects/_root.yaml - docs.yaml + - units.yaml diff --git a/src/repository_structure_example/application_arch/aspects/_root.yaml b/src/repository_structure_example/application_arch/aspects/_root.yaml new file mode 100644 index 0000000..d9d3b89 --- /dev/null +++ b/src/repository_structure_example/application_arch/aspects/_root.yaml @@ -0,0 +1,2 @@ +imports: + - aspects.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/functions/functions.yaml b/src/repository_structure_example/application_arch/aspects/aspects.yaml similarity index 100% rename from src/repository_structure_example/metamodels/default/functions/functions.yaml rename to src/repository_structure_example/application_arch/aspects/aspects.yaml diff --git a/src/repository_structure_example/application_arch/systems/spact.yaml b/src/repository_structure_example/application_arch/systems/spact.yaml index 6cb81c5..a28d317 100644 --- a/src/repository_structure_example/application_arch/systems/spact.yaml +++ b/src/repository_structure_example/application_arch/systems/spact.yaml @@ -23,4 +23,4 @@ components: # Интеграции между системами одного БЮ - id: swamp.frog.1cbit_finance direction: <-- - color: Yellow + color: Yellow diff --git a/src/repository_structure_example/application_arch/systems/srole.yaml b/src/repository_structure_example/application_arch/systems/srole.yaml index 7c07c93..2ef47c7 100644 --- a/src/repository_structure_example/application_arch/systems/srole.yaml +++ b/src/repository_structure_example/application_arch/systems/srole.yaml @@ -21,5 +21,5 @@ components: - Kafka - Nginx links: - - id: swamp.crocodile + - id: swamp.hippo.grafana direction: <-- diff --git a/src/repository_structure_example/business_arch/units.yaml b/src/repository_structure_example/application_arch/units.yaml similarity index 100% rename from src/repository_structure_example/business_arch/units.yaml rename to src/repository_structure_example/application_arch/units.yaml diff --git a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml index b01d225..d3c165f 100644 --- a/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml +++ b/src/repository_structure_example/artefacts/frog_paradise/pal1_landscape.yaml @@ -4,10 +4,10 @@ contexts: # Контексты представления архитектур location: Лягушачий рай # extra-links: true extra-links: false - uml: - $notation: plantuml # sber C4Model plantuml - $autor: Frog - $version: 0.0.1 - $moment: 20.11.2022 + # uml: + # $notation: plantuml # sber C4Model plantuml + # $autor: Frog + # $version: 0.0.1 + # $moment: 20.11.2022 components: - swamp.frog.* \ No newline at end of file diff --git a/src/repository_structure_example/business_arch/_root.yaml b/src/repository_structure_example/business_arch/_root.yaml index 4448ccb..8eac78e 100644 --- a/src/repository_structure_example/business_arch/_root.yaml +++ b/src/repository_structure_example/business_arch/_root.yaml @@ -1,3 +1,2 @@ imports: - - docs.yaml - - units.yaml \ No newline at end of file + - docs.yaml \ No newline at end of file diff --git a/src/repository_structure_example/documentation/examples/example.html b/src/repository_structure_example/documentation/examples/example.html deleted file mode 100644 index 22d70f6..0000000 --- a/src/repository_structure_example/documentation/examples/example.html +++ /dev/null @@ -1,2 +0,0 @@ -

Привет!

-

Это простейший пример HTML-документа, который выводится с использованием плагина DocHub.

diff --git a/src/repository_structure_example/documentation/examples/test.md b/src/repository_structure_example/documentation/examples/test.md deleted file mode 100644 index 36033b3..0000000 --- a/src/repository_structure_example/documentation/examples/test.md +++ /dev/null @@ -1 +0,0 @@ -Привет 2 \ No newline at end of file diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/.env b/src/repository_structure_example/enterprise_arch/tools/dochub/.env new file mode 100644 index 0000000..afe6a81 --- /dev/null +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/.env @@ -0,0 +1,238 @@ +# *********************************************************** +# Режимы сборки и развертывания +# *********************************************************** + +# DocHub может работать в трех режимах: +# (F) frontend - "Толстый" клиент. Вся бизнес-логика находится на стороне фронта. +# (FB) frontend + backend - "Тонкий" клиент. Бизнес-логика в основном находится на стороне backend. +# (P) IDEA Plugin - Плагин для IDEA. Бизнес-логика в основном находится на стороне плагина. + + +# *********************************************************** +# Переменные среды +# *********************************************************** + +# (F / FB) URI в формате DocHub корневого манифеста (обязательно) +# Варианты использования: +# /documentation/root.yaml - (F / FB) Относительная ссылка на файл расположенный в папке @/public/ +# https://dochub.info/documentation/root.yaml - (F / FB) прямая ссылка на внешний http/https ресурс +# gitlab:34:main@root.yaml - Прямая ссылка на на файл в GitLab репозитории. +# (F) Может использоваться только при указании VUE_APP_DOCHUB_PERSONAL_TOKEN или VUE_APP_DOCHUB_GITLAB_URL + VUE_APP_DOCHUB_APP_ID +# (FB) Может использоваться только при указании VUE_APP_DOCHUB_PERSONAL_TOKEN +# Структура ссылки: +# gitlab - протокол GitLab +# 34 - идентификатор репозитория +# main - бранч +# root.yaml - путь к файлу +# +# file://root.yaml - (FB) Прямая ссылка на файл в хранилище VUE_APP_DOCHUB_BACKEND_FILE_STORAGE. +# Если VUE_APP_DOCHUB_BACKEND_FILE_STORAGE не задан, то ./public/* + +VUE_APP_DOCHUB_ROOT_MANIFEST=/workspace/_root.yaml +# VUE_APP_DOCHUB_ROOT_MANIFEST=/documentation/root.yaml +# (F / FB) Идентификатор документа главной страницы (необязательно). По умолчанию dochub_welcome +VUE_APP_DOCHUB_ROOT_DOCUMENT=swamp.welcome + +# (F / FB) URL GitLab (необязательно) +# VUE_APP_DOCHUB_GITLAB_URL=https://foo.space + +# (F / FB) Персональный токен gitlab. Используется для локальной разработки +# VUE_APP_DOCHUB_PERSONAL_TOKEN=9H...FR + +# (F) Идентификатор приложения зарегистрированного в GitLab (обязательно, если есть VUE_APP_DOCHUB_GITLAB_URL и нет VUE_APP_DOCHUB_PERSONAL_TOKEN) +# https://docs.gitlab.com/ee/integration/oauth_provider.html +# VUE_APP_DOCHUB_APP_ID=5f3...f0 + +# (F) Секрет приложения (обязательно, если есть VUE_APP_DOCHUB_GITLAB_URL и нет VUE_APP_DOCHUB_PERSONAL_TOKEN) +# https://docs.gitlab.com/ee/integration/oauth_provider.html +# VUE_APP_DOCHUB_CLIENT_SECRET=1e4...384 + +# (F / FB) Сервер рендеринга PlantUML (По умолчанию www.plantuml.com/plantuml/svg/) +# При сборке через docker-compose по умолчанию localhost:8079/svg/ +# VUE_APP_PLANTUML_SERVER=https://plantuml-stage.samoletgroup.ru/svg/ +VUE_APP_PLANTUML_SERVER=http://localhost:9000/plantuml/svg/ + + +# (P) Если "plugin" сборка осуществляется для использования в IDEA плагине https://github.com/RabotaRu/DocHubIdeaPlugin +# Для production сборки используйте npm run plugin. +# Данный параметр удобен для разработки в режиме плагина, т.к. подключает моки $PAPI. +# VUE_APP_DOCHUB_MODE=plugin + +# (BF) Если "backend" сборка осуществляется для работы с backend +# Данный параметр удобен для разработки в режиме backend. +# VUE_APP_DOCHUB_MODE=backend + +# (F/BF) Определяет тип кэширования при сборке webpack +# memory - по умолчанию. Кэширование ведется в памяти. Оптимизирует только текущую сборку. +# filesystem - Кэширование ведется в файловой системе. Оптимизирует текущую и последующие сборки. Радикальный эффект заметен на последующих сборках. +VUE_APP_DOCHUB_BUILDING_CACHE=filesystem #memory / filesystem + +# (BF) Определяет принцип кэширования ответов к Data Lake через backend +# none - не кэшируется (по умолчанию) +# memory - кеширование в памяти. При перезагрузке буде очищаться. +# +# Иное значение рассматривается как относительный путь к папке кеширования. Папка должна существовать! +# В этом случае результат запроса будет сохраняться в файле. При перезагрузке кэш будет сохраняться. +# Для его очистки необходимо самостоятельно очистить папку с кэшем. +# Пример пути: +VUE_APP_DOCHUB_DATALAKE_CACHE=none + + +# (F / FB) Если "y" подключает в описание документацию DocHub +VUE_APP_DOCHUB_APPEND_DOCHUB_DOCS=n + +# (F / FB) Содержит ссылку на базовую метамодель. По умолчанию - /metamodel/root.yaml +# VUE_APP_DOCHUB_METAMODEL = /metamodel/root.yaml +VUE_APP_DOCHUB_METAMODEL = /workspace/metamodels/default/_root.yaml + +# (F / FB / P) Определяет движок рендеринга (graphviz | smetana | elk). По умолчанию ELK +VUE_APP_DOCHUB_RENDER_CORE=ELK + +# (F / FB) [КЭШ] Включить/отключить кэширование(NONE | HEAD | GET) - По умолчанию NONE +# VUE_APP_DOCHUB_CACHE=NONE + +# (F / FB) [УРОВЕНЬ кэширования] (1 | 2) - По умолчанию 1 +# 1 - [low level] кэш сетевых запросов(requests) +# 2 - [high level] full кэш(уровень 1 + манифест) +# VUE_APP_DOCHUB_CACHE_LEVEL=1 + +# (F / FB) Анализатор запросов JSONata +# Выводит информацию о выполняемых запросах в консоль +VUE_APP_DOCHUB_JSONATA_ANALYZER=y + +# (FB) Режим backend +# Если установлено, считается, что DocHub функционирует в режиме frontend + backend. +# По умолчанию DocHub функционирует в режиме "толстый клиент" (F). Т.е. вся бизнес-логика реализуется в frontend. +# VUE_APP_DOCHUB_BACKEND_URL= http://localhost:3030 + +# (FB) Порт для backend сервера. По умолчанию 3030. +# VUE_APP_DOCHUB_BACKEND_PORT=3030 + +# (FB) Каталог, где размещаются файлы манифестов +# По умолчанию @/public/ +# ВНИМАНИЕ! Размещение файлов в @/public/ будет приводить к их прямой доступности из фронта по URL. +# VUE_APP_DOCHUB_BACKEND_FILE_STORAGE=~/DocHub/storage + +# VUE_APP_DOCHUB_SSO_HOST=https://id-stage.samoletgroup.ru +# VUE_APP_DOCHUB_SSO_CLIENTID=Iq7k6TKn86a9Bij1ngSC7IYEH0VGYGZcQmIN5szj + +# *********************************************************** +# Примеры конфигурирования +# *********************************************************** + +# (F)******** Портал с документацией DocHub ***************** + +# Развертывание в режиме frontend (толстый клиент) +# содержимое файла .env - ПУСТО. + +# Команда сборки: +# npm run build +# Результат: +# - В папке ./dist будет сгенерированы статические файлы, которые нужно подключить к WEB серверу +# - Войдя на портал, пользователь увидит только документацию DocHub. +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием публичного сервера с ограничением размера контента. + +# (F)******** Портал с собственной документацией ***************** + +# Развертывание в режиме frontend (толстый клиент) +# содержимое файла .env: + +# VUE_APP_PLANTUML_SERVER=https://plantuml.local/svg/ +# VUE_APP_DOCHUB_ROOT_MANIFEST=workspace/sberauto/root.yaml + +# Команда сборки: +# npm run build +# Результат: +# - В папке ./dist будет сгенерированы статические файлы, которые нужно подключить к WEB серверу +# - Войдя на портал, пользователь увидит документацию DocHub +# - Войдя на портал, пользователь увидит документацию размещенную по пути ./public/workspace/sberauto/* +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием собственного сервера https://plantuml.local/svg/ (требуется развернуть). + + +# (F)* Портал с собственной документацией и без документации DocHub ** + +# Развертывание в режиме frontend (толстый клиент) +# содержимое файла .env: + +# VUE_APP_DOCHUB_ROOT_MANIFEST=workspace/sberauto/root.yaml +# VUE_APP_DOCHUB_APPEND_DOCHUB_DOCS=n + +# Команда сборки: +# npm run build +# Результат: +# - В папке ./dist будет сгенерированы статические файлы, которые нужно подключить к WEB серверу +# - Войдя на портал, пользователь увидит документацию размещенную по пути ./public/workspace/sberauto/* +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием публичного сервера с ограничением размера контента. + + +# (F)* Портал с собственной документацией из GitLab без авторизации ** + +# Развертывание в режиме frontend (толстый клиент) +# содержимое файла .env: + +# VUE_APP_DOCHUB_GITLAB_URL=https://gitlab.com/ +# VUE_APP_DOCHUB_ROOT_MANIFEST=gitlab:43847396:master@root.yaml +# VUE_APP_DOCHUB_PERSONAL_TOKEN=gl...4g +# VUE_APP_PLANTUML_SERVER=https://plantuml.local/svg/ + +# Команда сборки: +# npm run build +# Результат: +# - В папке ./dist будет сгенерированы статические файлы, которые нужно подключить к WEB серверу +# - Войдя на портал, пользователь увидит документацию размещенную по в репозитории GitLab - gitlab:43847396:master@root.yaml +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием собственного сервера https://plantuml.local/svg/ (требуется развернуть). + + +# (F)* Портал с собственной документацией из GitLab с авторизацией ** + +# Развертывание в режиме frontend (толстый клиент) +# содержимое файла .env: + +# VUE_APP_DOCHUB_GITLAB_URL=https://gitlab.com/ +# VUE_APP_DOCHUB_ROOT_MANIFEST=gitlab:43847396:master@root.yaml +# VUE_APP_DOCHUB_APP_ID=a00...27f +# VUE_APP_DOCHUB_CLIENT_SECRET=5d8...372 + +# Команда сборки: +# npm run build +# Результат: +# - В папке ./dist будет сгенерированы статические файлы, которые нужно подключить к WEB серверу +# - Войдя на портал, пользователь будет перенаправлен на авторизацию GitLab +# - После авторизации пользователь увидит документацию размещенную по в репозитории GitLab - gitlab:43847396:master@root.yaml +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием публичного сервера с ограничением размера контента. + + +# (FB)******** Портал с документацией DocHub и backend ***************** + +# Развертывание в режиме frontend + backend (тонкий клиент) +# содержимое файла .env - ПУСТО + +# Команда сборки: +# npm run backend +# Результат: +# - Будет собран frontend проект в режиме backend +# - Будет запущен nodejs сервер на порту 3030 (http://localhost:3030/), где будет поднят портал и backend сервер +# - Войдя на портал, пользователь увидит только документацию DocHub. +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием публичного сервера с ограничением размера контента. + + +# (FB)* Портал с собственной документацией и backend и без документации DocHub** + +# Развертывание в режиме frontend + backend (тонкий клиент) +# содержимое файла .env: + +# VUE_APP_DOCHUB_ROOT_MANIFEST=workspace/sberauto/root.yaml +# VUE_APP_DOCHUB_APPEND_DOCHUB_DOCS=n + + +# Команда сборки: +# npm run backend +# Результат: +# - Будет собран frontend проект в режиме backend +# - Будет запущен nodejs сервер на порту 3030 (http://localhost:3030/), где будет поднят портал и backend сервер +# - Войдя на портал, пользователь увидит документацию размещенную по пути ./public/workspace/sberauto/* +# - Рендеринг PlantUML диаграмм будет осуществляться с использованием публичного сервера с ограничением размера контента. + + +# Больше информации о переменных среды выполнения +# https://cli.vuejs.org/ru/guide/mode-and-env.html diff --git a/src/repository_structure_example/enterprise_arch/tools/dochub/docker-compose.yml b/src/repository_structure_example/enterprise_arch/tools/dochub/docker-compose.yml new file mode 100644 index 0000000..f7463c1 --- /dev/null +++ b/src/repository_structure_example/enterprise_arch/tools/dochub/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.3' + +services: + plantuml-server: + build: + context: . + dockerfile: Dockerfile.jetty + # image: plantuml/plantuml-server:jetty-local + image: mumg/plantuml-elk:latest + container_name: plantuml-server + ports: + - 9000:8080 + environment: + - BASE_URL=plantuml \ No newline at end of file diff --git a/src/repository_structure_example/images/c4_context.png b/src/repository_structure_example/images/c4_context.png new file mode 100644 index 0000000000000000000000000000000000000000..6d8639e400c90d3f72eadb6b24946bce46d54025 GIT binary patch literal 103424 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 L0;E9nozH&*>BB?A literal 0 HcmV?d00001 diff --git a/src/repository_structure_example/images/contexts_switch.png b/src/repository_structure_example/images/contexts_switch.png new file mode 100644 index 0000000000000000000000000000000000000000..0aab60d939a15994ef2845fa61b5ffbd6f63e273 GIT binary patch literal 47564 zcmdSBc{tVW`!=dU36(iXiOiWPQz0!GQsyxwGDRg*%G^N9kTTDik~wpR$`lC+Wy+9{ zS%wfw>~r@b-s;gs&}-kAYcU54(#9rJV7l2d(%6g>wxSQvr=-rNi5ahLpci7wSf z;wAGj+Zq29-k$tq2J$VhGBPgj6pi#Dr%Lh(7ZjgkqaVNM8s(z5v~$(t{P~Y!?&GsF z11{q+?sFeRSQ}_@6=We4=Scq{lH(5_!~gfUrcHOZ?Tp^~+HF}a_d|(Y_bS)s#-BWY z|6OlInj-~2U)-$or>DyDUKp;o*{tO{MNdYikes2JeDChvyHj)ph9;Urd^*p+kBxQZ zUT}REwBPMueE>l(OIPcHjisfpm9hXnF@;x3s;9nQDKXT{%uG3$mDZmSCutw4veZi- z6BoC+*eY-HU3m9>3No^JJxgor$87RXiSHh>4VF2KDCTX>d?`whbrsTi9)yp)fB$V; z@@cjcx0CeGBx^D=Gus5KWpF)KethT=v*^7!a$JjgL-0Y31er8rh3U3;xOn+n?rj%v z&x0J1miWDd_;`~vwTQH{QA}=s|E$c-b?31)7ruD$!ey#M+GQ%ds>)kMMdjMHYs*ue zdMhiAKdDNqm$`|=B zRyg0^vHSS-Ue2gy-%zob9FuZK!uKCPS_%ak9QNsLY@TMJcaXecqmswGw?IOYfk2QkCC^(hu{O!9JD(ka5y>cYq?*SV>pmf!Xo?RQBr zX4)DfZvAWK%iEO~xSZ*#l=AXB!?Jr1olrSjaXyr&`04X! zJ-b&<<9leOrKK5{9PuZPG7!u%_LKs%gYQKf+!%q9~?C0eYc|OJkz6P zxKbau&qKhbxA^L*WpV1at(;QM6Po(^OtgFvYL&fHo!Q+PTBj2qb^PC#8)s#0U3$4@ zD@X6^JBwJ2B_l3z@uHuyzkdCC`SK-?##7Fea;Ndwn3x62XWEmo$Lt6kTAG@gpP!#o z4q_e}9uAF+w6eEvtzxAm-@cu60hzkJeSO}We^+EKEvyWBmlhYRtEygYOR9Mva^%4A zYs?1^3Pz}5D>KIXI;{UK8JW$eXQ~M05vdy;8CrMl++h=Fb?C2}JK_1eqobo3f#tq3 zyS%WVCD@f`S~W3Yvv==fS=ZU^+qV-5DFs&=E|ojhfBpJY_~vw1?sRuvUtgd7mqMim zC1(E&{aj;%7bg3~EZ+B(+Plv5goK8E%DqrZwNI$ne(=&}=~~SXK3nGCk@+9nvD`~c z_=C)R3;sC#f6TYlrKj(4`)JVb+vocC_i|2-fCN&g`4*2?_wL`{Curti#pxGWwLCrPE_AVi;oO#gZ&FMoQfO$j1_e=5QBf7|AXD&vK+kZ>6i4%1ti&m9 zWMBcZBuV?PhQ`Jz2T5;2TG;~9AOF7H7{Bz!t)4-FqbALjlC774j4WV_o!#`Ow6j&E z+fEDM#biGApW~&So}W($xV8F8gosyiDWi4>SFH~t1S~8ptgL?hT^S^8^&;iTbdAI= z)`*)oZ)zUBg#-ODK{kp{=UsJmbxn=gL|ZbBw5DK^i}RJ?I)663U2deyp)|d8=~A|S zu8?WvwHr6SzHwjq@ZrOihTw~p9&7I&9Y8SY2;Q0ddXl5CpkPRN)N5mXEpVT3R_9Ca z%?+==zY7GWr>2b4&nM`lpI`g)=i!3~b?@Hk=Uqw=7Qgb2nxo>@4`XNN;(2Qiq={!AU7;%#Ie=Y&lwJNljc>xY{nj;_oN%CobdDw)UeT0OGb93{MXHd4T- zR#;VCT~~1JvxYovNL19m&2cOU@%`7Ymv|3{HR5&31SCFzW=GbjkPxkx*Niw=L@xXE zo}r96eSJ+?Pn&R=2B93V<-mbB<2h`EoZFI3>(i6F4;)ulQK4yKmAw^2oWhYA8+#{= zBSKMCDtG7SGu!K`s;UHCT=u<7LPA2bYm4IqiEBL~erCk?wY9Z{K)x*7PR?OV8vhK(ZiZKRxmuP9deLjxtZSm(ldGcb(OB`C7kgzbF zQ^E}fX(h)?vYdIt)&9kUT#{Y)3%mnQ3R3nis@&XM{|V%=TDpzP7mu;gXjxf}p%D5{ zyqc>#n&DN+pIeftpLQWx_Z}%r&To0R=iu$Y zmKep4s-EM>5u?as$BuQ+tgP~PEPLD zty_c+92~RcvGCKuBZ$ExbpFKm0|TC`^F!;Cfyk!-4P8!6L&|#h_#Wc2e>c5ygS^fXah?h27gu>M-xz8+e>?Jd9i9I$w))EJJV?#;i6G|B+jm&&PLl{9^)} zfR&k93A^29#GIoO|_@V|=OhSy@=*TzX@i*v)S& za+5q7utn+fz$@3~FCn=m<)I@|o#KqgI&+M|@^oDFu%qLFs9)2(w^rxPf=8a8i+jHO z<;)`%2iBDu9U5$#@%BE}H%M5T&I$6~ms&$n20o|fKV|fL_|Q9FJr7x8c}kD>fT{E4 z8(Su*oN#W{PQ?3T_5SBb@&Av%ReHJ|P&KLV{?5H#D03Yhx_)MDEF{c2f6jgFr=^O@ z`wubVN*qi1TS@1G=G@a0MgmN!2a_yL|0JQ?IuaHqWw`$Z-}>a8xrb!Sq0Jvbg2al+ z3OY7ws;cZM&z^A>7zTBbK8&~Ve4^Z&8v_+@AMQOGp;qjtzhlP^eSQ7&=g-?v;yQdn zuhrZm$Hv=jZv5TLCO0!YY<~H&&+&GKefuOmR_BpgQ%Xr`JE_k2<+U$)RU0g*5$PIi z#eRDtBO?hGSFQl8a2K3MtY>gVV9aAh4*VX7__ zl98!E(zMz6ZxwL4nc8WTy<12h;X@>vF-vUyAIqXDm`U;s+~FG zH>-!b*CY~@nmW?(@^M#z4^BPPa0989qXruq8SQ6bA(5FszkV3YE8jI{?FXvIy`1~^ zb)ocz=fbd3?m6}l6;hcM_E?^ZAaD#tWk<-n|GI|bj_bT~`SQ1K-$a}z+IH?eMA7Qx#1#lGIT;tB3o-##W%F_?`bgpwJl>*#)2aS~NqUX*R$bPwwVe{1Etbnr6&p2&COLI1JU!GH>q%L ztgCz0)@KbSM{q_+01XVaBIp>q#vQTmOoGPmEea9to!JKVC`1Uns78xa<;VI*jndB_ z#ICw|7f+xR#~;5QOW@cqd4rYqdVg8Lxwzx|giKavU(M-uGX~bHrHS$K+5nFd>IQwn zO*4$I;dB6CT`}tF>?}E-AcM;Pb7f`ku3i3*9_3zoBSWi~p?TN-?vp1^aQ1fYd`O}I zTDit053E3Uh=GAlj*hWlPsyhruK)dutnG!(6h7utyJcd0Jkfi$Ba^;2xh}t;;7gJ9 z-d;0&Sgd@|nKNgCgM-DbKdPTSJ0dlUU_nu!^i9!9Jwqrqucr(i0oOP~c=ztzdgZwc zkrO98=D*ccR#vVjVr>Z$*z8VkHG(d996jeB;*HM*0##En2xCcWZ{(bPWqA9Xzt4aMEq*TcRhT)>}ElWx9*8n6kkE zRCZ|PQr5BQ&vLKvcav3n-PV4ljahRt1s*kc(O>1g`QX9!&RWC?R|+U91=~(qqYD?B zEyPNGu*x`R^isX?{L|InZ?e2ds&TX~C^ETxz$A8;FM~tx^fCice)Qt*b zU6rrePQvyyw0bX0zO=PHAsR+{;%J)x`?9gIF)@j^8Uk8Hs5WoqfiQ39 zz}moe-Tls;1#zwe5;jkwqE@u*?+n%Yg8qV?6FSPBti8;@^6^?197&|5tG|8-o0R2a zyRt51LY{4Bj|q*ToQ6kBk@) z7&!9TXAk(Kk&%&y^42OD`AVAo`x^^$RF*d1&VovN{`o~qOFKC^N$3BdDtxsiPEyY< zFfh=4V_ljSm@ufyIoj9Px3qKxnPsi@JR6Aj&`ySMHOpJKjwgx}Cl*E;Y!5P%@+PnP zsZ*zFnTt$>HbkziqG_sU`9 z9nO6=65T1)Hpw&U#4N@fj%MmoH!9j$b!^bxV*IVWrO?|tAY zO}p@h91G@FX&Gr5C16yDdkPO{=%mHLt8A0sPO9XBvM<@ZR?WT?S`wCKzKE9?J_!vi zG<&;4QnD2K1TLD<+2j>2uCA^Qv5Tm5LZYPSRtFuaNWA6XQ111&VybBU<0VeIgK{2* z%PgI>mq}7Ua$#3*aSLPN=k4biG?pG|xd z1u|o$J0?EM`KJ5_M|=NXnnd`oay@_k+@pw^StJRhCChhNx`puG@NAUOs7`{huC}{N zrY;K;6A&F|hzioJ*V^d5&aHU!+VK>PKHCytuRDuls%f7PWgWQ}Bn1U?u|B3t&5y;Q z*>yBp6e5TgAFqAAs^|T8IZ8Xzq{!+~SDYG-woUDWU0PaN0PUvvm$%5T&rDDM(K^sp zXnA%+rTx>VPtE_nkZ7<@>M>#-Tl~z7OKg=0?Ck98Uv}dDksXhkR<5J)t%`YXtfh-w z&iSHuc!px2zn{n}BJw2h&~4N>uPhIMNV(i)=TZ%J8RrQaHi`$WM6+nrrbT!2%7Ivk ztD#SyesqWz`JHqc*?X;=oYME6=^O1dwV0R~Zf@>%&Ru#Ckg%~QoyI;OWn3t+O*8WT ze}t7pQz#rvtgPyUgR-|e7EeBvbq)5uj}5eGPw{O_WRGfY2LzrchcxHU_RQMo7clR- zlqU(=mfkpc;6S$V&mVoI+jdZY8>h1cOoNKGw757uSXHH%{LOV+?0t%gW7m5-nwyVG zNEBmdfWaHzzn8i_pQOU6{Ugk@(4rA|d=7eEhE^)V8DUDNg^K8FwAHP*%pvvFt3UWT zDel48P;hCq!nn`*QSbkGRWS3Fr(8p=FBQnX#)boGiIo*Mt+bR>#;-a7ld^eaF6@xt zg_q8^ZUqMfsHv$T6-CHd-(x;-0Q%Zy1Dkiey!QrR40!Rqd-tGzjkm;gKR!UpCKIhCd6#)VU-fG|9S$jO{n!D&|}C=(3<=M0<>R^ zrEWNvwvNC5pYXK#O4TrK>yPmZOG~dI2H@tTgBKSTq}`S>p0ioo$cg~l zfKuJvrAcpYm);myZDs^&(X+dYmnkVLyWhUuU+%`h+{4wO08(?_|^FNUX zYFb-0ei)xN-pwl2*3i(<*r>Ylf{UMjYJF{SalEzr_qD#4*Gac))0PAVGZ4Ya$*CSI zk(snretrHl(1*)l9y@*=B9N{1R9tj4?!>+)|I{Yc0kB-}`T)ky=^9lIBYwf=SVx>C zM0lIlyZ-(YKurK;9xMJ`=L1pHv)<%oWMr(at|of@p1dE|3m7PRk-SbPwiRIqk5qVgJv|XOijPUDJO|B8$qO0Zqs)7pk+!*O6}XXzILat z{+yq0OShC`+CfY9E$(@1runv=dpJ{&Y}1}SW6*sL=vr~~duw|4-+F$$gq!2Vhfo^FtiWEfCH<+y}=QHCgKF*m_tidE<99) z6Xjo2Ozbyh-ZBzJak0eu;wchWyt49qxn@hB;MdB`>i0@qGE5e{Wi|Sr^4Y(t`)??W zQYwYk1r-QM+WyuMlyO~C%p)Oj1e^i-F=!Z2zx_5y3sxMyJyqtVliQ**Rn!@7o1oC| zB|m>mlLwBO1jucC&T{d+;G(bJg$9P((nNvl+$9O;5Z`igvibJs%X6b^zuQ&zBvf-f ziB9(Yu=v_3L^g6$zw8(k6fe*_pj3q;N0`VI8Xcs?Og!Y1w^=6hspV%52jy1}px)>6 zQ&UjbbicTWAZ>q%c9UkxOn>2>;-BPz-_!g}U#s5c<=ay2-ODE9%xhdS@wR^}*{W0~ z-)5v_HLX(nvdKrU-hfEDcM6t!E<3sX8E>4}kPqSy&*lFzo&~m9U3!);DvY5;lW_8b z&PR=pFKjtCCKwkZ5(IbuF+7m@Y&)bZtK@5XsO|o{#-4K^7^)4%ukpuRwf|jUr&Jkf z$>*T3m(&I0s!bDE`ywFi5FVY{|MSGkqtFAFuUkAEIz*BVR(B}9j+EXXn9btf7amF1 zUTJV=KP6MaKXJ8EiNi1F8AqI}bhSy4!aqf1Fl)#2&~L>a^rB%LIx;M(pBMysx08go zO05Ke-Lgh3avyYNHJblCfH3v%vm4gb*Cs!B_(eZ6sej%PPfaeHk>j9rTGV*QzgJ1O z+grGtd)66Lhx_uknae&Af0Kgr!a*T%Q606PgO-$!L`G=-{jSlYmb9U~#n#tmp_Xbq z^s3wylq4beTU=*NZPI<;EszaJGRb;XkHA`Nx-{FXt~Vzv1GRV9oFv^SuXGl(|2_k; zKMxpB66J<=oc-4uREkX%_39b=Z1#d5D$4(;)%yrV$0)Ks4D?DWuN=@={4A&Gv5WHH zeQt;$Gd*Nv#oU6=wkrrI93~^XEzEV86`I6WTtMxX!%XWMpUD*OKh0fgJl(DtN+zgz$&{-T`(N4|U1DNry1^Gmzs8$8cXJ1Ulj8-$ zTT4gv9sB#WSRROTpRLbp5^1V+i}Nl>vv=}~r0zQtt`n)nRAVoa8cNsUMoT$$Xf<*t z!;Sv3kofoy@{o@wh_pxM?e`Zb45}x|(oBT0ll{2ASJ&y`(wgZ#J+(mD_${X;2S^f_DW6eu$(mIoFvc9<~*}58aE$LPp@UC_0h0>I>jlY#U{|mc1^)m zYSk+2l%XLrZD*F=G{kt2oeHP%U?fxncb7D6KBlPevYJuxhDoyY3`@*I->y6sXct>m z@F)Ix?K!P;-Z4^~J>^$Q={BX9^+V^?OeD3ApZ##<{3rGB?B8_L5ll5XceZio1@gL| zv@`MxpXO>^OE*k8opXoN?srfaGmSw)$Zq;72Mde!BGvdBf|B&c0(;sQ{L?yI{4#~r zt5NCW?W#}f>^yp6K_?sC0}3Cc-%e$G`B-XFb0JPGsMgeIijSwjGW;~b-uSiY%od)3 zGy#`5@ekY~$5OBHG)*39lHe`#XEx99nckNGA~Krr$}58B#u+BQC5h2 zvDI_2E?hmBFy+WcOZ(>Vf?ArM(V43ayuQ|(V*>ZXt*of~M0tzH|H})Y6u!LInxZq6S8P|>E`hMoR$mczwVWyRImnIY1?zf4Ogsyo|Y?y>yF;S_K?bn31`G=%<0rz-ljR88%>aKh}) zSkH7BKW2qo%Qm?QL0&>u5#<}kJz^2pjRd?#xtrWKy91^Ln!LR2)x_&I&z*XI#Bbz% zsFvIaXI|Of&BA3S%b(Wfqx5ebEzVJLyp4)ix$b(F;1ct?@EIoum0!t`wIiWfOm>`2 z_~!_Rw3bkVA?;a$Q9SQs=G1!)v}N~XFRv1-A8s4_`=&D3V*b(X#jzGNp!_q?mm^r1 znYBMt4Fj(Qul6iAtafs6&}!Mv+ECxnKpz6F6qt;d0cyt)GO;|}4H606fk>{DmaU-7 zaF}DGT4h>?NS@d)xoxm_=kGP0Jh(S#A5kt;Ptvx05WSc|OFN@M{6z#afKYrED(~gPysDK1#Ffp9pbHiSx z_?*!chh6B}#@bf_pYqcy&Cd5Z^@d`or`T%1XwYrfu=tW+(># z#9T279tl_DicR74e4aRe*OAq8Ox4SU@9~gSwS* z24>R3Qo&{&rjq_)LVAXUgi9#`t`09A*pz>oV&t(J9IV}ZSEb9;L|h1oYUFHp4a$k) z{rqHTPkx%Y(dpn0ea;fbV^KM}#`T4Mma5WcL(NV#+2yn!>WU|Z_q%(t2Ys}t7MVLI z&EQDZyl|h2I&{lpwo`{wpzmY}L4^h11oQio(Mw3#3^lp*)%&kDwh1~0+rDSZpv<+Tgt9IFGzoZ$BH$V3= z|J_U7taRSy2e@l#|5T&TuFC72uPIk%H*NFWABuk~^-ALX98|``d8fsnsIuUt+R_=O z`hl(AmC3SPWNoeP#7G_CnXYNb$Yx$4*VNe?PTCx{0!=RICLEMJ^j_uh?={o-JG1wT zvB)~k4qV%I<_|fgRIti3nVjmYM~yyl@$g`m;)rP2rd04eJ#oiTU0vPKu&3eOJ;%`v z*|Ve2z8@Ws#Ozf7$I$2BVpgT5p1aG?7pW%Y>W5nvjntg zAk&%M+|~X#-)-4eLQ%DLFVXAXk%dyKq3LJW&6HC72t^W+tz5C$x$C!67WlnoXfAf? z3+P_)-=?+x=j^?bT;`01P^Y=g@+l`4B_W;uir53+6X_kE=vM3|9`R$3lv|D5{-p-?3%O7RZmQmkS^reIFkOq%k)91jU}~Y$P?C+zGV&4d0s{ za)1^glZBLh2?`E0tfVih`F9qr*w_-$?qDdsar<_euP?cI&1Pk`!m5s8ZD9@##p+IG zSu!8<`Oh;mO^$UUoE+wxaTn$XlZ2Jyd;1CJ`y}3lreie{O`tg03U#C_9<*ET z_TkT;+qvn7+V;h%YfcpSjFf?T4~HoUtoAl4OegGFRR^Ia;}zN?V?0k!#Yyc?q!s04 za@(VOBdXda?vkd&PG+kQu!p^)_9!Hho2Fh-m@dltyM0}!?5R9HVDRGE)VxneHJ?1Z z(dBz#2r3J-tcMy*1>cS;ZSVZLwR2%>Nf_;X$To$7{FedcM0U{-bRvImF4BHICw2Kj zAd6@DB)M<%moIDR46ItvQdQc@X`bC?ySSScjnOh2Z8d%?74@J8?6jxN&Fg}UrB>}( zPc4lKyBhL#%t}xwg=K7iz4PE(N-6p+WC47UM}q%sx^vuoHu7*!E;*6>ciQ$6Lm!B$ zNj>Y~v;}j6BC5-LWOer*Q*F1k=CnO|atm1!Z^PDXct27 zfWe50DiFFHS{vw(hy?u#lwF4phjj2e74qoq>2_K-xW7^V+xU4P)YHBDRE{nreF zS>i_jZ8QrZFr)t^2fY=_7Y@a5D2*hWLbAWM3$Hp*GWm%HPA^IrW((`DwrxmEP)?A*h8)4?GVy*P9~z{V`ZVrB&!oBttA zGza?!2HepsqoFy>N^G(a19$WBAxoZzH2@Me`iE()Q&7)cKj(Y?xn(whz7o8XWv^b9 zUi;$s^LwN14-t57@S>r&53_~hWnEvy#a+N%J>dEe<ZIowvh=N%RmbwJ|@wx0DwXwixC-jy(B8W*7WjE#*k zIUm^pLmfm;z^7YN9jb=W@$nvOKS#-_m}s=HJ?OhKGBU!e`We?wPfu_CAzG`%wiB5j zf+OA_7-_BOJ65|jUOIJi3S?%z(fJ?}S$EzbJb(W0dwQZx9ad%s;I)E5sTx%RoV6QX z8QYEwI7eiqq$nc#zI+)#;|TgM-iRj!YzJM>`Nt$}U2~oD=8nwxvOL~wxiITy-W^1- z?9i<(j#C|c?DRBRaDv^unQv7v-kL}sGIc{+&zF*s$b0OVUT$`EfB$RHgMSTB0|SGB z5sF>Ac8Q9L!uj@Veja-I8Ehvy8h?NNxX==c9yVALE~wjk8P|Bh&@f!+Vl0t*_ijM} z(igs2>qQy3c<~}cze{MUDl0b(di{As#0sSud&9bNR6mCqJ-5v}XfAHsb|1PQN#J2P z%NPHEXe+W-OXun!6g%1&#xu1$A~eM)Cy9RdZ4c!_?M#M9^sEsB00V*b=4slQy8IAI zy->T*oo9dnHcrw%0|~u0G~|f#`MBffis1H5QcQ+mKAxWYVhjZs8Z27ajFI8|dQFXu zlQ^MS^@fql^Yc<(>r$KJ=)!?8E!H$~4R%EB+D1vHdCe=2_)~>I7~{9*Gk!uRc;(8K z)a5qQa}b${ywcKZILh4~zW_W5&w5i2(b9&*#FUd1WA|Sfktc4?f76iv`^@ERVPPRr z#c5qpFyUExIyC(|Xu;F@f4^eTy--0aeh_jW_)_}Te)yoCi{BVWw@_PKyD6N{31trj z{@{t*nnksH4$6p7U$M2dwYL|pk)$R|vJpEROtj!HqWF4qVmJ-jFK&Jr>g0MNK!{$P zM8h96>VqCVg6j7*+d#O*S|eVHoz9=-_%&Vu>6_ma@Y`PN3(6(xHbvEYkuR%}1C zz=(F_$Z7FR3bZK!%Wx1ufqqW3r#`-TF&o5zwtar+?a7lT^`Zg^Q>Kq$`~nU|Kcf)m z0g(<;(al1&%DIHs;vhj~BxBGT-A_(?@^aeC;Z2!cLSBpGhiSAxs(#GO;9SIphHB$V z(2X+4HP$PNMpF;u^zYxlD?j?uWmcXN!PW3ScljvZeQPCNSWyJYG z&lU_VR(NP;Mx78PbP>RqsmkK5-)`60{xA7v_cg~h{{A5uyTSDk3VLXqp^fwx=3^8& zDyp!suzt{-%Nec(ZqO5-bcVfe#>czSxaw6p+M9%N&io14Sg=j{{&FCg`96A}h+q;yf9|nG1ukB?Pu{rPt^<_SF61j=xTKf;j()IK0Ff|f)fdRj;WnqX!#9-md4$XIZn@uU z%RrtQ(IPe7-7DE8mtZdHg&(~cO^g%B3!Iz;mlPkX#*{-$yLT@xF1j$l`SG#k@BIFw z=!&9BcbNo#z4zGZTlHOV)G6L?Gc!5M)W*D(hLO7|DP=rXZz?L*qK=K&d2kzPEeeWm#Q(H|6YA}+in&K-n!T!Zza_at&Vm_XvMXER1&P_Qn2rMDLY%K`|0nKcTWqnb%RAKK&NpK%ClXrbiV18YHIa zjO*xsArz35Uglf8f5MK0c|gp9*!2?)+^PQZqTrDYT*cn~VuT-JJMKX%3T751wa8;N zN^IKAF))H-ESxb0OgbqvzgfQcb5z=C3_e~CCWfsD>y}G9W7J93MTzr`;#WUC!!dx% zrv?u5ckj4`h39av*9D&8JziB*c(pt#N3#l{|1S7ocKPDhBJ0O0oaRQiaN-kbsGK{f zH;VZ%CgT^0Hilkg?9%4N-Jw*zIhwP_CZ z?uhkW&eU8nX3JR&GuZqPt0zk>an_t>NXLx|7)Z`xm{5`q)yL%G#r6xDwI+Z-*enoE z=spG~$E9Rs++botZTcf)^kh@*Y}zOIYV9%G0QT|2ITHqZ+yw0St0cS3)03~9Cqu{f zf2d&86<^PrzfVEu$4Py}iU=Am%IPI(;d5&HD&0-WnW)g{XhVPi#-sWKWo6eNy`3mI zFz2GX9pAfLj+}-@pS`v9ICcyj&K%RKOF~iEOz7yG(c2BX^FB&6gDkOOxUg&(z+hl` zFl|DzQ|gr%72D979+r|S!`*>P!lsA?HpQ=>KOe05Bgd%dfgXUv(Cn<7*ZS>}3l8Wd z+I7M3a5cFaY9Cx-Bk*N|O2HD=5+goNvJn@-hc!H2PNAr%n5Cb~Z&DRD=!YXsNxpUK zR{TYlOed*rU$y@8EDU3IT{)9eQ!-!^xSNSp6XXQ0YgwtOSAbubgiJE7+zhFDmzAEL z-s_^$`||oL$I&MAHvKiyF#7`wAG(VRi;H%ksCWsQtdCix6$!6y{jkMbBRr93@*>%X zM@AYR9TgUzD_&CoPM-%qhl>jz)C^W-NK9TZ=j!3)pT zV?bC?R1;6!5tESUXlk0Gk6l7AVTZLY!q(9RikXv>gB~~33qg3p4FHSq)Pgtu1jq(pnxLSd zzY2eZBWpvVj)Yn50~AD($c^nqg}+qg^=8U*I6`d{R}>T!By(#fIx_Mlq64!In3Bu7 zk$d;f#E}FPc#v%Xkl4`M+uPbY zhugz_kL~+uP!B`>5qUq);ksQzTvEZP5l=HSuYx^q zP(&ilZIO;67DNg5Ul4+}xB)By)T|AV(=2h~M z#*v1{zz(Yo$e$O$Mgh@*;~VB|P*4HPyg^jtnEESRvcdY~|1Rku&9AD>%*=SFwr#cK8y^2d6Gp>#;l8fK{UL;B>+VLp6~_43L(yMx=S$G=r)F04D&D7 zAQ0E&iwX;^KF057WJHK%03&5|va+!7GX-wjy7htVOV5=7l2`cb`Sbd3?opD3i>UlH zjZ95Vy}8D}AsI!)1dKWX%dD1Gh}#s_n_nr={PECxb)KD8FH>8o=qJo;ND)XO!L8c~ z7Phun0)4NQO!v3QhhPGyydJh_RNX|EL57D?<~@_!DH4H*A+8>x$nCp@h88@z7{8>P z`&eIJ48o>}z0u`ih6 z0PJ7J9Al#QMhuiP+zgfihx`@B9b0sKPMkRLpsspOmedns?ONP&mYE*#Y!e4!RlQo{ z;I}INTIFhkPNc=XhZ0bb$75e<@)rYGffof@?RS{IJeSPjo zHSf2RO$hpEq`#CcEiFxVE#0heojXK(_2ht-%SH{NAU7wc_*VsKJhXOJIq8;<;S=mD z5G%Y0>p04k^_zoPdds4;qhJr;zk}n!oN`d=W)jY;qT~)Xs^T%mp@AQGBV3cr*MkiX zS@bNcN3UNkK~cd!yAPeXjS1KFPy`3bW`uXHZGYLDJ%4c__Q5{12Cl2Ce*g>LLCvhG zX(lHLi=qW+6%u3stk!_35K4mC?i=+)+Ciyc126r4vH0T&Bq(E=Y z1sEOcd>l#D2vs*ua`U(Km91pvr&Lpa9~D@qSvm;!6Q0Jue~o zAk8bid8jtpvx1uQ`+GFfJnUy!o$OnT^RKkQo^mB6k7gV=y{PnOX)?7n$*-Wm0p%M( zrbIx-kj7X8DE`{1E$rGE=gxLi5b{xp;5zGTX}OLBg`5A6O*d9Fq>3kC&$`81gmA{! z3op@ZYj1CdV>CJ%uA@@wqcZOC#UVJ{_0nGd6u~Q zE(j-U@;X;N2;SGy+>B$gfpjrbd z2cwGf&E&4drFk)=X*cd=viT9ci<-K_ZBFVWFwS)#=RhVwHHd9NK}*Za2f^DQL1gP? z`+4`mK{C&$+serhoWHis$m^jSkV|JmfI4?r^JuM0`PyQ`gO6s%)_d}MRqvy;mcicx zTbwhi<hC-CNJum}1 zrOSG6tONI6|N7d<>E{;tV{`Q8`)S|YUf_U%5RyZd^1QM9)&V5$h}hWPq*M3d^*vQ2 zIgEUPlXH}}V$$C%8VkF~BXmAmgk464rGPAH``hrh!bhPq$;n+>4U@l~Aa7KK84B}) z+en33VI!-TEX^Wdm-aF*?`k@D@ZeFsEa#u!Rg+hwBqd!zsY!O%t^-~fvzn8CMK|E- zoFAv1DC;;bPtmgX5hgfhkOcX7^X3bZP{{z@H3vHD-@PM&=k>LP-=x+z5&(F*MB-FO zW|%s6U%{1!w%ijv1tZYMm+}(5m5m?v)SEjvI{pM5HOMhczBQEh{rh(~zyj*c4_Yfu zX<}%_&rDbT9xmnKW4VgFTOaq?8j&2x$c6itkc>>`!-}_H>?zTRJnI-1?A59JJHS+s3T(P^VlX%g z+dZ_zv@|YnH-6y0>U2y^V|x5EDP5w@`xAyoM~}JAnwTY?M8gly6ZpNh_McHduy9Ov zJ%Y|<77giR@lP#@%e`^a%k4#820Xcq%7M0GKTazS^y+ESfGY`)vAtIH^Y?E=UPD?f z1~7wI3iI%6Uuo;Rch6DkQF^Z!=@Us)X-8>AsjpaCj!sOJU9PzYQira?35-G_wq@L? zh$I!46kSMq3JMChZoPc;=o}WB`+U63l`D%{*GbdwFyZEEMDXcw|HqpOQG(>JhBrSj zuo;vTfHN<(7PJR4$=x|>%-~$@elY-KtS9_1si~%B2-^&}85A76XYbzDrY3*{MA}?$ z$uy$Yr9WfoK19uzpak>t^VsHY3}%7r!LAyA;?7S~@4qFO)GV2nr#utPx}S}WZU6o| zfYC7BqA!Ar%e5AXjg9>^)oF)70lti-mv`x&867PFXon#i*5|y7uQ1H8aOVd4TTqnV zR@wQ`QLhkIwPy#bNCpA)I@V#DPR##}xEh>*Gs$(;18_9X2$@2#Za z$^SNk{q6qKW}~4>o5aSpw)xNJM}715>g=fs$48z7qzf}xK}A|vX#X8Omj#nCkwOOPYE~E+`V@A@5cK{n(d1srd4rM_t4%OF6 z&kM_4^Z$H%Y(Gq94Lr+t-f3#TW683YdFYr0VW{i1Wi!)8;l)*T9c^vhx$FARzEzMk zNiEZcCJsNH)sRj3=zyGPi#je1R^}qqU z8#gX>9N(*R8oeo!nukT;;^wAnhkFf}DvyPV*^h6@a6W>u=HzfUEelJN2z7%4pabwf zzik}6g}BKMXxeKRGDv#*&(UU^Cv=C!#hnm69(liDdk*}@^sj-S^ySMJM6D)&1Oby8 z=$<`3&bI}Yb#52|8{j1PJ@}jTpQjSD>0Wa5G$D$~m^d4m%idGp3-0+slXta2EJon) z;eR9ea@UYrYd7u_si~>k%ryIbA3jWf{ya;l97Y3mb!up-WFBORyEbGOpO{&Q8EI;^ zNy@rcSs0eyNa&@@)P*hYJZbV}^`-!gKLMaiQbHofuzve*w2h-b0na#Gt{%bbd^nokzP$qJhfwS^{t@YfKcWeoF~uk#6Gw(71Xw6B(G*Rb zEQ36ue$HX2eyuDKfQBm?ix&@JYtMANP` zm^zDXF(VD#|K}+O|LuVX3Xh5k zK(J?qH;+>KDhfr(N=pwN*hNM1okwB3S7U^F6i)yUk+;ojHf zC$R+5Fl2nZ08=1*mfqIeD-(U@j%%WW{DWP={*=~ns}IaakN#8KYp#>*(ErP(m;c)+ zv`^kS3^My{<^qwRCSqKpr?)rdm;@gKB~qrJ8G(a#aS!dTy{iqio5}e%S-bHZ1!3X5 z;ldh$W~djk+}vYMmAi=KY~xTt&^qkUs{Zl?vo6fL^?rIC#55_wgJfiV`jlj~fiO8V z)Q&yGq)2Y|v**vjLfBwd5F9_1ZR2S#%~1DedP}k~0Si!gjB0cK?OtRWfaDzg+}QjU90++- z9;+ez4bn3oLjZ95`l6vf~RcTtZjs6GB{$KI7fiP7fc$d0&hkU`#5F zxqd6h@5%H;#Q61V*J!9J`JqyS?v(h7#moQ@*u;nO;Q0#}zQP+F6BUJK?f>SDCp_mS zCZxv<{9RuMn&^PG)=~m#DlsA9uP^_m1n4D3w_17%u23Ci!VvIbh{MVP-!PA&PoaR; z$9KsiVq%V108`%m6Xj;)w1M@g4bXAN@88P;K)uPj5>a&*PeCv*Fce@q`(NqQk|a$|89q$Z%edq`csf9I+2+iR4_L0N3E|>_gGO5RQ?t zF{OJM&M{DuJ(_s%j#)N^YyCOq9~9*5?2IKBSZb30f+eLYwZzu4N2tjgs^{K+B;OGn z6a;6%ILdZao$~W>KgMU^+W=1JrFP7)oWRIXQIS(}DW^jFUA~8=YG}d%k^%?Q9y~Zu zVrRg^lRj7Z2XE=1@pPTd-SyDk&foH6>-)uz*!hR1ScFe-Ts)hm$Tg%bq!ju~fL*nZ zi&B-ZG4@$TaR{&Cv(ap&KpU->zZm=pU83Su@=v=@<}JpVX%4twkeic#{W1Q|n6&lN z{fizSixT)93o%=T8N1A^tgMU-jGv;}7}{vz__td0HXeTyu5uG`2oRc?P5J@^ga>@z zTsIh~GZ(Tsj})pKsLu1}W7s@1;medMet9!7#I*!5Nuj_QgkDk@+ZVUzO!zG}lI>*^QIEdb_A^AqwUMSf}^m{>| zY_q&KHDzmRY6?(_S}+BLzOQev-09(MQj>ss#}&wYC_DK@MLK$VogE$E?4$?7&?uTZ znQuRJss^+3$S8$|0zWqb0s^3I!Jf_NHCU}dZA`UM5SrFbqlkRm3ij^g6c!O7$jeI% z3i5#G0}t1L!t;rEx7!DN;S>cBr@FH8&h9|G z7f-MwItw^6s%Os%8yBmntIt5K2T#ISD{3q$rrq6T6?x$$mz3;oZnnc{83j8CM|>-g zE7mh1ApvL@Lx%ep8DXD3nWFaY_>F%2e&?4j&{$)lqi@4@5+BbxJiLqpht*s8^^04P z^=DZC9yTBdRiP)}42>FONsXr`q`lTxu~On`XVxUq?rWRuSUs>bBA0 ze_ypFPQc3WvFxFs0Fpnv>DrP0;r{*mSIo^Z(S+%6Y1Z?XE`0?K!+f)Y<>RA8c%B$c zcK~vIU%qs9c7kD$WFGi?C@CosW>9djHc)U-CCY|jm;TV&iU(JbdQ^CJm$7l|t5@!N zdYJB__f`B-?v$RHnTZpVsgn*TZ(#k;T*ua8z!J0_fY3l&o8+~G%57TVta|Cv zhTZufVx;=Hb9jVAcw(ac<;w~)8mg+vpC^G71A~GdBubH)ZJ9V=k|*!6DgpqBl0uP; z(jpS`0mdyn1rFIz-@pLRC}Q5fzrL>STa@923z&xHm6O{*RlvKvm0dXY1lNMiQJjD@ zR>v;Ix@RNi{JC>5Xox7j$22D{8Sz9CCn_uD+T(p5zj*%qOy%r;NS!b#;azDIp_}2+ zJpiktqobH7C5S;zXFYTHLC>{YXuaWVuz9cFrGd=@?-hod4+)_?uX*Xxd7-;V^0=M} zr0rE23U&xkNW!3$o9I88YCePHcI~)3l)}2FVEUj-|FVGI>jpeE;{AJ!1J5Jg2S$$w z2%G>>eSIB8(bm=tXc3Is*~k|UGST5hae2P@^4&b%I6NgBdvh_FmqEPqngpO4+ASpOegf6`x@bgy{hT#%o|ZbOmKc?!*%VhaZ#15ET@l1#GF? zm$WkP8d^Gjh+S7-|2UYTyZawNjBh8!tv;YDF9$>G9;$h7eQ0!G*HB7n714MQ77`*k z>F^y=QRP@2aZ><`ZfZJ+L3Iv+%LRrcARr+zGgu|RUU@f>*_mU#C_EyL@ z;+&p=0j5!3m6wMI=it#Q`>EeOVLweg-DV?k?pzD$F#wbMzHtD3Bx_b2@P%iK2j`xj zzc(&NMn(n`>K&b(c&1rL+|rxYbx4rcj-wIpC#6B@iy3i`7b>*u7M7Mu*B-MRqh!~` zZdpD>KaPSO-jkH2sp)Ar*V+-t=$^#C!+!xhWN*#z3JdSuz5AGmNHo$;_qsBqVfJIk zT+m^VV95nBg^Ul4l&)br>z;@8=Az8h;48~5y10))!=JLbFJ5fu|6i=Vc{G;)zxIt% zq7b4%A}S5ipp=Brq=8B$k&;T0DMcZYlID^`iRKv%iU<{j9yy|M;Ahs)pEhR}N+fQ~!b>Ap zSJrC~l5v*&OUGNZe*X9UMGeejWr_cXZQ?@Fif3hJf+@sYzkaB9eeObwM!S&jQ67Fi zX@|qYUb7|g^Yh&s*$@K<4LTPWcc5RjNI<-ZZ^^i>RR@%Q)n<(g#rte@+n(04xt5`; zR;{vKl~>Z1J?!W5^ozOJ~h;qJV1Dib?a) z_4cpH`LcTL+O;k&&p#PYy*$Q=7$KiUn$>{%RT%F1cJF;33mOE2 zsJ_%x+VQ8Rt}n)#kf zPSuauQ2Olf(nY)CGc&c9qW6!vn61 zw~wfuZConZ&n~-EG9fYX*ol}kXSUlsD|@%<)3Xf^|6Z~Q?ZW2UOpJc?Xqv&A9m#7G zZr#eB6!Obq49kR#nDX43Dv`Rt*H?e*<+Eo^r%gMbe5kNdH|qN*wIISwQl$B<8F!sK znipe=e6vqlUZ+D%Po&2wc$s2@##uZlR6HvP+?FOsLzd_?amC%*01*e`pW zcssNC<$8&iE}=$tq86ZBq0Yf0;@h*nbL`5vCQ|DVuW+*Mg{}Y0OE@UD^ZMo4?0# zmnTh{#Et&$;W40{Z0p`Xf7Q<&B_r2;^lJgGe*D|Xlc}fCKO-T~+{Y7*07MRW+J@)a18^-vz?!WszfbQvfeEf`A zv*7CIF>(4r_rCpg2vMLCpc3hzBt_FsD2wP-8g zGdWoPXMuNuZR|9KtxBR|Vu|}Y=bAR+B?iJGqdXyoPMmq%^`x7Wzs-A!zJb+*5w8uyaKjL4hiy5fd8<=Z-1g9ZkMtH@ipm4l|?I z+qZvz8=_hQU`A$U$g%WKpQ1ADwUF05@g@0z5_%INP~{>4YkQKWLIrg8g`?%LuxV#X zGJCAm>8qkL>#FbUs~*|OjWtSf9@%R=w1sf1MgFDQD0ugdp_x)BN)`>0mHTz)5m*B^ zP+miJW8%w|6KW$)o{XgS+bd)0l^P>^(3Ht z+Sb2>8`yzf%F0jEx>`=3zJ~A0^ zBJqlgi>Da4)9r!v(1Ey(H8?rdc<16{#}F1pk8E1Dngov#*-fyQYNw)wL@*W_IrVyLUf7CdqHTr#3>QU1I9gsf-UmwxQ}e zIPDAt7i4P0#haK|PuttOC+MA=+=CJ~=s83h%F4=`3T7T2V`AZ;g;&yzPd8TWxp(hg zbaZrTY8(AF0kAr9Jlz8uC3@n9!Rje5=38sG*!1<4ScAQV|uWPn(8(;5WgH^?||_sfpjc;4D@` zMS1yMUp{phL0(_Kl@^Hl76BLjG=h6o-RyAwU-g(NmctfWTfe6Z;ijZ0i@Cb<{c#Z) zieR2fkuI9`0V3w<;c?^Et@6HI3KrkXWfa7A?p*0Olgtvg-raY`#kp#)F}7!G07&3R z;#Xa~joWtiY&oSGz&V!&eK}A=Lx+ibVG61_?$V`h2ac*9sQg`9dl&HoTp2+IG0nY{ z6sXWROx8AT{Nvh75djE@N)3Y=MEk?x;a*(E_3QKI&$osjLlt%Ps+N%hKEF9uZund3Ma@0) zqSCWDZIgKrT7dgi_1gPutBUH&h95HT?OL{VI{R|<&3De@3+Xl zHwg(to^=3)1O(u3?=_*?#g&y{1T%kU`3-j-zm%Sd@8aYV*4ac_K;Y?!g+EmV?m)yS ze0g?>;naEaYDh44tsdIKD{f@;>G_xJLdWt0dHo%=gde1u68^a??OyOJ*)y`ANYq%2vC;v z^|K1ljy*Pe@Dal{i3!Tj4d?Z3to?KR%U>+V9Qhpw9v$C$`1nrGA>h0m4o8PnFPg`gL^f#7}$%tm?Wic*0w3;MbUhj|OpHZ3l znn$PyetUmLT0K=lAnyHd5Hf+rNb*1g(Gz6%O)Hzc@W#%aU2HDXq$o&czIbtHhHqQz z<`d@=6MI)B`L&GSVcb(*Ufp1X*DTuH(VH16pouYO48l zV9%bVDc&V7y?gzK%rY}GmY8#F)D#nwvg5w~F-&ucq}tKt0ErG!p(Lh8 zMyX1w*wk>CiiU7XcX;Ra?ZXZH@xGt{Kgrq0MxDBM&(_MSku(I5tQxECa|U?u?OP?a zTZsHcjW0gP1Q6lE*`uY{Vz+O=eiB%%lFq5kX7v;0q{NRK9Xcb+Krn#k_eXq0T6NSbt8guKsnO_mMSOvA}B4(gfMHYt|^S!H*r| z^blb!vPf}gNq_X{^YL9-oVtP+9WV*yl0fG@ z>5k;#C~8gFzJ^Y}~YoV=#EpRkN{o@7g7MW$5f^gZ{3v_dcW2?T4W)*2ag-;gr6(rpysH zl?c4h?S$UO-OpC52ho6^ob0nx_JO)cs?oG*kBE2_Z*Fg&zWrWVnN2H@lQTtvva8{k z0qGog!}t`PA&f;`m1lAO@&MWQOvDjJsaR1W>TcEs`o>~6Xl`eo&sIP-^GKa;u6Gg= zO6oW3MHR04RK)Nr*zd>|@#;)ZxDg$_5J%S(@t>TCy0ee1R=bU$j~>1M@7v!^8$#cxw)PWmK}lCOOKODSO@wk zE=ak(HOiD6U0ha=>di30-$euRxOMJ(2db(TmzF*-bhowrM5OO&OqI<_Y%bmYoFWjM zWrFcHm-rP7(}Ix88hiUrTd+WWYxcM`K}td~R{eLKlhZ@>7Pri?h-A>;Uh6F9w2xpWmRTSj1UN~G%2xw8#}7Nj0%B+6_oe=RJk_w4ZmOe8F@s}8ao zX+;<@xb5<(nfZ-6^B?8qmbSK9+S=`55BnnJXBEmTDvrk*^Vj(aV9Mk^%7pRS4ssL3v=%0&#gK$+;k2lo+v~TnCuddqwm}pg_E_BXvW8 zV^Htb=APJtR(tz)lLOjIuG^z(a5#{2*Vl`N9?Hl23+xIc7{$_qKLBeQ_8T(`zt~w@ z4;wM!>Bb2Q7cKrZ-lv>uYe;MVbG|#N9T+9) zKz7rM_?f$oiVO~Y2O346OmoGUB0-YjIyl}13+fOp!J(q7y$S$!_H0lms=GiH|9qgn zNqTzKy}b>POk|X}Nvr4=*ap%>ZkV79!DREc^iq4ulr9Ft4b=Y-X(lMoUAJynbZ9pz zDWG^}h-_MF^dx;(%T~X~Ss?h~yOhmi+a1_7Ja4CKb|dK%8$Y>!+D=5C!KprKw-B93 z2XFZGaboRFHaSo!!!(mmUgn^c=h4U3zFRdB-1p?e5?Z_<-C#5WCCYpk>I9fK;6oC> zaoc5TE*4LpJ`sft@hE4bAC`d33#Gk?Zxzm=?nR>erak;CuG4qlM=zsWUGaB4+3`M8F8-j}Ldu$>PXjKMnfszFq_{rmd^7#9AjOuL$bBpewt-y#T=6 zqOWXxQFl17aH4%AWdvIVDR^f?1qFrj$!|zzTQ-RGUUsFYngXZ(3?BTZB0UG#jLeYM zF`sK^G z%2H@;PyA}*>^x%oeYp4^BfEmrciBGWq{kxClCm;`SPjkpQOebucw$&Dl2*%6Y9F6g44UG&8^q+hFnrPS8o$y29F=;~|!nz*-2ffT_@j#J6#KA4w_iP<_1 z@eU8c%iSIOT8&LEG)~Z66affn3s7Q!L@>(%T1V2_)lwuYh;j-GUDmIkb8dc5W10{w znjl?({@8fxhet;|B^I);X?Qe-45^!IMfc{Y)ff&VT0z98nPKml&JK{JL!lxjz_$Io zQ&in7R{;BixdQb%Ri9-E%U7=Czj~!JYLugsle%11M#iY#d%+0Qf+%)+ zcYg#Zi1r8?d=!g?J@4}2_P!ITXU-r}dYO@7YHRC9kfShubS1204Jc)w?i?r5wW$@* z>*?}QCk^~|58w!k-hKNXmIrni)w_F&;(5fqXNG(4xPgZP9AEA6iUYcS>(^^-zn|_U zkx5;1A}Wf#OQ6EqLm(nb2aon0vGn%d@7+W6^IyK~-F+HrlS7G=ijk7-bUbtVbaiG0V@Py# z#7)+GwYj0OroMg(WECY6SsysedtsdbN3~AY*C$?}wgkV_<&hUsLyhV zy^9fBpyWtPGc>+<>eRN2naMOYR zax&aC)-;_qh<5w(Qx|Go*)LX1(b}5#G zz#m9FtgWo5ERo1lE+S0qH{=md%L~}dS0<0H6`28euP}26Pe+CUcw?asKHV7(#Lw?9 zn@C$)3WkA2Q$Q0|7HlfhK&tC$ELg@bSkUt2aXn!x?Qi`DGA<|c1vC`C4a|jqdrS20-IDHUp?pEa1~7?K(Kge@fbrh13G59=)#Z`CADI!qh*SvwQa@Umq)QQMX`WT4jsm zZ5J#W%%mTsmu4EQ zbW0Nx2(&0Jp_MPGGVaO&f@j2JpQHK(@u{gQ zYOidUlUVbtt8HWQzm8D6qC3>ReezpE0 zG&SNQw>HvX#K*^jTaz&k_j|+w2wBfp-7lAIeu%}>rEAxA4AN7yzOenu6VV1lw8j$|0W*O2 znH>j!$yLLp0UHjx#f!a|M1UEl{Y5s+Ycr`3+Bd_AOyKD3d;-JKaQd-fDB_YlaNn zeaK`42;7lBfBsN%m;dTC+tf6Og)>qfn}WrQmo8bt3Y@5?N3FjnBxIhZ0`phIsY7;< z#k^jWwZLdxjg4hgtp8@!Wv<0N_fP0>g zdhzx5jT>Wp_qZFV82Ijz{Z=C_1Y<^MuA|?xuuy%Q(0Ef-H>a9Tf!CS&mrSd=_=ANL zi@kr8aGW4i-oazXDoaW*!JX~WcP0nOn3>hq)tUIqjK2kqEmyorEOzKFvZblE2^fK^ z!4d&KE*^09(xoNy<{dhId{&$I5Vr{@gv+?UZ&KgH_jNT>M{Q2Gc66*{Nl)L`H-Qm2 zWayB@HRr!bj&r-}2ERym!?_o=73x)kTBrnbOnTsMfAIM6miAOp9c#c6Q_~ER>naC_ zErvHmxM_U=^h0wp<|sid2%bOD-^N0?$Xqg!fEO*U37ha{=8PE!4;|uAISm)ZLQ3GW zZZuCX9d#h;+t%yT_mZtX-#hLTK0w~JQkdm}euC0fsT%fV71ms=W(qvwZ@HrfP%gy* z{pFw9+6UL9NNYpPx~Rd)AWL>2yYNGp~&MUe6k(+}=pxI^|XQw->p@hTvoBiR@7c325E%`RYtya7ae>1d;}a4PXQy~nm{AfOe>ON$bk|N% znqM$ZIbBY@f(Pwf8Td{ZHOdX_Y=S2S36K*M8w0)eMNe=S zQ*&^3-Ybh3l}V?2M~=LrRf0-F$INji)CxJlPo6%7)RZs8Q6l^KbIOIrY3~yQhdk=T z(o0V_`l$Bl!u|j@AqV57I6q&kC>>j0J@NNz1YKzCYc|Om6H6%mOWHRfaDx@yIo-b2 zoe6vZKfw~e;S$aI{7bLMexo90mnO0^zN}j?MS`F))69&9S$1d+2ObCqG6CN#OLW`z z{Sx>D(z^qgFj8Tb{{<+dj-dQU`>H(N`50E8Z>2Cpf>EZ4-hxv=?94C#n!>_D2{k&$ z#<i;jSFSi&Ssg>o8Wz@Thj9`x z=(1%eS(B+5NW7r08R1eCpd2RKhN-JR=@C7_K!6|7zJMJzsD@{$vHV@ueHPzBm zhc<+5i;x$EBcqYw2M-E|7E%3xOw&8!Pc!GY_;SFL6A-3|c*&ns%dTDXn6Q-TuXl^7 zAXnEaN>bVnPLe5RuR|ihOvx%J*xcJPZgW)kj({f|7c7T<7$6O@m&m51s7L{Y|H(@- z)ytQMKSuui^=ssiAzolL!ob`FtS)(FOV;HZr=P}F$qn%e3fI? zGT|H+V?>rlz~g{SnVIKwL&fegd^FeJza171fPLJ!1il~Lt$=7TE2^nEgN322c$5~d4t@>Iv{*wSER%g`*J`r~?&Kp9Nc!h>PufGM3kdD7cOLut~anT={)eE8!I4aO1?jJB6Faby45C%p<**>|n6JM$s=H?gXU&6ESd+bKQs_$fTdUBp6 zz^pRHc{~rAgO%&oe|nNF!vN2Lmlaw|Q`eNsKl+g97xu6u9w7YqK3%{g7{@UY-d+0t zC(AfldP)0S&ij0dJ{9$P;w&*Yu4~@Hg;b&6JT4nkh+A4(@-+^~PZMc$5zexjdm-OZ z2P&|`i!N%elB{gZ{7Y;C??s~ujr?VF2j7|Do-p^%VU{8QeR97+bysp8KjuUZ)kP-l zJC*+r^k1)rasGXJ3!_K>8;N=s#x`vMTkhU~v5iu&{Uz3~wmr?ymoEyJ<7YcSz=s^J zqP9~}&Ob+Xt&Q5-huVv5Y-XQ%aa7x`&BUf3Z6Y#?aHm7w9lUI75Et6^H$uYjAc#*& zioWlFqXL$#^m*wv0a6gzst!IOZ{s{2O4E2HvnkSJkMS7Ny!jSf?MDvS`sJ6pY?25Ji-H z7K1w&?8d3+L<|QHK2QBP?F@w$U7Yme&K>WXb{es$^QXJL)F5upb5%gQ7cO+o6zjNV zJ4hd8V@J{eSd(|Z|F|beYj2{F?6Um@AliZj8|ZHO?z`>ucBE#Xj=R*l8_tviz{aQa zFp)+i0*EsUFU)z+t{XOtjhgG~>N@1;=zr{bV?y^nV={`02B>)gm@>@EsEbL&zYlu* zhup0m1VICq)4+^Vye$@xqWE%YSheUUE<4VeUR*$X9E#0*p3tuEPkMf5R z@sEtL3>TkbUFu%x<^QP#P<;@{WzkhE&{P0$r?0^~lJ_Kjv%JWb zj9v`V(NQoE?|6#7O2lPZV}u6Go>t-7;_v8q5on#lq~*wKh(P8Z$RUCd56WFl?G!je zl%c~68roCDaHDh(mHZVA1Xiy%VS<-w|P9^&g!8a-(;%1+F-OtNIBH2pc z{V&0vx6lLxBkI#CKqLD-{szDXJtT38>XKv7gDhymUmQ#iv!dr>9^2|_WMtn)g$_YV zwO^}Vz9Z>^CgmQPh)E<8JS4rKf-6i(0viM`YST{hYq^d2v$3!%nZtTmA|WZc#oN1h z4K-U~`uZ*ofXNR_RD-fZd;VK4Z9Y^0pf8PbX}4~9($^75$WW}l)-{r}E2+??(?x*M zh#+W~dyiYEuvM^N@h)R=(hRDnRa2!*Sfu*$pU|Z>3lL+ett}e#eO>$DJO<~>*guoH z@$Y_IzC5gL#WtovM$2yt2SEEZ6ie{li>+v1hAPhnVN1WjrmXA*tj)5b;SdJ!-o9lh zM}~n2Kr6NjOi*BL6Gx!k0x0_0{@3e;BO;CnX43dEf=}TRKK#m+J+gu<0s<>Mf|4@S zzXn*3RUynhK@BI|zRfYRU`y;R>m^GF!ka=(309@%18t6m7ngvxPyci-DTJqjB2nsFcYcW@(g`!U7 zSSs2+M|M9nanbwtW~VH$WSYPr)rk=lH7O|{v$HRnuMjoPCIlcXpp)8r;DDh=c{YKo zZ@+%R<}{|Hr=6c%H4A}WVi)Dv5NP@NH{*l=WZ28gtD&{k7x)_3N{9!JhBRDEJmImG z$hHfArYdGAm|z0K9&R9J{KEE2Q(`lxcDFq4It0K6DKY)5?S51J%|BB_5xKeEjh4B!HXA!o6#;GCv4bI=lU{5 zTT`>QqT)^^!;xBAv{`6FNOsMJ^L6WzkI_z3ru-0Ot1M$?38=6lOl4^-%Tpxukp= z2hL#C0rX@P3eXKlsk>Px@QQi7ai=NnY=TIfoT4rle)Q-tUYeQ!emy6lwFKs*J_Hkl z=YnVoY*@T#5&!IrUe}NtOGQhIYELKPMbLz>cTrmsJfQ6;LP#Vq z1pXp52h^Ir)GQtzi? zy!wPzb^Q2c@yn;c6q5Pr*1;^leEwX&swx%&ig_UKL`d@b`b|L=Bz(wuhFngTj(|Nj zKk}0>0kqB(_GreM)~@}^a-=Ky{N)R1sqD2ewm`Yq7vk_aUBGsn-`@%`F9qw2?X!d$ zo|h2ZH*7%YJdsgM>?K*Xgq);h{RRwRxT}r}uz#IYD9e}%>6R@4NHOv|y$J5sc-GN; zQCt>PyuCza%RD}x_Q|&Imd%^*rRZVAWYHPoJf4o;jujkh8bfyZD%ykc) ze4bOP7dks%MbbztsW!a%U+3`HsMuZ6ZP6S<81cZFWY?P{D5v+yuY}J|hCL+;${Hh= zzIORC7_|^J=|d?lD4Q95o@3gLEXW1|S+G-9bY^)Gna~40k_k!3=PmZgMvht$2pMj; z_%_8^GZYo%w;@)nDQmzlz=!6To7*un;1y?o^BtWcs}a=C`0P|bP`-Kl-ztB04Unsv z@$UZ%q@5?~x5bY4xqRu;ic!7G&u>+Jg;y#EGs4b1&&dgC*a6FxrIl0d-{1rVo)I>A zN9RLFm5n6A!o$m&k8ysewiShgpWozY=bDsGZQ~|S98o#?I(NEiY-i{TXG&CBe-fF` z?9Ird#vfn5a;1r79XA}aO)3?({)2HY%p|}@;&HGj)+Qn}^d@#Z_KQZZwzm&lDdr4@ zMu_8X9-{X~hs_XTQsk-mm%4W;W1|yG?M4|*{)fmzA%4HHyIHA#to8oAT-fB`hnzUw z-B=>`-mm8*I*L5X z_4xR5YCU2V`Hu!J!%|$V%{|K`@4mL?=+TG%ThqF4ntVsoYPx+2g7ZSvz~>ZOhd#*M z-9WG}jLu;e^}gXY6cMi0LxohQrWdt8T(Rl}(X6MjO+$ZBA&FD$ywc$R3CXXPZJ-2d zt)|Fe(;=mK(BqZq82zKiB$TRqEcT8b`2EKZ0!$nBfSZ>-Z+bX6G9%3F0J7(hy0r`i zU%t)igYmUlvSjAg-TvDUtJNIc5H1uY%a*BdgRo%zGul0@4rxk`4BL~s0(k@o$>!m% zBLNJG-UD#?MJ7CA)VgKto^*l9{2A>MWs_QmEYY_9NrQDZ)%DBwlq|Q(aG( zc}b~CPrZpF*#0pu9Bo#Jx~ttcq)B@a=xSnZefRX~Qo={8Px6Z*FGB<@gRB0}0RG^n@a-3+m|ojGtUPhQ|Y+{V&lce_fbH_1B0-8d}6qST7!%NZUZHl z-Ak)&ME0C9DQrbv7cGaqNWLEo_}5cU_Pe6&f~TA5CefX!U_C*|z2YNwN1m(~nX-Vi zuuGo;$fvFZfm@Jxy8@#El+NfCfq)CwFhR~orxriz-=lo^#?t0v`pdO+bcDVe_a0I~ z9M#6#bu(<<=1iUeiKA8#?X zBGQfapLSn8n-S{3t3XvHo%~k2vb2XE)f?7>{dlYc1{AHdN*_1n`gF?d?3|o#5)!Cq z)%^nrh9D!DGSsk4Vp|EL94I(VXQPo-wfu3tjlaU7PbtIc$XHof zVPrLiIt6YWOWyDAgpRx8#heRr$)0MK^X8p8bH+r2S=y|$AB`oT56m$ra?%>sSBQ3K zDIoC=j`gko(Wj}T!z{tOsjqL(OXto_O|2d@O-hbw*qpUI!bCX%1r+DJ9!`=x%;k=ZspG>70wE@)3PsKx40AW zb{4iI&>MUUT+LusrhN=Ol4*}66tpTv6$(WOPqVWf36r3dk%s;TFDAKxc?TUtDk3y^ zD+x;wPdqZj>!^99rKGS3`hjd@*TYq-SH~{9*H^}05DWf8_w5yO>lxL1p!C>Vani4S zzZOh40z>unO*wGD-1X7C+TggPv>&lffble{8S&kMESN$A!*5KB;sM-FNO*?tjQ?k$ zs}P3UwzuIQn?%`4HvlO=AbK-DPOr<1&6FVVW1PcCR%Y>VFnjZ}su8+e8QB#aWNxHR zNJ7 zu&`Bh{4}`e-<1qwFJHzc+0w>F`Iq`Z=Dap;>>(w^><9+8s7LUN%zpCZ=B-;9;Z>M6 zP}HC-NqoJ687MXul4QI>GCJFJSe2|@xl$cbry7#YM(SLFpbH=2r)|Qs9=vDAl1`KJ>aM%ZNzH((5b>kr~ zr1rHmS*Q;fR4b&7+`s=hJzaI=W7ndYD*@#ke238<(u9 zuFxK|%*dOt&1e|Y*varTMMA>_`EAE8?OJ+xYlV>-cvkAzqRu;>#9T2;wme$|TYX}AOOMMg+ey_i|>gGF&W6C1G z{*AM{cv*or{{zkiW=8n`BDR5Q!9_!1CJGPqX%z<{ant%{^!e-8kg8eTL_}g}kUD*}RTGo5G6 zm;tzT{`}xr9T2hY^Tj*H8e}j&+UFz_-Lp%%PjcCD&Dz4jcR)@pF8{j`YS0Fu173dp z4cO>EXJ^{`$>CF?(>v7jvCMDYc=rPgDCi*7EKdpjyn@ufuQ3(Ij{!q~97@7or~nQf z+BQbC;}hfd%)KDhtsAC3QW8eF+Kp?3lW|10b|J=5kgMgI#^1Rk98`tV$Mvjd&wfHt zSu7H-&P{TWtAv)reB-&Swo3Cf>IN zigkRdIrt;$(r4 zB~JWJAjdr&krhmN$@#ZyA27e%O^x>=9NgKv{ISK@sR95tEMTL z=NB{He`NS^s;Nj%c0MSgk;GqCbon4=e&?qSla&z0dp}cOBn?Y!)PI5FK~<<+^O* zExkcEHB{u+uU}7`b5Pt!<&?z)yrMyI;+EW;_AEoiBA0Z#+He&xHqV0EfO>?1o1ZtI zc%treUXo|yt-p+dT^brl$;MNry)J)%(~0njRR!XACvv4vubU3!S6!Cqk*l3+9+%YsY%M)% zNu`W>ur2}l9@;)H$r~cAhSVt(JV<|d?5l&3pc~+}l<;;L71;*H4?iDwCG2FKVASr` zqAu&jFK*j{&+=^FLo>>M{X#J;od3|b_PtYFQ*(164A-bpc{w?2Id+PXc%-O5oD!L5 zr~IRpLAJqry17*&293B$jk>y)X0rci`wVv6GApZWl_=&5RtQOHXLro2BPX~IT=}xS z_K9}bvi3<;DJCbapDZo1Le|SLV4_YUqqH(E&5XoD7fJ9X z*f-2)IEh8qF;5kwo1=tYE-VJ=ffvVy59SSQwUDt=saSNbNuW*i8{tw)5}oa@_r7v* z%J5TNhJ3T0AtC$=Ah-3wxlE{Y=+E%I)W(X5;!uPACvUMFp*U8}{`!Q01I4^9@7We6 zp>=@-K(fTU=+h2)W17FVoFUOD^st55f2(P zh>xN9dHnS0*&Vaj%y0Pd4hy|;BpPq2%ne4! zdiUwjh}rd@yoyl2z+m!_WR1bfgo9_%uu!EyfisE$&{a1)X`8`u3jAZodc%9(o_gqE z38*|sAzqdWZQm8Pb~=W?MC~oejZHJ z8Si5IlwP6{fcC<7wi++B3u+V~JiRC*)nj-oY`&}aoyA6piz&*f!AIHSZ8$R`xTI~O z%O4UX_`+l=X@}+r9;gczh>P8a#adg;jv?G|ES|@EN3lHnM4MkYV?eEz{rGX>=+VIn zk^sp540CXYpUJvQ^OlHmCN5gvCy2cHX~KMgt^+hUb2{ z^Hiiar&V&&U{zI|_d3Yxr`HKCv9Z6vlpMXnA|lp0J3m#i!qYLW`P&sB(95y0iz4Td z*dk6YGdFjoiP$@sQ%Lkxa^Jt_imWviZa-B0N|Ylbk$7Qy&4Q;B5UJxV<*|+qF*Sas z@ndsmV}S&`I5@`mK9)B8Qi;nAZs8rQX6DRUbzBl~?&3J{fH%bz6@OSxJ&h@M{ab$O za!kE(HiP2h%++*t8>nT24zj8Um-7#4$x6`U>@Y52nT#{Xr0#ZV>N3=_s>F=>z(HI|@g>(VQ@5|w5AI}^)9r~tzvEI#?61%+4(!pal*OsHLBWgq~TgY*%up?ua zb~f6x1gnh=SL5P1p=uP9b)XxM*V>JPShJXuUf2(=@X)aBtCPfk6y7_&PVDxzzQ{8mRXb+t*o`#}B-dS1(roTrg8DdB|t>(Xm@Tg$~JqxaFo<=ppcfvS( ztC`vEk3%!pPiH5sHMg;|i~HTzQNkw?TGIP-L1yM4!pNo*pb(&|;Qmd&^Ua~g-`red zE**T6&7oJD7lbGK(O4~9RW5+u#l=+-<1c+VG9QGk(s5^#7bPqT)XQ6MfI1OSm`6bz zf|Pk3Iz&aqF&lKc@AfeB8(^Ni>D;g&{ZBLIN~4>nXQ`r3qP&7z=Hrp>drvbueun$P z0MXU=APIvmbSrqgoG#PDW%T8X*RB!I1tv)0i?(Aa@jh|NLGb{FqqrvVE?w{{#L*@6 z)1Q~N=>;(Kw3d#s-5Pz{z(y!(?!d8W{@mwSq^)$CLG`j4g=GKR4lBNga2>~w9ov;z zN?FJ10!!EmhD$p`T>)abx9Wkv)~t`K3y!8XmhK^Tarzty6w?oNWGCnu-bv5s39B2I z+HCePW9q;cf)mN?*&L2`X}?;~t<^V`9*LMpzz8n>Z3HhgFi6vOJLA;9zI}^CJKDEz z*>&?*Uj#GM*1iy0)c5q2E1dCW#v)-V z>|9S|#v!A!Gmhr z3S&imZN_DAg7D>J5K2^z*tv1%&Yc@KcvzROUUf*3{<>)(%=~NpccAI;p`1~2X}4-< z4qjN?s5xmW3^e1}w`6en&!16%QV|hffBu|f0uagke=k_&h2u@`Dn~p|&R-OT~vTrIGIZ2Fjj!&*$WKXm$al5E4eh#m-*!aBG+RB4_?lrrU)rknm15i%; z*FDpHS8?L-;o8zY+GBPM%|d^TyjrnOpCJv;gzMfpy!zc3QHT3CGaoz{ zsHv&RsUBnq!`A9f!4JD}i@JCJT~{Y1A%S@MF5(1=YkEHTl5yrZ3g;CS+3i~ojVMrm z)FOOct)WBT-Clp3DF>L0$^GQyJg|;}%@J%zi2xMc@K$g0^Fu$_mmt}tx#Ub%>JG~I z3bzSt`k;f%h-Xp91qWm79guqd99$I)eF5Kq+Ng*UY_Xl+!oPob>g_dI12|QPi=u{8 z4HD-6uRTuT=b4Q{AF=d?Lk_-P%Q*9o0-{&1Km0h8?dba*Z`}Uy^y%Iw6)O7P@jwnJ zw|MihqedYG{|h_}Dhrpu${?Df?PPf8Ni;4CnU-Ap_5T3dys7*slb z3?v4P#=|q6MFK`t4kRQEfyx^_x<)vggTvwaxu#r46@ki!`BF+U?T;7v;0vo-o z`$pl0?h}-$0x&qG*3ZA!gc*x6ux0=LxY2u{HNmOUm&4JJba8v;n3=t#ww$jh$e1%T zdm3|W!Llu1&w*-;dt&ucgJ@0sV^oOI3>>`NI1-ECp4wXb1ll3aI>9XT*+K?nu{%!# z3jvK~v_#PV8$*}~usmQpi!m@z%pe*ur~%LlFzcT^QaDIK{25OT>kB`COnXq05ngiH zVj}4q#4hCD`+dOg{_kU3LY_E`;k4#3v#;Cm?>9SVfBl%=mpz%K)BxA5eiRqyiMxCN zJD@+har5T9?V^>J;jM(JR4RAsYvzRYRm>S?cv4a8tv@n+mhRN^UCS@^iuyTA_L|2psTI_o65_05nm+7Z{6A7QfOYnD{}1Hji8_BD`nJ@H~w5?*^~k zKo2wJ>*V(Ie*?P*1>r5rP54tpfE?eMF1>pmP|Q7q5Pig~8p z_^y|@TE^p->p=MZwlr)Lt|jlNGv|Q;SYrV}0L`u2NC)(}^-w-0fEj)viB9pty>Rpa zOw6!%rwYDsP&?=#xWs?946$LJenn!dn?>r-8tT|Z3d89%9X|9Kx&A$gGbl7Ge&M)=Nm>%`2#PUKgGkfeLAE1N8~tzv<-<- z2;^C2qwRnfI*Uz#69ed(GAoFuXVlqq899Kd-rDKwTp8!`w}x0GC6Bswd{|-M;&Swa)juC%>(PsR&vgB6n2g2St&*ISh_a zN(|H1e*60Mj__N3Ulnu`fU(wA3)4@wip(TL%sxL&(k*vQBpoYjXRS@dO$gQA5=5Z5|9 zxToWujIx?Un~~AE@Y^r{c@eyMv)WoK>CM%CeY=P_OnD+My5Z_$liiZhxiGfVC*7KO z`bNMfqf6^DimljPk?}q9;+_s^33yTg(;PAT(U-baVaiN)M7nyVbY{>1NqpKH<=^^PA9@XJNZVwHx4{&Fet zX2tHu9$0A^IREZVvw#E<&%}g)H|fvqwJh={kG@2h_bHql5OBIRbZF>_q_vg}>0;}sQTI4Od%{6>CP5j&j z$%7L7wtARR+Wb(FGu@A=X$L<%wMQ=E!mPRSMgugB`;Bm}7tymg_A-}?_Y5e!{Vr#7 zxA<$~KQ48CG+ybmH*F=)&L8UYS55ul*SL!bMsp?O$BF*Ff75B8 zsN*!J%rE)ETUhS8HFMLtP3wl3`$bnQQqJx_HhTAav5lRK6ffSC&6D1{rCa>u?!CiJ zrs>8n9zW)jp^fCLCt~$_(jxDdDCYGoa0xpo8F>5I+}KHL%;n?zOY{yiQdr%iK)P$Z zGEZYpm}tw2GjGIhr>XrJdG^rP_noInE-wO9e0Y~qt`m(O=! zM>27A7^V7nwfx0|fc4{-x5$s$X1ZdSX!f|7Kiz;G0HL*@!XXVp#Lc- zdGe|$$JbnN?4;U0=|Fn@NDWD@`ks8p3R#`L?68qlveKxmy{gOYZ(I`})mwA@Ks70& z;H6UBpIe)w7AI`HKHoj2@mHrr1(EE))rPJ=yavUu>Dv2iy3xL_m!EbzC?2mE@a37c zk<;=Ew-q&`#4LWagt@rJyvh~f=RXf+wW+p$*HgXFC8m>3pNT8B4XliR^X;q0ec{>3 z>gWGBQvdtg!hbjJ+L+~4^Hr4=gpCL=wOAPMxYsD_O?v&+pxlekuWTtfReCG%vX@5b z=*|blpKbkCIm|Tamz}$mdXkdP8DpI#myel>^q=~+S}V`u_r=4~R+61nrL6zVxEUt8 zveWY15wgAFY{V`*h-S!FR`)Go-;9LXfy1SSoP6ve*G3Dpi`!{?yd1 z@YjGx!cja-DfjACQCu7|a~W^W$$LX4ot?Zdk|vy@Pne4{Y;WBFhGd)_?cXNM@&O&; zyLG2<%a!~kR!yJI4n6d}xI$&rpZqYx6s_7{FGKu4W zjg13eP5}3|l%FtR0@iY`@O+i^Koo@>nPAL}2FRY{%M0i<1Rp+=w$)3Pyc6nnYwO3- zWD7l}Y1J?Z$1BwZkp?1(pR*r+{goec(SPPdN4X=tDtC^H_w%sK*WCDW@Ex#V>QQ7I z$cGpAS_7~&|B~gbS;IMlXq}jG_LaC)!Rninq<}?@G~^Oq|3N04oR~;Ynipq$%8eET z(|Zn@RFfaV_~Fz(|Q+r0^~{FXtZwUCp72<~-J4ednIk@1v~DS#y7Y@#jVx{Do!p zINCjOzmaN;8jQ70Il$A8V}Ktlpsk!C%qQM`+g9(fOg!?KB&A-|&q&+!!MYAGGA27O z9zf+UZoKRG@ee>w1tqj8JmcKowdg!J1lTXr>E!5@UDQzeOx2JZ?SG1=Y$o4C2MgfW zO^u@^_$r4MOa@g(T_w?YR!-wUz zRKscv-B*lugOSpe!~PzppC$G0UVK`Os?YV_AAA~LLtV_3xODL%>+uKYLPrjl0^kjY zVQ^3Sh{rUbS4s~VInhjUvUPUP9qm7?<^C9iL@tLCiHkGG`m~`4f|wVK7aCJ1xZB+| zOabN>6jYX#g`!rC^yT@yZWpHpmO1ik*G9-9I@_e=WVZXw)2EHY7MAT$TDE+|aHydd z(|@z#W@;R!L6bLDxTYF(kX?@qtf#SXr@fEHzK2uR9XnS2)BOrzxNU=`RC7a>9p7xx zqVwL1Fi1;HaxgLy`A|_!Ka2!XR&ob8=&MK1)4$XS@DCf=t8g=?f!_`DzyJLCeicsJ zq1c%>TI}bOB%4?PT^u?xydUb5vKl(F0i4}BY!5BY#iRg25)CJ(=`7Jy7_-gnLMCD_ zUY6%fqJ}Ub{in#ufAZoYBZD-0vbAJ^; z)RhYtbZO__*tPG`PHlYD&sEkKm}AV?v0UiW6PKCTLZ-_!e2}+~z`{NLJ08gbKtXP9 z?t=%$`}{vJ`wZg??YG(A-#=*qp^2k5Ci*qs!odpjJQg-*I$jzUMPK>C^N8Mhi=|R! zOS*M*L7j+}JHZf42VxI;Zg^WaH3m8D{QfLN<`2!J;Im*3GKkZGM($lo81JY1MUUz( zwMeLvp(*N-&;Llros@}H;CTTdVOj_-<-Bbwr;CA!Xv>8ltLHR~v;R+9=Ni&v6vpv| zT3*n!Btb@0)-a@tI`{2l&WNF|QWeGx( zWjJpiG!1fQnypaKz{;)X02ZU65hkm--cTQVqtf{g3rC*AgrsO*v z0rgc?+!G$LCPFN_%T4s~^yn^a*={uLRC)$<@N_%{-{@=2?pvzS4>A6KUS1m z*iZzc_%ih|oLM5ou1;5#nIAnth~_ zG^LN5ZI#zaE@V;Lg^_+{23}SBW*Rdoj%y*|1uW3h1w!HVHMnt5MhB4M;*IX^iP^cX zZf=>WE+3q?)-I!sB(+7#B5(%fX_n67rU79;l}xgy9tPVg6b$GJD>LFI0s%oH?OgjQ z<1`l+?^!Zc<;UzJm5)ZbMabV>UZM-{;Qcu6dE`^RE_2?{n@|qFOuk;3{q#*Br&ydy z7UAX9Qdj{UvVMOz4<^?S5FV}f68{7j0Wd6mZ*e10O*OxI>CDObhp$FPI{FT(`t9~? z(^&repw#fVr6r57C!~QSX_n^CYLEa5I~{>?(j@+AMJcg3>M+RWXm0CAv>8Hu-l03G z&kLfg1tlyeaB~&_sx#+mBAIdtu9*w-Uu-s^ZYX|&*2&|#MFcMTf3T|u2L;6^SkOH- z%klpB%0eEjI1MyNt9{l%qO=P6>q8q(kSwj?`Ri3euTseb3kmI^_@8wnb4rKj43w$3 zIua{ZT}()u0&7r{j}#PA7`6-GA}ks%b3a_pk>Z}ADO_qT&m$FH>1aP4+N`~@&AQLn z88N)m-~21_UY1_zY}_rJnmi;v$$xvqVk1UJ!bN4QI;LHoQSlcQat$m1 literal 0 HcmV?d00001 diff --git a/src/repository_structure_example/images/plantuml.png b/src/repository_structure_example/images/plantuml.png new file mode 100644 index 0000000000000000000000000000000000000000..2da7b38a4c725035f2c59df229ed945a6b71b2a9 GIT binary patch literal 113045 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
%6SoTB-SQw`! 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[(jo<9m+42|B@djJkBkUMn*<-%asUFr{(pJAw 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 literal 0 HcmV?d00001 diff --git a/src/repository_structure_example/images/repo.png b/src/repository_structure_example/images/repo.png new file mode 100644 index 0000000000000000000000000000000000000000..b21e4f3c758f577d7a925acc0787e228f7e4f315 GIT binary patch literal 48996 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%0D3dQk%e*H0tw+o_0EAkY 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 # Переносим необходимую информацию из контекста в презентацию ( $id := $params."dh-context-id"; @@ -41,7 +41,7 @@ entities: }; /* Если явно указан puml файл, просто рендерим его */ - $substring($context.uml, -5) = ".puml" ? ( + ($type($context.uml) = "string") and ($substring($context.uml, -5) = ".puml") ? ( { "type": "plantuml", "source": $toDirectRes($context.uml) @@ -57,6 +57,7 @@ entities: : $result; ) ) + template: templates/context.puml source: > ( $id := $params."dh-context-id"; diff --git a/src/repository_structure_example/metamodels/default/functions/_root.yaml b/src/repository_structure_example/metamodels/default/functions/_root.yaml deleted file mode 100644 index 4cb01e1..0000000 --- a/src/repository_structure_example/metamodels/default/functions/_root.yaml +++ /dev/null @@ -1,2 +0,0 @@ -imports: - - functions.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/rules/_root.yaml b/src/repository_structure_example/metamodels/default/rules/_root.yaml index 1f2e67e..5d25a4c 100644 --- a/src/repository_structure_example/metamodels/default/rules/_root.yaml +++ b/src/repository_structure_example/metamodels/default/rules/_root.yaml @@ -1,2 +1,4 @@ imports: - - validators.yaml \ No newline at end of file + - validators.yaml + - technologies.yaml + - systems.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/validators/systems.yaml b/src/repository_structure_example/metamodels/default/rules/systems.yaml similarity index 100% rename from src/repository_structure_example/metamodels/validators/systems.yaml rename to src/repository_structure_example/metamodels/default/rules/systems.yaml diff --git a/src/repository_structure_example/metamodels/validators/technologies.yaml b/src/repository_structure_example/metamodels/default/rules/technologies.yaml similarity index 100% rename from src/repository_structure_example/metamodels/validators/technologies.yaml rename to src/repository_structure_example/metamodels/default/rules/technologies.yaml diff --git a/src/repository_structure_example/metamodels/default/samples/01_components.yaml b/src/repository_structure_example/metamodels/default/samples/01_components.yaml deleted file mode 100644 index 40e7827..0000000 --- a/src/repository_structure_example/metamodels/default/samples/01_components.yaml +++ /dev/null @@ -1,23 +0,0 @@ -components: - component1: - title: Component1 - entity: component - links: - - id: component2 - title: Связь 1 - direction: <-> - component2: - title: Component2 - entity: component - links: - - id: component3 - title: Связь 1 - direction: <-> - component3: - title: Component3 - entity: component - links: - - id: component1 - title: Связь 1 - direction: <-> - diff --git a/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml b/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml deleted file mode 100644 index cf912fb..0000000 --- a/src/repository_structure_example/metamodels/default/samples/02_contexts.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Базовое описание сущности "Contexts" -contexts: - test: - location: Проба/Тест - title: Проба - extra-links: false - template: test.puml - components: - - dochub.front - api: - fetchComponents: > - ( - { - "test.c1": { - "title": "test", - "entity": "component" - }, - "test.c2": { - "title": "test", - "entity": "component" - } - } - ) diff --git a/src/repository_structure_example/metamodels/default/samples/03_documents.yaml b/src/repository_structure_example/metamodels/default/samples/03_documents.yaml deleted file mode 100644 index 67e5eb1..0000000 --- a/src/repository_structure_example/metamodels/default/samples/03_documents.yaml +++ /dev/null @@ -1,10 +0,0 @@ -docs: - test: - location: test - type: plantuml - source: > - ( - $ - ) - template: test.puml - diff --git a/src/repository_structure_example/metamodels/default/samples/_root.yaml b/src/repository_structure_example/metamodels/default/samples/_root.yaml deleted file mode 100644 index f252652..0000000 --- a/src/repository_structure_example/metamodels/default/samples/_root.yaml +++ /dev/null @@ -1,4 +0,0 @@ -imports: - - 01_components.yaml - - 02_contexts.yaml - - 03_documents.yaml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/default/samples/test.puml b/src/repository_structure_example/metamodels/default/samples/test.puml deleted file mode 100644 index 9224f4d..0000000 --- a/src/repository_structure_example/metamodels/default/samples/test.puml +++ /dev/null @@ -1,17 +0,0 @@ -@startuml - participant Participant as Foo - actor Actor as Foo1 - boundary Boundary as Foo2 - control Control as Foo3 - entity Entity as Foo4 - database Database as Foo5 - collections Collections as Foo6 - queue Queue as Foo7 - Foo -> Foo1 : To actor - Foo -> Foo2 : To boundary - Foo -> Foo3 : To control - Foo -> Foo4 : To entity - Foo -> Foo5 : To database - Foo -> Foo6 : To collections - Foo -> Foo7 : To queue -@enduml \ No newline at end of file diff --git a/src/repository_structure_example/metamodels/validators/_root.yaml b/src/repository_structure_example/metamodels/validators/_root.yaml deleted file mode 100644 index a29194e..0000000 --- a/src/repository_structure_example/metamodels/validators/_root.yaml +++ /dev/null @@ -1,6 +0,0 @@ -imports: - - technologies.yaml - - systems.yaml - - -