Brian Perry: Extending The Drupal API Client

import RadCallout from '../../../components/rad/RadCallout.astro';

As a result of our Pitch-burgh funding, the current focus of the Drupal API Client is to create a fully featured client for Drupal's JSON:API implementation. Even with that goal, we've focused on making our work extensible for other API formats in the future through the implementation of an ApiClient base class. Functionality that could apply to any API client is added to the base class, while anything specific to JSON:API is added to the JsonApiClient class (which extends ApiClient.)

Recently, we have been working on adding Decoupled Router support to our JSON:API Client. I found this implementation to be a great example of the extensibility of the library, so I wanted elaborate on it in a blog post for those who may want to extend the API Client in the future.

The existing JsonApiClient has the following method to retrieve data for a resource:

await client.getResource('node--article', '3347c400-302d-4f6c-8fcb-3e74beb002c8');

Ideally, users of Decoupled Router could also get an identical response by resolving a path:

await client.getResource('/articles/give-it-a-go-and-grow-your-own-herbs');

To achieve this, we first needed to provide a way to reliably get data from Decoupled Router.

The Decoupled Router Endpoint

With the module enabled, Decoupled Router exposes an endpoint with the following structure:

/router/translate-path?path=<path>

Given a path like /articles/give-it-a-go-and-grow-your-own-herbs the endpoint could provide a response similar to:

{ "resolved": "https://dev-drupal-api-client-poc.pantheonsite.io/en/articles/give-it-a-go-and-grow-your-own-herbs", "isHomePath": false, "entity": { "canonical": "https://dev-drupal-api-client-poc.pantheonsite.io/en/articles/give-it-a-go-and-grow-your-own-herbs", "type": "node", "bundle": "article", "id": "11", "uuid": "3347c400-302d-4f6c-8fcb-3e74beb002c8" }, "label": "Give it a go and grow your own herbs", "jsonapi": { "individual": "https://dev-drupal-api-client-poc.pantheonsite.io/en/jsonapi/node/article/3347c400-302d-4f6c-8fcb-3e74beb002c8", "resourceName": "node--article", "pathPrefix": "jsonapi", "basePath": "/jsonapi", "entryPoint": "https://dev-drupal-api-client-poc.pantheonsite.io/en/jsonapi" }, "meta": { "deprecated": { "jsonapi.pathPrefix": "This property has been deprecated and will be removed in the next version of Decoupled Router. Use basePath instead." } } }

While easy to make sense of, this response technically doesn't follow the JSON:API spec, which prevents us from using our existing JSON:API Client without modification. We could write a small amount of custom code in JsonApiClient to fetch and handle data from this endpoint, but this case is exactly what our ApiClient base class is intended for. With a similarly small amount of code we can extend the ApiClient class to add only what is unique to the Decoupled Router endpoint, while getting access to all of the features of the base class at the same time.

So rather than writing code specific to JsonApiClient, we decided to create a new DecoupledRouterClient class that our JsonApiClient could then make use of.

Extending ApiClient

For the sake of example, a simple Decoupled Router client could look like this:

// DecoupledRouterClient.ts import { ApiClient, type ApiClientOptions, type BaseUrl, } from "@drupal-api-client/api-client"; export class DecoupledRouterClient extends ApiClient { constructor(baseUrl: BaseUrl, options?: ApiClientOptions) { super(baseUrl, options); const { apiPrefix } = options || {}; this.apiPrefix = apiPrefix || "router/translate-path"; } async translatePath(path: string) { const apiUrl = `${this.baseUrl}/${this.apiPrefix}?path=${path}`; const response = await this.fetch(apiUrl); return response.json(); } }

In our constructor, the only modification we need to make is the default value for the API prefix. While the base class doesn't have a default, Decoupled Router uses router/translate-path. Now when instance of DecoupledRouter is created without this option, it will use the default.

We then define a translatePath method that:

  • Takes a path of type string
  • Uses the fetch method provided by the base class to make a request to Decoupled Router
  • Returns a promise with the provided json data

