Account created on 16 June 2008, almost 17 years ago
  • Principal Developer / Drupal Practice Lead at Velir 
#

Merge Requests

More

Recent comments

🇺🇸United States kevinquillen

I needed a quick off the shelf theme, client liked the layout/colors vs all the others on drupal.org. I was able to edit the right pieces to get it to work for me (header/footer/nav).

🇺🇸United States kevinquillen

A few notes:

I suggest leveraging the AI module as an optional dependency for Search API Solr to facilitate embeddings and/or some generic tagged service that can collect more. But being that the AI module is popular, its a good place to start.

The Typesense module uses the AI provider manager to get a list of providers to offer as embedding options:

https://git.drupalcode.org/project/search_api_typesense/-/blob/1.0.x/src...

That gives you a list of models to use, which would expand support beyond HuggingFace (which AI also supports too ).

Then, the field type vectorDimension should be configurable, as each one has a different limit. OpenAI for example is 1534 (something like that), and others vary quite a bit. This should open up options for people using different providers that have different embedding capabilities.

I am not sure where Transformers come into play, but its possible not everyone would have access to edit the ini conf to enable it. Is it possible to swap that out to use the AI provider capability of embedding on the fly to query with?

🇺🇸United States kevinquillen

One minor point is that it is a little hard to determine (for non technical or novice users) which version they should be using. For example:

https://www.drupal.org/project/entity_clone

Both are the same color and at a glance look basically the same, 2.1 is D11 compatible though. But there are projects out there that may have two major version branches running in parallel, but one is the 'recommended' one to use. Maybe some visual cues or improvements can be done to help distinguish beyond blue tinted release boxes.

🇺🇸United States kevinquillen

Yeah. At the time it was the simplest way to do model filtering, but they keep adding more and a regex will only get more complex. It was a way to keep people from selecting a model that wasn't going to work. Perhaps we should change that now.

🇺🇸United States kevinquillen

Test failure appears to be in the base Domain module.

🇺🇸United States kevinquillen

So if we are using guzzle directly and not the drupal client your issue might indeed be true.

If I read this right, specifically with OpenAI, the OpenAI client library uses Guzzle and not the one from \Drupal::httpClient, which would preconfigure it properly.

https://github.com/openai-php/client/blob/main/src/Factory.php#L180

