From 7f409d5466a95f5e054d2db4ce41ec0a81b67ac3 Mon Sep 17 00:00:00 2001
From: Guillaume Grossetie <ggrossetie@gmail.com>
Date: Fri, 6 Nov 2020 13:22:36 +0100
Subject: [PATCH] Integrate Asciidoctor Kroki gem

---
 Gemfile                                      |  1 +
 Gemfile.lock                                 | 44 ++++++++++++++++++++
 lib/banzai/filter/kroki_filter.rb            | 12 +++---
 lib/banzai/pipeline/ascii_doc_pipeline.rb    |  1 -
 lib/gitlab/asciidoc.rb                       | 22 ++++++++--
 lib/gitlab/asciidoc/kroki_block_processor.rb | 37 ----------------
 lib/gitlab/kroki.rb                          |  8 ----
 7 files changed, 68 insertions(+), 57 deletions(-)
 delete mode 100644 lib/gitlab/asciidoc/kroki_block_processor.rb
 delete mode 100644 lib/gitlab/kroki.rb

diff --git a/Gemfile b/Gemfile
index 80d617286d1a6..e0ec983ad7678 100644
--- a/Gemfile
+++ b/Gemfile
@@ -159,6 +159,7 @@ gem 'wikicloth', '0.8.1'
 gem 'asciidoctor', '~> 2.0.10'
 gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
 gem 'asciidoctor-plantuml', '~> 0.0.12'
+gem 'asciidoctor-kroki', '~> 0.2.0'
 gem 'rouge', '~> 3.25.0'
 gem 'truncato', '~> 0.7.11'
 gem 'bootstrap_form', '~> 4.2.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index b9fb5d1085ee0..45bdca0bb4d91 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,7 @@
 GEM
   remote: https://rubygems.org/
   specs:
+    Ascii85 (1.0.3)
     RedCloth (4.3.2)
     abstract_type (0.0.7)
     acme-client (2.0.6)
@@ -71,6 +72,7 @@ GEM
     addressable (2.7.0)
       public_suffix (>= 2.0.2, < 5.0)
     aes_key_wrap (1.1.0)
+    afm (0.2.2)
     akismet (3.0.0)
     android_key_attestation (0.3.0)
     apollo_upload_server (2.0.2)
@@ -84,6 +86,21 @@ GEM
     asciidoctor (2.0.10)
     asciidoctor-include-ext (0.3.1)
       asciidoctor (>= 1.5.6, < 3.0.0)
+    asciidoctor-kroki (0.2.0)
+      asciidoctor (~> 2.0)
+      asciidoctor-pdf (= 1.5.3)
+    asciidoctor-pdf (1.5.3)
+      asciidoctor (>= 1.5.3, < 3.0.0)
+      concurrent-ruby (~> 1.1.0)
+      prawn (~> 2.2.0)
+      prawn-icon (~> 2.5.0)
+      prawn-svg (~> 0.30.0)
+      prawn-table (~> 0.2.0)
+      prawn-templates (~> 0.1.0)
+      safe_yaml (~> 1.0.0)
+      thread_safe (~> 0.3.0)
+      treetop (~> 1.6.0)
+      ttfunk (~> 1.5.0, >= 1.5.1)
     asciidoctor-plantuml (0.0.12)
       asciidoctor (>= 1.5.6, < 3.0.0)
     ast (2.4.1)
@@ -563,6 +580,7 @@ GEM
     hana (1.3.6)
     hangouts-chat (0.0.5)
     hashdiff (1.0.1)
+    hashery (2.1.2)
     hashie (3.6.0)
     hashie-forbidden_attributes (0.1.1)
       hashie (>= 3.0)
@@ -839,12 +857,33 @@ GEM
     parser (2.7.2.0)
       ast (~> 2.4.1)
     parslet (1.8.2)
+    pdf-core (0.7.0)
+    pdf-reader (2.4.1)
+      Ascii85 (~> 1.0.0)
+      afm (~> 0.2.1)
+      hashery (~> 2.0)
+      ruby-rc4
+      ttfunk
     peek (1.1.0)
       railties (>= 4.0.0)
     pg (1.2.3)
     png_quantizator (0.2.1)
     po_to_json (1.0.1)
       json (>= 1.6.0)