Using an instance of this class would look something like:

// main.ts import { DecoupledRouterClient } from "./DecoupledRouterClient.ts"; const decoupledRouterClient = new DecoupledRouterClient("https://dev-drupal-api-client-poc.pantheonsite.io"); const translatedPath = await decoupledRouterClient.translatePath( "/articles/give-it-a-go-and-grow-your-own-herbs" );

<RadCallout>Check out this code sandbox for a live version of the example above.</RadCallout>

Taking Advantage of Additional ApiClient Features

With this example we already have a functional client, but quite a bit more is possible using the features of the ApiClient class we extended. For example, We can already make authenticated requests using any of the supported authentication methods:

// main.ts import { DecoupledRouterClient } from "./DecoupledRouterClient.ts"; const decoupledRouterClient = new DecoupledRouterClient("https://dev-drupal-api-client-poc.pantheonsite.io", { authentication: { type: "OAuth", credentials: { clientId: "client-id", clientSecret: "client-secret" } }, }); // API requests will now be authenticated const translatedPath = await decoupledRouterClient.translatePath( "/articles/give-it-a-go-and-grow-your-own-herbs" );

Our example Decoupled Router client could be updated to take advantage of built in caching, logging, or locale support. For example, the following modification would allow us to make use of the defaultLocale option if our Drupal site supports multiple languages:

// DecoupledRouterClient.ts import { ApiClient, type ApiClientOptions, type BaseUrl, } from "@drupal-api-client/api-client"; export class DecoupledRouterClient extends ApiClient { constructor(baseUrl: BaseUrl, options?: ApiClientOptions) { super(baseUrl, options); const { apiPrefix } = options || {}; this.apiPrefix = apiPrefix || "router/translate-path"; } async translatePath(path: string) { // If it exists, incorporate the default locale // into the apiUrl const apiUrlObject = new URL( `${this.defaultLocale ?? ""}/${this.apiPrefix}?path=${path}`, this.baseUrl, ); const apiUrl = apiUrlObject.toString(); const response = await this.fetch(apiUrl); return response.json(); } }

Routing is a common problem, so we've added a fully featured getResourceByPath method to our latest @drupal-api-client/json-api-client release. We've also published the Decoupled Router client as a standalone package for anyone who wants to use it separately.

While the caching functionality of the client can lessen the impact, getResourceByPath still makes multiple API calls for uncached data, which leaves room for improvement. We could optimize this in the future by providing support for the subrequests module. That is yet another client for a type of Drupal API that could use the ApiClient base class as a starting point.

We're closing in on the 1.0 release of @drupal-api-client/json-api-client. If you’re interested in contributing, check out our project page on Drupal.org, and join us in the #api-client channel on Drupal Slack.

LN Webworks: Must-Know Features Of Webform Module For Drupal 10

Image removed.

The Webform module works as a form of builder and submission manager within the Drupal framework, offering a wide range of levels of flexibility and ease for site builders. This tool empowers website creators to efficiently develop a range of forms, with the added benefit of default settings for quick implementation. Delving into its impressive features, the Webform module is known for its user-friendly interface. Users can swiftly create forms using default configurations or take advantage of the module's customization options to tailor forms to precise specifications.

Beyond this, the Webform module boasts a suite of powerful features, making it a core asset of the Drupal ecosystem. But there’s more to it. Let’s have an overview of the most important features and functionalities of the Webform. 

Tag1 Consulting: Gander Automated Performance Testing - Video Demo with Catch