But, the AiProviderClientBase class is injecting an instance of a client from http_client_factory with options from the provider configuration:

  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $client_options = $configuration['http_client_options'] ?? [];

    return new static(
      $plugin_id,
      $plugin_definition,
      $container->get('http_client_factory')->fromOptions($client_options + [
        'timeout' => 60,
      ]),

Which is then passed to the OpenAI wrapper to use as the Client:

https://git.drupalcode.org/project/ai_provider_openai/-/blob/1.1.x/src/P...

So I think the AI module(s) are doing the right thing here. I suppose one question is, here, where is this configuration set, or does more need to be added to the provider to pass along?

$client_options = $configuration['http_client_options'] ?? [];

🇺🇸United States kevinquillen

I'm not sure if 1024 is the limit anymore. I remember the docs used to mention that, but it looks like this was fixed in 9.3:

https://issues.apache.org/jira/browse/SOLR-16836

🇺🇸United States kevinquillen

Rebuilding node permissions seems to fix this issue, but I am not sure how it got into that state to begin with.

🇺🇸United States kevinquillen

I could be wrong but this seems like a big issue. The module appears to assign a domain_id record - but I think in order to adequately have this enforced and checked, you need to override the core AliasManager and AliasRepository to have their lookups respect the domain_id key. Otherwise it seems like you can enter any path for a Domain and if it matches a path assigned to another domain, Drupal will return that and route it to the user.

I am kind of stumped at how to get the desired behavior.

🇺🇸United States kevinquillen

This is a bit more challenging than I thought. So far, I came up with

    $global_config = $this->configFactory->get('system.site');
    $domain_config = $this->configFactory->get('domain.config.' . $domain_id . '.system.site');

    if ($domain_config) {
      $path = $domain_config->get('page.404');

      if (!empty($path)) {
        return $this->getAliasByPath($path, $langcode);
      }
    }

    if ($global_config) {
      $path = $global_config->get('page.404');

      if (!empty($path)) {
        return $this->getAliasByPath($path, $langcode);
      }
    }

    return $this->inner->getPathByAlias($alias, $langcode);

But this breaks valid, non-content paths (like /admin paths) and returns a 404. Attempts to load the route by path or alias seems to result in recursion errors.

🇺🇸United States kevinquillen

Thats what I mean, I don't think this takes over normal paste, because of its behavior. That is why its set to the CTRL SHIFT V shortcut.

🇺🇸United States kevinquillen

So it did work? Just clarifying because I did not install it yet (due to this thread) but checked through the CK5 docs first.

🇺🇸United States kevinquillen

Still getting this in 2.1.x dev:


TypeError: Cannot assign null to property CommerceGuys\Addressing\Validator\Constraints\AddressFormatConstraint::$validatePostalCode of type bool in Symfony\Component\Validator\Constraint->__construct() (line 120 of /var/www/html/vendor/symfony/validator/Constraint.php).
CommerceGuys\Addressing\Validator\Constraints\AddressFormatConstraint->__construct() (Line: 31)
Drupal\Core\Validation\ConstraintFactory->createInstance() (Line: 83)
Drupal\Component\Plugin\PluginManagerBase->createInstance() (Line: 86)
Drupal\Core\Validation\ConstraintManager->create() (Line: 394)
🇺🇸United States kevinquillen

Would it be possible to get this fix out into a stable release?

🇺🇸United States kevinquillen

Interesting, do you have an error trace beyond that? I only see one instance of max_tokens mentioned in the provider code:

https://git.drupalcode.org/project/ai_provider_openai/-/blob/1.0.x/src/P...

🇺🇸United States kevinquillen

For Acquia Platform Email, reply-to header is required or you will get this error (Amazon SES service). The patch made this work, although I do not understand why you have to uncheck 'Disable site email notification' to get reply-to to be set. Can't it just be set by default to the site mail that is sending the message out and not include the site mail as a (main) recipient?

       $recipient = '';
      $reply_to = NULL;
       if (!$notification->disableSiteMail()) {
         $recipient = \Drupal::config('system.site')->get('mail');
        $reply_to = $recipient;
    }

Why not simply $reply_to = \Drupal::config('system.site')->get('mail');?

🇺🇸United States kevinquillen

The CKEditor docs say:

With the plain text pasting feature, text pasted using the Ctrl/Cmd + Shift + V keystroke will match the formatting of the content you paste it into.

Are folks using CTRL SHIFT V, or just CTRL V (CMD V)?

When I try the demo page here:

https://ckeditor.com/docs/ckeditor5/latest/features/pasting/paste-plain-...

CTRL V pastes as-is from Clipboard, but CTRL SHIFT V pastes as plain text.

🇺🇸United States kevinquillen

I reconfigured the test to not rely on a custom module that also required Domain (for the domain.negotiator service in one of its classes) and this issue went away. It must be something in Domain module and similar modules that rely on core/lib/Drupal/Core/Path/PathMatcher::matchPath.

🇺🇸United States kevinquillen

I was able to trigger this in a simple kernel test where I want to check that some custom entity routes are marked as admin.


/**
 * Tests the EntityAdminHtmlRouteProvider.
 */
class EntityAdminHtmlRouteProviderTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'cci_api',
    'cci_card',
    'domain',
    'node',
    'text',
    'user',
    'system',
  ];

  /**
   * The entity type manager service.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * @var \Drupal\Core\Routing\AccessAwareRouter
   */
  protected AccessAwareRouter $router;

  /**
   * @var \Drupal\Core\ProxyClass\Routing\RouteBuilder
   */
  protected RouteBuilder $routerBuilder;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installEntitySchema('mymodule');
    $this->installSchema('system', ['sequences']);
    $this->entityTypeManager = $this->container->get('entity_type.manager');
    $this->router = $this->container->get('router');
    $this->routerBuilder = $this->container->get('router.builder');
    $this->routerBuilder->rebuild();
  }

  /**
   * Tests if the canonical route is correctly configured.
   */
  public function testCanonicalRoute(): void {
    $collection = $this->router->getRouteCollection();
    $route = $collection->get('entity.mymodule_entity.canonical');
    $this->assertTrue($route->getOption('_admin_route'), 'The route is marked as an admin route.');
  }

}

