Refactor finding process models to support finding other file types (#516)

This commit is contained in:
jbirddog 2023-09-29 09:38:45 -04:00 committed by GitHub
parent f3e7bbaa02
commit 37ae7e2f7f
3 changed files with 118 additions and 34 deletions

View File

@ -1,7 +1,10 @@
import os
from flask import current_app from flask import current_app
from spiffworkflow_backend.models.cache_generation import CacheGenerationModel from spiffworkflow_backend.models.cache_generation import CacheGenerationModel
from spiffworkflow_backend.models.db import db from spiffworkflow_backend.models.db import db
from spiffworkflow_backend.models.reference_cache import ReferenceCacheModel from spiffworkflow_backend.models.reference_cache import ReferenceCacheModel
from spiffworkflow_backend.services.file_system_service import FileSystemService
from spiffworkflow_backend.services.process_model_service import ProcessModelService from spiffworkflow_backend.services.process_model_service import ProcessModelService
from spiffworkflow_backend.services.spec_file_service import SpecFileService from spiffworkflow_backend.services.spec_file_service import SpecFileService
from sqlalchemy import insert from sqlalchemy import insert
@ -12,6 +15,15 @@ class DataSetupService:
def run_setup(cls) -> list: def run_setup(cls) -> list:
return cls.save_all_process_models() return cls.save_all_process_models()
@classmethod
def add_unique_reference_cache_object(
cls, reference_objects: dict[str, ReferenceCacheModel], reference_cache: ReferenceCacheModel
) -> None:
reference_cache_unique = (
f"{reference_cache.identifier}{reference_cache.relative_location}{reference_cache.type}"
)
reference_objects[reference_cache_unique] = reference_cache
@classmethod @classmethod
def save_all_process_models(cls) -> list: def save_all_process_models(cls) -> list:
"""Build a cache of all processes, messages, correlation keys, and start events. """Build a cache of all processes, messages, correlation keys, and start events.
@ -20,10 +32,13 @@ class DataSetupService:
from the database. from the database.
""" """
current_app.logger.debug("DataSetupService.save_all_process_models() start") current_app.logger.debug("DataSetupService.save_all_process_models() start")
failing_process_models = [] failing_process_models = []
process_models = ProcessModelService.get_process_models(recursive=True) files = FileSystemService.walk_files_from_root_path(True, None)
reference_objects = {} reference_objects: dict[str, ReferenceCacheModel] = {}
for process_model in process_models: for file in files:
if FileSystemService.is_process_model_json_file(file):
process_model = ProcessModelService.get_process_model_from_path(file)
current_app.logger.debug(f"Process Model: {process_model.display_name}") current_app.logger.debug(f"Process Model: {process_model.display_name}")
try: try:
# FIXME: get_references_for_file_contents is erroring out for elements in the list # FIXME: get_references_for_file_contents is erroring out for elements in the list
@ -32,10 +47,7 @@ class DataSetupService:
for ref in refs: for ref in refs:
try: try:
reference_cache = ReferenceCacheModel.from_spec_reference(ref) reference_cache = ReferenceCacheModel.from_spec_reference(ref)
reference_cache_unique = ( cls.add_unique_reference_cache_object(reference_objects, reference_cache)
f"{reference_cache.identifier}{reference_cache.relative_location}{reference_cache.type}"
)
reference_objects[reference_cache_unique] = reference_cache
SpecFileService.update_caches_except_process(ref) SpecFileService.update_caches_except_process(ref)
db.session.commit() db.session.commit()
except Exception as ex: except Exception as ex:
@ -52,6 +64,20 @@ class DataSetupService:
str(ex2), str(ex2),
) )
) )
elif FileSystemService.is_data_store_json_file(file):
relative_location = FileSystemService.relative_location(file)
file_name = os.path.basename(file)
(identifier, _) = os.path.splitext(file_name)
reference_cache = ReferenceCacheModel.from_params(
identifier,
identifier,
"data_store",
file_name,
relative_location,
None,
False,
)
cls.add_unique_reference_cache_object(reference_objects, reference_cache)
current_app.logger.debug("DataSetupService.save_all_process_models() end") current_app.logger.debug("DataSetupService.save_all_process_models() end")

View File

