Problem/Motivation
As it exists as of the Beta 3 release, the Library service (patternkit.asset.library) has a number of issues that could benefit from added attention and refactoring. The original intent of this class was as a demonstrated resolution to issues described in #3154343: [META] Update Asset Libraries and Discovery to use a Plugin System, but in practice it has some shortcomings. Key among these issues and opportunities for cleanup, are the following:
- The CacheCollector functionality is not properly implemented since library definitions are being indexed by library name instead of extension name. This results in the
get()method being used inconsistently, and theresolveCacheMiss()method to always kick off full discovery across all modules before persisting an individual result to the discovery cache. - Since assembled library definitions are not tied in any way to their providing extensions, the
getLibrariesByExtension()method results in a full discovery across all modules and then only returns the first library defined by that module which is inconsistent with the method documentation. - Access to pattern definitions that have been discovered requires a reload and parse through all library definitions again, when instead, this could realistically be cached on its own.
- The library service attempts to handle responsibility over too many things, including library file parsing, library definition creation, library overrides and altering, caching, pattern discovery, and pattern resolution. Maintaining all of this in a single class makes for a very complex implementation and makes things much more difficult to trace and debug.
Proposed resolution
To address these issues, I propose breaking up the Library class into separate services focused more on independent responsibilities.
LibraryNamespaceResolver (patternkit.library.namespace_resolver)
This service addresses the responsibility of mapping library namespaces to the providing extensions and their library definitions. To achieve this, it builds on top of the already assembled and altered library discovery information provided by Core via the @library.discovery service. This offloads much of the work for considering how other assets are handled in libraries that our module doesn't care about and lets our service focus on processing and capturing the pattern collection data at the library definition level before it is processed and individual patterns are discovered.
Once the library data is prepared with the inclusion of pattern-specific collection data, this service is responsible for caching this information.
PatternDiscovery (patternkit.pattern.discovery)
This service serves as the entry point for most developer needs to work with discovered pattern definitions. Through this service, the developer has access to retrieve pattern definitions that have been discovered, whether that be a specific pattern, all patterns within a given namespace, or all patterns that have been discovered across all namespaces. Behind the scenes, the PatternDiscoveryCollector service is used to discover and cache these pattern definitions.
PatternDiscoveryCollector (patternkit.pattern.discovery.collector)
This service is the driver for leveraging the pattern library data assembled in the LibraryNamespaceResolver and feeding it to the existing PatternLibraryParser services in order to discover available patterns. These discovered patterns are then cached for access via the PatternDiscovery service.
Remaining tasks
User interface changes
None
API changes
- See new services introduced above.
- Usage of the Library (
patternkit.asset.library) service is deprecated and marked for removal in the 1.0 release. - Pattern definitions should now be loaded through the
patternkit.pattern.discoveryservice. - The "Use Patternkit Library Cache" option temporarily has no affect until follow-up work is completed.
Data model changes
| Comment | File | Size | Author |
|---|---|---|---|
| #10 | patternkit-3295824--library-service-refactor--10.patch | 165.6 KB | slucero |
Issue fork patternkit-3295824
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
sluceroComment #3
sluceroI've drafted a change record here outlining these changes and their affect on common developer tasks: https://www.drupal.org/node/3295833.
Comment #5
sluceroI've drafted an additional change record here documenting flattening of schemas during dereferencing: https://www.drupal.org/node/3295843. This became part of the work during testing as I identified several issues the logic being used for dereferencing and the new solution simplified the process significantly. This should also indirectly address some of the challenges described by #3268970: Fetching schemas takes very long time since those reference requests will no longer be necessary.
Comment #6
sluceroAll tests are now passing, so this is ready for review and testing.
Comment #7
sluceroI've rebased this branch after merging in #3295055: Pattern Validation Sometimes Fails When Encountering Empty Objects and all tests are still showing green. This work is ready for review and testing.
Testing Instructions
To install the changes for this issue for testing using a Composer patch-based workflow, add the following snippet to your composer.json file.
Comment #8
sluceroI've received reports and examples from testers encountering issues with the work as of #7 and one of the most troublesome issues was loading of nested patterns containing potential for circular references resulting in an infinite loop and eventual crash of the request. This was encountered during the schema dereferencing process using the new logic to flatten schemas, so I've rolled back those changes in the last two commits and we'll have to revisit that effort with more testing and a more robust solution for caching or dereferencing into schema definitions after the beta 4 release. This may also imply some work needed in the underlying swaggest/php-json-schema library.
Rolling this back means the change record for flattening schemas is no longer relevant to this issue or the Beta4 release.
Comment #9
cyb_tachyon commentedThis is a fantastic update!
One feature I'd like to note that needs reviewing is allowing multiple modules/themes to contribute to a single library namespace along with overrides.
Since this is already a large refactor, it might be worth clarifying in the codebase handling the specific scenario, and how overrides are addressed. This is something that's inconsistent even in Drupal core so looking at if core maintainers have made any progress here could also be good.
Comment #10
sluceroThanks for the feedback @cyb.tachyon, that's a good note to keep in mind. The existing logic for that was something I maintained to some degree in the refactor, but I did add a @todo note to review how that's being handled currently and what functionality we should seek to continue supporting on it moving forward.
Testing Instructions
To ease testing of this feature, I'm attaching a patch file for the current state of the MR. If you're using a Composer-based patching workflow, you may install and test this work by adding the following to the patches section of your
composer.jsonfile:Comment #12
sluceroMerging in for release in Beta 4. See #3278420: Beta 4 release plan.