The test passes, but is marked with a deprecation. Not sure how to get around it.

🇺🇸United States kevinquillen

The proposed change seems to have no impact on Chrome 135 (Linux) in Drupal 11.1 for node forms. On my custom entity forms, the behavior in Chrome is not present (multiple clicks does not result in multiple entities created) using Gin for the admin theme. I tried Claro too, same result.

Interestingly enough, it does not happen at all in Firefox or Waterfox. It does happen in Brave.

🇺🇸United States kevinquillen

Yes... this would be great for seeing the relationships and all entity fields.

🇺🇸United States kevinquillen

Like others, just chiming in to say I ran into this exact issue in 11.1. In my case, I have a half dozen custom content entities that I recently added content moderation for (states of data, don't think of them like Nodes).

In any event, I made them using Drush 13. One of the things Drush generates is an entity form with boilerplate like this:

final class ResultForm extends ContentEntityForm {

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state): int {
    $result = parent::save($form, $form_state);

    $message_args = ['%label' => $this->entity->toLink()->toString()];
    $logger_args = [
      '%label' => $this->entity->label(),
      'link' => $this->entity->toLink($this->t('View'))->toString(),
    ];

    switch ($result) {
      case SAVED_NEW:
        $this->messenger()->addStatus($this->t('New result %label has been created.', $message_args));
        $this->logger('cci_result')->notice('New result %label has been created.', $logger_args);
        break;

      case SAVED_UPDATED:
        $this->messenger()->addStatus($this->t('The result %label has been updated.', $message_args));
        $this->logger('cci_result')->notice('The result %label has been updated.', $logger_args);
        break;

      default:
        throw new \LogicException('Could not save the entity.');
    }

    $form_state->setRedirectUrl($this->entity->toUrl('collection'));

    return $result;
  }

}

Upon adding content moderation and workflow against them, they started failing when testing "Published" -> "Draft" state. Its because parent::save returns false (which core modules consider okay) and that causes the exception to be thrown here.

Updating my save method to this, mimicing core Media/Node, made it work:

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $result = parent::save($form, $form_state);

    $message_args = ['%label' => $this->entity->toLink()->toString()];
    $logger_args = [
      '%label' => $this->entity->label(),
      'link' => $this->entity->toLink($this->t('View'))->toString(),
    ];

    switch ($result) {
      case SAVED_NEW:
        $this->messenger()->addStatus($this->t('New result %label has been created.', $message_args));
        $this->logger('cci_messages')->notice('New result %label has been created.', $logger_args);
        break;

      default:
        $this->messenger()->addStatus($this->t('The result %label has been updated.', $message_args));
        $this->logger('cci_messages')->notice('The result %label has been updated.', $logger_args);
        break;
    }

    $form_state->setRedirectUrl($this->entity->toUrl());

    return $result;
  }

It was disorienting because the false value returned made me think the save failed - but it doesn't fail. Its just not the default revision. I think this should be addressed as soon as possible, it seems like the code comments are incorrect as mentioned.

And yes, core and specifically entity forms are doing a very bad job at that.

Given that Drupal can make an excellent API platform for controlling and managing data beyond the "Node" type, there must be something that can be done here to improve clarity. The diff looks pretty simple - does it really matter if the saved entity was the default revision or not?

🇺🇸United States kevinquillen

I thought I could get away with this by creating a link template for my custom entity and specifying that in the matcher. While that was working, I wanted the link element to also have additional attributes added automatically (because this specific entity match should open in a modal window) - i.e. let JS take over. Without requiring any additional action from the editor. I could not figure out how to modify the element before it was added into CKEditor to have that.

I guess my only option because I am crunched for time is to add in https://www.drupal.org/project/editor_advanced_link and add a custom checkbox or two and work the rest from JS.

🇺🇸United States kevinquillen

Just came across this issue when trying to figure out how to resolve this for frontend developers on a project where invalid libraries syntax was not caught.

I located a schema here: https://json.schemastore.org/drupal-libraries.json

After hooking it to my IDE, it started noting incorrect syntax or keys in library files.

It would be nice if Drupal could also catch this and gracefully degrade (in our case, a missing 'css' key threw an exception on the site).