In this second part (check the first part!) of our Tag1 Team Talk on Gander, the new Automated Performance Testing Framework integrated into Drupal Core, we get a live demo from Nat Catchpole (aka. Catch), the lead developer on the project. Nat takes us on a tour through this high-impact tool developed by Tag1 in collaboration with the Google Chrome Team, showing you how you can get up and running with automated performance testing for your projects. Gander is poised to significantly impact Drupal's user experience, performance and Core Web Vitals by creating visibility into how Drupal performs on the front and back end. Catch shares his expert insights into the development and application of Gander and shows how easy it is for developers to start extending and using this today on their projects! Whether you're a Drupal developer looking to improve your project's performance or simply curious about the latest in Drupal technology, this episode offers valuable knowledge and practical advice on getting your Drupal website to perform optimally. With discussions on Gander's immediate benefits and future potential in the Drupal community, this episode is a must-watch for anyone interested in taking their projects to the next level...

Read more Mariano Tue, 02/06/2024 - 05:41

PreviousNext: Adding real-time processing to QueueWorker plugins

Projects no longer need to rely on unpredictable processing time frames. The SM project can intercept legacy Drupal @QueueWorker items and insert them into the Symfony Messenger message bus, effectively giving existing core and contrib queue workers jobs real-time processing capabilities.

by daniel.phin / 7 February 2024

This post is part 5 in a series about Symfony Messenger.

  1. Introducing Symfony Messenger integrations with Drupal
  2. Symfony Messenger’ message and message handlers, and comparison with @QueueWorker
  3. Real-time: Symfony Messenger’ Consume command and prioritised messages
  4. Automatic message scheduling and replacing hook_cron
  5. Adding real-time processing to QueueWorker plugins
  6. Making Symfony Mailer asynchronous: integration with Symfony Messenger
  7. Displaying notifications when Symfony Messenger messages are processed
  8. Future of Symfony Messenger in Drupal

QueueWorker plugins

@QueueWorker plugin implementations require no modifications, including the method of dispatch, data payload, or the processItem . The data payload must of course be serialisable. Fortunately, most QueueWorker plugins already comply since their data is serialised and stored to the queue table. As always, avoid adding complex objects like Drupal entities to payloads.

Runners

With queue interception, the sm command can be solely relied upon. Legacy runners such as Drupal web cron, request termination cron (automated_cron.module), and Drush queue:run will be rendered inoperable since they will no longer have anything to process. Consider decommissioning legacy runners when deploying queue interception.

Setup

Queue interception is a part of the primary SM module. Adding a single line in settings.php is the only action required to to enabling this feature:

$settings['queue_default'] = \Drupal\sm\QueueInterceptor\SmLegacyQueueFactory::class;

SM module will need to be fully installed before this line is added. Consider wrapping the line in a class_exists(SmLegacyQueueFactory::class) to enable in a single deployment.

Existing per-queue backends

Setup may be more complex if projects are utilising per-queue backends or anything other than the default database backend for queues, such as Redis. In that case, carefully evaluate whether to convert all or specific queues to use Symfony Messenger.

Whether per-queue backends are utilised can be determined by looking for queue_service_ or queue_reliable_service_ prefixed items in settings.php.

Routing

@QueueWorker jobs are converted to \Drupal\sm\QueueInterceptor\SmLegacyDrupalQueueItem messages in the backend. Knowing this class name allows you to configure transport routing. If routing for this message is not explicitly configured, it will naturally fall back to the default transport, or execute synchronously if there is no routing configuration.

Running the jobs

As usual, when a transport is configured, all you need to do is run sm messenger:consume to execute the tasks. The worker will either listen or poll for messages, and execute them in a very short amount of time after they are dispatched, in a dedicated thread. More information on the worker can be found in post 3 of this series.

The next post covers how Drupal emails can be dispatched to messages, so the web thread can execute faster.

Tagged

Symfony, Symfony Messenger

Drupal Core News: DrupalCI and all patch testing will be turned off on July 1, 2024

To continue automated testing, projects must convert to GitLab CI and contributors must switch from patches to merge requests by July 1, 2024.

As of July 2023, contributed projects are fully equipped to adopt GitLab CI. In October 2023 we announced that Gitlab CI testing of Drupal core was already five times faster than the legacy DrupalCI system. In our December 2023 maintainer email we announced that our legacy DrupalCI will be retired as soon as July 2024. Now we are announcing further details.

