diff --git a/package.json b/package.json index 5eebe838..2e96d821 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "axios": "0.21.1", "dateformat": "3.0.3", "jsonata": "1.8.6", + "mustache": "4.2.0", "swagger-ui": "3.24.3", "vue": "2.6.11", "vue-code-diff": "0.0.4", diff --git a/public/documentation/arch/components/examples/context.yaml b/public/documentation/arch/components/examples/context.yaml new file mode 100644 index 00000000..c2bc842d --- /dev/null +++ b/public/documentation/arch/components/examples/context.yaml @@ -0,0 +1,9 @@ +contexts: + #*********************************************************** + # Контекст-пример сервиса заказов + #*********************************************************** + dochub.example: + title: Контектс-пример + components: + - dochub.examples.orders + - dochub.examples.payment diff --git a/public/documentation/arch/components/examples/order.yaml b/public/documentation/arch/components/examples/order.yaml new file mode 100644 index 00000000..a0d3fd94 --- /dev/null +++ b/public/documentation/arch/components/examples/order.yaml @@ -0,0 +1,65 @@ +components: + #*********************************************************** + # Компонент-пример сервиса заказов + #*********************************************************** + dochub.examples.orders: + title: Сервис управления заказами + entity: component + technologies: + - PHP + asyncapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + # см. https://dochub.info/docs/dochub.templates + servers: + orders: + url: mqtt://order.host.net + protocol: mqtt + description: Orders gateway + channels: + order/create: + subscribe: + operationId: emitOrderCreate + message: + $ref: "#/components/messages/OrderCreate" + components: + messages: + OrderCreate: + name: orderCreate + title: Создание заказа + contentType: application/json + payload: + $ref: "#/components/schemas/order" + schemas: + order: + type: object + properties: + id: + type: string + format: uuid + customer: + type: string + format: uuid + curr: + type: string + description: "Валюта" + value: + type: number + description: "Сумма" + createdAt: + type: string + format: date-time + description: "Момент создания" + openapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + # см. https://dochub.info/docs/dochub.templates + paths: + /orders: + get: + summary: Получение списка заказов + responses: + '200': # status code + content: + application/json: + schema: + type: array + items: + type: string + format: uid diff --git a/public/documentation/arch/components/examples/payment.yaml b/public/documentation/arch/components/examples/payment.yaml new file mode 100644 index 00000000..2210a827 --- /dev/null +++ b/public/documentation/arch/components/examples/payment.yaml @@ -0,0 +1,65 @@ +components: + #*********************************************************** + # Компонент-пример сервиса оплаты + #*********************************************************** + dochub.examples.payment: + title: Сервис оплаты + entity: component + expert: R.Piontik + technologies: + - SberPay + - Go + asyncapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + # см. https://dochub.info/docs/dochub.templates + servers: + payments: + url: mqtt://pay.host.net + protocol: mqtt + description: Payment gateway + channels: + pay/payment: + subscribe: + operationId: emitPayment + message: + $ref: "#/components/messages/Payment" + components: + messages: + Payment: + name: payment + title: Оплата + summary: Сообщение по оплате + contentType: application/json + payload: + $ref: "#/components/schemas/payment" + schemas: + payment: + type: object + properties: + account: + type: string + description: "Номер счета" + curr: + type: string + description: "Валюта" + value: + type: number + description: "Сумма" + createdAt: + type: string + format: date-time + description: "Момент создания" + openapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + # см. https://dochub.info/docs/dochub.templates + paths: + /payments: + get: + summary: Получение списка счетов + responses: + '200': # status code + content: + application/json: + schema: + type: array + items: + type: string + format: uid diff --git a/public/documentation/arch/components/examples/root.yaml b/public/documentation/arch/components/examples/root.yaml new file mode 100644 index 00000000..792d9630 --- /dev/null +++ b/public/documentation/arch/components/examples/root.yaml @@ -0,0 +1,10 @@ +#*********************************************************** +# Компонет-примеры +#*********************************************************** +components: + dochub.examples: + title: Примеры +imports: + - order.yaml + - payment.yaml + - context.yaml diff --git a/public/documentation/arch/components/root.yaml b/public/documentation/arch/components/root.yaml index d2bd99d0..a3e0d208 100644 --- a/public/documentation/arch/components/root.yaml +++ b/public/documentation/arch/components/root.yaml @@ -5,3 +5,4 @@ imports: - gitlab.yaml - browser.yaml - web.yaml + - examples/root.yaml \ No newline at end of file diff --git a/public/documentation/docs/conception/mindmap.puml b/public/documentation/docs/conception/mindmap.puml index 7641d977..0080d71c 100644 --- a/public/documentation/docs/conception/mindmap.puml +++ b/public/documentation/docs/conception/mindmap.puml @@ -35,9 +35,15 @@ *** [[/docs/dochub.docs Пользовательские артефакты]] **** [[/docs/dochub.docs#plantuml Диаграммы, схемы и т.д.]] ***** [[https://plantuml.com/ PlantUML]] +****** [[/docs/dochub.templates#plantuml Шаблоны]] **** [[/docs/dochub.docs#markdown Документы]] ***** [[https://ru.wikipedia.org/wiki/Markdown Markdown]] ****** [[/docs/dochub.docs#markdown Встраивание объектов DocHub]] -**** [[/docs/dochub.docs#swagger API Контракты]] +****** [[/docs/dochub.templates#markdown Шаблоны]] +**** [[/docs/dochub.tables Таблицы]] +**** [[/docs/dochub.docs#swagger Контракты]] ***** [[https://swagger.io/ Swagger]] +****** [[/docs/dochub.templates#openapi Шаблоны]] +***** [[https://www.asyncapi.com/ AsyncAPI]] +****** [[/docs/dochub.templates#asyncapi Шаблоны]] @endmindmap diff --git a/public/documentation/docs/manual/datasets.md b/public/documentation/docs/manual/datasets.md index 2516e5df..d167cea8 100644 --- a/public/documentation/docs/manual/datasets.md +++ b/public/documentation/docs/manual/datasets.md @@ -75,8 +75,4 @@ datasets: ![Зависимый источник](@document/dochub.dataset.li) -[Далее](/docs/dochub.forms) - - - - +[Далее](/docs/dochub.jsonata) \ No newline at end of file diff --git a/public/documentation/docs/manual/docs/examples/asyncapi_template.json b/public/documentation/docs/manual/docs/examples/asyncapi_template.json new file mode 100644 index 00000000..ceabaf90 --- /dev/null +++ b/public/documentation/docs/manual/docs/examples/asyncapi_template.json @@ -0,0 +1,11 @@ +{ + "asyncapi": "2.4.0", + "info": { + "title": "Пример генерации асинхронных контрактов", + "version": "1.0.0", + "description": "Информация о контрактах собирается по всей архитектуре" + } + {{#content}} + ,"{{field}}":{{&body}} + {{/content}} +} \ No newline at end of file diff --git a/public/documentation/docs/manual/docs/examples/openapi_template.json b/public/documentation/docs/manual/docs/examples/openapi_template.json new file mode 100644 index 00000000..bc363157 --- /dev/null +++ b/public/documentation/docs/manual/docs/examples/openapi_template.json @@ -0,0 +1,16 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Пример шаблона OpenAPI", + "description": "Контракты собираются из нескольких компонентов архитетуры" + }, + "servers": [ + { + "url": "http://host.net", + "description": "Сервис заказов" + } + ] + {{#content}} + ,"{{field}}":{{&body}} + {{/content}} +} diff --git a/public/documentation/docs/manual/docs/examples/template.puml b/public/documentation/docs/manual/docs/examples/template.puml new file mode 100644 index 00000000..762340d8 --- /dev/null +++ b/public/documentation/docs/manual/docs/examples/template.puml @@ -0,0 +1,6 @@ +@startuml +title Сущности использованные при описании архитектуры +{{#entities}} +{{{.}}} {{{.}}} as {{{.}}} +{{/entities}} +@enduml \ No newline at end of file diff --git a/public/documentation/docs/manual/docs/root.yaml b/public/documentation/docs/manual/docs/root.yaml index 03664cee..af15207c 100644 --- a/public/documentation/docs/manual/docs/root.yaml +++ b/public/documentation/docs/manual/docs/root.yaml @@ -1,9 +1,13 @@ imports: - examples/table.yaml docs: + # ***************************************** + # Руководство по документам + # ***************************************** dochub.docs: location: DocHub/Руководство/Документы description: Документы + autor: R.Piontik # Кастомное поле - Автор документа type: markdown subjects: - dochub.front @@ -14,6 +18,7 @@ docs: dochub.markdown: location: DocHub/Руководство/Документы/Markdown description: Markdown + autor: R.Piontik type: markdown subjects: - dochub.front @@ -23,7 +28,8 @@ docs: source: markdown.md dochub.plantuml: location: DocHub/Руководство/Документы/PlantUML - description: Markdown + description: PlantUML + autor: R.Piontik type: markdown subjects: - dochub.front @@ -43,7 +49,8 @@ docs: source: asyncapi.md dochub.swagger: location: DocHub/Руководство/Документы/Swagger - description: Markdown + description: Swagger + autor: R.Piontik type: markdown subjects: - dochub.front @@ -53,7 +60,8 @@ docs: source: swagger.md dochub.tables: location: DocHub/Руководство/Документы/Таблицы - description: Markdown + description: Таблицы + autor: R.Piontik type: markdown subjects: - dochub.front @@ -61,6 +69,9 @@ docs: - dochub.front.spa.blank - dochub.front.spa.blank.doc source: tables.md + # ***************************************** + # Примеры документов + # ***************************************** dochub.example.pml: type: PlantUML subjects: @@ -85,3 +96,76 @@ docs: - dochub.front.spa.blank - dochub.front.spa.blank.doc source: examples/asyncapi.yaml + # ***************************************** + # Шаблоны + # ***************************************** + dochub.templates: # Пример генерации markdown документа по шаблону + location: DocHub/Руководство/Документы/Шаблоны + description: Шаблоны + type: markdown + autor: R.Piontik # Кастомное поле - Автор документа + approvers: # Кастомное поле - список согласующих + - P.Petrov + - S.Sidorov + - N.Nikolaev + source: > # JSONata запрос для формирования параметров шаблона + ( + { + "id": $self._id, /* Идентификатор документа */ + "autor": $self.autor, /* Автор документа */ + "approvers": $self.approvers, /* Согласующие */ + "docs": [docs.$spread().{ /* Другие документы автора */ + "id": $keys()[0], + "title": *.description, + "autor": *.autor + }][autor=$self.autor] + } + ) + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.blank + - dochub.front.spa.blank.doc + template: templates.md # Шаблон документа + dochub.templates.pml: # Пример генерации PlantUML документа по шаблону + type: PlantUML + source: > + ( + { + "entities": $distinct([ /* Получаем все использованные сущности при описании архитектуры */ + components.*.entity + ]) + } + ) + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.blank + - dochub.front.spa.blank.doc + template: examples/template.puml + dochub.templates.asyncapi: # Пример генерации AsyncAPI документа по шаблону + type: AsyncAPI + source: > + ( + $BODY := $mergedeep([components.*.asyncapi]); + { + "content": [$BODY.$spread().{ + "field": $keys()[0], + "body": $string($lookup($, $keys()[0])) + }] + } + ) + template: examples/asyncapi_template.json + dochub.templates.openapi: # Пример генерации OpenAPI документа по шаблону + type: OpenAPI + source: > + ( + $BODY := $mergedeep([components.*.openapi]); + { + "content": [$BODY.$spread().{ + "field": $keys()[0], + "body": $string($lookup($, $keys()[0])) + }] + } + ) + template: examples/openapi_template.json diff --git a/public/documentation/docs/manual/docs/tables.md b/public/documentation/docs/manual/docs/tables.md index 67f20839..9d45e29b 100644 --- a/public/documentation/docs/manual/docs/tables.md +++ b/public/documentation/docs/manual/docs/tables.md @@ -263,4 +263,4 @@ datasets: # Источники данных Результат: ![Предопределенная таблица](@document/dochub.table.dataset.post) -[Далее](/docs/dochub.datasets) \ No newline at end of file +[Далее](/docs/dochub.templates) \ No newline at end of file diff --git a/public/documentation/docs/manual/docs/templates.md b/public/documentation/docs/manual/docs/templates.md new file mode 100644 index 00000000..a34f20e9 --- /dev/null +++ b/public/documentation/docs/manual/docs/templates.md @@ -0,0 +1,362 @@ +# Шаблоны + +Шаблоны предназначены для генерации документов на основании результатов [JSONata](https://jsonata.org/) запросов +с использованием [mustache](https://mustache.github.io/) языка. + +## Markdown + +Предоставляется возможность динамически создавать документы: +```yaml +docs: + ... + dochub.templates: # Пример генерации документа по шаблону + location: DocHub/Руководство/Документы/Шаблоны + description: Markdown + type: markdown + autor: R.Piontik # Кастомное поле - Автор документа + approvers: # Кастомное поле - список согласующих + - P.Petrov + - S.Sidorov + - N.Nikolaev + source: > # JSONata запрос для формирования параметров шаблона + ( + { + "id": $self._id, /* Идентификатор документа */ + "autor": $self.autor, /* Автор документа */ + "approvers": $self.approvers, /* Согласующие */ + "docs": [docs.$spread().{ /* Другие документы автора */ + "id": $keys()[0], + "title": *.description, + "autor": *.autor + }][autor=$self.autor] + } + ) + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.blank + - dochub.front.spa.blank.doc + template: templates.md # Шаблон документа + ... +``` + +Код шаблона: +```mustache +{{=<% %>=}} +Результат: +* Идентификатор документа: **{{id}}** +* Автор: **{{autor}}** +* Согласующие: {{#approvers}}**{{.}}**; {{/approvers}} +* Другие документы автора: +{{#docs}} + * [{{title}}](/docs/{{id}}) +{{/docs}} +<%={{ }}=%> +``` + +Результат: +* Идентификатор документа: **{{id}}** +* Автор: **{{autor}}** +* Согласующие: {{#approvers}}**{{.}}**; {{/approvers}} +* Другие документы автора: +{{#docs}} + * [{{title}}](/docs/{{id}}) +{{/docs}} + +## PlantUML + +Доступна генерация PlantUML документов: +```yaml +docs: + ... + dochub.templates.pml: # Пример генерации PlantUML документа по шаблону + type: PlantUML + source: > + ( + { + "entities": $distinct([ /* Получаем все использованные сущности при описании архитектуры */ + components.*.entity + ]) + } + ) + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.blank + - dochub.front.spa.blank.doc + template: examples/template.puml + ... +``` + +Код шаблона: +```mustache +{{=<% %>=}} +@startuml +title Сущности использованные при описании архитектуры +{{#entities}} +{{{.}}} {{{.}}} as {{{.}}} +{{/entities}} +@enduml +<%={{ }}=%> +``` + +Результат: +![PlantUML по шаблону](@document/dochub.templates.pml) + +## AsyncAPI + +Важным приемуществом шаблонов, является возможность консолидации фрагментированных +артефактов в один. Например, можно в каждом отдельном компоненте описать контракты, а затем +собрать общий. + +Манифест документа: +```yaml +docs: + ... + dochub.templates.asyncapi: # Пример генерации AsyncAPI документа по шаблону + type: AsyncAPI + source: > + ( + $BODY := $mergedeep([components.*.asyncapi]); + { + "content": [$BODY.$spread().{ + "field": $keys()[0], + "body": $string($lookup($, $keys()[0])) + }] + } + ) + template: examples/asyncapi_template.json + ... +``` + +Шаблон: +```mustache +{{=<% %>=}} +{ + "asyncapi": "2.4.0", + "info": { + "title": "Пример генерации асинхронных контрактов", + "version": "1.0.0", + "description": "Информация о контрактах собирается по всей архитектуре" + } + {{#content}} + ,"{{field}}":{{&body}} + {{/content}} +} +<%={{ }}=%> +``` + + +Манифест компонентов: +```yaml +components: + ... + #*********************************************************** + # Компонет-пример сервиса заказов + #*********************************************************** + dochub.examples.orders: + title: Сервис управления заказами + entity: component + technologies: + - PHP + asyncapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + servers: + orders: + url: mqtt://order.host.net + protocol: mqtt + description: Orders gateway + channels: + order/create: + subscribe: + operationId: emitOrderCreate + message: + $ref: "#/components/messages/OrderCreate" + components: + messages: + OrderCreate: + name: orderCreate + title: Создание заказа + contentType: application/json + payload: + $ref: "#/components/schemas/order" + schemas: + order: + type: object + properties: + id: + type: string + format: uuid + customer: + type: string + format: uuid + curr: + type: string + description: "Валюта" + value: + type: number + description: "Сумма" + createdAt: + type: string + format: date-time + description: "Момент создания" + #*********************************************************** + # Компонет-пример сервиса оплаты + #*********************************************************** + dochub.examples.payment: + title: Сервис оплаты + entity: component + expert: R.Piontik + technologies: + - SberPay + - Go + asyncapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + servers: + payments: + url: mqtt://pay.host.net + protocol: mqtt + description: Payment gateway + channels: + pay/payment: + subscribe: + operationId: emitPayment + message: + $ref: "#/components/messages/Payment" + components: + messages: + Payment: + name: payment + title: Оплата + summary: Сообщение по оплате + contentType: application/json + payload: + $ref: "#/components/schemas/payment" + schemas: + payment: + type: object + properties: + account: + type: string + description: "Номер счета" + curr: + type: string + description: "Валюта" + value: + type: number + description: "Сумма" + createdAt: + type: string + format: date-time + description: "Момент создания" + ... +``` + +Результат: +![AsyncAPI по шаблону](@document/dochub.templates.asyncapi) + + +## OpenAPI + +Аналогично AsyncAPI можно собрать единый контракт для OpenAPI. + +Манифест документа: +```yaml +docs: + ... + dochub.templates.openapi: # Пример генерации OpenAPI документа по шаблону + type: OpenAPI + source: > + ( + $BODY := $mergedeep([components.*.openapi]); + { + "content": [$BODY.$spread().{ + "field": $keys()[0], + "body": $string($lookup($, $keys()[0])) + }] + } + ) + template: examples/openapi_template.json + ... +``` + +Шаблон: +```mustache +{{=<% %>=}} +{ + "openapi": "3.0.0", + "info": { + "title": "Пример шаблона OpenAPI", + "description": "Контракты собираются из нескольких компонентов архитетуры" + }, + "servers": [ + { + "url": "http://host.net", + "description": "Сервис заказов" + } + ] + {{#content}} + ,"{{field}}":{{&body}} + {{/content}} +} +<%={{ }}=%> +``` + +Манифест компонентов: +```yaml +components: + .. + #*********************************************************** + # Компонет-пример сервиса заказов + #*********************************************************** + dochub.examples.orders: + title: Сервис управления заказами + entity: component + technologies: + - PHP + ... + openapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + paths: + /orders: + get: + summary: Получение списка заказов + responses: + '200': # status code + content: + application/json: + schema: + type: array + items: + type: string + format: uid + #*********************************************************** + # Компонет-пример сервиса оплаты + #*********************************************************** + dochub.examples.payment: + title: Сервис оплаты + entity: component + expert: R.Piontik + technologies: + - SberPay + - Go + ... + openapi: # Кастомное поле, созданное для примера сборки контрактов из архитектуры через шаблоны + paths: + /payments: + get: + summary: Получение списка счетов + responses: + '200': # status code + content: + application/json: + schema: + type: array + items: + type: string + format: uid + ... +``` + +Результат: +![OpenAPI по шаблону](@document/dochub.templates.openapi) + + +[Далее](/docs/dochub.datasets) \ No newline at end of file diff --git a/public/documentation/docs/manual/forms.md b/public/documentation/docs/manual/forms.md index 841bf843..263509bb 100644 --- a/public/documentation/docs/manual/forms.md +++ b/public/documentation/docs/manual/forms.md @@ -35,4 +35,4 @@ links: ![Карточка компонента](@component/dochub.front.spa.workspace) -[Далее](/docs/dochub.technologies) +[Далее](/docs/dochub.radar) diff --git a/public/documentation/docs/manual/jsonata.md b/public/documentation/docs/manual/jsonata.md new file mode 100644 index 00000000..a3eee014 --- /dev/null +++ b/public/documentation/docs/manual/jsonata.md @@ -0,0 +1,159 @@ +# Расширение JSONata + +Проект использует язык запросов [JSONata](https://jsonata.org/) для анализа данных архитектуры. + +Для решения специфических задач инстурумента, базовая поставка JSONata расширена дополнительными функциями +и переменными. + +## Дополнительные функции + +### $mergedeep() + +Сигнатура: $mergedeep(array) + +Объединяет несколько структур в одну. В отличии от $merge, объединение происходит на всех уровнях +вложенности, а не только на первом. + +Запрос: +```json +$mergedeep([ + { + "key1": 1, + "struct1": { + "key2": 1, + "key3": 2, + "struct2": { + "key4": 1, + "key5": 2 + } + } + }, + { + "key1": 1, + "struct1": { + "key6": 1, + "key7": 2, + "struct2": { + "key8": 1, + "key9": 2 + } + } + } +]) +``` + +приведет к результату: +```json +{ + "key1": 1, + "struct1": { + "key2": 1, + "key3": 2, + "struct2": { + "key4": 1, + "key5": 2, + "key8": 1, + "key9": 2 + }, + "key6": 1, + "key7": 2 + } +} +``` + +В то время, как запрос: + +```json +$merge([ + { + "key1": 1, + "struct1": { + "key2": 1, + "key3": 2, + "struct2": { + "key4": 1, + "key5": 2 + } + } + }, + { + "key1": 1, + "struct1": { + "key6": 1, + "key7": 2, + "struct2": { + "key8": 1, + "key9": 2 + } + } + } +]) +``` + +к другому результату: +```json +{ + "key1": 1, + "struct1": { + "key6": 1, + "key7": 2, + "struct2": { + "key8": 1, + "key9": 2 + } + } +} +``` + + +## Дополнительные переменные + +### $self + +Содержит структу объекта, выполняющего запрос. + +**В настоящий момент поддерживается только в [шаблонах документов](/docs/dochub.templates).** + +Объект документа: +```yaml +docs: + ... + dochub.templates.pml: # Пример генерации PlantUML документа по шаблону + type: PlantUML + source: ... + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.blank + - dochub.front.spa.blank.doc + template: examples/template.puml + ... + +``` + +Будет доступен при выполненнии JSONata запроса: + +```json + $self +``` + +в таком виде: + +```json + { + "_id": "dochub.templates.pml", + "type": "PlantUML", + "source": ..., + "subjects": [ + "dochub.front", + "dochub.front.spa", + "dochub.front.spa.blank", + "dochub.front.spa.blank.doc" + ], + "template": "examples/template.puml" + } +``` + +[Далее](/docs/dochub.inheritance) + + diff --git a/public/documentation/docs/manual/radar.md b/public/documentation/docs/manual/radar.md index 494f77ab..2ec5640e 100644 --- a/public/documentation/docs/manual/radar.md +++ b/public/documentation/docs/manual/radar.md @@ -14,6 +14,8 @@ ![Технологический радар](@radar/tools) -[Далее](/docs/dochub.inheritance) + +Это все. + diff --git a/public/documentation/docs/manual/root.yaml b/public/documentation/docs/manual/root.yaml index 211ac2a0..309a0a4b 100755 --- a/public/documentation/docs/manual/root.yaml +++ b/public/documentation/docs/manual/root.yaml @@ -100,6 +100,15 @@ docs: - dochub.front - dochub.front.spa - dochub.front.spa.dataset + dochub.jsonata: + location: DocHub/Руководство/JSONata+ + description: Расширения JSONata + type: markdown + subjects: + - dochub.front + - dochub.front.spa + - dochub.front.spa.dataset + source: jsonata.md dochub.config: icon: settings location: DocHub/Конфигурирование diff --git a/public/documentation/docs/manual/rules/exceptions.md b/public/documentation/docs/manual/rules/exceptions.md index 11f2fdba..1c9f691c 100644 --- a/public/documentation/docs/manual/rules/exceptions.md +++ b/public/documentation/docs/manual/rules/exceptions.md @@ -16,4 +16,5 @@ rules: В примере создано исключение для валидатра "dochub.source". -Это все. +[Далее](/docs/dochub.technologies) + diff --git a/public/documentation/docs/manual/technologies.md b/public/documentation/docs/manual/technologies.md index 9871d11f..2e398d31 100644 --- a/public/documentation/docs/manual/technologies.md +++ b/public/documentation/docs/manual/technologies.md @@ -53,4 +53,4 @@ links: ![Карточка технологии](@technology/JavaScript) -[Далее](/docs/dochub.radar) +[Далее](/docs/dochub.forms) diff --git a/src/assets/base.yaml b/src/assets/base.yaml index 6881ed1f..43b315df 100644 --- a/src/assets/base.yaml +++ b/src/assets/base.yaml @@ -32,13 +32,13 @@ rules: ( $MANIFEST := $; $USED := $distinct(contexts.*.components); - [[[components.$spread().( + [components.$spread().( $ID := $keys()[0]; { "id" : $ID, "mask" : $USED[$wcard($ID, $)] } - )][$not($exists(mask))]].{ + )[$not($exists(mask))].{ "uid": "$dh-mm-ofb-" & id, "correction": "Добавьте компонент в контекст", "description": "Предполагается, что компонент не включенный в контекст не учтен.", diff --git a/src/components/Docs/DocAsyncApi.vue b/src/components/Docs/DocAsyncApi.vue index 8d9f6f13..04b24d82 100644 --- a/src/components/Docs/DocAsyncApi.vue +++ b/src/components/Docs/DocAsyncApi.vue @@ -3,50 +3,51 @@ + + {{ error }} + - - + + + + + diff --git a/src/components/Docs/DocTable.vue b/src/components/Docs/DocTable.vue index 5c8a4663..adf83ce9 100644 --- a/src/components/Docs/DocTable.vue +++ b/src/components/Docs/DocTable.vue @@ -66,7 +66,7 @@ const provider = datasets(); provider.dsResolver = (id) => { return { - subject: (this.manifest.datasets || {})[id], + subject: Object.assign({$id: id}, (this.manifest.datasets || {})[id]), baseURI: (this.$store.state.sources.find((item) => item.path === `/datasets/${id}`) || {}).location }; }; diff --git a/src/components/Schema/Schema.vue b/src/components/Schema/Schema.vue index 0440056e..3e8914f2 100644 --- a/src/components/Schema/Schema.vue +++ b/src/components/Schema/Schema.vue @@ -178,7 +178,7 @@ notEmpty = true; const title = this.makeRef('component', component.id, component.title); // Если компонент является системой, описываем его через DSL - let entity = component.entity.toString(); + let entity = (component.entity || 'component').toString(); // todo Костыль для совместимости. Нужно будет удалить, когда все перейдут на новый синтаксис switch (entity) { case 'system': @@ -207,11 +207,11 @@ result += `\n${entity}End()\n`; } else { if (aspectList.length || component.is_context) { - result += `${component.entity} ${component.id}`; + result += `${entity} ${component.id}`; result += `[\n${title}\n====\n* ${aspectList.join('\n----\n* ')}\n`; result += component.is_context ? `---\n[[/architect/contexts/${component.id} ≫≫]]\n]`: ']'; } else { - result += `${component.entity} "${title}" as ${component.id}`; + result += `${entity} "${title}" as ${component.id}`; } } result += '\n'; diff --git a/src/helpers/datasets.js b/src/helpers/datasets.js index a229a08d..bb71e0c5 100644 --- a/src/helpers/datasets.js +++ b/src/helpers/datasets.js @@ -11,7 +11,8 @@ export default function() { // Парсит поле данных в любом объекте // context - Контекст данных для выполнения запросов // data - данные требующие парсинга - parseSource(context, data) { + // subject - объект - владелец + parseSource(context, data, subject) { return new Promise((resolve, reject) => { // Константные данные if(typeof data === 'object') { @@ -19,7 +20,7 @@ export default function() { } else if (typeof data === 'string') { // Inline запрос JSONata if (/(\s+|)\(((.*|\d|\D)+?)(\)(\s+|))$/.test(data)) { - resolve(query.expression(data).evaluate(context)); + resolve(query.expression(data, subject).evaluate(context)); // Ссылка на файл с данными } else if (data.slice(-5) === '.yaml' || data.slice(-5) === '.json' || (data.search(':') > 0)) { requests.request(data) @@ -35,7 +36,7 @@ export default function() { resolve(query.expression(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)).evaluate(context) - ); + , subject); }).catch((e) => reject(e)); // Идентификатор источника данных } else { @@ -56,14 +57,14 @@ export default function() { getData(context, subject) { return new Promise((resolve, reject) => { const exec = (origin) => { - this.parseSource(origin, subject.source || (subject.data /* depricated */)) + this.parseSource(origin, subject.source || (subject.data /* depricated */), subject) .then((data) => resolve(data)) .catch((e) => reject(e)); }; if (subject.source || (subject.data /* depricated */) ) { if (subject.origin) { if(typeof subject.origin === 'string') { - this.parseSource(context, subject.origin) + this.parseSource(context, subject.origin, subject) .then((data) => exec(data)) .catch((e) => reject(e)); } else if ((typeof subject.origin === 'object') && !Array.isArray(subject.origin)) { @@ -71,7 +72,7 @@ export default function() { const data = {}; for (const key in subject.origin) { ++counter; - this.parseSource(context, subject.origin[key]).then((content) => { + this.parseSource(context, subject.origin[key], subject).then((content) => { data[key] = content; if(!--counter) exec(data); }).catch((e) => reject(e)); diff --git a/src/helpers/docs.js b/src/helpers/docs.js index aeef5ebe..a1cf36c4 100644 --- a/src/helpers/docs.js +++ b/src/helpers/docs.js @@ -6,11 +6,11 @@ export default { let result = null; const transport = (profile.transport || 'default').toLowerCase(); if (transport === 'gitlab') { - result = gitlab.makeFileURI(profile.project_id, profile.source, profile.branch, 'raw'); + result = gitlab.makeFileURI(profile.project_id, profile.template || profile.source, profile.branch, 'raw'); } else if (transport === 'http') { result = profile.source; } else { - const source = requests.makeURL(profile.source, baseURI || (window.origin + '/')); + const source = requests.makeURL(profile.template || profile.source, baseURI || (window.origin + '/')); result = source.url; } return result ? result.toString() : ''; diff --git a/src/helpers/requests.js b/src/helpers/requests.js index 8527185d..d4101d4c 100644 --- a/src/helpers/requests.js +++ b/src/helpers/requests.js @@ -18,9 +18,11 @@ axios.interceptors.request.use(function(params) { }); axios.interceptors.response.use(function(response) { - if (typeof response.data === 'string' ) { + if (response.config.responseHook) + response = response.config.responseHook(response); + if (!response.config.raw && typeof response.data === 'string' ) { const url = response.config.url.toLowerCase(); - if (url.indexOf('.json/raw') >= 0) + if ((url.indexOf('.json/raw') >= 0) || (url.slice(-5) === '.json')) response.data = JSON.parse(response.data); else if ((url.indexOf('.yaml/raw') >= 0) || (url.slice(-5) === '.yaml')) response.data = YAML.parse(response.data); @@ -161,6 +163,9 @@ export default { return result; }, + // axios_params - параметры передавамые в axios + // responseHook - содержит функцию обработыки ответа перед работой interceptors + // raw - если true возвращает ответ без обработки request(uri, baseURI, axios_params) { let params = Object.assign({}, axios_params); params.source = this.makeURL(uri, baseURI); diff --git a/src/manifest/query.js b/src/manifest/query.js index 9f9efc7f..7ac1775d 100644 --- a/src/manifest/query.js +++ b/src/manifest/query.js @@ -1,6 +1,5 @@ import jsonata from 'jsonata'; - const SCHEMA_CONTEXT = ` ( $MANIFEST := $; @@ -340,7 +339,7 @@ const SUMMARY_COMPONENT_QUERY = ` $MANIFEST := $; $lookup(components, $COMPONENT_ID).( $COMPONENT := $; - $ENTITY := $.entity; + $ENTITY := $.entity ? $.entity : "component"; $FORM := $MANIFEST.forms[entity.$contains($ENTITY)].fields; $append([ @@ -580,11 +579,55 @@ const ARCH_MINDMAP_ASPECTS_QUERY = ` )]^(id)] )`; +function wcard(id, template) { + if (!id || !template) return false; + const idStruct = id.split('.'); + const tmlStruct = template.split('.'); + if (tmlStruct.length < idStruct) return false; + for (let i = 0; i < tmlStruct.length; i++) { + const pice = tmlStruct[i]; + if (pice === '**') return true; + if (pice === '*') continue; + if (pice !== idStruct[i]) return false; + } + return idStruct.length === tmlStruct.length; +} + +function mergeDeep(sources) { + function mergeDeep(target, sources) { + function isObject(item) { + return (item && typeof item === 'object' && !Array.isArray(item)); + } + + if (!sources.length) return target; + const source = sources.shift(); + + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (!target[key]) Object.assign(target, { [key]: {} }); + mergeDeep(target[key], [source[key]]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + return mergeDeep(target, sources); + } + return mergeDeep({}, sources); +} + export default { - expression(expression) { + // Создает объект запроса JSONata + // expression - JSONata выражение + // self - объект, который вызывает запрос (доступен по $self в запросе) + expression(expression, self_) { const obj = { expression, core : jsonata(expression), + // Исполняет запрос + // context - контекст исполнения запроса + // def - если возникла ошибка, будет возращено это значение evaluate(context, def) { try { return Object.freeze(this.core.evaluate(context)); @@ -599,19 +642,9 @@ export default { } } }; - obj.core.registerFunction('wcard', (id, template) => { - if (!id || !template) return false; - const idStruct = id.split('.'); - const tmlStruct = template.split('.'); - if (tmlStruct.length < idStruct) return false; - for (let i = 0; i < tmlStruct.length; i++) { - const pice = tmlStruct[i]; - if (pice === '**') return true; - if (pice === '*') continue; - if (pice !== idStruct[i]) return false; - } - return idStruct.length === tmlStruct.length; - }); + obj.core.assign('self', self_); + obj.core.registerFunction('wcard', wcard); + obj.core.registerFunction('mergedeep', mergeDeep); return obj; }, // Меню diff --git a/uppml.sh b/uppml.sh index 18bc9a54..873e91c6 100755 --- a/uppml.sh +++ b/uppml.sh @@ -1,3 +1,4 @@ #!/bin/bash DOCKER_BUILDKIT=1 docker-compose up -d plantuml -# DOCKER_BUILDKIT=1 docker-compose up --build serve \ No newline at end of file +# DOCKER_BUILDKIT=1 docker-compose up --build serve +# https://codesandbox.io/s/jsonata-serializer-demo-q67m3?fontsize=14&file=/src/index.js \ No newline at end of file