+    polyglot (0.3.5)
+    prawn (2.2.2)
+      pdf-core (~> 0.7.0)
+      ttfunk (~> 1.5)
+    prawn-icon (2.5.0)
+      prawn (>= 1.1.0, < 3.0.0)
+    prawn-svg (0.30.0)
+      css_parser (~> 1.6)
+      prawn (>= 0.11.1, < 3)
+    prawn-table (0.2.2)
+      prawn (>= 1.3.0, < 3.0.0)
+    prawn-templates (0.1.2)
+      pdf-reader (~> 2.0)
+      prawn (~> 2.2)
     premailer (1.11.1)
       addressable
       css_parser (>= 1.6.0)
@@ -1049,6 +1088,7 @@ GEM
       crack (~> 0.4)
     ruby-prof (1.3.1)
     ruby-progressbar (1.10.1)
+    ruby-rc4 (0.1.5)
     ruby-saml (1.7.2)
       nokogiri (>= 1.5.10)
     ruby-statistics (2.1.2)
@@ -1176,9 +1216,12 @@ GEM
     tpm-key_attestation (0.9.0)
       bindata (~> 2.4)
       openssl-signature_algorithm (~> 0.4.0)
+    treetop (1.6.11)
+      polyglot (~> 0.3)
     truncato (0.7.11)
       htmlentities (~> 4.3.1)
       nokogiri (>= 1.7.0, <= 2.0)
+    ttfunk (1.5.1)
     tzinfo (1.2.8)
       thread_safe (~> 0.1)
     u2f (0.2.1)
@@ -1269,6 +1312,7 @@ DEPENDENCIES
   asana (= 0.10.2)
   asciidoctor (~> 2.0.10)
   asciidoctor-include-ext (~> 0.3.1)
+  asciidoctor-kroki (~> 0.2.0)
   asciidoctor-plantuml (~> 0.0.12)
   atlassian-jwt (~> 0.2.0)
   attr_encrypted (~> 3.1.0)
diff --git a/lib/banzai/filter/kroki_filter.rb b/lib/banzai/filter/kroki_filter.rb
index 95d1bcf7f27cc..881b390bd3688 100644
--- a/lib/banzai/filter/kroki_filter.rb
+++ b/lib/banzai/filter/kroki_filter.rb
@@ -1,18 +1,17 @@
 # frozen_string_literal: true
 
 require "nokogiri"
-require "zlib"
-require "base64"
+require "asciidoctor/extensions/asciidoctor_kroki/extension"
 
 module Banzai
   module Filter
     # HTML that replaces all diagrams supported by Kroki with the corresponding img tags.
     #
     class KrokiFilter < HTML::Pipeline::Filter
-      DIAGRAM_SELECTORS = ::Gitlab::Kroki::DIAGRAM_TYPES
+      DIAGRAM_SELECTORS = ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES
                               .map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
                               .join(', ')
-      DIAGRAM_SELECTORS_WO_PLANTUML = ::Gitlab::Kroki::DIAGRAM_TYPES
+      DIAGRAM_SELECTORS_WO_PLANTUML = ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES
                                           .select { |diagram_type| diagram_type != 'plantuml' }
                                           .map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
                                           .join(', ')
@@ -39,10 +38,9 @@ def call
 
       private
 
-      # QUESTION: should should we use the asciidoctor-kroki gem to delegate this logic?
       def create_image_src(type, format, text)
-        data = Base64.urlsafe_encode64(Zlib::Deflate.deflate(text, 9))
-        "#{settings.kroki_url}/#{type}/#{format}/#{data}"
+        ::AsciidoctorExtensions::KrokiDiagram.new(type, format, text)
+            .get_diagram_uri(settings.kroki_url)
       end
 
       def settings
diff --git a/lib/banzai/pipeline/ascii_doc_pipeline.rb b/lib/banzai/pipeline/ascii_doc_pipeline.rb
index 1f6a47bf939b0..90edc7010f45b 100644
--- a/lib/banzai/pipeline/ascii_doc_pipeline.rb
+++ b/lib/banzai/pipeline/ascii_doc_pipeline.rb
@@ -9,7 +9,6 @@ def self.filters
           Filter::AssetProxyFilter,
           Filter::SyntaxHighlightFilter,
           Filter::ExternalLinkFilter,
-          Filter::KrokiFilter,
           Filter::PlantumlFilter,
           Filter::ColorFilter,
           Filter::ImageLazyLoadFilter,
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index 3ea698550ec11..a7ab8e00126fd 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -2,6 +2,7 @@
 
 require 'asciidoctor'
 require 'asciidoctor-plantuml'
