development http://nerdstein.net/ en DrupalCon 2022 Recap http://nerdstein.net/blog/drupalcon-2022-recap <span class="field field--name-title field--type-string field--label-hidden">DrupalCon 2022 Recap</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><div>It has been a while since I wrote a blog post and it's an acknowledgement that I dialed back some of my normal Drupal community involvement. Between several years of maintaining and rebuilding SimplyTest.me on top of my personal and professional obligations, it was good to take a break. I have since been promoted at Acquia and invested a lot there. I’ve also enjoyed time with my family, and recently got a puppy. I feel it is now time to look ahead.</div> <h2>State of the Project</h2> <div>I was in the first in-person Drupal Camp in Florida a few months ago. I presented a talk evaluating the landscape of Drupal competition. I tried to present data-centered, objective research with a perspective that cut through opinionated nonsense I saw on the web. I categorized competition into frameworks, open source/proprietary, and PaaS/SaaS positioned. Having done so, I learned a lot about Drupal’s identity in the world and where it could better compete outside of it. Unfortunately the talk ended up not being recorded but <a href="https://nerdstein.net/sites/default/files/presentations/Evaluating%20the%20Landscape%20of%20Drupal%20Competition.pdf">I have linked to the slides</a>.</div> <div> </div> <div>While the CMS and DXP framework space is exploding, Drupal’s adoption numbers are trending down overall. While the talk covers this in greater depth, there were a few primary reasons my research uncovered:</div> <div> </div> <ul><li>A heavy focus on an extensible, ambitious development framework de-emphasized builders and adopters who want low/no-code solutions and are concerned with time-to-value</li> <li>Frameworks are positioned for an enterprise market, where extensibility is critical to business success.</li> <li>Adoption is favoring tools that have more SaaS-based delivery (including products that deliver Wordpress) because they address SMB needs: faster time-to-value, common everyday features, usability over extensibility, no-code, and no/low maintenance offerings.</li> </ul><div> </div> <div>Major questions around Drupal’s identity (mission, vision, purpose) reflected in market conditions where adopters didn’t really know what Drupal was supposed to do given the flexibility of features of Drupal and extensible framework could be leveraged to implement a high number of use cases across many different verticals and segments.</div> <div> </div> <div>I made a claim during the talk: to grow and become more viable, Drupal’s positioning and value must be clearer and move outside of just the developer ecosystem.</div> <h2>DrupalCon 2022</h2> <div>The buzz from the community was palpable. While attendance was roughly half of a “normal” DrupalCon, the community definitely showed up. I was impressed the numbers were as high as they were; you can tell how much people care. It was clear the community needed this event to restore connection. Being able to see so many co-workers and friends I didn’t see through the pandemic was heartwarming. And, while there were several safety precautions in place, it was so nice to share warm embraces, grab a beer, and be able to say in-person, “wow, I missed seeing you!” I give the Drupal Association a lot of credit for rising to the challenge of an in-person event in the pandemic. I thought it was an overwhelming success.</div> <div> </div> <div>One of the undertones of the event was the important question of where the community is going. The mission of “ambitious digital experiences'' was ambiguous and seemed to be losing steam in positioning Drupal with a clear identity or to address the current market needs. A highlight of the event, for me, was the Driesnote. I felt it not only answered the questions, but it was one of the most impactful Driesnotes to date.</div> <div> </div> <div>Dries proclaimed the mission of ambitious digital experiences complete. There is plenty of evidence to support this. Drupal’s framework has matured for the years with several major versions (8, 9, and 10) after a major modernization to support the broader PHP ecosystem and updated practices. Data has demonstrated that Drupal, with its robust framework and large number community-maintained projects, performs very well and has vast adoption in the enterprise (see my slides for details). While one can argue that maintaining ambitious experiences is likely never done and Drupal is certainly not “perfect,” Drupal itself seems to have met the criteria set out when this mission was created.</div> <div> </div> <div>So, what is next? Dries set a new vision for ambitious site builders. This is in homage to Drupal’s site building capabilities, which historically put Drupal on the map. Drupal 7 saw a massive rise in adoption by hitting the market in strategic ways. Tools like Content Types (structured content/data modeling) and Views (dynamic content listings/output formats) offered no-code ways to assemble Drupal. Drush (run-time automation and DevOps) and Features (configuration management) also hit key market needs. Both were further extended in Drupal 8 with the configuration management initiative and adoption of Composer. With the architecture and capabilities in place, Drupal is now reclaiming its commitment toward the site builder persona.</div> <div> </div> <div>My immediate response was a concern that this would be perceived as moving away from the developer persona. I think this is false. Drupal has and will continue to be an amazing framework that will see enterprise adoption for these reasons. The framework is the key enabler to power the new site builder mission. In addressing more low/no-code outcomes, Drupal can enable those with less programming skills and expand into new markets. Automatic Updates provides a way for site builders to update Drupal sites without knowledge of Composer. Project Browser provides discoverability and installation into community projects for common use cases. And, now there is more.</div> <div> </div> <div>Starter Templates stood out as a major improvement. Distributions have<a href="https://www.drupal.org/project/ideas/issues/3155656"> had rough edges</a>. They require ongoing maintenance from maintainers. Adopters are fairly locked into the features of the distro. And, distros do not have natural Composer integration today. One major benefit of using a distro is upfront, faster time to value: you get a complementary set of features enabled out of the box. A starter template offers just that but does not lock you into the distribution thereafter. This can actually be a greater benefit for harnessing Drupal’s extensibility without adhering explicitly to the distribution. I spoke to several agency leaders and distro maintainers that said they already do this and are looking forward to using this in favor of distros. I think this may ultimately lead to deprecating distributions.</div> <div> </div> <div>I also felt that it was a strategic benefit to move projects into Contrib to have a smaller core. We should be doing this exercise routinely as the demands of the web evolve. Core has the highest standards and it should have the most critical features only. Removing modules from core hopefully affords more time since it is less to maintain. I would argue that we should also consider bringing in some of the most prominent Contrib features, like Path Auto, to benefit from the standards and interoperability afforded by Core. If people are using something, there is a clear need.</div> <h2>What is next</h2> <div>Dries was upfront in sharing that his plans were a pragmatic, two-year plan that didn’t look too far ahead. This is smart, as the market is fast evolving. It’s always good to not overcommit. But, what else should Drupal be evaluating moving forward?</div> <div> </div> <div><strong>No-code theming</strong> - Drupal just launched amazing new themes in Claro (admin) and Olivero (front-end). While both harness modern front-end practices, deliver on accessibility goals, and give Drupal a long-awaited fresh coat of paint, it does not yet compete with Wordpress which offers many no-code configurable themes that can enable site builders to style a website without a custom theme. I think this should be heavily emphasized as an out-of-the-box Drupal that would not warrant any custom theming. Drupal could address a key differentiator from its largest competitor.</div> <div> </div> <div><strong>Usability</strong> - Drupal cannot just focus on features site builders need, it needs to focus on the usability itself. The experience of building in Drupal is still daunting with the sprawl of features it can be assembled to deliver. A common example is the Views UI; it is incredibly powerful but can be challenging to learn. Many SaaS based tools have emphasized ease of use through inline help, highly interactive user interfaces, and very clear documentation to fall back on. Drupal will need to address this to compete.</div> <div> </div> <div><strong>Marketing</strong> - Site builders need to see first hand why Drupal is great. We must tell our story. This goes back to the identity. We need videos, marketing, and promotion of all of the great features Drupal offers site builders. They should know how to model content, create Views, develop View Modes, and understand Blocks.</div> <div> </div> <div><strong>Layout Builder</strong> - I still think this is one of Drupal’s best features with the most potential. But, competitors like Elementor, Wix, and Squarespace have crafted modern, highly interactive experiences that focus on usability. I would love to see Drupal revisit this feature given how relevant this feature is for content authors.</div> <div> </div> <div><strong>Paragraphs/Blocks</strong> - Drupal should consider addressing the component-based problem once and for all. Paragraphs offers more features than Drupal’s native block system but they are incredibly similar. The gap needs to be resolved given we have two overlapping features that are heavily invested from the community. Core should consider adding features to address this gap and ensure blocks can address more predominant use cases.</div> <h2>Quick Hits</h2> <div> <ul><li>Angie Byron, webchick, won the Aaron Winborn award. It was amazing to see a long-time mentor and friend win, as she has been deserving of such recognition.</li> <li>Amazee.io offered really cool swag with custom dog tags and collars, while Acquia made donations to help community members in Ukraine.</li> <li>A Darth Vader bagpiping unicyclist blasted the imperial march into the Pantheon party, ensuring attendees got a true taste of Portland’s weirdness. (yes, this was super cool)</li> <li>I got so many t-shirts I had to ship them back.</li> </ul></div> <h2>Recap</h2> <div>DrupalCon reinvigorated the community with both new direction and long awaited connection. I feel the pivot will help Drupal address new markets and be more competitive. It should be a fun couple of years as Drupal takes on the new mission.</div> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon, 05/09/2022 - 19:59</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Mon, 09 May 2022 23:59:54 +0000 admin 135 at http://nerdstein.net SimplyTest.me From The Ground Up http://nerdstein.net/blog/simplytest-from-ground-up <span class="field field--name-title field--type-string field--label-hidden">SimplyTest.me From The Ground Up</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>When I took over as the project lead for SimplyTest.me, the previous lead shared three primary things with me:</p> <ol><li>The system had a non-trivial amount of technical debt and was rising more with time</li> <li>Significant changes were coming with Drupal 8, Composer, Drush, and more</li> <li>His interest was elsewhere</li> </ol><p>As part of my proposal, I promised to do what I could to revitalize this. While this effort has taken years, we have hit another major milestone in our journey: </p> <p><strong>We launched a completely new version of SimplyTest.me.</strong> We rebuilt the system from the ground up in an effort to revitalize the project. It’s time to tell the story.</p> <h1>The Old System</h1> <p>Architecturally, the SimplyTest I inherited had three basic systems: the web server front-end, the worker server backend, and a proxying system that glued everything together. Everything was manually maintained and largely custom. </p> <ul><li>The servers were hand-built and required a ton of maintenance. All of the scripts to run the system were custom. </li> <li>The worker backend was a glorified, monolithic LAMP stack that would provision and deprovision virtual hosts, databases, and hostnames on the fly. </li> <li>Cleanup routines would fail commonly on system updates, disks would fill, logs wouldn’t rotate, and the maintenance burden was high. </li> <li>Major upgrades to components or the operating system would have caused an indeterminate amount of breakage. If you tried to fix and subsequently broke any part of the system, this would impact all of the running instances. </li> <li>And, there was no failover or load balancing. </li> <li>The application was running on Drupal 7, which means it was almost three major versions behind. </li> <li>Backups did not readily exist.</li> </ul><p>SimplyTest did not have an infrastructure that afforded easy development or encouraged contribution from others in a safe and straight-forward way (without basically using the fragile production systems). Making changes often broke things and the systems would go down routinely. It was not good.</p> <p>People would complain. A lot. We tried to keep up. But, the service wasn’t stable, nor was it easy to keep up with the latest innovations our community was developing. People routinely let me know about it. They also would routinely blame SimplyTest for issues with their own projects by expecting me, the maintainer of the service, to debug their issues. I recall a vocal community member persistently accusing the system of being broken because “it works on their local system” only for us to find one of the dependent third-party assets had a broken link in their module’s library file (which they already had downloaded locally and they didn’t properly test it). </p> <p>It was an incredible effort to just keep the lights on. And, it wasn’t sustainable. It was important that we created a new future.</p> <h1>Rebuilding SimplyTest</h1> <p>While a fresh approach was desperately needed, we have finally reached a point where SimplyTest.me has been rebuilt from the ground up. We spared no opportunity to modernize the project, which allowed us to shift our attention back to serving the community. The following sections highlight our accomplishments around revamping SimplyTest.</p> <h2>Goals</h2> <p>Revitalizing the project needed to be prioritized around what our users would get value from. As an example, lowering technical maintenance or eliminating technical debt affords more time to develop the features of SimplyTest.me or more quickly introduce changes tied to new community innovation (see: Gitlab workflows in drupal.org, updated PHP version support in Core, Composer/Drush improvements, and more). Time spent working on the system would not go toward keeping the lights on, but actual value built into the project our users would benefit from.</p> <p>Increasing contribution and available contributors is a long-standing goal. Not only do I want SimplyTest to be used for lowering the technical barriers of contribution, it is the perfect platform for learning and contributing something meaningful back. We made conscious architectural decisions with an eye for contributor growth tied to technologies we felt people wanted to learn. And, revitalizing the systems happened with an eye toward a cleaner, modern architecture that would afford more straight-forward contributions without the overhead of legacy systems.</p> <p>A better, more stable service offers more value. If our users get value and they continue to get value, I believe they will help sustain the project through donations or contributions. And, they can focus on the great things Drupal offers. Put differently, if people routinely experience challenges with the service or have trouble getting value from it, this lowers confidence and they are less likely to invest. SimplyTest must seek and be in a position to drive more contribution to remain viable. As such, we hope our work to build a new SimplyTest will hopefully position it to reach its fullest potential.</p> <p>Another goal is to have SimplyTest be a showcase for the best that Drupal offers. It wouldn’t just be built <em>for</em> Drupal but <em>with</em> Drupal as well. If we found ways to make Drupal or it’s dependencies better through our efforts, we did so. This is how the ecosystem works and we wanted to be fully responsible for this.</p> <p>Any modernization would be adding value, not losing it. We aimed to have at least 100% feature parity with the old version. I’m proud to say we achieved this goal.</p> <p>Finally, we stuck to our core tenets. We remain committed to open source. The service would be and will forever be free to our users. And, our actions reflect our fundamental goal to lower the barrier of entry for those who want to use Drupal and learn about the wonderful community we’ve established. </p> <h2>Donations and Paid Labor</h2> <p>I don’t expect people to work for free. Some choose to volunteer and I’m really grateful for that.  But, when I took over, I had no money to pay for anything. I established an Open Collective to help with this. I began accepting donations. Initially, this helped pay for expenses like SSL certificates and various attempts to make development infrastructure. But, I saved up money to help pay for sponsored contributions. I was proud to offer some people work when they were out of jobs. I hope to do a lot more of this in the future.</p> <p>Most importantly, this allowed me to spend some funds toward the revitalization. The project started to get some much needed help and velocity toward our goal. I was able to pay people with specific skills to come in and do work faster and of higher quality. </p> <p>A lot of this progress was made possible by donations of small and large from both individuals and organizations. It was really cool to also see Drupal Camps, who often leverage SimplyTest for contribution days, make donations with some of their funding. And, companies like Centarro, sponsored development of specific features (the one click demos). </p> <h2>Sponsored Backend</h2> <p>Our existing worker backend was a significant source of pain and suffering. It was a hand-coded, hard to maintain, antiquated, impossible to scale platform. Even though a new application was planned, we started with a backend replacement so we could exclusively focus our time on the front-end later.</p> <p>Many requested the use of ephemeral architectural patterns. Issues were opened for analysis on a container-based infrastructure. However great the technology was to fit our needs, it was not feasible. The cost to move off of a monolith and into a cloud-based platform was high. The development and maintenance of infrastructure was not something I could do without significant learning. And, even if it was perfect for our needs, a cloud-based, containerized solution was both cost prohibitive and impractical. As such, we opened up a call for a sponsored solution. Elijah Lynn, Cameron Eagons, Greg Boggs, and I formed a committee to review proposals. We chose TugboatQA. We basically were given an out of the box, ephemeral, container-based infrastructure that was all driven through APIs and had nice abstractions for Infrastructure-as-Code and hooks to execute the specific Simplytest dynamic behaviors.</p> <p>This was basically broken down into two phases: </p> <p><em>Phase 1</em> - we used a repository-based approach for our Drupal 7 integration. We generated YAML files that had all of the infrastructure definition and commands, pushed the definition to a repo branch, and made use of the Tugboat CLI to invoke provisioning. Teardown was already included in our IaC definitions, so it automatically cleaned up!</p> <p><em>Phase 2</em> - TugboatQA made some microservice enhancements between our Drupal 7 integration and our new Drupal 9 launch. More APIs were created for provisioning and log management, allowing us to move away from the repository-driven approach and to drastically simplify this integration. It was also a really natural fit for the ReactJS front-end we created. </p> <p>This was my first time leading the project that I considered a vendor. I had a lot of hesitance tied to this decision, especially for selecting a proprietary tool. I will say, the introduction of TugboatQA was game changing for the health, maintenance, and viability of the project. Plus, the TugboatQA (and Lullabot) teams sponsored this work because they are also committed to open source. I feel this was a great decision with a lot of alignment for all parties involved. They have been fantastic partners.</p> <h2>PaaS Hosting</h2> <p>With a volunteer team and limited time/budget, you need to be very careful about what you maintain with a project like this. We learned the hard way how difficult it is to maintain your own infrastructure. In my mind, you can’t waste time reinventing the wheel. While it may seem to conflict with our open source roots, we chose a service and vendor that was closely aligned with our identity: Amazee Lagoon. We are grateful they sponsored the use of their service to help run our Simplytest Drupal 9 website. While their PaaS service is a paid offering, their product is open source. And, it had relevant features and a high degree of customizability we needed to be effective. Amazee also offered use of it’s CDN services, which help with both performance and security. Worrying less about a website affords time for us to build more value into the system. We’re fortunate to have access to this service to allow us to improve the service itself. </p> <h2>Drupal 9 Website</h2> <p>What a better way to showcase Drupal by using Drupal itself. We wanted to harness the best Drupal had to offer as part of our revitalization. We actually started our efforts with Drupal 8 when we began to create the new application, but we then upgraded to Drupal 9. We have leveraged many cutting edge features, like the Typed Data system and the web service capabilities, which help modern Drupal applications shine. We’ve been able to leverage core features and the underlying framework to create a set of services, data models, and API endpoints that pair perfectly with our React-based front-end.</p> <p>SimplyTest has also innovated significantly around the Drupal community project metadata. Because our efforts were “experience first”, we ran into challenges implementing the ideal experience in support of semantic versioning, compatibility of major versions of core within projects, and more. Major props to Matt Glaman and Benji Fisher for helping research and innovate around these challenges.</p> <h2>Fresh Design</h2> <p>Comic Sans is a sign of the nineties, and the nineties want it’s website back. We’re happy to oblige. With a totally revamped system, we needed a modern design that best represented the technical innovations under the hood. Ana Laura Coto helped with this by producing designs for us, including a new logo! This will help our experience, allow our work to shine, and best represent how cool and modern the new system actually is.</p> <h2>Updated Theme</h2> <p>Iris Ibekwe set up our initial efforts when we implemented the Drupal 8 theme. It was responsive, leveraged modern grid systems, and established build tooling with Gulp, NPM, and more. This work helped get the ball rolling toward implementing our new designs.</p> <p>A lot of time passed from when the original theme was set up and when we got the new backend fully built. Some newer tools came out that allowed us to upgrade some of the older tools and innovate further. Matt Glaman implemented Laravel Mix, which allowed for live refresh and replaced the theming framework with Tailwind CSS.  </p> <p>QED42 stepped up majorly to finish the job. Their team did a significant amount of theming work and QA. They applied some updated design revisions made by Ana Laura to address identified accessibility issues. As the backend was evolving, the theme was being completed in parallel by their team. The QED42 team got the job done and their team was a pleasure to partner with. Front-end work is one of my technical limits. I was grateful I had such great help with this area.</p> <h2>ReactJS Front-end</h2> <p>The interface for SimplyTest has a lot of conditional behavior. It’s highly interactive. This presents our users with an experience that simplifies much of the underlying Drupal complexities. Users enter a project, they pick a branch, they then only see relevant options to them based on what they want to test. Drupal has evolved a lot and putting the experience first for our users has meant a lot of work behind the scenes to abstract complexities. This work is never done, but our new ReactJS front-end is a major step forward.</p> <p>Matt Glaman created the React front-end and the connecting backend APIs from Drupal. ReactJS is a great compliment for the web services sponsored by Drupal. The React app maintains state, which can help to address conditional needs and guide users through the experience we want to offer. This was a complex task and Matt’s leadership helped make something from nothing. I am just learning ReactJS and was struggling to make the kind of progress on this we needed. Matt’s expertise was critical to connect both systems and make this a huge success. I’m far more confident that others can contribute to this part of the system and use the React app as a reference for learning (I certainly will be).</p> <h2>Automated Testing</h2> <p>We were making sweeping changes throughout the last several months. When you add in development activities in parallel, it is much easier to break things. Thankfully, automated testing was implemented throughout our process. </p> <p>As Matt was implementing the Drupal backend and the ReactJS front-end, automated testing continued to be developed at the same time. PHPUnit tests were helping with the Drupal endpoints. Cypress was used to test all of the front-end and interactivity. Having automated tests allow us to have a stable development workflow that ensures we’re less likely to break things when pushing changes.</p> <h2>Website Repository and Quickstart</h2> <p>SimplyTest is a Drupal profile maintained on drupal.org. But, our application runs like a conventional Drupal website managed by Composer. Having a repository with SimplyTest installed allows for more key outcomes. First, the repository maintains the Drupal website running SimplyTest.me in a conventional manner to other Drupal applications. This repository serves as the integration with our new PaaS hosting and it looks and feels just like a standard Drupal application (because, it is). Second, the repository is a quickstart for contributors to load the website locally with minimal work. Community members can readily contribute just like any other Drupal project. This helps save time for those who want to participate and don’t want to waste their time with all of the manual setup. We also maintain both DDEV and Lando support within this Quickstart, with some basic documentation we aim to continue to improve with time.</p> <h2>Violinist </h2> <p>Again, we don’t want to waste time with manual work if we don’t have to. Matt installed Violinist on our SimplyTest website repository, which scans Composer.json files on the main branch for Composer updates. If updates are found, Violinist creates a pull request for the update. This works for Drupal core, all of the contributed modules, and even the SimplyTest code on Drupal.org. Pull requests trigger our automated tests to get immediate feedback. And, it sounds wild, but I can basically update the site from my phone. This is immensely cool and something that comes in handy for rapidly applying security updates. </p> <h2>Modern CI/CD</h2> <p>Our new CI/CD workflow offers smaller, incremental changes we can test iteratively before deploying to production. Our workflow with Violinist, pull requests, Github Actions, and on-demand environments on Lagoon allow us to get testing feedback, deploy small changes frequently, test them before pushing to production, and deliver with high quality. We have moved from having a monolithic infrastructure that wasn’t change friendly to being able to rapidly test and deploy changes in an ephemeral, highly automated, and change-friendly infrastructure. It’s been awesome and it’s such a sharp contrast from where we were before.</p> <h2>Monitoring and Alerts</h2> <p>I leveraged the free version of Pingdom to put basic alerts on the website and worker backend when I took it over. I got alerted for outages, which were frequent before. All monitoring and alerting has been switched to the new PaaS website and leverage TugboatQA service notifications (and Linode as their vendor) that could impact our backend. I believe there are more opportunities to improve this in the future, but the basics are there.</p> <h2>Live Coding Streams</h2> <p>Not only did Matt Glaman help with development and architectural guidance, he had an idea to do live streaming sponsored by his company, Bluehorn Digital. I learned a lot from these streams and am confident many others have as well. Streams cover development, testing, issue queues, development tools, and more. It’s great having someone talk through the process the entire time to understand the details. The streams were great for smashing bugs, working on new features, diving into the architecture, and advocating and promoting SimplyTest itself. They continue to be something I look forward to and were an immense help to launching the new site.</p> <h2>Partners Program</h2> <p>Between Bluehorn Digital, QED42, TugboatQA, Centarro, and Lagoon, it is very clear SimplyTest would be in rough shape without these partners. SimplyTest would not even exist without Druid and Maloon who sponsored the legacy systems. It is equally important to say thanks and highlight their contributions to the project, so I established a partners program. For companies who continue to invest in SimplyTest, we will continue to recognize you and offer incentives as we are able. One incentive is the “first right of refusal” for sponsored SimplyTest work. Partners get first dibs when we choose to pay for enhancements or bug fixes. Please see my previous blog post on the topic to learn more about the kinds of partnerships we’re offering.</p> <h2>Social Media Presence and Talks</h2> <p>It is incredibly important to engage with the community. We sincerely want to know what you think, where we can do better, and how we can deliver the best service possible. Thanks to AmyJune Hineline, we have established a Twitter presence, we are active on our Drupal Slack channel, and we have given talks at several camps that have highlighted our plans and why we’re doing this. AmyJune routinely uses SimplyTest as part of new contributor workshops. Because of this, we see much more engagement. Users routinely file issues, send messages, offer ideas, and help us be better each day. We’ve seen donations from the community. It’s such a stark difference from when we had no presence when I took over the project. And, AmyJune has been a critical part of helping establish and build this community from basically nothing.</p> <h2>Contributors</h2> <p>There are too many people to thank that helped us get where we are today. And, this was a collection of things that led up to the big reveal of our brand new Simplytest.me application. None of it would have been remotely possible without several people and companies stepping up to help. I mentioned several people already in this post and there are countless more who cheered us on, offered advice, pitched in, made donations, and so much more. This effort is no longer “my” side project, it’s now “our” community project. Thank you, thank you, thank you.</p> <h1>Parting Thoughts</h1> <p>We have finally put SimplyTest back in a good place. This is worth celebrating. Our technology is best-in-class and is appealing for anyone to use or learn. We managed to drastically reduce the code and simplify the application, lowering the barrier of entry for SimplyTest itself. We reduced maintenance activities down to nearly zero for both the application and the underlying infrastructure to allow focus on value creation. We’ve built a community that has promises of growth. And, we have the tools in place to sustain it. In retrospect, all of these small steps have added up to something significant in the few years I’ve led the project. It truly was a team effort. I couldn’t be more excited to finally be at this point and share this with everyone. I am grateful for those who pitched in and even more happy to say, “we did it.”</p> <p>This whole thing has taught me a lot. In a lot of respects, SimplyTest is both the best and the worst of open source. It’s not only a free service but all of the code is open sourced. It’s a great example of both companies and individuals pitching in toward a common cause. But, after having led these efforts, I have seen first hand how people can treat open source maintainers when they get behind a keyboard. Some users expect a lot but fewer are willing to pitch in and make it a success. Our system remains largely volunteer-led, donation funded, and services sponsored. We’ve been highly resourceful with what we have (which is very little in the grand scheme of things). At times people just seem ungrateful. On a personal level, I wish this was more harmonious. I’d love for more help (financially or through direct contribution). I’d love for more awareness for those who expect more than we can reasonably offer. Regardless, we try to put our users' needs at the forefront of the service. We welcome the challenge to serve everyone. And, we are in a much stronger position to serve people <em>now</em> but it took so much time and effort to get there.</p> <p>Another lesson in open source is how hard it is to have a big impact without resources. Working alone requires investing a lot of time and effort outside of your day job. It’s a fast way to burn out. SimplyTest.me historically relied on volunteer labor, sponsored systems, and had no money. Druid and Maloon graciously paid for all of the web, worker, and proxy servers. But, without funds, we couldn’t pay to get help from subject matter experts when it was needed. We just had to roll up our sleeves, work hard, figure things out, and be really scrappy. We picked up the phone to call friends when it was urgently needed to keep things running. But, one can only do this so long. And, I became fully aware of why the previous maintainer decided to move on. A large portion of time was focused on incidents and “keeping the lights on” while wanting to build the future. Wholesale change was needed and it makes it even more rewarding to launch our brand new system.  </p> <p>One more side note… the largely volunteer Drupal community is an amazing example of what can be accomplished at scale. A small number of contributions by a lot of people can add up to something significant. And, yes, SimplyTest is a part of the community. But, SimplyTest certainly has not seen the vast contribution that the Drupal ecosystem benefits from. We will continue to aspire to reach and achieve the kind of scale the broader community benefits from.</p> <h1>The Future</h1> <p>We need to grow into this new project we just launched. We just shed a ton of technical debt and can finally breathe easier after a rough few years. We still have a lot to learn and improve upon, and we need to work out the bugs. It was a lot of change in a (relatively) small amount of time, but we feel confident in the decisions made and to put us in a position of success. It’s incredible what a small number of highly motivated and capable people can do with clear goals, clear strategies, and clear purpose.</p> <p>In the coming weeks, I will be publishing a quarterly roadmap talking about key objectives we wish to hit moving forward. We will start with a pause in new features to fix bugs, stabilize, and identify improvements. We will then announce a prioritized set of features which will be informed by the feedback we get.</p> <p>We thank all of you who supported the service through our transition and when the system did not live up to your expectations. We’re confident that the future is bright, and we look forward to having SimplyTest serve the needs of the community for a very long time to come.</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed, 09/22/2021 - 18:58</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> <div class="field__item"><a href="/taxonomy/term/6" hreflang="en">people</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Wed, 22 Sep 2021 22:58:00 +0000 admin 133 at http://nerdstein.net Drupal CI/CD with TugboatQA and Github Actions http://nerdstein.net/blog/drupal-ci-cd <span class="field field--name-title field--type-string field--label-hidden">Drupal CI/CD with TugboatQA and Github Actions</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Vacation this year has been amazing. I've caught up on some of my long-standing to do list, like launching this new nerdstein site and digging into some of the newer technology I've wanted to explore. SimplyTest has some great new contribution I am excited about. I've spent a ton of time with the kids coloring, playing, and just enjoying these moments while they are young. It is shocking how quickly they have grown. I've cooked a lot and those close know how much I enjoy doing that. I've blogged more - finally - after a long hiatus and lack of motivation. I am currently enjoying an Eight &amp; Sand Brewing - <em>Loco Mo Sim DDH</em> and listening to the Gravity album from Our Lady Peace. The kids are currently asleep. I wanted to share some things I've explored today.</p> <p>Automation is important for consistency and for ease of use. It's one of the tenets of DevOps. Automation is the cornerstone of creating a great experience with SimplyTest.me. I've spent years creating continuous integration solutions for customers to help them be more effective. Yet, I never did that for my personal website... until now.</p> <p>Building off of my new website launch, I wanted to keep the momentum going. Recall from my blog post yesterday, I set up the basics of TugboatQA to replace my development server with something more modern. I also set up a new Linode for my production server. I wanted to learn Github Actions for deployment automation, as I had never used it. I desired to have an end-to-end CI/CD pipeline built completely with GitOps. And, I wanted as much of this automated as possible.</p> <p>I was pleasantly surprised with how smoothly things went. Maybe I shouldn't be, as I'm basically building a pipeline for a simple website. Let me share a bit more about this experience should you find it beneficial.</p> <h2>Pipeline</h2> <p>This pipeline basically happens in the following workflow:</p> <ul><li>I work locally and push changes to a branch to Github (Composer, custom code, config, etc)</li> <li>I make a pull request for this change off of the branch</li> <li>Pull requests are subsequently deployed into their own Tugboat environment (executes a fresh environment that pulls production databases and files with automated deployment)</li> <li>I review the code for anomalies or missing things like config</li> <li>Manual testing occurs on the Tugboat environment (note: I need to work on automated testing; I plan to implement the visual regression test of TugboatQA in the future)</li> <li>The pull request is merged after it passes my thorough review ;)</li> <li>A production deployment happens once the code is merged</li> </ul><p>For the sake of what I consider novel, I'm going to focus on two main areas of this pipeline: <strong>TugboatQA</strong> and <strong>Github Actions</strong>. I assume a production server exists, but I will describe what is needed to manage assets from production (the source of truth for content in Drupal). And, I assume people are good with the well-documented pull request workflow methods. Please reach out if any further details would be helpful.</p> <h2>Commentary: Drupal Assets</h2> <p>My production site is a basic LAMP stack with some specifics for Drupal, like Composer and Drush (site local, of course). Any true Drupal development workflow needs to readily be able to get databases and files from production as the source of truth. Files are relatively easy with the Stage File Proxy module in Drupal, which can replace local files with those served remotely. Drush Aliases are a great way to do the database backups. But, I wanted to avoid putting credentialed data that into a public repository (note: these features are often offered by Drupal PaaS providers and you should use it). I opted to generate a script that could be invoked upon deploy and used <em>mysqldump</em> to create a gzip'd SQL file on-demand. Easy peasy bash script-ezy (sorry, I'm watching too much Food Network during this pandemic hell).</p> <h2>TugboatQA</h2> <p>TugboatQA replaced my development environments with automated environments per change (pull request). TugboatQA natively has Github integration and the Tugboat repository settings allow for new environments to be provisioned upon pull request.</p> <p>Tugboat's infrastructure-as-code capabilities resemble Docker Compose (I denote this as the services layer), but Tugboat has specific hooks that allow for automation scripts to run at specific events within the lifecycle of an environment. The offering is catered for building great environments in a variety of platforms. As such, the services layer is readily configurable, allowing it to have parity with the Linode server tied to a set of Docker-based images. I'll probably try this out for a NodeJS-based workflow in the future. </p> <p>I was able to add in the basic Drupal deployment steps into these hooks for the desired automation. This can and should be specific for your project. Mine did the basics: Composer install, drush config import, drush update database, and drush cache clear.</p> <p>Finally, TugboatQA generates a public and private key for every project. I was able to create a locked-down, Tugboat-specific user on my production server where I copied the TugboatQA public key into the <em>authorized_keys</em> for that user. This allowed me to explicitly control  what Tugboat could and could not do within the server. This allowed me to only have Tugboat run a script on production that made a database backup and leveraged <em>scp</em> to retrieve the generated backup into the Tugboat environment. </p> <h3>Example</h3> <p>You can see how I've configured the TugboatQA behavior through its config.yml directive:</p> <div style="font-size: 11px;"> <script src="http://gist-it.appspot.com/github/nerdstein/nerdstein-drupal8/blob/master/.tugboat/config.yml"></script></div> <h2>Github Actions</h2> <p>I wanted something to kick off a production deployment. Much like AWS Lambda, this type of capability only needs to execute a script which is in sharp contrast to a more persistent, permanent server like production. This deployment automation only needed to happen after I reviewed a change in Tugboat and after I merged a pull request. Github Actions had just what was needed to do this.</p> <h3>Secrets Capability</h3> <p>One of the "secret" sauces of Github Actions is the integration of their secrets feature. Think of this as a private, secure key value store. Instead of committing scripts with a bunch of private information (keys, server IPs, usernames, passwords, etc), you can store your secrets in the repository settings and pull them within committed, ever changing scripts. Very cool.</p> <h3>Events</h3> <p>I only need my production deployment to happen after a pull request is merged into <em>master</em>. This signifies that a change has been reviewed, tested on Tugboat, and it is not expected to break a bunch of stuff.  Github does not explicitly have a pull request creation or merge event in Github Actions. However, on further thought, what I really care about is when I push code to <em>master</em>. Suppose I have a hotfix, like a security update in Drupal. Yes, I <em>should</em> push this through normal channels. But, time is of the essence. So, I may want the opportunity to commit code directly to master. Yes, this <em>can</em> break things but I can't rule out the possibility of needing to do this if I feel like it needs to be done (process for the sake of process is not effective). Pushing <em>any</em> code to master actually covers me for any deployment I care about - a hotfix or a pull request. I focused on this <em>code push</em> event for the basis of a production deployment, which was supported by Github Actions.</p> <h3>Automation</h3> <p>Much like Jenkins is commonly leveraged as a CI tool to automate remote deployments, I didn't want Github Actions to do much more than serve as a proxy to my production server. In a similar fashion to Tugboat, a Docker image can be leveraged in each step of a Github Action "used" to execute steps of your workflow within a specific runtime. I chose a simple "SSH Action" image that allowed me to run commands remotely with a clean abstraction to SSH into my production server. I was able to readily embed my secrets into this script thanks to this abstraction, which had parameters for the SSH credentials that I embedded the secrets into. </p> <p>Like what I did with Tugboat, I added deployment commands for the basics of a Drupal deployment. I updated Composer and leveraged Drush to update the database schema, import the config, and clear the cache. One notable difference was pulling in the most up-to-date code from the <em>master</em> branch. If I had something more complex, this could have accounted for environment-specific differences with tools like <em>Config Split</em>.</p> <h3>Example</h3> <p>You can see how I've implemented the Github Actions configuration through it's provided interface in Github:</p> <div style="font-size: 11px;"> <script src="http://gist-it.appspot.com/github/nerdstein/nerdstein-drupal8/blob/master/.github/workflows/deploy.yml"></script></div> <h2>Conclusion</h2> <p>It is astonishing that people routinely accept the norm when there are better options available. How do you choose to invest your time? Personally speaking, I have better things to do than manually deploying code when I want to update my website and subsequently scrambling to fix things that break when my QE process was to blame. Imagine those that have to deal with this type of thing frequently and in their professional career. These "manual" approaches add up in terms of technical debt and required investment. As technologists, we need to be better and push to make the systems we build serve us too. Ultimately, it benefits those we're trying to serve by freeing us up to do better with the time and opportunity we have. </p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue, 12/29/2020 - 18:54</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Tue, 29 Dec 2020 23:54:38 +0000 admin 123 at http://nerdstein.net http://nerdstein.net/blog/drupal-ci-cd#comments Finally! A website refresh http://nerdstein.net/blog/new-site-2020 <span class="field field--name-title field--type-string field--label-hidden">Finally! A website refresh</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Let me welcome you to the new nerdstein.net. I'd like to thank Ana Laura Coto for help with the design. I'd like also thank Jonathan Daggerhart for being there when I needed an assist. I am really happy to finally share this project with the world.</p> <p>This has been a long time coming. Three years. Yes, you read that correctly. Three years. Why? I prioritized learning. And, I am slow and methodical. This website is a culmination of many things I learned over a three year span.</p> <p>But, three years?!? Yep. I have a rewarding and challenging full time job at Acquia. I have a family with two young kids. This project was all done in my spare time and at my convenience. Most of my time and learning is invested in SimplyTest.me. But, this project gave me a high degree of freedom on my own timeline. I wanted to learn things well and understand things properly. I did it piece by piece. Drupal evolved a lot in three years, as well. There was a lot to learn. And, then 2020 happened (I'll hopefully blog more on that soon).</p> <p>For the rest of this blog post, I'd like to reflect on these efforts and learning.</p> <h2>From Drupal 7 to Drupal 8 and then Drupal 9</h2> <p>Drupal evolved significantly from Drupal 7 to Drupal 9. My original goal was to launch this with Drupal 8. Then life happened. Then Drupal rolled more features and made it even easier and better to launch my site. I'll share some specifics below. Regardless, Drupal has established itself as a great tool for progressive enhancements.</p> <p>One notable change on this journey is how the community adopted Composer. I recall the very basic support when Drupal 8 was launched. Major changes were made when I upgraded the site to Drupal 8.7, which totally revamped the Composer work. I did the same when Drupal 9 came out. The community made some significant progress, but I'd love to see this stabilize now. I made heavy use of Composer and it's DevOps capabilities to script some of the integrations with my design system. This area of work was not well documented, especially in moving between versions and resolving issues.</p> <h2>New server</h2> <p>I was long overdue for a tech refresh. I had the same VPS running my Drupal 7 site for over seven years. It served me well. I spun up a VPS for a few Drupal 8 sites I hosted for local non-profit orgs that couldn't afford it. My Drupal 8 development site ran there for several years. The non-profits moved on due to grant funding and poor technical support (hint: if you can't do something well, don't do it). And, that Drupal 8 server rapidly dated itself.</p> <p>I was able to shut down both VPS servers and create a brand new Ubuntu 20.04 on Linode. Linode has been a sponsor of SimplyTest.me, helping offset the cost of it's development infrastructure. I'd hardly call the experience of Ubuntu 20.04 learning; it was a breeze setting up a Drupal 9 environment. These systems have come a long way even in seven years. Shutting down the VPS servers is bittersweet. I've removed some significant tech debt but those servers were rock solid. No regerts.</p> <h2>Design Systems and Drupal Integrations</h2> <p>The biggest emphasis of learning was the front-end work of the design system. I am still so far from an expert. But, I definitely understand it now. I started with Pattern Lab. I broke down Ana Laura's design into a series of atomic components. I developed them. I learned more about SCSS and building of front-end assets. I mocked up these patterns with JSON data objects and finally built out representative pages with the pattern.</p> <p>Once the design system was done, I started on the Drupal integration. My goal was to pull as much as possible from the design system (no cheating!). Composer made it easy for me to keep the design system and theme separated. I leveraged the Components module and proceeded to site build and theme simultaneously. Some of this was challenging. It was a fight between properly structuring content, overriding Twig themes, leveraging the Components module to include pattern markup, and parsing Drupal-related data objects.</p> <p>I blogged about this several times, both in terms of the concepts I used and the architecture I leveraged. Please feel free to read more about this if you are interested.</p> <h2>Twig. All the Twig.</h2> <p>Developing Twig was hands-down the most time consuming part of this project unquestionably. I spent countless hours digging through Drupal rendered objects, raw data, attributes of that data, and much more. I tweaked the display settings, created new view modes, did more tweaks, changed more Twig, and this went on endlessly. Data objects passed to Twig were massive and nearly impossible to debug without printing keys, refreshing, and traversing deeply nested objects. The syntax changed with every different entity and then even more with Views. Yes, Drupal is incredibly powerful and extensible. But, I continue to be surprised by the developer experience around Twig. I just wish this was simpler. I know the community has developed several utility modules. I need to dig into those more.</p> <h2>Media System: image links</h2> <p>I had aspirations of contributing a composite field type module back to Drupal core. Developing this module, which never got complete, was a huge rabbit hole. I thought it would be relatively straight forward to inherit the core-provided image and link field types into one new field type. This was significantly more complex, especially when dealing with cardinality, managing data storage/schemas, and dealing with field widgets that were built atomically. This approach was rapidly becoming complex and it didn't seem like it needed to be. Then, help was on the way...</p> <p>Daggerhart shared with me the new Media entity delivered as part of the Media system in Drupal 8. I was able to create an Image Link media type that was composed of an image field and a link field. This was super easy. From this, I got all of the expected benefits: Views support, easy integration with other entities, and the perks of the new media features in Drupal. Lesson learned: spend some time figuring out what parts of Drupal can be used before diving into code.</p> <h2>Migration from Drupal 7</h2> <p>My website is quite simple. It's a basic blog. I wanted a simple way to migrate the content. Drupal offered that with it's native Drupal to Drupal migrate UI.</p> <p>Set up was simple. I was able to set up a secondary database within settings.php for my Drupal 7 database. I archived to the files directory on Drupal 7, extracted them in a specific file path, and entered that path in the UI.</p> <p>When I ran it, I got a nice confirmation page of potential issues with the Drupal 7 configuration, modules, and structure. I worked through it, ran the migration, and it basically worked. I had a few nodes that did not properly map their type. I cleaned this up through the database by manually editing the node type in the node and node revisions tables, followed up with a cache clear. I also had a few nodes that did not display the body field content. I simply re-saved the node and it worked.</p> <p>I had to run this migration multiple times over three years as I refreshed the content on my current site. The UI didn't offer a rollback (this would be a nice feature through the UI) and had a big red note of caution when running multiple migrations. I did it anyways and it seemed to work fine for me (again, with my simple use case). Buyer beware.</p> <p>To learn what happened behind the scenes, I audited all of the migration classes and configuration that were created. Kudos to those who worked on making the migration experience a positive one. I was really surprised how much was automated, how insightful it was, how smooth it went, and how easy it would have been to override the underlying config it created.</p> <h2>TugboatQA</h2> <p>As I previously shared, I wanted a tech refresh. I thought about spinning up a development server, but this isn't really a modern CI/CD workflow. Having such a positive experience with TugboatQA on SimplyTest.me, I wanted to rapidly see changes before posting them live. And, I wanted something that didn't require me to mess around with another server. TugboatQA for the win.</p> <p>Set up was straightforward. This may be potentially because I understood most of the concepts from SimplyTest.me, even though the use case couldn't be more different for my personal website. I followed the documentation and configured it within a few hours (love their IaC set up). I actually found a bug in how I implemented Composer paths, which I subsequently fixed.</p> <h2>Ported Block Type Templates module to Drupal 9</h2> <p>Again, I spend a lot of my personal time on SimplyTest.me. I still moonlight with some module maintenance, but I do a pretty poor job at this if I am being honest. I did use the Block Type Templates module I developed to have more fine grain control of block Twig. This was immensely helpful with the design system integration. But, I had to port it to Drupal 9. I also cleaned up the issue queue and added some nice features. I hope this helps others, too.</p> <h2>Future Goals</h2> <p>This website is definitely not done. I have some open issues I've logged. I need to clean up some content, fix even more styling, and finish the Tugboat implementation now that I've launched. It's done enough for me to not maintain two websites any longer. Please feel free to let me know if you find any issues.</p> <p>I also want to add more DevOps. When a PR gets merged, I want to set up a GitHub action to trigger the deployment on prod: pull updates from Git, load the config, run the database updates, and clear the cache. My confidence is a lot higher with Tugboat and my CI/CD workflow in the mix. This should be fun.</p> <p>I'll be finally adding SSL support for my site, but I'd like to do this by learning more about the edge layer. I need more research but leaning towards using CloudFlare for this.</p> <p>Pattern Lab has evolved majorly since I first set up the design system. I'll be upgrading this with time and I have the Drupal integration versioned to allow for changes like this to be done in the future.</p> <h2>Final Thoughts</h2> <p>I'm glad to finally be able to launch this thing. I learned a lot doing this. Drupal is still a fantastic option for digital experiences. It does so much. Drupal 9 brings all of the previous years of learnings, new features, great performance, modernized dev workflows, and continues to deliver a highly extensible framework. I thought a lot about developing this in something like Gatsby, but would have still wanted a FOSS backend like Drupal. It did everything I needed natively.</p></div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon, 12/28/2020 - 22:43</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> <div class="field__item"><a href="/taxonomy/term/6" hreflang="en">people</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Tue, 29 Dec 2020 03:43:46 +0000 admin 122 at http://nerdstein.net Getting Started with Drupal Rector Development http://nerdstein.net/blog/getting-started-drupal-rector-development <span class="field field--name-title field--type-string field--label-hidden">Getting Started with Drupal Rector Development</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Drupal 8 introduced a lot of changes. Its usage of best-of-breed dependencies, like Symfony, represented a major shift for Drupal. Drupal 8 also moved to a more nimble release cycle with its major and minor releases, allowing a major version of core to get new features in minor releases. Between dependencies releasing updates and changes to core being more common, Drupal needed to evolve to manage change. One way this happens is through deprecations. This provides a way for Drupal to evolve, identify old code as deprecated, and offer guidance on a replacement. Deprecations were especially common as Drupal evolved from a largely procedural approach to one leveraging more object orientation and some of the design patterns Symfony uses, like controllers, services, and more. But, the great thing about this approach for deprecations is that deprecations do not get removed until the next major release of Drupal. This buys the community time to progressively fix deprecations within their code and is somewhat expected between major releases. But, what if addressing deprecations could be automated?</p> <p> </p> <p>Rector, in its simplest format, is an automated, fancier "find and replace" tool for deprecations. It tries to identify the correct symbols and conditions to perform substitution within a library of rules. Each rule has two primary parts: what it is seeking to replace (statement, method, function, argument, and more, represented as a class) and a processing method to perform the replacement. Each rule returns the object with the proper replacement. This processing is great for nuances tied to deprecations, like optional arguments. Rector’s underlying tooling has fine-grained symbolic control that allows for the highest degree of tuning around deprecations.</p> <p> </p> <p>Did you know that Drupal is able to automate updating deprecated code? Did you know that these updates represent the bulk of the code development work required to upgrade from Drupal 8 and 9? Did you know that this approach works for core, contributed projects, and for custom code? And your help is needed.</p> <p> </p> <p>This blog post demonstrates how to create and run Rector rules, with some tips along the way. If we all paused from our hectic lives and made just one Rector rule, we could hit a really high percentage of refactoring (note: it may be impossible today to do this kind of automation and hit a flawless 100%). The future goal of this should be upfront time savings with a follow-up manual review and testing to ensure the automation worked.</p> <p> </p> <h2>Background</h2> <p>In my undergrad, we learned about compilers, particularly Lex and Yacc. In grad school, we leveraged symbolic execution for vulnerability analysis. I didn’t find either experience all that satisfying. Both represent a broader umbrella of symbolic computing, with different intended purposes. In short, the code in programming languages can be broken down into its smallest, atomic parts, line by line, part by part.</p> <p> </p> <p>Let’s break down a simple statement, <i>$number = 8;</i>. A variable in PHP starts with a "$" character and the context of the variable can change based on the location of a variable. The "=" sign refers to the assignment operator, where the variable set is to the left of the equals sign. "8" represents a literal value, not generated like a function. All of this is considered a statement, which is terminated with a ";" character.</p> <p> </p> <p>The key takeaway is that even a line of code that is trivial to type is a series of symbols with specific semantic meaning tied to the program language itself. Now, imagine expanding this into conditions, functions, objects, and much more.</p> <p> </p> <h2>Getting Started with Drupal Rector Development</h2> <p>These steps should serve as a high-level walkthrough to getting started. Development best practices are still being established, and I encourage joining the #rector Slack channel on drupal.slack.com to get help, offer feedback, and coordinate contribution.</p> <p> </p> <h3>Step 1: Getting setup</h3> <p>The Palantir.net team has set up <a href="https://github.com/palantirnet/drupal-rector-sandbox">a sandbox project</a> with instructions on how to rapidly set up an environment for Drupal Rector development. The development instructions follow most Github standards, where a fork of the Drupal Rector project is created to observe the standard pull request workflow.</p> <p> </p> <p>To identify a deprecation to work on, check the consolidated list of deprecation notices in public modules <a href="https://dev.acquia.com/drupal9/deprecation_status/errors">here</a> (filter by category "not covered by rector" and click on the "total occurrences" column for a sense of impact). Mention the deprecation on the rector channel on Drupal slack for discussion and check that someone hasn’t already filed the issue on <a href="https://www.drupal.org/project/rector">the drupal.org issue queue</a>. If no one has worked on it, file an issue in the <a href="https://www.drupal.org/project/issues/rector">issue queue</a> and assign it to yourself for the unassigned deprecation you wish to work on.</p> <p> </p> <h3>Step 2: Mock up code and analyze</h3> <p>It is important for you to mock up code examples for all variations that demonstrate both the old (has deprecations) and the new (remediated) code. Yes, <strong>you should do both</strong>. Why? Drupal Rector dependencies ship with an analysis tool that shows you how statements are composed. It breaks down a statement into a hierarchy of parts. Running the analysis tool against the old and new code helps you identify differences.</p> <p> </p> <p>A specific example file for <i>Drupal::url</i> can be found <a href="https://github.com/palantirnet/drupal-rector/blob/master/rector_examples/src/DrupalURLStatic.php">here</a>, found in a file named <i>DrupalURLStatic.php</i>. To run the analysis tool against that file, as an example:</p> <blockquote><p>vendor/bin/php-parse web/modules/custom/rector_examples/src/DrupalURLStatic.php</p></blockquote> <h3> </h3> <h3>Step 3: Develop the Rector Rule</h3> <h4>3.a. Creating the Rule</h4> <p>Create a new rector rule class within <a href="https://github.com/palantirnet/drupal-rector/tree/master/src/Rector/Deprecation">this directory</a>. Check <a href="https://github.com/palantirnet/drupal-rector/tree/master/src/Rector/Deprecation/Base">existing base classes</a> for some helpful starting points that will simplify the logic within your rule. When possible, extend a base class, otherwise, extend the <i>AbstractRector</i> class from the Rector utility. Please bear in mind that if a base class exists, this likely automates a lot of the processing. You may not need to follow every step defined below, but I capture the basic steps you need without a base class.</p> <p> </p> <p>Begin with a <i>getDefinition()</i> method that provides a human readable description and simple before and after code sample.</p> <p> </p> <p>Next, create a <i>getNodeTypes()</i> method. Running the analysis tool in step 2 against both the old and new code samples offer guidance on how to create the remediated code returned from the processing in the rule. The generated output of the analysis tool will help highlight exactly what development changes are required to get an old code sample to become the new one. For efficiency and best practice, your starting point should be the highest order difference between the old and new code. Are you replacing a whole statement? Are you replacing a function or just an argument? Within this function, return an array of matching class types tied to the highest order class. This will send all instances of this class as an argument to your processing function when the Rector engine executes. Classes are represented as one or more PHPParser <i>node</i> class types. Please note, this is not a Drupal node, but a PHPParser base class representing the breakdown of the PHP language (e.g. functions, objects, statements, etc).</p> <p> </p> <p>Finally, create the processor by creating a <i>refactor(Node $node)</i> method. It’s common in this method to further refine the conditions of the passed argument. For instance, if you pass a deprecated class method like <i>getEntity</i>, the argument will receive a class of type <i>ClassMethod</i>. A simple condition to check the name attribute of this instance will refine the processing further.</p> <p> </p> <p>Proceed to create the new desired code hierarchy in the refactor method from the differences between the code samples. As an example, if you only need to change the name of a method, replace the name attribute in the passed argument and return the object at the end of the processing. Processing differences can be drastically more complex, but encouraging the use of base classes and patterns should lower the complexity over time.</p> <p> </p> <p><i>An example of a Rector rule</i></p> <script src="https://gist.github.com/nerdstein/45421b67f608c3ffc5f73301f239fd59.js"></script><h4>3.b. Register the Rule</h4> <p>Rules should be associated with a deprecation. A deprecation associates to an issue that identifies what major/minor version of core that introduced the deprecation. Any rules that are created from the deprecation need to be registered in the correct config file found in this directory. If there is not an associated config file for the major/minor version, create a new YAML file with the class name for the rule. Add this new YAML file to <a href="https://github.com/palantirnet/drupal-rector/blob/master/rector.yml">the project’s YAML file</a> and the new YAML file referenced in the <a href="https://github.com/palantirnet/drupal-rector/blob/master/config/drupal-8/drupal-8-all-deprecations.yml">'all deprecations' YAML file</a>.</p> <p> </p> <p><i>The rector.yml configuration file</i></p> <script src="https://gist.github.com/nerdstein/1374b5fa5d9fa44e472f03dc603712c6.js"></script><p><i>An example configuration file that registers a Rector rule</i></p> <script src="https://gist.github.com/nerdstein/c009fb04278722dd4ce4c16844aced1b.js"></script><h3>Step 4: Running and Validating</h3> <p>Double check your YAML configuration to ensure the rules are properly registered.</p> <p> </p> <p>To see a dry run of the rector engine processing, use the following to process all of the rector examples:</p> <blockquote><p>vendor/bin/rector process web/modules/custom/rector_examples --dry-run</p></blockquote> <p> </p> <p>This will help to debug and iterate on your new rector rule. Running this command will display code that matches Rector rules and code that does not. Hopefully the matches are the old code that needs to be updated to the new code. The new code should come back clean.</p> <p> </p> <p><i>A partial example of a log output</i></p> <script src="https://gist.github.com/nerdstein/db11132d37e6d21763d899a0107e7d44.js"></script><p>Remove the <i>--dry-run</i> flag and replace the path to actual code to have Drupal’s rector engine perform code refactoring.</p> <p> </p> <h2>Summary</h2> <p>Automating deprecations can be complex but it is high impact work. It should become our new muscle memory as we continue to deprecate code in the future. Imagine handing developers a tool capable of automatically performing maintenance to code indefinitely. We can get there.</p> <p> </p> <p>I want to thank the following people for reviewing and contributing to this blog post: Dan Montgomery, Ofer Shaal, Ken Rickard, Dries Buytaert, Gabor Hojtsy.</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Tue, 05/05/2020 - 12:53</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Tue, 05 May 2020 16:53:14 +0000 admin 120 at http://nerdstein.net http://nerdstein.net/blog/getting-started-drupal-rector-development#comments Drupal 9 Deprecations with SimplyTest.me http://nerdstein.net/blog/drupal-9-deprecations-simplytestme <span class="field field--name-title field--type-string field--label-hidden">Drupal 9 Deprecations with SimplyTest.me</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Drupal 9 readiness is as easy as cleaning up deprecations for your Drupal 8 project. SimplyTest is well positioned to help with this.</p> <p> </p> <h2>Deprecation Reporting</h2> <p>You can generate a report of the deprecations by performing the following steps:</p> <p> </p> <ol><li>Go to <a href="https://www.simplytest.me">simplytest.me</a></li> <li>Under “Enter a project name:”, start typing the name of your project and select it</li> <li>Select the relevant 8.x branch (not release, as this will also scan unreleased commits)</li> <li>Expand “Additional Options”</li> <li>Click “Add Additional Project”</li> <li>Start typing “Upgrade Status” and select it</li> <li>Click “Launch Sandbox”</li> <li>Go to “admin/reports/upgrade-status”</li> <li>Select your project</li> <li>Click “Scan”</li> </ol><p> </p> <p>This will generate a list of the deprecations. Do not close the modal or the browser tab, as we will use it later.</p> <p> </p> <h2>Filing an Issue</h2> <p>If you are testing a Drupal.org project, file an issue against that project for the deprecations report. Upgrade Status will report back for an existing issue, but please double check that an existing issue does not exist. I used the following steps to submit the report:</p> <p> </p> <ol><li>Go to the project page and click “file issue”</li> <li>Select the appropriate 8.x branch</li> <li>Issue title: “Drupal 9 Compatibility”</li> <li>On your reporting tab, click “Export as HTML”</li> <li>Copy the HTML source containing the table of deprecations</li> <li>Paste this into the issue description</li> <li>Click “Save”</li> </ol><p> </p> <h2>Remediation</h2> <p>Currently, the only way to get a completely remediated Drupal 9 project is to manually address each item found in the report. From personal experience, addressing deprecations is a drastically simpler exercise than performing a Drupal 7 to 8 port. This is primarily because Drupal 8 was a big shift off the island and now is concerned about dependencies. Architecturally, Drupal 8 changed a lot of APIs, introduced Composer, changed paradigms (e.g. procedural to object oriented), and leveraged modern design patterns. Drupal 9 leverages basically the same architecture as 8, but changed major versions primarily to remove deprecated functionality.</p> <p> </p> <p>Again, while the work is manual, the task should be relatively straightforward code refactoring. And, there is good guidance. Many of the items listed in a report also link to the relevant drupal.org issue for the deprecation. This is useful to identify how to properly remediate the deprecation and offer relevant context.</p> <p> </p> <h2>Automated Remediation</h2> <p>I expect deprecations to be the new norm as dependencies end-of-life or changes are made to core with minor releases. We can come to expect more frequent major releases that follow the same paradigm as 8 to 9. But, wouldn’t it be cool if this could be automated?</p> <p> </p> <p>The <a href="https://www.drupal.org/project/upgrade_rector">Upgrade Rector</a> project built by Gábor Hojtsy, based on the drupal8-rector project created by Dezső Biczó at Pronovix, aims to do just that. It aims to build off of the Upgrade Status module by automatically remediating deprecations. Each deprecation maps to a specific rector rule. These rules map deprecated code to their updated equivalents.</p> <p> </p> <p>Palantir recently wrote a blog post on the Upgrade Rector project. They are actively looking for contributors to work on rector rules in <a href="https://github.com/palantirnet/drupal-rector">their Github project</a>. As of now, only <a href="https://github.com/palantirnet/drupal-rector/tree/master/src/Rector/Deprecation">one rule</a> for “drupal_set_message” is completed, which is why this process is not yet fully automated. We all need to help get this up to 100%, if possible. Many thanks to Palantir and <a href="https://www.palantir.net/blog/jumpstart-your-drupal-9-upgrade-drupal-rector?utm_content=120033669&amp;utm_medium=social&amp;utm_source=twitter&amp;hss_channel=tw-24758605">this blog post discusses the project</a>.</p> <p> </p> <h2>Future State</h2> <p>Why stop there? Move the Upgrade Rector into core. New deprecations in core should mean new rector rule(s) are created and should be a gate in releasing a new deprecation in core. Build infrastructure into drupal.org to periodically scans for these deprecations, files an issue against the project if new deprecations are discovered, post patches to fix them, and fire the automated tests. While I still envision manual testing would be needed to validate the results, this basically would help keep modules maintained automatically. The beginnings of this discussion are happening <a href="https://www.drupal.org/project/rector/issues/3117497">here</a>.</p> <p> </p> <h2>Resources</h2> <ul><li><a href="https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/deprecation-checking-and-correction-tools">https://www.drupal.org/docs/9/how-to-prepare-your-drupal-7-or-8-site-for-drupal-9/deprecation-checking-and-correction-tools</a></li> <li><a href="https://www.drupal.org/project/upgrade_rector">https://www.drupal.org/project/upgrade_rector</a></li> <li><a href="https://www.palantir.net/blog/jumpstart-your-drupal-9-upgrade-drupal-rector">https://www.palantir.net/blog/jumpstart-your-drupal-9-upgrade-drupal-rector</a></li> <li><a href="https://www.drupal.org/project/rector/issues/3117497">https://www.drupal.org/project/rector/issues/3117497</a></li> <li><a href="https://github.com/palantirnet/drupal-rector">https://github.com/palantirnet/drupal-rector</a></li> </ul></div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Wed, 03/04/2020 - 19:07</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Thu, 05 Mar 2020 00:07:23 +0000 admin 117 at http://nerdstein.net http://nerdstein.net/blog/drupal-9-deprecations-simplytestme#comments SimplyTest.me release welcomes TugboatQA, Centarro, and Linode http://nerdstein.net/blog/simplytestme-release-welcomes-tugboatqa-centarro-and-linode <span class="field field--name-title field--type-string field--label-hidden">SimplyTest.me release welcomes TugboatQA, Centarro, and Linode</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>On the evening of September 13th, <a href="https://simplytest.me">SimplyTest.me</a> launched a major new release of the product. The product release achieved three major goals:</p> <ol start="1"><li>Replacement of the backend with TugboatQA</li> <li>A new one-click demo framework</li> <li>A new development environment</li> </ol><p>All efforts could not have been possible without the generous sponsorship from <a href="https://tugboat.qa">TugboatQA</a> (a product created by <a href="https://www.lullabot.com">Lullabot</a>), <a href="https://www.centarro.io">Centarro</a>, and <a href="https://www.linode.com">Linode</a>. And, community time offered from <a href="https://www.hook42.com">Hook 42</a>.</p> <p>The rest of this post outlines the release and its significance.</p> <h2>TugboatQA Backend</h2> <h3>How did we get here?</h3> <p>The backend infrastructure of SimplyTest.me was one server running as a single point of failure. A bulky, monolithic server running a LAMP stack and concurrent virtual hosts/databases/etc. The system had no viable backup, no load balanced failovers (a money problem, not a technical problem), and no affordable replacement.</p> <p>Community conversations were held <a href="https://www.drupal.org/project/simplytest/issues/2939497">to advise on an open source replacement</a>. Many agreed on a panacea that included the use of Docker and Kubernetes running on a cloud-based provider. This gave end users cleanly isolated instances and an infrastructure capable of rapidly provisioning and terminating instances through well-adopted, right fit technologies. This could have been an impactful contribution back to Drupal, too.</p> <p>While this approach would have been great, who would pay for it? Rough numbers showed that even with the use of tiny spot instances through AWS, we still would have paid at least a few thousand dollars a month for our free-to-use service. Significantly more during major events, like DrupalCon and camps. And, no money or sponsorship to change course. Furthermore, we would have needed to do a ton of technical work across the full stack to achieve this goal. Since the current site still needed to be maintained, this approach would have certainly been a challenge.</p> <p>Fears had been growing around the sustainability and maintainability of our underlying infrastructure. The backend system repeatedly would go down and over a decade of technical debt had built up. The divergence of Drush 8 and 9 and support for past versions of Drupal also complicated things. The issues were becoming more and more complex. I needed to solicit the help of experts like <a href="https://www.drupal.org/u/elijah-lynn">Elijah Lynn</a> and <a href="https://www.drupal.org/u/greg-boggs">Greg Boggs</a>, while trying to respect their available time.</p> <p>Matt Westgate and I connected at <a href="https://www.drupalcampnj.org">DrupalCamp NJ</a> 2019. In 2018, he introduced me to TugboatQA. But, at the time, the lack of an API held back any integration possible with SimplyTest.me. That changed by 2019 as the product matured. This was welcomed news, but I maintained a degree of skepticism that a QA-based DevOps tool could be leveraged for the full set of needs. I maintain the opinion that SimplyTest is more difficult than people initially acknowledge, regardless of people’s good intentions to make suggestions or offer advice.</p> <p>When we revisited our conversation, SimplyTest.me just opened our <a href="https://opencollective.com/simplytestme">OpenCollective</a> but still had very little funding raised. I had a lot of technical questions for the Tugboat team around features and we discussed their high-level approach to achieve my goals. It seemed possible. We exchanged some high level statistics on usage and I finally asked how much it would cost. Their team graciously agreed to pay for our backend. Game changer.</p> <p>While we had a long road to build out this replacement, the generosity of both Lullabot and TugboatQA to pay for the backend solved a huge hurdle and may have saved the future of SimplyTest.me. It ended up meeting our goals for a new backend. Concerns were raised from the community about not using an open source service/product. This type of situation raises awareness around the harsh realities people like me face when trying to sustain open source communities and run a service with limited fundraising, mostly volunteer led efforts, and minimal corporate backing. I’ll blog about this topic later, but both <a href="https://dri.es/balancing-makers-and-takers-to-scale-and-sustain-open-source">Dries</a> and <a href="https://www.lullabot.com/articles/healthy-open-source-maintenance">Lullabot</a> wrote excellent articles about related topics.</p> <h3>How did we approach this?</h3> <p>The best engineered solutions are ones that are iterated through what is learned. We made two major architectural passes to iron out design decisions.</p> <p>TugboatQA is based on the concepts of previews. Previews are created from branches on repositories, where new code in the repository could be deployed on a preview for quality assurance. Previews are configured by a set of scripts defined in a YAML file on that branch, similar to a Docker Compose file. SimplyTest.me instances get code from external repositories specific to what a user selects. If SimplyTest were to use a repository, it could only leverage the YAML configuration since the code would be pulled elsewhere.</p> <p>We started with this approach to create previews from branches. SimplyTest.me, still running Drupal 7, leveraged PHPTemplate to create dynamic preview definitions in YAML. The YAML would change based on what SimplyTest users selected to test. Definitions could clone Drupal, select a version, pull down projects, load patches, and perform installations. This generated YAML was pushed to a unique branch in a repository, where each branch represents the desired submission. All git related details were made configurable and leveraged a Github-based PHP project to properly respect environments and not push code with credentials. Once the branch existed, the Tugboat API was invoked to create the preview through Tugboat’s downloadable CLI tool. I personally ported a Backdrop TugboatQA module into Drupal 7 and posted it <a href="https://www.drupal.org/project/tugboat">on Drupal.org</a> for others to benefit from. This module is a simple utility and abstraction for access tokens, logging, and invoking Tugboat commands uniformly.</p> <p>After the first pass at the architecture, the process was generally working. We had scaffolding for Drupal 7 and 8, projects, patches, distributions, and installation logic. The primary issue was performance. It took a lot of time to create each preview. Each provision performed similar, preliminary steps: it created the containers for the infrastructure, loaded things like PHP-APC and PHP extensions, cloned Drupal, installed Drush, etc. TugboatQA had already solved this in their platform with a feature called Base Previews. In short, there are one or more branches that held the YAML definition of cached previews. SimplyTest was able to extract common scripts for Drupal 7 and 8, copy base previews, and apply the preview-specific configuration in the preview’s own specific branch. This sped things up significantly.</p> <p><a href="https://www.drupal.org/u/volkswagenchick">AmyJune Hineline</a> led testing efforts to make sure we maintained feature parity from the old to the new backend. We iterated and worked through a defined set of feature-based issues that she created until we stabilized the new system. This methodical approach to testing with a set of issues tied to features will help us when we look to launch Drupal 8 in the not-too-distant future.</p> <p>While we still have some known gaps causing failures, like executing make files for Drupal 7 distributions, we were comfortable launching the new backend and remediating issues as they arise.</p> <h3>Key Outcomes</h3> <p>This new backend has already had significant impact on the project and the service offered to users.</p> <ul><li>Radically simplified systems</li> </ul><ul style="margin-left: 40px;"><li>Removal of the old backend server</li> <li>Removal of HAProxy logic</li> <li>Removal of backend SSL certificate</li> <li>Empowered the SimplyTest web application to define and control previews created</li> </ul><ul><li>Drastically reduced the amount of supporting code that built up over years</li> </ul><ul style="margin-left: 40px;"><li>Removal of legacy bash scripts that were retrofitted for newer versions of Drupal</li> <li>Replaced the custom web application to backend interaction with a cleaner, decoupled approach: shared repository and web service API</li> </ul><ul><li>Offered users drastically better stability</li> </ul><ul style="margin-left: 40px;"><li>Removal of maintaining highly customized legacy servers that were difficult to maintain/update with updated technologies and changes potentially impacted the entire service, not just specific instances</li> <li>Isolating failures to either service provider outages and/or gaps in the SimplyTest application, not a bespoke, highly customized backend server</li> </ul><ul><li>Rapid changes are now possible</li> </ul><ul style="margin-left: 40px;"><li>New instances are not tightly coupled to other running instances</li> <li>Simplification opens up new opportunities for contributors to participate without having the concern of bespoke infrastructure setup.</li> </ul><h3>Acknowledgments</h3> <p>I want to thank TugboatQA (<a href="https://www.drupal.org/u/matt-westgate">Matt</a>, <a href="https://www.drupal.org/u/q0rban">James</a>, <a href="https://www.drupal.org/u/blc">Ben</a>), Jonathan Daggerhart (<a href="https://www.drupal.org/u/blc">daggerhart</a>), AmyJune Hineline (<a href="https://www.drupal.org/u/volkswagenchick">volkswagenchick</a>), and <a href="https://www.hook42.com">Hook 42</a> (giving me contribution time and sending me to camps that I used to do this work and collaborate/sprint on this effort).</p> <h2>One Click Demos</h2> <h3>How did we get here?</h3> <p>Drupal distributions have been a significant issue for SimplyTest. Drupal 7 had stronger opinions on distributions, primarily around the use of specifically-named make files. Drush enabled scripting covered most of the use cases to build distributions programmatically. But, loose dependency definition and inconsistent use of standards still caused many issues -- many of which community members looked to me to help triage and support after SimplyTest would fail to create instances due to issues that were introduced.</p> <p>Drupal 8 still feels like the wild west for consistencies between distributions, primarily caused by how core initially adopted Composer (which is evolving and may be worth revisiting for impact to distributions). A simple comparison between popular distributions like Lightning, Commerce, and Contenta demonstrates my point. Each distribution would likely require custom considerations for SimplyTest to work properly.</p> <p><a href="https://www.drupal.org/u/rszrama">Ryan Szrama</a> approached me to see if SimplyTest could be used to serve as the demo framework for <a href="https://drupalcommerce.org">Drupal Commerce</a> (specifically, the Belgrade demo). This presented an opportunity to improve SimplyTest, raise funds, and explore paid contributions (which was a goal of mine outlined during the vision/roadmap presented at<a href="https://www.drupalasheville.com"> Drupal Camp Asheville</a> 2018). Ryan agreed to sponsor both the underlying architectural changes to enhance SimplyTest and the specific implementation for Drupal Commerce.</p> <p>Side note: it is inspiring to see how Ryan has established a sustainable business primarily around an open source product that routinely gives back to Drupal. Centarro continues to find creative ways to give back even more.</p> <h3>How did we approach this?</h3> <p>While our primary goal was to launch a one-click demo feature for Drupal Commerce, SimplyTest is a tool that serves all people and it would have been a wasted opportunity to not build in the capability for additional one click demos. We leveraged a custom hook to allow submodules to register a one click demo. This behaves similarly to the plugin system in Drupal 8. Submodules then invoke the hook, which references a YAML definition. It is expected that the definition supports other SimplyTest features for patches, loading of other projects, and more. Base previews still need to be defined manually and managed through the repository, for now. SimplyTest then loads the registered one click demos and displays each as a button through the web UI.</p> <p>We shipped this release with support for both Drupal Commerce and the Umami demo. We welcome others interested.</p> <h3>Key Outcomes</h3> <ul><li>Created a new feature that made the creation of Drupal-based demos with one click</li> <li>Created an extensible framework that solved a long-term difficult issue with distributions and allowed other distributions to be easily rolled in</li> </ul><h3>Governance</h3> <p>I still have not fully established guidelines to help with the governance of this new feature. I believe it’s important for businesses that sponsor SimplyTest to be eligible for their open source distribution to be delivered as a “one click demo”. However, completely open source demos, like Umami, should be eligible too. Fundamentally, I believe it’s important to focus on widely adopted cases around Drupal 8. Maintaining every distribution across every version of Drupal seems like a challenge for this approach. While there will be some discretion, I promise to work with the community in good faith to roll this out for future potential needs.</p> <h2>Development Environment</h2> <p>Given the fact that we were making a significant change, we were able to revisit the creation of a development server from <a href="http://nerdstein.net/blog/season-simplytest">my last attempts over new years</a> (TL;DR - way too difficult). In many ways, the new backend simplified the overall systems. A new dev server was imperative for our testing efforts to launch the new backend. The legacy backend server represented the most complex part of setting up a development environment. And, this was now going away. This made the task a lot more simple to accomplish.</p> <p>Linode offered SimplyTest sponsorship money to use their infrastructure. We were able to leverage these funds to run a Linode for the web front-end during the entire time we were developing the integration for the new backend. A very basic LAMP stack was created and the Drupal database and files were cloned from the production Drupal application. This unblocked the involvement of others to help test and prepare for the new release.</p> <p>I thank Linode for giving us this assistance and I continue to support them in other endeavors. We requested and just received additional funding for continued support of the development infrastructure.</p> <h2>Conclusion</h2> <p>When I transitioned to lead the project, I knew I had challenges ahead of me. I knew the backend needed modernization. I knew I wanted more people and companies to help participate in the sustainability of the system. I knew we would need funding to open up future opportunities. I had ideas but no clear path to move it forward. I invested as much time as was needed to get to this day and it wasn’t even possible without the help of others and the new, generous support of TugboatQA, Centarro, and Linode combined with contributed time from Hook 42. I’m glad we got here. The work is not done. But, I feel much better about where this is heading now that we have this major milestone behind us. I also continue to be grateful for the largely volunteer-led efforts from community members that helped in all capacities.</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Sun, 10/06/2019 - 17:19</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Sun, 06 Oct 2019 21:19:32 +0000 admin 115 at http://nerdstein.net http://nerdstein.net/blog/simplytestme-release-welcomes-tugboatqa-centarro-and-linode#comments A DevOps Primer http://nerdstein.net/blog/devops-primer <span class="field field--name-title field--type-string field--label-hidden">A DevOps Primer</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>I just closed about 100 browser tabs from an early year activity. While it’s embarrassing I left those tabs open so long (going on five months), I wanted to leave them open to reflect on what I learned. And, what a better way to reflect than a blog post.</p> <h1>The Soapbox</h1> <p>Bear with me for a moment. I subscribe to the philosophy that DevOps represents a line of thinking and a way of working, not a technical product. Like security, when someone says, “I’ll sell you DevOps”, it’s a scam. For those “doing” it right, you need to focus on foundational concepts built into continuous learning. This ideology has been communicated in talks and blog posts by some of our communities’ sharpest minds, including <a href="https://twitter.com/dev_meshev">Michelle Krejci</a>, <a href="https://twitter.com/geerlingguy">Jeff Geerling</a>, <a href="https://twitter.com/schwartz_cio">Mark Schwartz</a>, and more. And, in my opinion, there is a direct correlation to Agile frameworks. Concepts like retrospectives to share learnings, sprint planning to prioritize, and backlogs to organize and track work thoughtfully. Most of these ceremonies are open to the whole team to participate, so they naturally advocate for transparency and connection.</p> <p>Key point: DevOps is ongoing evolution through experience. Technical progress is just one type of outcome, but equally important are solutions involving people and process.</p> <h1>DevOps Philosophy</h1> <p>Change is at the heart of DevOps. Digital products change. People change. Organizations change. DevOps reflects a pragmatic approach to change. Many people discuss it, especially solutions. But, any solution reflects a change that is, hopefully, driven by what is learned and what can deliver better, more impactful results for actual people and real problems encountered.</p> <p>As a basis, start by evaluating what change occurs. On the surface, change can look like any of the following:</p> <ul><li>System and software updates, especially security ones</li> <li>Continuous Integration (deployments of change increments, automated/manual processes)</li> <li>Changes in the team (people leaving, people being promoted to new roles)</li> <li>Organizational changes (launching a new product, leadership transitions, exploring new markets)</li> <li>Incidents (getting hacked, services going down)</li> <li>Exploring new technology</li> <li>Sunsetting legacy technology</li> </ul><p>A mature DevOps practice is capable of adapting to change ongoing and remaining effective. An equally important philosophy is the openness and willingness to change. This is built on trust and the recognition that every team member is capable of bringing value: to allow people to raise their voices, drive change, and have impact.</p> <p>DevOps can be transformational. It can be really hard and a non-natural fit for the “command and control” models traditionally established with embedded IT departments and IT vendors. Mark Schwartz writes about this concept routinely in <a href="https://aws.amazon.com/blogs/enterprise-strategy/shu-ha-ri-detaching-from-and-transcending-command-and-control/">blog posts</a> and in his book “A Seat at the Table: IT Leadership in the Age of Agility.” Again, organizations suggesting they can just buy DevOps would be in for a rude awakening or really only purchase an incremental step relevant at that point in time only. Sure, you can purchase a two million dollar Kubernetes, cloud-based infrastructure framed as DevOps. But, what problem are you really trying to solve? Chances are, you’ll be in a position of opening up countless other problems around vendor lock-in, governance, maintenance, and enablement you never anticipated. A better place to start is in the cultural change needed to identify problems, define risk, share ideas and make smarter investments capable of having real impact now and in the future.</p> <h1>Bare Minimum DevOps</h1> <p>Now that I’ve set the context for my thoughts on what DevOps actually is, how do you do it? I’ve long wanted to make a blog post defining some bare minimum DevOps concepts. I’ve seen so many differences when I interact with a client or project. I’ve wanted to point back to something and highlight my thoughts foundationally. The range of differences reflect the varying states of learning organizations are at. And, it’s natural given many of the concepts represent years of learning how to build quality, robust digital solutions that are capable of adapting to change.</p> <h1>Case Study: SimplyTest.me</h1> <p>Remember the browser tabs and the learning I did? I was working on SimplyTest.me. As of right now, it is a perfect example of a system desperately needing some DevOps love. There has been a lot learning and needs are defined. As the project lead, I’ve long wanted to automate routine failures, establish a more easy-to-change system, and open up SimplyTest.me for broader contribution. But, the current system is very hard to do so as it’s developed. The infrastructure is highly customized, the tool is fairly complex, and, subsequently this makes it harder to change. This may make for a future blog post, but the concepts shared reflect this recent exploration for SimplyTest.me and things I’ve encountered over the years.</p> <h1>Environments</h1> <p>Change cannot and should not happen only on production systems. Any change needs prepared, tested, and then deployed to prod. At a minimum, you need both production and non-production environments. Change increments then get vetted through non-production environments before being released to production. A non-production deployment helps vet any changes made on a local system before they hit production.</p> <h1>Parity</h1> <p>Parity is a key concept for evaluating change. As an example, if you are running different versions of PHP across environments, evaluating a change increment on one environment may not match the results of what gets deployed to production.</p> <h1>Evaluation Criteria</h1> <p>If you change things, you need to establish a means for evaluating the change. This could be both manual or automated testing of a change. It could also be part of your development workflow on what needs to be tested when implementing a change. But, the key is you need to have some process defined. And, this process needs to respect both the change increment itself and some means of looking at regressions throughout a system. Because, any change made in one part of the system can unknowingly impact the rest of the system. Establishing some criterion to evaluate change may be just as important as the change itself.</p> <p>This is not confined to technical change either. When making process or organizational change, there should be some metrics and data used to measure efficacy. After all, if you are making a change, how do you really know if the change worked?</p> <h1>Source of Truth</h1> <p>Also known as the “system of record”, a source of truth is a data integrity concept. Some aspects of environments are more flexible to change than others. Most production systems maintain data (users, content, files, etc) that most likely should not change as change increments are deployed. And, non-production systems leverage production systems as the source of truth, often by synchronizing or replicating a “point in time” snapshot of the data to mirror production data before a change is evaluated. The source of truth is left untouched until the change is ready and often throughout a deployment of a change. Third party systems may be the source of truth for other enterprise functions. For example, an email server may be the source of truth for notifications sent from a system. Recognizing the differences and responsibilities of specific systems and environments help understand source(s) of truth and how they serve a larger change process.</p> <h1>Backups</h1> <p>Even with the best of intentions, changes can break things. Be prepared at any point to restore from a backup, both by rolling back changes and by restoring databases. Test this process (really… it needs to work in the most inopportune of times). Having timely and relevant backups are hugely important for restoring botched deployments, standing up systems after a security incident, and generally gives people piece of mind. Make sure you understand how to restore a backup and bonus points if both the backup and restoration processes are automated.</p> <h1>Keys</h1> <p>Identity management is a really deep and hard problem in IT. On the surface, people think identities apply only to users. But, systems need a proper way to be identified and subsequently verify their authenticity. Keys are a widely adopted approach. Often there is a public and private key, where the public key is referenced by other interacting systems. The private key is used as the verification and is owned strictly by the system. Maintaining keys is a critical way to ensure only specific systems are capable of accessing and associating a system to another (often through users and their specific permissions).</p> <h1>Secured Channels</h1> <p>When systems are interacting, one needs to be concerned with the channels in which systems communicate. Secured channels help encrypt communication. The most common of which is the use of SSL and certificates. Tools like LetsEncrypt have brought SSL to the masses, allowing for a free way to generate (and re-generate) SSL certificates capable of happening in an automated fashion. This helps mitigate the risk of someone intercepting and browsing your traffic between systems.</p> <h1>Scripting</h1> <p>One key way people apply what they have learned is through scripting things that happen routinely. Scripting also can be as simple as a Bash script to perform a routine operation in a consistent fashion or a robust development framework with conditional scripts executing routinely and on-demand. Those intimidated by scripting can start off basically by leveraging tried and true bash scripts that run non-interactive commands across any environment. You can get a long way without introducing complexities found in more robust frameworks.</p> <h1>Code Repositories</h1> <p>Code repositories are the cornerstone of DevOps. They are the source of truth for all system code, any scripts that run, and can be critical in any disaster recovery/restoration process. Code repositories maintain a record of changes and the users who made the change. Code repositories can be cloned to any environment and fetched as needed (the “D” in “DCVS”). Concepts like branches allow for changes to be staged before being merged into a branch associated with production environments. Releases can be implemented as tags that reference specific commits. And, many popular tools (Github, Gitlab) offer features, like pull/merge requests, that are advantageous within a development workflow.</p> <p>Also, code repositories maintain change events. Creating a new branch may provision a new environment (this has been made popular by CI/CD models) that allow for hands-on testing of changes before release. Merging a branch can destroy an environment and trigger a deployment to a production or non-production environment. And, tools like pull/merge requests allow for code to be reviewed. All of these events are exposed to a broader opportunity that DevOps can leverage. For example, running automated tests can occur when commits are pushed up to a branch or when a deployment to a non-production environment occurs. None of this is possible without a code repository.</p> <h1>SSH</h1> <p>When events occur and scripts need to be run, how do they run on the actual systems? SSH is the answer. SSH is the protocol used to remotely execute commands from one machine to another. I remember the days where every company had huge in-house server closets with expensive terminals to manage the servers. Now, we can purchase affordable infrastructure managed remotely, upload keys, and open terminal sessions that are directly connected to these remote machines. SSH has made much of this possible and is critical for DevOps.</p> <h1>Jenkins</h1> <p>Continuous integration (CI) tools exist to centralize and execute what you have automated. I questioned if this needed to be on the “bare minimum” list. Inevitably, you can do a lot without such a tool, but you won’t get the mileage you could otherwise. CI tools do a great job of logging execution and maintaining a history of execution. Imagine developers having to save their terminal history. Such tools help teams have a central platform where multiple users can log in, access can be control, parameters can be selected to use common scripts, and more. Tools like Jenkins, Travis CI, and CircleCI have native integration with code repositories, can help manage keys and servers, and can manage complex, conditional workflows.</p> <h1>Systems Infrastructure</h1> <p>All of the aforementioned concepts rely on an infrastructure that runs the systems. At a minimum you need at least one server for production and seperate server(s) for non-production infrastructure/environments. Backups should be maintained offline and outside of the servers.</p> <h1>Infrastructure as Code</h1> <p>While it’s not “bare minimum,” container-based, cloud technology can create a flexible infrastructure capable of provisioning and tearing down servers when change events occur. This maintains a durable, extensible infrastructure capable of responding to change. Tools like Docker can help create the definition and images needed for containers. Cloud-based platforms have APIs for creating instances as needed, managed volumes for persistent storage (logs, databases, objects/files), and monitoring needed for auto-scaling. Production infrastructure, or specific aspects of it, should be persistent. Load balancers and proxying become common when implementing this type of infrastructure, including DNS management. Again, while this may not be completely necessary, it’s certainly an appealing option for those building something from the ground up or people looking to level up an existing infrastructure.</p> <h1>Documentation</h1> <p>This may seem commonsensical, but there must be documentation. At a minimum, I look for a README in the code repository, describing how to set up a project, a technical architecture overview, a description of the infrastructure, and a brief rundown of what is found in the code repository. Non-technical users often need Google Docs or something outside of the technical repository. The most common things I’ve seen are flow charts articulating the DevOps workflows, diagrams of the systems, and high-level commentary on insight and motivations. After all, changes in teams or members on teams can be caught completely flat footed without good documentation.</p> <h1>Summary</h1> <p>As I shared, I think DevOps is sometimes a misrepresented topic because I believe it starts with a way of thinking, is often associated with transformation, and is not strictly about technical outcomes. I’ve seen variations in maturity and tried to share what I consider to be a bare minimum set of concepts people need to both practice and apply DevOps. I can’t imagine the number of browser tabs I’ve opened over the years researching this topic. Again, it’s simply not enough to purchase a product and expect the potential outcomes. What do you consider DevOps to be? What set of bare minimum concepts did I miss?</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Mon, 04/29/2019 - 22:06</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/6" hreflang="en">people</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Tue, 30 Apr 2019 02:06:10 +0000 admin 114 at http://nerdstein.net The Season of SimplyTest http://nerdstein.net/blog/season-simplytest <span class="field field--name-title field--type-string field--label-hidden">The Season of SimplyTest</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><p>Last year was spent primarily learning about SimplyTest. We did make some progress, but I think “keeping the lights on” for a system of this complexity was quite a feat after the project transfer. It’s a unique and fairly complex endeavor that bridges all elements of an open source project, a completely free service, and underlying infrastructure. I see all of the good and the bad that comes from each aspect: system maintenance, feedback from community members, customer service (Slack, Twitter, etc), system outages, and more. I recognize how valuable this service is to the community and I strive to offer the best service possible.</p> <p>I serve as the SimplyTest lead. I do most things and do not have a team. I steer both the current system (primarily for stability) and maintain the vision to help modernize, simplify, and scale SimplyTest with time. I balance my time by prioritizing impact, knowing I work full time and have a family. There are times progress stalls given time constraints and this seems to mirror much of the concerns people raise around a perceived “stagnant” system.</p> <p>There isn’t vibrant additional contribution to any aspect of SimplyTest. But, I do want to recognize and thank Greg Boggs for his help, especially with system administration. Iris Ibekwe, who developed a new Drupal 8 theme for our future front-end. Several members of my team (Jonathan Daggerhart, Ryan Bateman) at Hook 42 have helped start build out aspects of the Drupal 8 front-end with me. I welcome help and also need to do a better job of asking for it.</p> <p>There are little to no finances tied to SimplyTest. We are fortunate to have server-level sponsors (shout out to Maloon, Druid, and Linode), but we must make use of free services/tools, pay personally out of pocket, or (very recently) get reimbursed through donations. All labor has been volunteered and some sponsored by Hook 42 (thank you so much!!). I personally have paid for a new design and logo, stickers, domains, and more, long before the Open Collective was established.</p> <p>At the end of the year, I often take time to work on outside things (e.g. professional development or community work). 2018 was no exception. I had a full backlog of items I wanted to tackle with SimplyTest and wanted to share the last two months of progress.</p> <h1>Development server</h1> <p>Given the underlying complexity of the various distributed systems involved, it is incredibly hard for people to set things up on their own. This hurts contribution.</p> <p>Thanks to a generous donation from Linode, we received credit on their platform for new infrastructure. This allowed us to set up both a front-end server and a worker server to be used for development. While this is quite close to being finished, this is a huge milestone for SimplyTest to ensure we vet changes on non-production systems and foster an environment that is safe to make changes such that others can learn the system to contribute.</p> <h1>Clean up procedures</h1> <p>The setup of the new development servers required snapshots and images of the underlying infrastructure containers. Just the action of creating these caused the system to go down due to a lack of storage. This uncovered an underlying issue where spawned instances were not being properly cleaned out and the subsequent images were massive.</p> <p>Processes were developed to manually clean these out. Based on an increase in usage, the system subsequently went down several other times. This was then scripted and moved into daily cron. We are actively monitoring the disk utilization to ensure these processes hold and stabilize the system.</p> <h1>Continuous integration</h1> <p>A brand new Jenkins server was set up to afford opportunities to automate tasks in and between servers. Attempts were made to automate the creation of the development servers. But, there were many complications encountered, like the previous wildcard certificate used for the instances that was not scripted through LetsEncrypt. The storage issues ultimately prevented the automation from moving forward and the amount of technical complexity to make this a reality with the current system is likely not worth pursuing a complete solution. However, it’s nice to have this tool in place to move forward and maintain a platform for learning and rolling in automated improvements.</p> <h1>New SSL for the web frontend</h1> <p>During the creation of the development servers, the scripts leveraged to generate SSL certificates through LetsEncrypt were executed on the development servers multiple times due to both networking issues and updated configuration. Cleanup was required to remove some failed past attempts on the production system tied to certificate renewal. All of this work required the LetsEncrypt service to be invoked multiple times in a development-only capacity.</p> <p>Sadly, this effort caused us to hit the LetsEncrypt rate limit. Within a few days, the SimplyTest.me SSL certificate expired (at the most inopportune of times). While it required funding, this ended up being an opportunity for us to purchase and place a more permanent SSL certificate to minimize maintenance moving forward (LetsEncrypt has quarterly certificates). This was our first use of the expense system in our Open Collective to reconcile and publicly share the expense.</p> <h1>Monitoring</h1> <p>Given the expansion of infrastructure, it’s imperative to have monitoring in place. As a start, we’re leveraging the free offering of Uptime Robot, which now covers outages for all production, development, and CI infrastructure. There are opportunities over time to expand this for storage thresholds, proactive SSL certificate notifications, SSL expirations, and more.</p> <h1>Expanded donations</h1> <p>We’ve not done a great job of marketing our Open Collective because I believe firmly that people need to be informed of where SimplyTest is heading and can recognize what incentives come with their donations (aside from the service running).</p> <p>There is a lot of strategy involved and many ideas I wish to explore in the coming and future years (e.g. infrastructure for a modernized system and a paid internship to help those underrepresented in tech). However, several people in the Drupal community and the Drupal Association, has joined as backers. I can’t thank you all enough. I welcome others to join as backers and will work to outline these opportunities and vision moving forward.</p> <h1>Conclusion</h1> <p>I’ll continue to invest my time to make SimplyTest the best it can be. It’s a labor of love but one of the most rewarding and unique contributions we have in our community. I couldn’t be more confident in where this is heading and look forward to many vibrant years ahead for the service.</p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Fri, 01/18/2019 - 14:56</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> <div class="field__item"><a href="/taxonomy/term/6" hreflang="en">people</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Fri, 18 Jan 2019 19:56:53 +0000 admin 111 at http://nerdstein.net Custom REST Resources in Drupal 8 http://nerdstein.net/blog/custom-rest-resources-drupal-8 <span class="field field--name-title field--type-string field--label-hidden">Custom REST Resources in Drupal 8</span> <div class="clearfix text-formatted field field--name-body field--type-text-with-summary field--label-hidden field__item"><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Introduction</span></span></h1> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Drupal 8 is a great platform for storing structured data and exposing web service endpoints. This offers Drupal a competitive advantage when creating a decoupled application or building Drupal as part of a larger enterprise of systems. Core offers many complementary out-of-the-box features to publish web services and configure them in different ways. This includes roles/permissions, Rest Web Services, Serialization, Views, and more. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Since Drupal is so robust out of the box, it often minimizes the need for custom development. But, Drupal’s framework has support for any customized needs. The following article describes how to create a custom REST endpoint in Drupal 8, as we share how to conditionally return a quote from a provided Arnold Schwarzenegger movie. </span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Development</span></span></h1> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Assuming you </span><a href="https://www.drupal.org/docs/8/creating-custom-modules"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">have a custom module already created</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and enabled, we can leverage the native autoloading features to register a new plugin class for a custom REST resource.</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Place a new </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">MyCustomResource.php</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> class in your module’s </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">src/Plugin/rest/resource</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> directory. Create any missing directories as needed. The REST resource will automatically get discovered after </span><a href="https://www.drupal.org/docs/user_guide/en/prevent-cache-clear.html"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">clearing the cache</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">.  </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 1: </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">modules/my_custom_module/src/Plugin/rest/resource/</span><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;"><a href="https://gist.github.com/nerdstein/9f5c71e40e6d073890cba9ea638d895b">MyCustomResource.php</a></span></span></p> <p> </p> <script src="https://gist.github.com/nerdstein/9f5c71e40e6d073890cba9ea638d895b.js"></script><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">This example demonstrates an exposed GET endpoint found at the path </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/api/custom/arnold</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. Let’s walk through the code to get an understanding.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Namespacing</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">If the PHP class is placed into a module’s </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">src/Plugin/rest/resource</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> directory, Drupal will automatically register the class and knows, given the path, to associate it with a REST resource. This is a common pattern leveraged by Drupal’s Plugin API.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Annotation</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Annotations define static attributes and configuration of the REST resource. Lines 16-27 demonstrate the annotation for the REST resource class. Most notably, a unique ID (no spaces and use of underscores) is shown in line 20 and a human recognizable label on line 21. Lines 23 and 24 define the endpoint for the API, for which requests can be made.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Base Class</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Classes are capable of implementing interfaces (a common, shared definition of expected functions) or extending base classes that have defined functions. Line 28 demonstrates the name of the custom class, which should match the filename per object oriented best practice. Extending </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">ResourceBase</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> as a base class allows us to inherit standard REST resource functionality and only needing to develop our specific functionality within our class.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Dependency Injection</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The use of an overridden </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">__construct</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and the complementary </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">create</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> functions demonstrate how we use dependency injection to load additional system resources into our class. In this example, I load serialization formats, a specific logger, the current user, and the current request from Drupal’s runtime container. The request and current user extend the constructor of the </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">ResourceBase</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> class and saved into the </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">currentUser</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> and </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">request</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> attributes of a class instance defined in lines 35 and 42 respectively. This demonstrates how to extend your REST resource for any additional class attributes.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Request Method</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Web service endpoints define the expected methods of the request. Drupal’s REST resource plugins support requests by defining specific methods within the class. Line 99 demonstrates a </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">get()</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> method that corresponds to a GET request available from the endpoint. All code within this method is used to generate the response. </span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Caching</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Responses can be cached from the endpoint to help with performance. Lines 117-121 demonstrate specific caching directives that can be used at your discretion. In this example, I’ve set it to not cache requests.</span></span></p> <h2 dir="ltr" style="line-height:1.38;margin-top:18pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 16pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Response</span></span></h2> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Line 124 demonstrates the response triggered by returning a ResourceResponse object with the desired data.</span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Configuration</span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Once REST resources are developed, they still require configuration. Start by downloading and enabling the </span><a href="https://www.drupal.org/project/restui"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">REST UI module</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> to manage Drupal’s REST endpoints at </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/admin/config/services/rest</span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">.  You should see your new REST resource defined in the list. </span></span></p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"> </p> <p><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-1.png" style="width: 650px; height: 346px; border-width: 4px; border-style: solid;" /></p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The REST resource endpoint will be activated once enabled and configured. Configuration includes selection of the exposed methods (from those available from your class), the authentication (select cookie to use Drupal’s default authentication tied to it’s login), and the serialization formats (JSON is commonly used for tools like React and Vue).</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Finally, you need to define who is able to access the endpoint. </span></span></p> <ol style="margin-top:0pt;margin-bottom:0pt;"><li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Go to Drupal’s permissions page, found at </span><span style="font-size: 11pt; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">/admin/people/permissions</span><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">, </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Search for the permission tied to your REST resource, e.g. “access {METHOD} on {RESOURCE NAME} resource”, where method corresponds to the method defined in your class, like GET/POST/DELETE/PUT, and the resource name is the </span><span style="font-size: 11pt; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">label</span><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> in your class annotation. </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Select the applicable roles that you wish to access (which will be verified by your authentication method). Selecting anonymous will allow your endpoint to be publicly accessible.</span></span></p> </li> </ol><h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">An Extended Example </span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The previous example outlined the basics of Drupal’s REST resource API but did not describe how to leverage other Drupal features. I’ve created a second example that is more relevant for retrieving Drupal-sponsored content. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 2: </span><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">modules/my_custom_module/src/Plugin/rest/resource/</span><a href="https://gist.github.com/nerdstein/417c8ef105205d8159386a26c771ee9b"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-style: italic; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">MyCustomResourceRandom.php</span></a></span></p> <p> </p> <script src="https://gist.github.com/nerdstein/417c8ef105205d8159386a26c771ee9b.js"></script><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The updated example assumes the use of an “arnold_quotes” content type, which leverages the “title” field for the quote and has a boolean “bonus” field to tag specific quotes as a bonus feature. It also assumes the use of a custom permission for accessing bonus quotes, which can be assigned to a user by role. </span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The example leverages an </span><a href="https://api.drupal.org/api/drupal/core!lib!Drupal.php/function/Drupal%3A%3AentityQuery/8.2.x"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">entity query</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> to conditionally load quotes to first filter by a movie parameter and bonus products, if the user has the correct permission. A random returned node is loaded and the quote is returned through the response.</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Example 2 better exemplifies a use case for a custom REST resource in Drupal 8 and potential opportunities to interact with the other Drupal subsystems.</span></span></p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Testing </span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Intuitively, you may go to a web browser to test your endpoint. But, web service endpoints are configured to only accept requests in specific serialized formats and methods. A standard web browser request likely won’t create a request that matches your endpoint settings, but Drupal will still return return a result that likely prints an error and is in the serialized format you selected (e.g. JSON).</span></span></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">The easiest way to test your endpoint is through a tool like </span><a href="https://www.getpostman.com/"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">Postman</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. After downloading, configure a request to your endpoint.</span></span></p> <p> </p> <ol style="margin-top:0pt;margin-bottom:0pt;"><li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Specify the method (e.g. GET)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Specify the full URL to the endpoint (e.g. <a href="https://mysite.com/api/custom/arnold">https://mysite.com/api/custom/arnold</a>)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Add a parameter for “_format” and specify the desired response serialization format from one configured by the endpoint (e.g. “_format=json”)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Add any other parameters for the endpoint (e.g. “movie=Predator”)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Set a header for the correct mime type by setting “Content-Type” to “application/json” to match expected request serialization formats</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">If authenticated, click “Cookies” and enter a cookie value for your Drupal site after logging in (you can get this from your browser)</span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Click “Save” your Postman settings to persistently store </span></span></p> </li> <li dir="ltr" style="list-style-type: decimal; font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre;"> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Click “Send” to test the request</span></span></p> </li> </ol><p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-2.png" style="width: 550px; height: 244px; border-width: 4px; border-style: solid;" /></p> <p> </p> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><img alt="" data-entity-type="" data-entity-uuid="" src="/sites/default/files/uploaded/custom-rest-resource-3.png" style="width: 550px; height: 188px; border-width: 4px; border-style: solid;" /></p> <p> </p> <h1 dir="ltr" style="line-height:1.38;margin-top:20pt;margin-bottom:6pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 20pt; font-family: Arial; background-color: transparent; font-weight: 400; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">Further Reading</span></span></h1> <p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span id="docs-internal-guid-5d8de94b-7fff-0625-1eb2-b1d110556652"><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">More details can be found on the </span><a href="https://www.drupal.org/docs/8/api/restful-web-services-api/custom-rest-resources"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">official drupal.org documentation</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;">. And, just as I was finishing the article, Open Sense Labs published </span><a href="https://opensenselabs.com/blog/tech/creating-custom-rest-resource-get-method-drupal-8"><span style="font-size: 11pt; font-family: Arial; color: rgb(17, 85, 204); background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; text-decoration-line: underline; text-decoration-skip-ink: none; white-space: pre-wrap;">a similar article</span></a><span style="font-size: 11pt; font-family: Arial; background-color: transparent; font-variant-numeric: normal; font-variant-east-asian: normal; white-space: pre-wrap;"> that is both helpful and explanatory.</span></span></p> </div> <span class="field field--name-uid field--type-entity-reference field--label-hidden"><span lang="" about="/users/admin" typeof="schema:Person" property="schema:name" datatype="">admin</span></span> <span class="field field--name-created field--type-created field--label-hidden">Sun, 12/16/2018 - 18:46</span> <div class="field field--name-field-tags field--type-entity-reference field--label-above"> <div class="field__label">Tags</div> <div class="field__items"> <div class="field__item"><a href="/taxonomy/term/1" hreflang="en">Site Building</a></div> <div class="field__item"><a href="/taxonomy/term/8" hreflang="en">development</a></div> <div class="field__item"><a href="/taxonomy/term/5" hreflang="en">drupal</a></div> </div> </div> <section class="field field--name-comment-node-blog-post field--type-comment field--label-hidden comment-wrapper"> </section> Sun, 16 Dec 2018 23:46:38 +0000 admin 109 at http://nerdstein.net http://nerdstein.net/blog/custom-rest-resources-drupal-8#comments