What's happening?

Image removed.

Some DrupalCI features are already turned off

Projects without DrupalCI testing configured cannot add it anymore. Direct access to log output and artifacts on DrupalCI is no longer available. Results are still summarized on the Automated Testing tab of those project's pages.

With the exception of Drupal 7, it is not possible to run tests on patches against Drupal core anymore. Even for Drupal 7, patches need to be sent for testing manually.

Changing DrupalCI schedules ends on April 30, 2024

After April 30, 2024, DrupalCI schedules can not be added or changed, except to remove DrupalCI testing. Tests will keep running with existing configured schedules until removed.

DrupalCI and all patch testing will be turned off on July 1, 2024

No DrupalCI tests will be executed after this date. It will also no longer be possible to run tests on any patches on Drupal.org on any project. Existing test results will be kept for six months.

All results from DrupalCI tests will be removed on January 1, 2025

This is the end of DrupalCI data retention. No testing results will be available beyond January 1, 2025.

What to do about it?

Most projects can start GitLab CI testing with a provided template .gitlab-ci.yml

Using GitLab CI for testing may seem intimidating at first but the Drupal Association engineering team and a number of community volunteers have spent a lot of time to make it as easy as possible for you to adopt GitLab CI for testing.

There is a preconfigured .gitlab-ci.yml template that will set up everything you need to test your project. This template has several useful features, including a variables file that will be automatically updated by the Drupal Association, to make sure you’re always testing against the currently supported versions of Drupal.

Most project maintainers won’t have to make any changes to the template, just commit the template to your project and your testing should work right away!

Set up GitLab CI for your drupal.org project today.

Contributors must use merge requests on all projects to get automated testing after July 1, 2024

GitLab CI, like almost all modern CI systems, is designed to test merge requests. If you have ever contributed to a project on GitHub, you pretty much already know how it works. The advantage of Drupal.org merge requests is that they are collaborative by default, so you and fellow contributors can work in the same fork.

Read the best practices for contributing through merge requests.

DrupalEasy: Why you should care about using settings.local.php

Image removed.

Teaching folks why a settings.local.php file is an important part of setting up a personal development environment is so important to us here at DrupalEasy that it is a foundational part of both of our long-form Drupal training courses.

While preparing for an upcoming podcast mini-series I've been invited to participate in with the Talking Drupal folks, I'll be mentoring someone who is looking to re-enter the Drupal development scene after missing out on Drupal's transition from its pre-Symfony days. One of the tasks I have outlined for this person is to set up a settings.local.php file. When I went to find a good resource for the "why" and the "how", I came up empty. I couldn't find a single, up-to-date resource that conveyed what I feel is important.

So, that's what this blog post is all about. 

Why use settings.local.php?

Using a settings.local.php is all about configuring your local development environment for a project to be as useful to you, the developer, as possible. Default settings in this file do things like force all errors to the screen and disabling some of the (but not all) Drupal caches.

This file also pulls in the sites/development.services.yml file which contains some useful service class parameters and overrides - again, things that are useful for local development.

Setting up a settings.local.php literally takes less than two minutes, and when I see a Drupal developer struggling to figure out a white-screen-of-death error on their local environment, I can't help but 🙄

In addition to adding Drupal's core-dev dependencies and the Devel module, enabling the settings.local.php file is literally one of the first things I do when setting up a new site.

How do I enable the settings.local.php?

I'm so glad you asked.

Step 1

Copy sites/example.settings.local.php to sites/default/settings.local.php. Use a user interface (like the MacOS Finder) or the command line - it doesn't matter.

cp web/sites/example.settings.local.php web/sites/default/settings.local.php

Step 2

Uncomment the conditional include for settings.local.php in your sites/default/settings.php file - it looks like this:

# if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) { # include $app_root . '/' . $site_path . '/settings.local.php'; # }

Uncommented, it looks like this:

if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) { include $app_root . '/' . $site_path . '/settings.local.php'; }