@ -1,5 +1,6 @@
import json import json
import os import os
from collections.abc import Callable
from collections.abc import Generator from collections.abc import Generator
from contextlib import contextmanager from contextlib import contextmanager
from datetime import datetime from datetime import datetime
@ -18,6 +19,11 @@ class ProcessModelFileNotFoundError(Exception):
pass pass
DirectoryPredicate = Callable[[str, int], bool] | None
FilePredicate = Callable[[str], bool] | None
FileGenerator = Generator[str, None, None]
class FileSystemService: class FileSystemService:
"""Simple Service meant for extension that provides some useful """Simple Service meant for extension that provides some useful
@ -38,6 +44,45 @@ class FileSystemService:
finally: finally:
os.chdir(prevdir) os.chdir(prevdir)
@classmethod
def walk_files(
cls, start_dir: str, directory_predicate: DirectoryPredicate, file_predicate: FilePredicate
) -> FileGenerator:
depth = 0
for root, subdirs, files in os.walk(start_dir):
if directory_predicate:
subdirs[:] = [dir for dir in subdirs if directory_predicate(dir, depth)]
for f in files:
file = os.path.join(root, f)
if file_predicate and not file_predicate(file):
continue
yield file
depth += 1
@classmethod
def non_git_dir(cls, dirname: str, depth: int) -> bool:
return dirname != ".git"
@classmethod
def not_recursive(cls, dirname: str, depth: int) -> bool:
return depth == 0
@classmethod
def standard_directory_predicate(cls, recursive: bool) -> DirectoryPredicate:
return cls.non_git_dir if recursive else cls.not_recursive
@classmethod
def is_process_model_json_file(cls, file: str) -> bool:
return file.endswith(cls.PROCESS_MODEL_JSON_FILE)
@classmethod
def is_data_store_json_file(cls, file: str) -> bool:
return file.endswith("_datastore.json")
@classmethod
def walk_files_from_root_path(cls, recursive: bool, file_predicate: FilePredicate) -> FileGenerator:
yield from cls.walk_files(cls.root_path(), cls.standard_directory_predicate(recursive), file_predicate)
@staticmethod @staticmethod
def root_path() -> str: def root_path() -> str:
dir_name = current_app.config["SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR"] dir_name = current_app.config["SPIFFWORKFLOW_BACKEND_BPMN_SPEC_ABSOLUTE_DIR"]
@ -137,6 +182,10 @@ class FileSystemService:
workflow_path = FileSystemService.process_model_full_path(process_model) workflow_path = FileSystemService.process_model_full_path(process_model)
return os.path.relpath(workflow_path, start=FileSystemService.root_path()) return os.path.relpath(workflow_path, start=FileSystemService.root_path())
@classmethod
def relative_location(cls, path: str) -> str:
return os.path.dirname(os.path.relpath(path, start=FileSystemService.root_path()))
@staticmethod @staticmethod
def process_group_path_for_spec(process_model: ProcessModelInfo) -> str: def process_group_path_for_spec(process_model: ProcessModelInfo) -> str:
# os.path.split apparently returns 2 element tulple like: (first/path, last_item) # os.path.split apparently returns 2 element tulple like: (first/path, last_item)

View File

@ -2,7 +2,6 @@ import json
import os import os
import shutil import shutil
import uuid import uuid
from glob import glob
from json import JSONDecodeError from json import JSONDecodeError
from typing import TypeVar from typing import TypeVar
@ -153,6 +152,11 @@ class ProcessModelService(FileSystemService):
path = os.path.join(FileSystemService.root_path(), relative_path) path = os.path.join(FileSystemService.root_path(), relative_path)
return cls.__scan_process_model(path) return cls.__scan_process_model(path)
@classmethod
def get_process_model_from_path(cls, path: str) -> ProcessModelInfo:
relative_path = os.path.relpath(path, start=FileSystemService.root_path())
return cls.get_process_model_from_relative_path(os.path.dirname(relative_path))
@classmethod @classmethod
def get_process_model(cls, process_model_id: str) -> ProcessModelInfo: def get_process_model(cls, process_model_id: str) -> ProcessModelInfo:
"""Get a process model from a model and group id. """Get a process model from a model and group id.
@ -180,13 +184,18 @@ class ProcessModelService(FileSystemService):
awesome_id = process_group_id.replace("/", os.sep) awesome_id = process_group_id.replace("/", os.sep)
root_path = os.path.join(root_path, awesome_id) root_path = os.path.join(root_path, awesome_id)
process_model_glob = os.path.join(root_path, "*", "process_model.json") if recursive is None:
if recursive: recursive = False
process_model_glob = os.path.join(root_path, "**", "process_model.json")
process_model_files = FileSystemService.walk_files(
root_path,
FileSystemService.standard_directory_predicate(recursive),
FileSystemService.is_process_model_json_file,
)
for file in process_model_files:
process_model = cls.get_process_model_from_path(file)
for file in glob(process_model_glob, recursive=True):
process_model_relative_path = os.path.relpath(file, start=FileSystemService.root_path())
process_model = cls.get_process_model_from_relative_path(os.path.dirname(process_model_relative_path))
if include_files: if include_files:
files = FileSystemService.get_sorted_files(process_model) files = FileSystemService.get_sorted_files(process_model)
for f in files: for f in files: