asciidoctor / asciidoctor-kroki

Asciidoctor.js extension to convert diagrams to images using Kroki!
https://kroki.io/
MIT License
146 stars 47 forks source link

asciidoctor-kroki silently skip some plantuml diagrams rendering #421

Open romkavt opened 12 months ago

romkavt commented 12 months ago

asciidoctor-kroki v 0.17, asciidoctor.js 3.0.2. Building single HTML file using node.js.

Some PlantUML diagrams does not rendered without any visible error from asciidoctor-kroki or Node.JS. A the result, I've got a package of HTML documents from build procedure, where some documents has broken diagrams.

The asciidoctor-kroki options are:

const html = asciidoctor.convertFile(source, {
  to_file: false,
  standalone: true,
  safe: 'safe',
  extension_registry: registry,
  attributes: {
    'kroki-server-url': krokiServer,
    'kroki-fetch-diagram': true,
    'kroki-default-options': 'inline',
    'imagesoutdir': dstDir + 'images',
    'imagesdir': 'build/images',
    'allow-uri-read': 'true',
    'data-uri': 'true',
    'icons': 'font',
    'webfonts!': '',
    'stylesheet': 'build/images/' + cssDestinationFilename,
    'kroki-plantuml-include': path.resolve(__dirname, 'plantuml-styles.iuml'),
    'uri-project-root': projectRootUri,
    'uri-repository-root': repositoryRootUri
  }
})

The fragment of resulting HTML document source is:

<div class="paragraph"><p>plantuml::sdk-hierarchy-v2-yooid.puml[svg]</p></div>

See here, the asciidoctor left original plantuml block source instead of HTML block. The document contains about 15 diagrams, most of them build successfully, but some has been skipped like this.

I've thorougly inspected all the diagrams in the document and I found PlantUML internal error (java.lang.IllegalArgument wzception) in some of them after PlantUML upgrade to version 1.2023.9.

I know how fix these diagrams, but there is a problem with asciidoctor-kroki: the asciidoctor-kroki implementation suppress errors from PlantUML, shows successful build with broken documents. There is a problem for large documentation project with hundreds of documents.

I'm not JavaScript developer, but I can try to help you with debug and fix that if you tell me some suggestions where to find the cause in asciidoctor-kroki source.

ggrossetie commented 12 months ago

Could you please provide a source file that reproduces this issue?

romkavt commented 12 months ago

Here is a fragment of asciidoctor document with problem diagram:


Предназначен для сервисов, которым требуется:

*  Возможность получить авторизацию YooId для последующей работы через один или несколько сервисов API

plantuml::sdk-hierarchy-v2-yooid.puml[svg]

=== Привязка Кошелька

Here is a source plantuml diagram file:

@startuml
title Базовый модуль SDK: YooId
!include <archimate/Archimate>
!include lib/plantuml-styles.iuml

Grouping(SDK, "SDK") {
    !include lib/sdk-component-yooid.iuml
    !include lib/sdk-component-tmx.iuml
}

Application_Component(App, "Приложение ЛК YooKassa")
Rel_Aggregation(App, SdkTmx, "содержит")
Rel_Aggregation(App, SdkYooId, "содержит")

Application_Function(FuncApp, "Личный кабинет YooKassa")
Rel_Assignment_Up(App, FuncApp, "реализует")

Application_DataObject(UserAuthorization, "Авторизация\nпользователя YooId\n(авторизация СЦА)")
Rel_Association_Left(App, UserAuthorization, "хранит\nна устройстве")

!include lib/sdk-component-clientid.iuml
@enduml

The problem root is

Grouping(SDK, "SDK") {
}

The plantuml 2023.9.1 crashes with java.lang.IllegalArgumentError and asciidoctor-kroki ignores this showing successful build with <div class="paragraph"><p>plantuml::sdk-hierarchy-v2-yooid.puml[svg]</p></div> in resulting HTML document.

ggrossetie commented 12 months ago

Thanks! Since you are using a relative paths with the !include directive, could you please provide the directory structure? Do you use !include inside lib/plantuml-styles.iuml, lib/sdk-component-yooid.iuml, lib/sdk-component-tmx.iuml or lib/sdk-component-clientid.iuml?

romkavt commented 11 months ago
<Project_Root>/
    build.js
    sdk/
        sdk-hierarhy-usecases.adoc with plantuml::sdk-hierarchy-v2-yooid.puml[svg]
        sdk-hierarchy-v2-yooid.puml
        ...and other adoc and puml files
       lib/
           sdk-component-yooid.iuml and other iuml files

But dir structure doesn't matter, I think. The error triggering instruction is Grouping(SDK, "SDK") { } and you can comment out all includes — the error still reproducible.

ggrossetie commented 11 months ago

OK, so the following should be enough:

@startuml
Grouping(SDK, "SDK") { }
@enduml
ggrossetie commented 11 months ago

I get the following error:

<p>GET <a href="https://kroki.io/plantuml/svg/eNpzL8ovLcjMS9cIdvHWUVACkkqaCtUKtVwAaXkHMw==" class="bare">https://kroki.io/plantuml/svg/eNpzL8ovLcjMS9cIdvHWUVACkkqaCtUKtVwAaXkHMw==</a> - server returns an empty response or a 404 status code - plantuml::/path/to/diag.puml[]</p>

I also got a message in the console:

Skipping plantuml block macro. GET https://kroki.io/plantuml/svg/eNpzL8ovLcjMS9cIdvHWUVACkkqaCtUKtVwAaXkHMw== - server returns an empty response or a 404 status code

test.js

const asciidoctor = require('@asciidoctor/core')()
const kroki = require('asciidoctor-kroki')

const registry = asciidoctor.Extensions.create()
kroki.register(registry)

const html = asciidoctor.convert('plantuml::diag.puml[]', {
  to_file: false,
  standalone: true,
  safe: 'safe',
  extension_registry: registry,
  attributes: {
    'kroki-fetch-diagram': true,
    'kroki-default-options': 'inline',
    'imagesdir': 'build/images',
    'allow-uri-read': 'true',
    'data-uri': 'true',
    'icons': 'font',
    'webfonts!': '',
  }
})

console.log(html)

diag.puml

@startuml
Grouping(SDK, "SDK") { }
@enduml
$ node test.js
Complete output ```` Skipping plantuml block macro. GET https://kroki.io/plantuml/svg/eNpzL8ovLcjMS9cIdvHWUVACkkqaCtUKtVwAaXkHMw== - server returns an empty response or a 404 status code Untitled

GET https://kroki.io/plantuml/svg/eNpzL8ovLcjMS9cIdvHWUVACkkqaCtUKtVwAaXkHMw== - server returns an empty response or a 404 status code - plantuml::/home/guillaume/Workspace/tmp/asciidoctor-js/diag.puml[]

romkavt commented 11 months ago

Thanks. Could you suggest how I can enable verbose (debug) logging/tracing the kroki requests/responses within asciidoctor-kroki.js? I'am not JavaScript developer, but I think, I can make some code navigation and updates in JS code.

romkavt commented 11 months ago

I've spent some time to debug this issue and found something. I think this is concurrency issue.

I have root build.js file with list of documents like this:

renderer.convert('sdk/index.adoc', 'whitelabel-sdk.html')
renderer.convert('sdk/tc5-yoomoney-integration-hld.adoc')

the broken images are always in second file. If I change the sequence of these files — the broken images are always in second file, it doesn't matter of order or contents of the file.

I found, if I init extensions instance for every execution - the problem doesn't appear anymore:

convert: function (source, destination, repositoryRootUri='.', projectRootUri='.') {
        const registry = asciidoctor.Extensions.create()
        kroki.register(registry)
        const html = asciidoctor.convertFile(source, {
                to_file: false,
                standalone: true,
                safe: 'safe',
                extension_registry: registry,
                attributes: 
...
ggrossetie commented 11 months ago

Someone created a similar issue in Asciidoctor.js: https://github.com/asciidoctor/asciidoctor.js/issues/1709

@mojavelinux Does that ring a bell? We are probably doing something wrong (i.e., our assumptions were wrong) but it seems that we need to manually (re)activate extension registry if the registry is used multiple times.

I vaguely remember a bug about extensions and activation but I cannot fully remember.

ggrossetie commented 11 months ago

Might be related: https://github.com/asciidoctor/asciidoctor/commit/28c69077665c655b37281029feb1876d98527093

mojavelinux commented 11 months ago

That's correct. This should have been addressed in 2.0.18. However, there's no guarantee it covers every use case. A registry is not really designed to be reused. Ideally, it should be created per conversion.