🇺🇸United States kevinquillen

According to the README, UUID should not be copied into any domain.config* file. This seems like it could be a simple addition to the Domain module to prevent that from happening with:


declare(strict_types=1);

namespace Drupal\mymodule\EventSubscriber;

use Drupal\Core\Config\ConfigEvents;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\StorageTransformEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * Listen for configuration events and remove UUIDs from domain.config entities.
 */
final class ConfigExportSubscriber implements EventSubscriberInterface {

  /**
   * Handle the storage transform export event.
   *
   * @param \Drupal\Core\Config\StorageTransformEvent $event
   *   The event.
   */
  public function onStorageTransformExport(StorageTransformEvent $event): void {
    $this->removeDomainConfigUuid($event->getStorage());
  }

  /**
   * Handle the storage transform import event.
   *
   * @param \Drupal\Core\Config\StorageTransformEvent $event
   *   The event.
   */
  public function onStorageTransformImport(StorageTransformEvent $event): void {
    $this->removeDomainConfigUuid($event->getStorage());
  }

  /**
   * Remove UUIDs from any domain.config configuration entity.
   *
   * @param \Drupal\Core\Config\StorageInterface $storage
   *   The storage interface.
   */
  protected function removeDomainConfigUuid(StorageInterface $storage): void {
    $domain_configs = $storage->listAll('domain.config');

    foreach ($domain_configs as $domain_config) {
      $config = $storage->read($domain_config);

      if (isset($config['uuid'])) {
        unset($config['uuid']);
      }

      $storage->write($domain_config, $config);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    return [
      ConfigEvents::STORAGE_TRANSFORM_IMPORT => ['onStorageTransformImport', -99],
      ConfigEvents::STORAGE_TRANSFORM_EXPORT => ['onStorageTransformExport', -99],
    ];
  }

}

is there any reason why this does not exist, or why I should not do this? The domain_config_test module includes several YAML files that also have no UUID with them.

🇺🇸United States kevinquillen

#14 so you are saying you have to delete the UUID manually every time from a domain.config.* file? The issue you linked to goes to a 404.

🇺🇸United States kevinquillen

The proposed change would add filter-ability similar to what Node and Users get in Views along with "Current Domain" options. This makes it easier OOTB for those who have or implement custom entities to work with Domain.

🇺🇸United States kevinquillen

I tried looking at other examples of outbound processors in core and contrib, but I could not locate any that themselves called Url::fromUserInput. The only route I can think of is to at least try/catch here so it prevents the OP from occurring and offers some way of recovering and fixing the data.

This was really hard to replicate - somehow my issue (and assume OP issue) was that $path was passed as NULL (or ''). Which should not happen, and the how it happened... not quite sure. But a try/catch won't harm anything here.

🇺🇸United States kevinquillen

I ran into this with a misconfigured Site Studio component and a Link field that was returning "" because it could not look up the user input value, it did not resolve to any entity in the database.

The problem was here:


  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    // Load the active domain if not set.
    $active_domain = $options['active_domain'] ?? $this->getActiveDomain();

    // Only act on valid internal paths and when a domain loads.
    $external = $options['external'] ?? FALSE;
    if (is_null($active_domain) || $path === '' || $external === TRUE) {
      return $path;
    }

    // Set the default source information.
    $source = NULL;
    $options['active_domain'] = $active_domain;
    $entity = NULL;

    // Get the current language.
    $langcode = NULL;
    if (isset($options['language'])) {
      $langcode = $options['language']->getId();
    }

    // Get the URL object for this request.
    $alias = $this->aliasManager->getPathByAlias($path, $langcode);
    $url = Url::fromUserInput($alias, $options);

I think at a minimum the lines:

    // Get the URL object for this request.
    $alias = $this->aliasManager->getPathByAlias($path, $langcode);
    $url = Url::fromUserInput($alias, $options);

should be wrapped in a try catch. Log the path, request, options, alias and return $path. That gives someone a chance to trace back where the input came from, potentially, and lets pages load.

I could not access the Content admin, URL Alias page, URL Redirect page, or the Admin Content view or areas like that until I resolved this.

To get back up and going to debug the problem for your specific local database, you can do:

if ($path == '') {
  return $path;
}