+require 'asciidoctor/extensions/asciidoctor_kroki/extension'
 require 'asciidoctor/extensions'
 require 'gitlab/asciidoc/html5_converter'
 require 'gitlab/asciidoc/mermaid_block_processor'
@@ -23,7 +24,14 @@ module Asciidoc
         'source-highlighter' => 'gitlab-html-pipeline',
         'icons' => 'font',
         'outfilesuffix' => '.adoc',
-        'max-include-depth' => MAX_INCLUDE_DEPTH
+        'max-include-depth' => MAX_INCLUDE_DEPTH,
+        # This feature is disabled because it relies on File#read to read the file.
+        # If we want to enable this feature we will need to provide a "GitLab compatible" implementation.
+        # This attribute is typically used to share common config (skinparam...) across all PlantUML diagrams.
+        # The value can be a path or a URL.
+        'kroki-plantuml-include!' => '',
+        # This feature is disabled because it relies on the local file system to save diagrams retrieved from the Kroki server.
+        'kroki-fetch-diagram!' => ''
     }.freeze
 
     def self.path_attrs(path)
@@ -50,8 +58,8 @@ def self.render(input, context)
         block ::Gitlab::Asciidoc::MermaidBlockProcessor
 
         if Gitlab::CurrentSettings.kroki_enabled
-          ::Gitlab::Kroki::DIAGRAM_TYPES.each do |name|
-            block ::Gitlab::Asciidoc::KrokiBlockProcessor, name
+          ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES.each do |name|
+            block ::AsciidoctorExtensions::KrokiBlockProcessor, name
           end
         end
       end
@@ -59,7 +67,13 @@ def self.render(input, context)
       extra_attrs = path_attrs(context[:requested_path])
       asciidoc_opts = { safe: :secure,
                         backend: :gitlab_html5,
-                        attributes: DEFAULT_ADOC_ATTRS.merge(extra_attrs),
+                        attributes: DEFAULT_ADOC_ATTRS
+                                        .merge(extra_attrs)
+                                        .merge({
+                                                   # Define the Kroki server URL from the settings.
+                                                   # This attribute cannot be overridden from the AsciiDoc document.
+                                                   'kroki-server-url' => Gitlab::CurrentSettings.kroki_url
+                                               }),
                         extensions: extensions }
 
       context[:pipeline] = :ascii_doc
diff --git a/lib/gitlab/asciidoc/kroki_block_processor.rb b/lib/gitlab/asciidoc/kroki_block_processor.rb
deleted file mode 100644
index 078c72f835bdd..0000000000000
--- a/lib/gitlab/asciidoc/kroki_block_processor.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require 'asciidoctor'
-
-module Gitlab
-  module Asciidoc
-    # Kroki BlockProcessor
-    #
-    class KrokiBlockProcessor < ::Asciidoctor::Extensions::BlockProcessor
-      use_dsl
-
-      on_context :literal, :listing
-      parse_content_as :simple
-
-      def process(parent, reader, attrs)
-        diagram_type = @name
-        diagram_text = reader.string
-        create_kroki_source_block(parent, diagram_type, diagram_text, attrs)
-      end
-
-      private
-
-      def create_kroki_source_block(parent, diagram_type, diagram_text, attrs)
-        # If "subs" attribute is specified, substitute accordingly.
-        # Be careful not to specify "specialcharacters" or your diagram code won't be valid anymore!
-        subs = attrs['subs']
-        diagram_text = parent.apply_subs(diagram_text, parent.resolve_subs(subs)) if subs
-        html = %(<div><pre data-kroki-style="display" lang="#{diagram_type}"><code>#{CGI.escape_html(diagram_text)}</code></pre></div>)
-        ::Asciidoctor::Block.new(parent, :pass, {
-          content_model: :raw,
-          source: html,
-          subs: :default
-        }.merge(attrs))
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/kroki.rb b/lib/gitlab/kroki.rb
deleted file mode 100644
index fff1aa14a7ad5..0000000000000
--- a/lib/gitlab/kroki.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-# frozen_string_literal: true
-
-module Gitlab
-  module Kroki
-    # QUESTION: should we use the asciidoctor-kroki gem?
-    DIAGRAM_TYPES = %w(plantuml ditaa graphviz blockdiag seqdiag actdiag nwdiag packetdiag rackdiag c4plantuml erd mermaid nomnoml svgbob umlet vega vegalite wavedrom).freeze
-  end
-end
-- 
GitLab