From 27657484a8e8891552d630c2e74b0948742ef0e8 Mon Sep 17 00:00:00 2001
From: Arnaud Levy <contact@arnaudlevy.com>
Date: Tue, 4 Jan 2022 11:35:29 +0100
Subject: [PATCH] clean architecture

---
 app/models/communication/website/git_file.rb | 21 ++++-
 app/services/git/providers/github.rb         | 84 ++++++++++----------
 app/services/git/repository.rb               | 17 ++--
 docs/websites/export.md                      | 26 ++++--
 4 files changed, 93 insertions(+), 55 deletions(-)

diff --git a/app/models/communication/website/git_file.rb b/app/models/communication/website/git_file.rb
index d67ff35c1..5b420edc3 100644
--- a/app/models/communication/website/git_file.rb
+++ b/app/models/communication/website/git_file.rb
@@ -30,8 +30,21 @@ class Communication::Website::GitFile < ApplicationRecord
     website.git_repository.add_git_file git_file
   end
 
-  def synced?
-    previous_path == path && previous_sha == sha
+  def synchronized_with_git?
+    git_sha == previous_sha
+  end
+
+  def should_create?
+    !synchronized_with_git? || previous_path.nil? || previous_sha.nil?
+  end
+
+  def should_update?
+    !synchronized_with_git? || previous_path != path || previous_sha != sha
+  end
+
+  def should_destroy?
+    # TODO
+    false
   end
 
   def path
@@ -55,6 +68,10 @@ class Communication::Website::GitFile < ApplicationRecord
 
   protected
 
+  def git_sha
+    @git_sha ||= website.git_repository.git_sha previous_path
+  end
+
   # def add_media_to_batch(github)
   #   return unless manifest_data[:has_media] && about.respond_to?(:active_storage_blobs)
   #   about.active_storage_blobs.each { |blob| add_blob_to_batch(github, blob) }
diff --git a/app/services/git/providers/github.rb b/app/services/git/providers/github.rb
index 78fea8cb2..b6daf07d2 100644
--- a/app/services/git/providers/github.rb
+++ b/app/services/git/providers/github.rb
@@ -6,32 +6,40 @@ class Git::Providers::Github
     @repository = repository
   end
 
-  def add_to_batch( path: nil,
-                    previous_path: nil,
-                    data:)
+  def create_file(path, content)
+    batch << {
+      path: path,
+      mode: '100644', # https://docs.github.com/en/rest/reference/git#create-a-tree
+      type: 'blob',
+      content: content
+    }
+  end
+
+  def update_file(path, previous_path, content)
     file = find_in_tree previous_path
-    if file.nil? # New file
-      batch << {
-        path: path,
-        mode: '100644', # https://docs.github.com/en/rest/reference/git#create-a-tree
-        type: 'blob',
-        content: data
-      }
-    elsif previous_path != path || git_sha(previous_path) != sha(data)
-      # Different path or content
-      batch << {
-        path: previous_path,
-        mode: file[:mode],
-        type: file[:type],
-        sha: nil
-      }
-      batch << {
-        path: path,
-        mode: file[:mode],
-        type: file[:type],
-        content: data
-      }
-    end
+    batch << {
+      path: previous_path,
+      mode: file[:mode],
+      type: file[:type],
+      sha: nil
+    }
+    batch << {
+      path: path,
+      mode: file[:mode],
+      type: file[:type],
+      content: content
+    }
+  end
+
+  def destroy_file(path)
+    file = find_in_tree path
+    return if file.nil?
+    batch << {
+      path: path,
+      mode: file[:mode],
+      type: file[:type],
+      sha: nil
+    }
   end
 
   def push(commit_message)
@@ -43,6 +51,16 @@ class Git::Providers::Github
     true
   end
 
+  def git_sha(path)
+    begin
+      content = client.content repository, path: path
+      sha = content[:sha]
+    rescue
+      sha = nil
+    end
+    sha
+  end
+
   protected
 
   def valid?
@@ -69,22 +87,6 @@ class Git::Providers::Github
     @tree ||= client.tree repository, branch_sha, recursive: true
   end
 
-  def git_sha(path)
-    begin
-      content = client.content repository, path: path
-      sha = content[:sha]
-    rescue
-      sha = nil
-    end
-    sha
-  end
-
-  def sha(data)
-    # Git SHA-1 is calculated from the String "blob <length>\x00<contents>"
-    # Source: https://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html
-    OpenSSL::Digest::SHA1.hexdigest "blob #{data.bytesize}\x00#{data}"
-  end
-
   def find_in_tree(path)
     tree[:tree].each do |file|
       return file if path == file[:path]
diff --git a/app/services/git/repository.rb b/app/services/git/repository.rb
index 7cd2916b4..573c34c51 100644
--- a/app/services/git/repository.rb
+++ b/app/services/git/repository.rb
@@ -16,6 +16,10 @@ class Git::Repository
     mark_as_synced if provider.push(commit_message)
   end
 
+  def git_sha(path)
+    provider.git_sha path
+  end
+
   protected
 
   def provider
@@ -27,11 +31,14 @@ class Git::Repository
   end
 
   def sync_git_files
-    git_files.each do |git_file|
-      next if git_file.synced?
-      provider.add_to_batch path: git_file.path,
-                            previous_path: git_file.previous_path,
-                            data: git_file.to_s
+    git_files.each do |file|
+      if file.should_create?
+        provider.create_file file.path, file.to_s
+      elsif file.should_update?
+        provider.update_file file.path, file.previous_path, file.to_s
+      elsif file.should_destroy?
+        provider.destroy_file file.path
+      end
     end
   end
 
diff --git a/docs/websites/export.md b/docs/websites/export.md
index e7fdeb9fe..d5de3dadf 100644
--- a/docs/websites/export.md
+++ b/docs/websites/export.md
@@ -64,10 +64,15 @@ def update
 end
 
 def destroy
-
+  @page.save_and_sync
 end
 ```
 
+
+Pour les reorder :
+```
+```
+
 TODO gérer la suppression correctement
 
 ## Code
@@ -85,11 +90,12 @@ Tous les objets qui doivent être exportés vers Git :
 - peuvent présenter une méthode `static_files` qui liste les identifiants des git_files à générer, pour les objets qui créent plusieurs fichiers
 
 ### GitFile
+
 La responsabilité de la synchronisation repose sur Communication::Website::GitFile, notamment :
-- le fichier doit-il être synchronisé ?
-- le fichier doit-il être créé ?
-- le fichier doit-il être déplacé ?
-- le fichier doit-il être supprimé ?
+- l'information est-elle intègre, synchronisée avec le repo ? (previous_sha et previous_path cohérents avec le repo git)
+- le fichier doit-il être créé ? (non intègre, ou pas de previous_sha/previous_path)
+- le fichier doit-il être mis à jour ? (non intègre, ou previous_sha/previous_path différent du sha/path)
+- le fichier doit-il être supprimé ? (TODO)
 
 
 Pour cela, le git_file dispose des propriétés suivantes :
@@ -98,8 +104,14 @@ Pour cela, le git_file dispose des propriétés suivantes :
 - identifier (l'identifiant du fichier à créer, `static` par défaut, pour les objets créant plusieurs fichiers)
 
 
-Et pour générer les fichiers, il dispose des méthodes :
+Pour informer sur les actions à mener, il dispose des méthodes interrogatives suivantes :
+- synchronized_with_git? (pour évaluer l'intégrité vs le repository)
+- should_create? (pour savoir s'il faut regénérer ou pas)
+- should_update? (pour savoir s'il faut regénérer ou pas)
+- should_destroy? (pour savoir s'il faut supprimer)
+
+
+Pour générer les fichiers, il dispose des méthodes :
 - to_s (pour générer le fichier statique à jour)
 - sha (pour calculer le hash du fichier à jour)
 - path (pour générer le chemin à jour)
-- synced? (pour savoir s'il faut regénérer ou pas)
-- 
GitLab