    // Get the URL object for this request.
    $alias = $this->aliasManager->getPathByAlias($path, $langcode);
    $url = Url::fromUserInput($alias, $options);

in DomainSourcePathProcessor.php. That will prevent the pages from crashing so you can trace back the error. In my case, I snagged a database, fixed the content issue, and pushed the database back under maintenance to get the site back online.

🇺🇸United States kevinquillen

Updated patch, removes newline.

Without the change(s) the module version is not usable in Drupal 11.

🇺🇸United States kevinquillen

kevinquillen made their first commit to this issue’s fork.

🇺🇸United States kevinquillen

Something in the way this patch handles UUIDs was causing fatal errors with Site Studio on importing new configuration with:

2024-12-19T15:10:04Z  [notice] Finalizing configuration synchronization.
2024-12-19T15:10:04Z In ConfigImportCommands.php line 291:
2024-12-19T15:10:04Z   The import failed due to the following reasons:
2024-12-19T15:10:04Z   Unexpected error during import with operation create for media.type.vector_
2024-12-19T15:10:04Z   image: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 's
2024-12-19T15:10:04Z   ource_uuid' cannot be null: INSERT INTO "coh_usage" ("s
2024-12-19T15:10:04Z   ource_uuid", "source_type", "requires_uuid", &quot
2024-12-19T15:10:04Z   ;requires_type") VALUES (:db_insert_placeholder_0, :db_insert_placehol
2024-12-19T15:10:04Z   der_1, :db_insert_placeholder_2, :db_insert_placeholder_3); Array
2024-12-19T15:10:04Z   (
2024-12-19T15:10:04Z       [:db_insert_placeholder_0] =>
2024-12-19T15:10:04Z       [:db_insert_placeholder_1] => media_type
2024-12-19T15:10:04Z       [:db_insert_placeholder_2] => media-mid
2024-12-19T15:10:04Z       [:db_insert_placeholder_3] => field_config
2024-12-19T15:10:04Z   )
2024-12-19T15:10:04Z   Unexpected error during import with operation create for field.field.media.
2024-12-19T15:10:04Z   vector_image.field_media_svg: SQLSTATE[23000]: Integrity constraint violati
2024-12-19T15:10:04Z   on: 1048 Column 'source_uuid' cannot be null: INSERT INTO "c
2024-12-19T15:10:04Z   oh_usage" ("source_uuid", "source_type", "req
2024-12-19T15:10:04Z   uires_uuid", "requires_type") VALUES (:db_insert_placeholder
2024-12-19T15:10:04Z   _0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placehol
2024-12-19T15:10:04Z   der_3); Array
2024-12-19T15:10:04Z   (
2024-12-19T15:10:04Z       [:db_insert_placeholder_0] =>
2024-12-19T15:10:04Z       [:db_insert_placeholder_1] => field_config
2024-12-19T15:10:04Z       [:db_insert_placeholder_2] => d38bcb72-e5ae-4e9a-99b8-3298da041238
2024-12-19T15:10:04Z       [:db_insert_p

These errors are very odd, because it implies UUIDs are NULL, when they aren't. So I went digging, and after removing patch #31, I couldn't get this error at all. Putting it back made it happen again.

I think it is because of this:

$originalUuid = $this->getOriginal('uuid', FALSE);

where it should be:

$originalUuid = $this->getOriginal('uuid', FALSE) ?? $this->get('uuid');

because according to getOriginal in Config:

   * Original data is the data as it is immediately after loading from
   * configuration storage before any changes. If this is a new configuration
   * object it will be an empty array.

originalUuid was coming up NULL for new configuration objects, where $this->get('uuid') was returning the UUID stored in the configuration yaml file. I am not 100% on all the things this patch is doing, but I think this is a potential problem which was hard to track down, but we need the functionality this patch provides (Domain specific config).

Attached is an updated patch with that change.

🇺🇸United States kevinquillen

Not sure what happened, but after removing the patch, clearing cache a few times, restarting ddev, re-applying the patch, the error does not occur anymore.

However it will still export with the same UUID until you have deleted all previously created records and flushed it out of Drupal, just a note.

🇺🇸United States kevinquillen

Happens to me on a brand new site with the very first configuration export and then the next import. This does seem site breaking IMO, because its really easy to walk into that and then get stuck.

Patch #31 alone throws this error:

ArgumentCountError: Too few arguments to function Drupal\domain_config_ui\Config\ConfigFactory::__construct(), 4 passed in /var/www/html/docroot/core/lib/Drupal/Component/DependencyInjection/Container.php on line 259 and exactly 5 expected in Drupal\domain_config_ui\Config\ConfigFactory->__construct() (line 45 of /var/www/html/docroot/modules/contrib/domain/domain_config_ui/src/Config/ConfigFactory.php). #0 /var/www/html/docroot/core/lib/Drupal/Component/DependencyInjection/Container.php(259): Drupal\domain_config_ui\Config\ConfigFactory->__construct()

Visiting the site shows:

The website encountered an unexpected error. Try again later.
Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException: Circular reference detected for service "domain_config_ui.factory", path: "options_request_listener -> domain.route_provider -> path_processor_manager -> path_processor_front -> domain_config_ui.factory -> exception.fast_404_html". in Drupal\Component\DependencyInjection\Container->get() (line 147 of core/lib/Drupal/Component/DependencyInjection/Container.php).
Drupal\Component\DependencyInjection\Container->get() (Line: 430)
Drupal\Component\DependencyInjection\Container->resolveServicesAndParameters() (Line: 237)
Drupal\Component\DependencyInjection\Container->createService() (Line: 177)
Drupal\Component\DependencyInjection\Container->get() (Line: 454)
Drupal\Component\DependencyInjection\Container->Drupal\Component\DependencyInjection\{closure}() (Line: 243)
Symfony\Component\EventDispatcher\EventDispatcher::Symfony\Component\EventDispatcher\{closure}() (Line: 206)
Symfony\Component\EventDispatcher\EventDispatcher->callListeners() (Line: 56)
Symfony\Component\EventDispatcher\EventDispatcher->dispatch() (Line: 241)
Symfony\Component\HttpKernel\HttpKernel->handleThrowable() (Line: 91)
Symfony\Component\HttpKernel\HttpKernel->handle() (Line: 53)
Drupal\Core\StackMiddleware\Session->handle() (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle() (Line: 28)
Drupal\Core\StackMiddleware\ContentLength->handle() (Line: 32)
Drupal\big_pipe\StackMiddleware\ContentLength->handle() (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle() (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle() (Line: 36)
Drupal\Core\StackMiddleware\AjaxPageState->handle() (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle() (Line: 709)
Drupal\Core\DrupalKernel->handle() (Line: 19)

on Drupal 11.0.9.

🇺🇸United States kevinquillen

The module lacks a composer.json, so I cannot require a forked version via Composer.

🇺🇸United States kevinquillen

Could we get these changes in so it is installable in Drupal 11?

🇺🇸United States kevinquillen

I've been using a lot more Playwright than Cypress lately. One reason was I needed to get up and run it in CI very quickly, which was not simple in the past with Cypress and do some multi device testing. It wound up being a lot easier to configure and reason about, and better on execution and performance. I think it would be a beneficial add.

Playwright also has a UI mode: https://playwright.dev/docs/test-ui-mode

You can also use the browser Recorder (dev console) to playback and export test specs from the interactions. They aren't always 100% perfect but can save a lot of time.

🇺🇸United States kevinquillen

Think this is all that is required to make it installable in Drupal 11.

🇺🇸United States kevinquillen

+1 from me, sounds good.

🇺🇸United States kevinquillen

#10 the reason for this is to not break compatibility with Javascript deserializers written for JSON:API specification. The module can still include everything in the relevant areas as long as it does not drop 'attributes' 'relationships' etc. I suspect many libraries like JSONA simply expect them to exist with values. Without them, it cannot parse responses. You can see this here:

https://github.com/olosegres/jsona/blob/master/src/builders/JsonDeserial...

here is another example in another library:

https://github.com/SeyZ/jsonapi-serializer/blob/master/lib/deserializer-...

or another:

https://github.com/ShadowManu/jsonapi-deserializer/blob/master/src/index...

This would require frontend developers to write their own parsers which can take several dozens of hours or more. At least with the option the structure can be preserved without losing the additional features (including all resources possible).

Production build 0.71.5 2024