Step 3

There is no step 3. This blog post is complete. 
 

Specbee: Style Made Simple with Acquia Site Studio’s Style Guide Manager

Ever wished you could tweak your web page’s header font or switch up your CTA color, but skipped it to save your developer from the hassle? With Acquia Site Studio’s Style Guide Manager, you don’t have to! Non-technical content marketers now have the power to make styling tweaks to their Drupal site theme via an intuitive interface. Are you interested in learning more? Come on in! What is Site Studio Style Guide Manager Previously Acquia Cohesion, Site Studio is a composable, low-code digital experience tool by Acquia that enables users to build and assemble pages with less to no code. Check out our previously written article about how to build component-based websites on Drupal using Site Studio.   Style Guide Manager is an optional module that you can enable within Site Studio. It centralizes the design elements like colors, typography, and components in one place, making it easier to maintain a cohesive look and feel throughout your site. It provides a set of style guides that are theme-specific. You can create style guide(s) to manage Site Studio styles within your Drupal theme settings. You can even override your theme styles using the Style guides. Advantages: Change the appearance of your Drupal website based on the active theme. Apply global styles and change the appearance with a simple-to-use interface. Create design systems for multi-brand and multi-sites. Style guide manager has two interfaces: Style guide builder Style guide Style Guide Builder The Style Guide Builder is a tool within Acquia Site Studio that allows you to actively create and manage the Style Guide. It streamlines the process of updating and maintaining the design standards. Changes made here are automatically applied throughout the site. Style guide The output of a Style Guide Builder - a set of design guidelines, is the Style Guide. The Style Guide is a collection of design rules, standards, and components that define the visual appearance of your website. Creating a Style Guide First, make sure you have installed Acquia Site Studio.  Navigate to /admin/cohesion/style_guides  Click on Add Style guide Add the Title Click the + button below the Style Guide form to add the form fields. Add the fields such as Font picker, Color picker, etc. with the combination of form layout fields such as Group accordion, and Tabs as per your requirement. Give a meaningful name for the Field Label so it will generate a meaningful token. This is how your Style Guide creation page will look like: You can also see the preview below of your Style Guide form builder and how it looks on the theme settings. Click on Save and Continue Managing your Styles Now that you have successfully created a Style Guide, let's see where and how you can manage your styles. Navigate to your theme appearance settings (here mine is: /admin/appearance/settings/techx) Add your values such as fonts, colors, etc., and save the configuration. This is how your style guide will look like after successfully saving the configuration. Now, you need to use the Style Guide tokens in the styles to see your styles on the front end as per the Style Guide values. Tokens are predefined variables or placeholders representing design elements such as colors, typography, spacing, and other visual properties. This is not just restricted to styles, you can use these tokens throughout your Site Studio like templates, or components as per your requirements. For example: Make sure you enable the token variable mode and then add your tokens. Implementing the Style Guide With Site Studio’s Style Guide Manager, you can create multiple style guides as per your requirements. You can see all of them in your theme settings as shown below This is what my front end looks like. You can change the values in the Style Guide per your requirements and verify the page/site accordingly. Final Thoughts Site Guide Manager is a very useful tool for non-technical users like content editors and marketers to see all the styles in one place with an easy-to-use user interface and modify them according to their choice. All of this without depending on a developer.        Are you looking to build highly engaging, omnichannel, and result-driven solutions with Acquia and Drupal? We are Acquia partners and we know what it takes to get you there. Talk to us today to find out more.        

Web Wash: Getting Started with Webform in Drupal (2024)

Webform is a Drupal module that allows you to create forms directly in Drupal without using a 3rd party service.

It can be used to create basic “Contact Us” and complex application forms with custom field logic and integration.

In this getting started guide you’ll learn how to:

  1. Create a form
  2. Add elements to form
  3. Customize form with conditional logic
  4. Embed the form
  5. Send submissions to Google Sheets

This tutorial accompanies the video above.