<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://glideguide.blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://glideguide.blog/" rel="alternate" type="text/html" /><updated>2025-10-22T14:31:10+00:00</updated><id>https://glideguide.blog/feed.xml</id><title type="html">GlideGuide</title><subtitle>Hi, we&apos;re Dan and Joey. Together we&apos;ve got 20+ years of experience engineering pro-code  solutions on the ServiceNow platform and coaching and mentoring junior devs. Follow us for tips &amp; tricks, tutorials, and best practices to take your skills to the next level.</subtitle><entry><title type="html">GlideQuery Perks, Part 3: Pure Functions, Fluent Method Chaining, and Object Re-usability</title><link href="https://glideguide.blog/2025/02/17/glidequery-perks-part-3.html" rel="alternate" type="text/html" title="GlideQuery Perks, Part 3: Pure Functions, Fluent Method Chaining, and Object Re-usability" /><published>2025-02-17T00:00:00+00:00</published><updated>2025-02-17T00:00:00+00:00</updated><id>https://glideguide.blog/2025/02/17/glidequery-perks-part-3</id><content type="html" xml:base="https://glideguide.blog/2025/02/17/glidequery-perks-part-3.html"><![CDATA[<p><span class="lead">Compare these bog standard GlideRecord and GlideQuery examples</span> and you’ll see something is different between them:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">gr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideRecord</span><span class="p">(</span><span class="nx">someTable</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="nx">someField</span><span class="p">,</span> <span class="nx">someValue</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addNotNullQuery</span><span class="p">(</span><span class="nx">someOtherField</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">query</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">gr</span><span class="p">.</span><span class="nx">next</span><span class="p">())</span> <span class="p">{</span>
  <span class="c1">// do the thing...</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="nx">someTable</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">someField</span><span class="p">,</span> <span class="nx">someValue</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">whereNotNullQuery</span><span class="p">(</span><span class="nx">someOtherField</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">someFields</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">record</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// do the thing...</span>
  <span class="p">});</span>
</code></pre></div></div>

<p>The difference might be subtle or it might jump out at you right away. They’re the same number of lines and they seem to be doing all the same steps in the same order (indeed they are). What is it about GlideRecord and GlideQuery that makes the syntax appear so different, and, if this is important, why is it important?</p>

<h2 id="gliderecord-is-founded-on-object-mutation">GlideRecord is founded on object mutation</h2>

<p>In the GlideRecord example, we first instantiate an object and assign it to a variable. From that point on we call methods on the object that cause the object to change. We don’t have to assign the output of those method calls to any new variable, because each time we add a new query the object itself changes. When we execute the query the object changes again, and each time we iterate to the next record the object changes yet again. And the whole time it remains assigned to the same variable name.</p>

<p>These changes in the object are sometimes called mutations or side effects, and some consider it best practice to avoid them. But aren’t variables meant to vary? After all, it’s right there in the name “variable”. What’s really wrong with allowing the GlideRecord object to mutate like this? We saw one extreme (and thankfully uncommon) example in <a href="/2023/01/30/glidequery-perks-part-0.html">Part 0</a> and <a href="/2024/09/07/glidequery-perks-part-2.html">Part 2</a> where mutation due to the <code class="language-plaintext highlighter-rouge">next</code> method caused undesirable behavior, but are there other drawbacks?</p>

<h2 id="glidequery-objects-are-immutable">GlideQuery objects are immutable</h2>

<p>In functional programming there’s a design pattern called a pure function. Pure functions always produce the same output given the same input and have no observable side effects. All GlideQuery objects are immutable, meaning there’s no way to change them after they’re created. All the methods of the GlideQuery <abbr>API</abbr> are pure functions, so, when you call any of them, instead of mutating the object, the method leaves the original object unchanged and returns a new object that’s like the old one but with the new step applied.</p>

<p>This is cumbersome, but it helps to see what’s really going on if we rewrite the GlideQuery example above like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">query1</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="nx">someTable</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">query2</span> <span class="o">=</span> <span class="nx">query1</span><span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">someField</span><span class="p">,</span> <span class="nx">someValue</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">query3</span> <span class="o">=</span> <span class="nx">query2</span><span class="p">.</span><span class="nx">whereNotNullQuery</span><span class="p">(</span><span class="nx">someOtherField</span><span class="p">)</span>
<span class="kd">let</span> <span class="nx">stream</span> <span class="o">=</span> <span class="nx">query3</span><span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">someFields</span><span class="p">)</span>
<span class="nx">stream</span><span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">record</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
  <span class="c1">// do the thing...</span>
<span class="p">});</span>
</code></pre></div></div>

<p>Here you can see clearly that with each new step we get back a new GlideQuery or Stream object (we’ll talk more about Stream and Optional objects in Part 4). In this example we’re assigning each object to a new variable, then using that variable to call the method for the next step.</p>

<h2 id="fluent-method-chaining">Fluent method chaining</h2>

<p>One immediate benefit of this pure function architecture is it enables GlideQuery’s fluent method chaining style. We usually don’t have any need to keep all those intermediate objects so we don’t bother assigning them to variables. Each chained method call simply acts on the new object returned from the preceding method call. This generally leads to shorter and cleaner-looking lines of code and fewer overall keystrokes.</p>

<p>When you chain methods like this, even though the method calls span multiple lines, you’re actually creating one long single expression. This means in some cases, like our first GlideQuery example above, you don’t need to assign anything to a variable. If you do start with declaring a variable, keep in mind the value assigned to the variable will be the result of the entire expression.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">n</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="nx">table</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">someField</span><span class="p">,</span> <span class="nx">someValue</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">count</span><span class="p">()</span>
</code></pre></div></div>

<p>The GlideQuery object instantiated on the first line never gets assigned to <code class="language-plaintext highlighter-rouge">n</code>. Instead, the entire method chain is evaluated and, ultimately, the variable will be assigned the return value from the <code class="language-plaintext highlighter-rouge">count</code> method.</p>

<p>And notice the indentation convention used here. Indenting each of the chained method call lines with a single tab is deliberate and recommended since it reminds us the whole thing is one long expression, not a series of individual expressions or statements.</p>

<h2 id="object-re-usability">Object re-usability</h2>

<p>A less obvious but very powerful feature of this pure functional architecture is object re-use. Since the intermediate objects are never mutated, if we do decide to save one by assigning it to a variable, we can go back and re-use it later.</p>

<p>Let’s say we want to send an e-mail to everyone in our department who got an above average score on the most recent customer satisfaction survey. In order to do that, we first need to know what the average is. For simplicity, let’s assume we’ve already calculated each team member’s score and stored it in a custom field on the User [<code class="language-plaintext highlighter-rouge">sys_user</code>] table.</p>

<p>Without GlideQuery, the best way to do this is with GlideAggregate to get the average and then GlideRecord to loop through the users and send the e-mails.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">ga</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideAggregate</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">department</span><span class="dl">'</span><span class="p">,</span> <span class="nx">someDepartment</span><span class="p">);</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">addNotNullQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">addAggregate</span><span class="p">(</span><span class="dl">'</span><span class="s1">AVG</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">query</span><span class="p">();</span>
<span class="nx">ga</span><span class="p">.</span><span class="nx">next</span><span class="p">();</span>
<span class="kd">let</span> <span class="nx">average</span> <span class="o">=</span> <span class="nx">ga</span><span class="p">.</span><span class="nx">getAggregate</span><span class="p">(</span><span class="dl">'</span><span class="s1">AVG</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">let</span> <span class="nx">gr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideRecord</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">department</span><span class="dl">'</span><span class="p">,</span> <span class="nx">someDepartment</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addNotNullQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="nx">average</span><span class="p">);</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">query</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="nx">gr</span><span class="p">.</span><span class="nx">next</span><span class="p">())</span> <span class="p">{</span>
  <span class="c1">// send e-mail...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Notice how the first four lines are nearly identical in the two different blocks of our script? This is a simple example, but imagine if our query were even more complicated. Wouldn’t it be great if we could somehow only write those lines once? We can’t do that with GlideAggregate and GlideRecord, but we can with GlideQuery.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">query</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">department</span><span class="dl">'</span><span class="p">,</span> <span class="nx">someDepartment</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">let</span> <span class="nx">average</span> <span class="o">=</span> <span class="nx">query</span>  <span class="c1">// using query once</span>
  <span class="p">.</span><span class="nx">avg</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">();</span>

<span class="nx">query</span>  <span class="c1">// using query a second time</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">u_csat_score</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&gt;</span><span class="dl">'</span><span class="p">,</span> <span class="nx">average</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">someFields</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">user</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// send e-mail...</span>
  <span class="p">});</span>
</code></pre></div></div>

<p>After calling the <code class="language-plaintext highlighter-rouge">avg</code> and <code class="language-plaintext highlighter-rouge">get</code> methods you might think the <code class="language-plaintext highlighter-rouge">query</code> object is all used up, but since it doesn’t mutate like a GlideRecord object we can re-use it—even adding an additional where clause—to send the e-mails.<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>I’ve used this in Script Includes where many different methods start by executing nearly the same query. The query can be assigned to a property of the object and then re-used across all the methods. They’re also great for enabling more dynamic function behavior since immutable GlideQuery objects can safely be passed around as function arguments. Once you know what’s possible you’ll find all sorts of uses for this feature.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Hopefully I’ve convinced you GlideQuery’s stateless, immutable design is safer than GlideRecord overall and enables both a more fluid syntax and some handy new use cases. Stay tuned for Part 4 where we’ll cover the myriad ways you can process Streams and Optionals. <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<hr class="footnotes" />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>This is possible in the first place due to GlideQuery being one <abbr>API</abbr>, not split into separate <abbr>API</abbr>s for aggregate and normal queries. However, even if our example didn’t involve GlideAggregate and instead used GlideRecord twice, the first GlideRecord object couldn’t be re-used the way a GlideQuery object can. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Joey</name></author><category term="glidequery" /><category term="glidequery perks series" /><summary type="html"><![CDATA[Compare these bog standard GlideRecord and GlideQuery examples and you’ll see something is different between them:]]></summary></entry><entry><title type="html">GlideQuery Perks, Part 2: JavaScript Not Java</title><link href="https://glideguide.blog/2024/09/07/glidequery-perks-part-2.html" rel="alternate" type="text/html" title="GlideQuery Perks, Part 2: JavaScript Not Java" /><published>2024-09-07T00:00:00+00:00</published><updated>2024-09-07T00:00:00+00:00</updated><id>https://glideguide.blog/2024/09/07/glidequery-perks-part-2</id><content type="html" xml:base="https://glideguide.blog/2024/09/07/glidequery-perks-part-2.html"><![CDATA[<p><span class="lead">Another point I made in <a href="/2023/01/30/glidequery-perks-part-0.html">Part 0</a> of this series</span> was that GlideRecord is a Java object and its various methods return Java types and more Java objects, so it doesn’t behave like native JavaScript. In contrast, though GlideQuery uses GlideRecord to perform database operations in its private implementation, its public interface is written entirely in JavaScript and was intentionally designed to behave like JavaScript.</p>

<h2 id="javascript-native-variable-types">JavaScript native variable types</h2>

<p>A big part of how GlideQuery acts like JavaScript is its consistent use of JavaScript native types. For example, if you ask GlideQuery to return the contents of an Integer or Decimal column in the ServiceNow database, GlideQuery will give you the value as a native JavaScript number. If you ask it to return the contents of a True/False field, you’ll get a native JavaScript boolean.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">inc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">exampleID</span><span class="p">,</span> <span class="p">[</span><span class="dl">'</span><span class="s1">description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">sys_mod_count</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">])</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">();</span>

<span class="k">typeof</span> <span class="nx">inc</span><span class="p">.</span><span class="nx">description</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">;</span>    <span class="c1">// true</span>
<span class="k">typeof</span> <span class="nx">inc</span><span class="p">.</span><span class="nx">sys_mod_count</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">;</span>  <span class="c1">// true</span>
<span class="k">typeof</span> <span class="nx">inc</span><span class="p">.</span><span class="nx">active</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">boolean</span><span class="dl">'</span><span class="p">;</span>        <span class="c1">// true</span>
</code></pre></div></div>

<p>As you can see, they’re not Java types nor are they Java objects. Refreshingly, GlideElement is nowhere to be found here.</p>

<h3 id="in-hindsight-gliderecord-is-more-sane-than-i-thought">In hindsight, GlideRecord is more sane than I thought</h3>

<p>Years ago I adopted the best practice of just always using <code class="language-plaintext highlighter-rouge">getValue()</code> whenever interacting with columns on a GlideRecord object (and <a href="https://snprotips.com/blog/2017/4/9/always-use-getters-and-setters">many</a> <a href="https://www.servicenow.com/community/developer-articles/gliderecord-hints-tips-common-issues-and-good-practices/ta-p/2323766">other</a> <a href="https://www.servicenow.com/community/developer-forum/get-field-value-of-gliderecord-best-practice-method/m-p/1619218">people</a> <a href="https://www.youtube.com/live/j57cXWGQD98?feature=share&amp;t=1025">have</a> <a href="https://www.servicenow.com/community/developer-blog/tnt-the-importance-of-using-quot-getvalue-quot-when-getting-data/ba-p/2273338">also</a>, or have advocated for some <a href="https://codecreative.io/blog/is-gliderecord-getvalue-the-king-of-the-string/">alternative</a>). But in researching this series I’ve come to realize GlideElement objects actually behave more intuitively than I remembered.</p>

<p>Because Integer and Decimal columns are evaluated to GlideElementNumeric objects, you can do math with them directly. Similarly, because Boolean columns come out as GlideElementBoolean objects (and Java booleans), they work fine in logic operations and conditionals.</p>

<p>But there are two significant issues with GlideRecord and GlideElement objects that drove us all to adopt <code class="language-plaintext highlighter-rouge">getValue()</code> in the first place, and they’re both handily avoided by GlideQuery’s use of native types.</p>

<h3 id="no-mutable-object-shenanigans">No mutable object shenanigans</h3>

<p>As I explained in Part 0, GlideElement objects mutate when the <code class="language-plaintext highlighter-rouge">next()</code> method is called, producing some unexpected behavior. Since column values returned by GlideQuery are just JavaScript native types (strings, numbers, and booleans) and not GlideElement objects or any other Java types, they’re naturally immutable:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="p">[];</span>

<span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">inc</span><span class="p">)</span> <span class="p">{</span>
    <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">inc</span><span class="p">.</span><span class="nx">number</span><span class="p">);</span>
  <span class="p">});</span>

<span class="nx">result</span><span class="p">;</span>  <span class="c1">// ['INC0010001', 'INC0010002', 'INC0010003', ...]</span>
</code></pre></div></div>

<p>(Note there are better ways to load values into an array with GlideQuery, so don’t follow this example—I’ll give you a better one in Part 4 of the series.)</p>

<p>We already know using <code class="language-plaintext highlighter-rouge">getValue()</code> turns GlideElement objects into JavaScript primitive types, preventing unexpected behavior, but GlideQuery shines here for not having the problem in the first place. And stay tuned as I’ll have more to say about object mutation in Part 3.</p>

<h3 id="strict-comparisons-are-possible">Strict comparisons are possible</h3>

<p>Another benefit of GlideQuery’s use of native types is they can safely be strictly compared with other values.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">inc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">getBy</span><span class="p">({</span> <span class="na">number</span><span class="p">:</span> <span class="dl">'</span><span class="s1">INC0010005</span><span class="dl">'</span> <span class="p">},</span> <span class="p">[</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span> <span class="p">])</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">();</span>

<span class="nx">inc</span><span class="p">.</span><span class="nx">number</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">INC0010005</span><span class="dl">'</span><span class="p">;</span>   <span class="c1">// true</span>
<span class="nx">inc</span><span class="p">.</span><span class="nx">number</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">INC0010005</span><span class="dl">'</span><span class="p">;</span>  <span class="c1">// true</span>
</code></pre></div></div>

<p>As I mentioned in Part 0, GlideRecord’s inconsistent behavior here made me distrust strict equality in JavaScript in my early ServiceNow days. Continuing the pattern, using <code class="language-plaintext highlighter-rouge">getValue()</code> and/or casting to the appropriate native type works the same way, but GlideQuery gives us the value in the correct native type without any casting or coercion. Easy peasy.</p>

<h2 id="javascript-native-objects">JavaScript native objects</h2>

<p>In addition to column values being native types, even whole objects returned by GlideQuery are native JavaScript objects. I can think of three reasons why this is useful (and you can probably think of more).</p>

<h3 id="easier-debugging">Easier debugging</h3>

<p>Maybe the simplest implication of GlideQuery returning native objects is the whole object can be logged out for quick and easy debugging.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">inc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">exampleID</span><span class="p">,</span> <span class="p">[</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">])</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">();</span>

<span class="nx">gs</span><span class="p">.</span><span class="nx">debug</span><span class="p">(</span><span class="nx">inc</span><span class="p">);</span>

<span class="c1">// {</span>
<span class="c1">//   "sys_id": "...",</span>
<span class="c1">//   "number": "INC0010005",</span>
<span class="c1">//   "short_description": "Email server is down"</span>
<span class="c1">// }</span>
</code></pre></div></div>

<h3 id="easier-json-serialization">Easier <abbr>JSON</abbr> serialization</h3>

<p>Another nice use case for this is that objects returned by GlideQuery can be directly serialized into <abbr>JSON</abbr> for sending across a <abbr>REST</abbr> connection. How many Scripted <abbr>REST</abbr> <abbr>API</abbr>s have you written where you had to copy data out of a GlideRecord object into a native object before it could be serialized? Consider this example I found in ServiceNow’s documentation (<a href="https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/custom-web-services/reference/r_ScriptedRESTExampleStreamVsLO.html">“Scripted <abbr>REST</abbr> <abbr>API</abbr> example - streaming vs object serialization”</a>):</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="nx">runOperation</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">result_arr</span> <span class="o">=</span> <span class="p">[],</span>
  
  <span class="nx">gr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideRecord</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">);</span>
  <span class="nx">gr</span><span class="p">.</span><span class="nx">setLimit</span><span class="p">(</span><span class="mi">100</span><span class="p">);</span>
  <span class="nx">gr</span><span class="p">.</span><span class="nx">query</span><span class="p">();</span>

  <span class="c1">// iterate over incident records and build JSON</span>
  <span class="c1">// representations to be streamed out</span>
  <span class="k">while</span> <span class="p">(</span><span class="nx">gr</span><span class="p">.</span><span class="nx">next</span><span class="p">())</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">incidentObj</span> <span class="o">=</span> <span class="p">{};</span>
    <span class="nx">incidentObj</span><span class="p">.</span><span class="nx">number</span> <span class="o">=</span> <span class="nx">gr</span><span class="p">.</span><span class="nx">number</span> <span class="o">+</span> <span class="dl">''</span><span class="p">;</span>
    <span class="nx">incidentObj</span><span class="p">.</span><span class="nx">short_description</span> <span class="o">=</span> <span class="nx">gr</span><span class="p">.</span><span class="nx">short_description</span> <span class="o">+</span> <span class="dl">''</span><span class="p">;</span>
    <span class="nx">result_arr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">incidentObj</span><span class="p">);</span>
  <span class="p">}</span>
  
  <span class="k">return</span> <span class="nx">result_arr</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span>
</code></pre></div></div>

<p>This looks simple enough, but imagine we had a lot more fields to load into the objects to be serialized—the more fields we had, the more lines of code we’d need.</p>

<p>This is much nicer to implement with GlideQuery. Since the objects are already native objects, we can push each one onto the array as is and return the array with full confidence it can be safely stringified into <abbr>JSON</abbr>.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="nx">runOperation</span><span class="p">(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">result</span> <span class="o">=</span> <span class="p">[],</span>

  <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">limit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">inc</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">inc</span><span class="p">);</span>
    <span class="p">});</span>
  
  <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span>
</code></pre></div></div>

<p>(As before, note this is a poor way to create arrays with GlideQuery. I kept this similar to the GlideRecord example for a more apples-to-apples comparison. Again, keep an eye out for better array examples in Part 4.)</p>

<h3 id="native-object-methods">Native object methods</h3>

<p>Yet another benefit to GlideQuery’s native objects is all of JavaScript’s object methods can be used such as <code class="language-plaintext highlighter-rouge">hasOwnProperty()</code>. We can also use helper functions like <code class="language-plaintext highlighter-rouge">Object.keys()</code> and features like <code class="language-plaintext highlighter-rouge">for...in</code> loops.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">inc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="nx">exampleID</span><span class="p">,</span> <span class="p">[</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">])</span>
  <span class="p">.</span><span class="kd">get</span><span class="p">()</span>

<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">field</span> <span class="k">in</span> <span class="nx">inc</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">gs</span><span class="p">.</span><span class="nx">debug</span><span class="p">(</span><span class="nx">field</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">: </span><span class="dl">'</span> <span class="o">+</span> <span class="nx">inc</span><span class="p">[</span><span class="nx">field</span><span class="p">]);</span>
<span class="p">}</span>

<span class="c1">// sys_id: ...</span>
<span class="c1">// number: INC0010005</span>
<span class="c1">// short_description: Email server is down</span>
</code></pre></div></div>

<p>(And if you’re lucky enough to be on the Tokyo release or later and in a context where you can use modern JavaScript language features, you might find a use for <code class="language-plaintext highlighter-rouge">Object.entries()</code> or <code class="language-plaintext highlighter-rouge">Object.values()</code>.)</p>

<h2 id="conclusion">Conclusion</h2>

<p>Because GlideQuery behaves so much more predictably in always returning JavaScript native types and native objects, a lot of confusion and potential bugs are avoided. We get the simple convenience of rarely if ever having to convert values and objects into other kinds of values and objects, and we get the benefits of JavaScript native methods for the various types. I’m certain I would’ve more intuitively understood and had a lot more trust in the platform in my early years if I’d cut my teeth scripting with GlideQuery instead of GlideRecord. <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<p><em>Next in the series: <a href="/2025/02/17/glidequery-perks-part-3.html">Part 3: Pure Functions, Fluent Method Chaining, and Object Re-usability →</a></em></p>]]></content><author><name>Joey</name></author><category term="GlideQuery" /><category term="GlideQuery Perks Series" /><summary type="html"><![CDATA[Another point I made in Part 0 of this series was that GlideRecord is a Java object and its various methods return Java types and more Java objects, so it doesn’t behave like native JavaScript. In contrast, though GlideQuery uses GlideRecord to perform database operations in its private implementation, its public interface is written entirely in JavaScript and was intentionally designed to behave like JavaScript.]]></summary></entry><entry><title type="html">GlideQuery Perks, Part 1: Unambiguous Boolean Logic</title><link href="https://glideguide.blog/2024/08/18/glidequery-perks-part-1.html" rel="alternate" type="text/html" title="GlideQuery Perks, Part 1: Unambiguous Boolean Logic" /><published>2024-08-18T00:00:00+00:00</published><updated>2024-08-18T00:00:00+00:00</updated><id>https://glideguide.blog/2024/08/18/glidequery-perks-part-1</id><content type="html" xml:base="https://glideguide.blog/2024/08/18/glidequery-perks-part-1.html"><![CDATA[<p><span class="lead">I published <a href="/2023/01/30/glidequery-perks-part-0.html">Part 0</a> and <a href="/2023/02/08/glidequery-perks-part-0.5.html">Part 0.5</a> of this series so long ago</span> you probably thought I forgot about it, but this is a favorite topic of mine so I couldn’t leave it unfinished. 😅 If you haven’t read the first part, I highly recommend catching up before reading this.</p>

<p>One of the problems I mentioned in Part 0 is that GlideRecord is squirrelly when it comes to precedence of logical <abbr>AND</abbr> and <abbr>OR</abbr>. GlideQuery avoids this issue by disallowing ambiguous boolean logic.</p>

<!--more-->

<h2 id="mind-your-ands-and-ors">Mind your <abbr>AND</abbr>s and <abbr>OR</abbr>s</h2>

<p>To avoid uncertainty in queries, GlideQuery’s interface requires you to nest <abbr>AND</abbr>’s and <abbr>OR</abbr>’s that could have more than one possible order of operation.</p>

<p>For example, “A <abbr>AND</abbr> B <abbr>AND</abbr> C” raises no cause for alarm, as does “A <abbr>OR</abbr> B <abbr>OR</abbr> C”, but “A <abbr>OR</abbr> B <abbr>AND</abbr> C” is problematic. How so? You could simply evaluate the logic in order, taking the <abbr>OR</abbr> first and then the <abbr>AND</abbr>. However, in most programming languages and query languages, including JavaScript, <abbr>AND</abbr> takes precedence over <abbr>OR</abbr> (without any need for parentheses; cf. how multiplication takes natural precedence over addition in arithmetic), but, curiously, in ServiceNow queries—both encoded queries and GlideRecord queries—<abbr>OR</abbr> has always taken precedence over <abbr>AND</abbr>. In fact, with normal calls to <code class="language-plaintext highlighter-rouge">addQuery</code> and <code class="language-plaintext highlighter-rouge">addOrCondition</code>, it’s not even possible to force <abbr>AND</abbr> to take precedence over <abbr>OR</abbr> (you have to switch to using an encoded query with the <code class="language-plaintext highlighter-rouge">^NQ</code> operator).</p>

<p>Given the inconsistency, you can see there’s a lot of potential for confusion here. Rather than simply evaluating a query from left to right, and rather than letting either logical operator take precedence over the other, GlideQuery takes the approach of simply always requiring parentheses (in a manner of speaking) so that there is never any ambiguity about order of operations in a query.</p>

<p>The following is fine in GlideQuery:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CONTAINS</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">blue smoke</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">assigned_to.name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">ITIL User</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>And this is fine, too:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">orWhere</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CONTAINS</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">blue smoke</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">orWhere</span><span class="p">(</span><span class="dl">'</span><span class="s1">assigned_to.name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">ITIL User</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<p>But the following will throw what GlideQuery calls a “nice error”:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">orWhere</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CONTAINS</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">blue smoke</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">assigned_to.name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">ITIL User</span><span class="dl">'</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="nice-errors">Nice errors</h2>

<p>You might think this is annoying, but GlideQuery throws a lot more errors than GlideRecord very much on purpose. It’s trying to help you discover bugs or potential problems immediately after writing your code, rather than silently failing in such a way that you may not notice problems until weeks down the road (and probably in the production environment by then). We’ll talk a lot more about nice errors in Part 5 of this series.</p>

<p>In the case of our third example above, the error is “Ambiguous query: cannot contain multiple where() expressions with an orWhere() expression”, and, as with all nice errors, execution of your script will simply stop. GlideQuery refuses to go any further in order to avoid the possibility of any data corruption or worse. I’m almost never jazzed when I see a nice error, but I’m really glad GlideQuery throws them because that little bit of pain up front helps me identify the problem immediately and avoid what would probably have been a lot more pain later.</p>

<h2 id="how-to-write-parentheses-in-glidequery">How to write “parentheses” in GlideQuery</h2>

<p>The way GlideQuery disambiguates whether <abbr>AND</abbr> should come before <abbr>OR</abbr> and vice versa is by having you pass a new table-agnostic GlideQuery object as the only parameter to a <code class="language-plaintext highlighter-rouge">where</code> or <code class="language-plaintext highlighter-rouge">orWhere</code> clause, like these examples:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// (active OR blue smoke) AND itil user</span>
<span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">orWhere</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CONTAINS</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">blue smoke</span><span class="dl">'</span><span class="p">))</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">assigned_to.name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">ITIL User</span><span class="dl">'</span><span class="p">);</span>
  
<span class="c1">// active OR (blue smoke AND itil user)</span>
<span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">orWhere</span><span class="p">(</span><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">CONTAINS</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">blue smoke</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">assigned_to.name</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">ITIL User</span><span class="dl">'</span><span class="p">));</span>
</code></pre></div></div>

<p>Doing it this way feels somewhat cumbersome (count your parentheses carefully!), but if a little awkwardness is the price to pay for clearer code, I’m here for it. Usually, writing them the way I have above is readable enough, but you could make this look less busy and complicated by assigning the sub-queries to variables beforehand.</p>

<h2 id="conclusion">Conclusion</h2>

<p>ServiceNow’s inexplicable “<abbr>OR</abbr>” bias has always bothered me, but the way these GlideQuery clauses can be nested addresses my gripe and makes the boolean logic more transparent and precise. And recall that with GlideRecord you <em>can’t</em> make <abbr>AND</abbr> take precedence over <abbr>OR</abbr> without resorting to encoded queries, so being able to do so with normal calls to <code class="language-plaintext highlighter-rouge">where</code> and <code class="language-plaintext highlighter-rouge">orWhere</code> with GlideQuery is a massive improvement.</p>

<p>That’s it for this installment. I hope this starts to persuade you GlideQuery is superior to GlideRecord and should be the way forward for all pro-code solutions on the ServiceNow platform, and I hope you’re looking forward to future articles in the series. Cheers! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<p><em>Next in the series: <a href="/2024/09/07/glidequery-perks-part-2.html">Part 2: JavaScript Not Java →</a></em></p>]]></content><author><name>Joey</name></author><category term="GlideQuery" /><category term="GlideQuery Perks Series" /><summary type="html"><![CDATA[I published Part 0 and Part 0.5 of this series so long ago you probably thought I forgot about it, but this is a favorite topic of mine so I couldn’t leave it unfinished. 😅 If you haven’t read the first part, I highly recommend catching up before reading this. One of the problems I mentioned in Part 0 is that GlideRecord is squirrelly when it comes to precedence of logical AND and OR. GlideQuery avoids this issue by disallowing ambiguous boolean logic.]]></summary></entry><entry><title type="html">Hardcoded Values Locator</title><link href="https://glideguide.blog/2024/08/17/hardcoded-values-locator.html" rel="alternate" type="text/html" title="Hardcoded Values Locator" /><published>2024-08-17T00:00:00+00:00</published><updated>2024-08-17T00:00:00+00:00</updated><id>https://glideguide.blog/2024/08/17/hardcoded-values-locator</id><content type="html" xml:base="https://glideguide.blog/2024/08/17/hardcoded-values-locator.html"><![CDATA[<p><span class="lead">I don’t know about your ServiceNow environment</span>, but we’ve got a lot of technical debt built up in ours in the form of hardcoded values (users, groups, etc.) in legacy Workflows and Flow Designer Flows.</p>

<p>For example, we might’ve hardcoded some specific user to be an approver on a request, but of course it’s only a matter of time before that user transfers departments, gets a promotion, or leaves the company and then that process is broken. In an effort to mitigate the issue, we might use a group instead, but if nobody’s paying attention it doesn’t take long for all the users in the group to rotate out and then we’re right back in the same boat.</p>

<p>Though it probably deserves a blog post all its own, preventing/solving this problem by establishing better practices up front isn’t my topic today. Instead I want to share a project I worked on recently that’s enabled us to be more proactive in reducing incidents related to the problem.</p>

<h2 id="the-script-include">The Script Include</h2>

<p>Here’s a Script Include that can help you locate hardcoded values in legacy Workflows and Flow Designer Flows:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">HardcodedValuesLocator</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">table</span><span class="p">,</span> <span class="nx">tableQuery</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>

    <span class="c1">/////// INITIALIZATION ///////</span>

    <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">tableQuery</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span>
      <span class="nx">tableQuery</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">();</span>

    <span class="kd">var</span> <span class="nx">displayColumn</span> <span class="o">=</span> <span class="nx">gs</span><span class="p">.</span><span class="nx">getDisplayColumn</span><span class="p">(</span><span class="nx">table</span><span class="p">);</span>

    <span class="kd">var</span> <span class="nx">records</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="nx">table</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">tableQuery</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="nx">displayColumn</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="nx">displayColumn</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">record</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">acc</span><span class="p">[</span><span class="nx">record</span><span class="p">.</span><span class="nx">sys_id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">record</span><span class="p">[</span><span class="nx">displayColumn</span><span class="p">];</span>
        <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
      <span class="p">},</span> <span class="p">{});</span>

    <span class="kd">var</span> <span class="nx">recordIDs</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">records</span><span class="p">);</span>

    <span class="c1">/////// PUBLIC FUNCTIONS ///////</span>

    <span class="kd">function</span> <span class="nx">findInFlows</span><span class="p">(</span><span class="nx">flowQuery</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">flowQuery</span> <span class="o">=</span> <span class="nx">flowQuery</span> <span class="o">||</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>

      <span class="kd">var</span> <span class="nx">flows</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_hub_flow</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">flowQuery</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">flow</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">flow</span><span class="p">.</span><span class="nx">sys_id</span><span class="p">);</span>
        <span class="p">},</span> <span class="p">[]);</span>

      <span class="kd">var</span> <span class="nx">actions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_hub_action_instance</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">flow</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nx">flows</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">flow</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">flow$DISPLAY</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">acc</span><span class="p">[</span><span class="nx">action</span><span class="p">.</span><span class="nx">sys_id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">action</span><span class="p">;</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">{});</span>

      <span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_element_mapping</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">actions</span><span class="p">))</span>
        <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">id</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">mapping</span><span class="p">.</span><span class="nx">values</span> <span class="o">=</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="sr">/</span><span class="se">\b</span><span class="sr">/</span><span class="p">);</span> <span class="c1">// split on word boundaries</span>
          <span class="k">return</span> <span class="nx">mapping</span><span class="p">;</span>
        <span class="p">})</span>
        <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">return</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">values</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nb">Boolean</span><span class="p">(</span><span class="nx">records</span><span class="p">[</span><span class="nx">value</span><span class="p">]);</span>
          <span class="p">});</span>
        <span class="p">})</span>
        <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">mapping</span><span class="p">);</span>
        <span class="p">},</span> <span class="p">[]);</span>

      <span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span>
        <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_variable_value</span><span class="dl">'</span><span class="p">)</span>
          <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">document</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">sys_hub_action_instance</span><span class="dl">'</span><span class="p">)</span>
          <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">document_key</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">actions</span><span class="p">))</span>
          <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">variable.reference</span><span class="dl">'</span><span class="p">,</span> <span class="nx">table</span><span class="p">)</span>
          <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">)</span>
          <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">document_key</span><span class="dl">'</span><span class="p">)</span>
          <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">variable</span><span class="p">.</span><span class="nx">id</span> <span class="o">=</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">document_key</span><span class="p">;</span>
            <span class="nx">variable</span><span class="p">.</span><span class="nx">values</span> <span class="o">=</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">,</span><span class="dl">'</span><span class="p">);</span>
            <span class="k">return</span> <span class="nx">variable</span><span class="p">;</span>
          <span class="p">})</span>
          <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">values</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
              <span class="k">return</span> <span class="nb">Boolean</span><span class="p">(</span><span class="nx">records</span><span class="p">[</span><span class="nx">value</span><span class="p">]);</span>
            <span class="p">});</span>
          <span class="p">})</span>
          <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">variable</span><span class="p">);</span>
          <span class="p">},</span> <span class="p">[])</span>
      <span class="p">);</span>

      <span class="kd">function</span> <span class="nx">getDisplayValuesWithReferences</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">result</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">var</span> <span class="nx">flow</span> <span class="o">=</span> <span class="nx">actions</span><span class="p">[</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">id</span><span class="p">].</span><span class="nx">flow$DISPLAY</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
          <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">acc</span><span class="p">[</span><span class="nx">flow</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">acc</span><span class="p">[</span><span class="nx">flow</span><span class="p">]</span> <span class="o">=</span> <span class="p">[];</span>
          <span class="p">}</span>

          <span class="kd">var</span> <span class="nx">intersection</span> <span class="o">=</span> <span class="nx">mapping</span><span class="p">.</span><span class="nx">values</span>
            <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
              <span class="kd">var</span> <span class="nx">record</span> <span class="o">=</span> <span class="nx">records</span><span class="p">[</span><span class="nx">value</span><span class="p">];</span>
              <span class="k">if</span> <span class="p">(</span><span class="nx">record</span><span class="p">)</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">record</span><span class="p">.</span><span class="nx">trim</span><span class="p">());</span>
              <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
            <span class="p">},</span> <span class="p">[]);</span>

          <span class="nx">acc</span><span class="p">[</span><span class="nx">flow</span><span class="p">]</span> <span class="o">=</span> <span class="nx">acc</span><span class="p">[</span><span class="nx">flow</span><span class="p">].</span><span class="nx">concat</span><span class="p">(</span><span class="nx">intersection</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">{});</span>
      <span class="p">}</span>

      <span class="kd">function</span> <span class="nx">getSysIds</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">result</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">mapping</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">var</span> <span class="nx">flowId</span> <span class="o">=</span> <span class="nx">actions</span><span class="p">[</span><span class="nx">mapping</span><span class="p">.</span><span class="nx">id</span><span class="p">].</span><span class="nx">flow</span><span class="p">;</span>
          <span class="k">if</span> <span class="p">(</span><span class="nx">acc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">flowId</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
            <span class="nx">acc</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">flowId</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">[]);</span>
      <span class="p">}</span>

      <span class="k">return</span> <span class="p">{</span>
        <span class="na">getDisplayValuesWithReferences</span><span class="p">:</span> <span class="nx">getDisplayValuesWithReferences</span><span class="p">,</span>
        <span class="na">getSysIds</span><span class="p">:</span> <span class="nx">getSysIds</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="kd">function</span> <span class="nx">findInWorkflows</span><span class="p">(</span><span class="nx">workflowVersionQuery</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">workflowVersionQuery</span> <span class="o">=</span> <span class="nx">workflowVersionQuery</span> <span class="o">||</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">published</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>

      <span class="kd">var</span> <span class="nx">workflowVersions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">wf_workflow_version</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="nx">workflowVersionQuery</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">workflow</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">workflow</span><span class="p">.</span><span class="nx">sys_id</span><span class="p">);</span>
        <span class="p">},</span> <span class="p">[]);</span>

      <span class="kd">var</span> <span class="nx">activities</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">wf_activity</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">workflow_version</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nx">workflowVersions</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">workflow_version</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">workflow_version$DISPLAY</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">activity</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">acc</span><span class="p">[</span><span class="nx">activity</span><span class="p">.</span><span class="nx">sys_id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">activity</span><span class="p">;</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">{});</span>

      <span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_variable_value</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">document</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">wf_activity</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">document_key</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">activities</span><span class="p">))</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">variable.reference</span><span class="dl">'</span><span class="p">,</span> <span class="nx">table</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">value</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">document_key</span><span class="dl">'</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
          <span class="nx">variable</span><span class="p">.</span><span class="nx">values</span> <span class="o">=</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">,</span><span class="dl">'</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">variable</span><span class="p">;</span>
        <span class="p">})</span>
        <span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
          <span class="k">return</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">values</span><span class="p">.</span><span class="nx">some</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nb">Boolean</span><span class="p">(</span><span class="nx">records</span><span class="p">[</span><span class="nx">value</span><span class="p">]);</span>
          <span class="p">});</span>
        <span class="p">});</span>

      <span class="kd">function</span> <span class="nx">getDisplayValuesWithReferences</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">result</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">var</span> <span class="nx">workflow</span> <span class="o">=</span> <span class="nx">activities</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">document_key</span><span class="p">].</span><span class="nx">workflow_version$DISPLAY</span><span class="p">.</span><span class="nx">trim</span><span class="p">();</span>
          <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">acc</span><span class="p">[</span><span class="nx">workflow</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">undefined</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">acc</span><span class="p">[</span><span class="nx">workflow</span><span class="p">]</span> <span class="o">=</span> <span class="p">[];</span>
          <span class="p">}</span>

          <span class="kd">var</span> <span class="nx">intersection</span> <span class="o">=</span> <span class="nx">variable</span><span class="p">.</span><span class="nx">values</span>
            <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
              <span class="kd">var</span> <span class="nx">record</span> <span class="o">=</span> <span class="nx">records</span><span class="p">[</span><span class="nx">value</span><span class="p">];</span>
              <span class="k">if</span> <span class="p">(</span><span class="nx">record</span><span class="p">)</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">record</span><span class="p">);</span>
              <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
            <span class="p">},</span> <span class="p">[]);</span>

          <span class="nx">acc</span><span class="p">[</span><span class="nx">workflow</span><span class="p">]</span> <span class="o">=</span> <span class="nx">acc</span><span class="p">[</span><span class="nx">workflow</span><span class="p">].</span><span class="nx">concat</span><span class="p">(</span><span class="nx">intersection</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">{});</span>
      <span class="p">}</span>

      <span class="kd">function</span> <span class="nx">getSysIds</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">return</span> <span class="nx">result</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">variable</span><span class="p">)</span> <span class="p">{</span>
          <span class="kd">var</span> <span class="nx">workflowId</span> <span class="o">=</span> <span class="nx">activities</span><span class="p">[</span><span class="nx">variable</span><span class="p">.</span><span class="nx">document_key</span><span class="p">].</span><span class="nx">workflow_version</span><span class="p">;</span>
          <span class="k">if</span> <span class="p">(</span><span class="nx">acc</span><span class="p">.</span><span class="nx">indexOf</span><span class="p">(</span><span class="nx">workflowId</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
            <span class="nx">acc</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">workflowId</span><span class="p">);</span>
          <span class="k">return</span> <span class="nx">acc</span><span class="p">;</span>
        <span class="p">},</span> <span class="p">[]);</span>
      <span class="p">}</span>

      <span class="k">return</span> <span class="p">{</span>
        <span class="na">getDisplayValuesWithReferences</span><span class="p">:</span> <span class="nx">getDisplayValuesWithReferences</span><span class="p">,</span>
        <span class="na">getSysIds</span><span class="p">:</span> <span class="nx">getSysIds</span>
      <span class="p">};</span>
    <span class="p">}</span>

    <span class="c1">/////// RETURN ///////</span>

    <span class="k">return</span> <span class="p">{</span>
      <span class="na">findInFlows</span><span class="p">:</span> <span class="nx">findInFlows</span><span class="p">,</span>
      <span class="na">findInWorkflows</span><span class="p">:</span> <span class="nx">findInWorkflows</span>
    <span class="p">};</span>

  <span class="p">})();</span>
<span class="p">};</span>
</code></pre></div></div>

<h3 id="basic-usage">Basic usage</h3>

<p>So far we’ve been using this by just calling it manually in a background script and having it log out the results, but you could conceivably call it from a scheduled job to either notify someone or raise tasks to clean up newly discovered issues. Even better might be calling it from an <abbr>ATF</abbr> test.</p>

<p>When you call the HardcodedValuesLocator you must provide a table you’re looking for hardcoded values from (<code class="language-plaintext highlighter-rouge">sys_user</code> and <code class="language-plaintext highlighter-rouge">sys_user_group</code> are the common ones, but go nuts if you have something else you’re looking for). You get back an object with a couple of methods, <code class="language-plaintext highlighter-rouge">findInFlows</code> and <code class="language-plaintext highlighter-rouge">findInWorkflows</code>, which do pretty much what they say on the tin. You can of course assign the object to a variable and then make the subsequent method call like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">hvl</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">hvl</span><span class="p">.</span><span class="nx">findInFlows</span><span class="p">();</span>
</code></pre></div></div>

<p>Or I like to chain the method calls together more fluidly like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">findInFlows</span><span class="p">();</span>
</code></pre></div></div>

<p>The result here is another object that has the data you’re after, but not yet in a form that’s immediately useful. You have two options at this step, (1) get the data as sys_ids by calling the <code class="language-plaintext highlighter-rouge">getSysIds</code> method. This would be useful if you’re building some automated process and you need the result to be machine-readable so you can perform subsequent processing yourself.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">machineReadableResult</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">findInFlows</span><span class="p">()</span>
  <span class="p">.</span><span class="nx">getSysIds</span><span class="p">();</span>
  
<span class="c1">// Your processing logic goes here...</span>
</code></pre></div></div>

<p>Or (2) get the data in human-readable display values with the <code class="language-plaintext highlighter-rouge">getDisplayValuesWithReferences</code> method, and here you’ll probably want to send the result to the System Log:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">humanReadableResult</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">findInFlows</span><span class="p">()</span>
  <span class="p">.</span><span class="nx">getDisplayValuesWithReferences</span><span class="p">();</span>
  
<span class="nx">gs</span><span class="p">.</span><span class="nx">info</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">humanReadableResult</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</code></pre></div></div>

<h2 id="advanced-options">Advanced options</h2>

<p>There are a few more parameters you can pass to these functions to make your searches more targeted. First, when you call the HardcodedValuesLocator Script Include you can optionally pass a GlideQuery object as the second parameter. This is useful if you’re only looking for specific users, like inactive ones:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">,</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">().</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">false</span><span class="p">);</span>
</code></pre></div></div>

<p>Additionally, you can filter which Workflows you’re searching by optionally passing a GlideQuery object to the <code class="language-plaintext highlighter-rouge">findInFlows</code> method, for example, if you only want to search in currently published Flows:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">hvl</span><span class="p">.</span><span class="nx">findInFlows</span><span class="p">(</span><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">().</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">));</span>
</code></pre></div></div>

<p>Your use cases for this will likely vary from mine, so you’re welcome to call these methods with whatever queries you need, but here’s an example script I’ve been using to categorize and sort hardcoded users and groups by most concerning to least concerning:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="c1">// Get some useful data up front</span>
  <span class="kd">var</span> <span class="nx">catalogWorkflows</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sc_cat_item</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">workflow</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">workflow</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">catItem</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">catItem</span><span class="p">.</span><span class="nx">workflow</span><span class="p">);</span>
    <span class="p">},</span> <span class="p">[]);</span>

  <span class="kd">var</span> <span class="nx">catalogWorkflowVersions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">published</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">workflow</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nx">catalogWorkflows</span><span class="p">);</span>

  <span class="kd">var</span> <span class="nx">otherWorkflowVersions</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">published</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">table</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">!=</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">sc_req_item</span><span class="dl">'</span><span class="p">);</span>

  <span class="kd">var</span> <span class="nx">groups</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user_grmember</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">user</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">group</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">groupBy</span><span class="p">(</span><span class="dl">'</span><span class="s1">group</span><span class="dl">'</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">aggregate</span><span class="p">(</span><span class="dl">'</span><span class="s1">count</span><span class="dl">'</span><span class="p">);</span>

  <span class="kd">var</span> <span class="nx">groupsWithActiveMembers</span> <span class="o">=</span> <span class="nx">groups</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">user.active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">groupMember</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">groupMember</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">group</span><span class="p">);</span>
    <span class="p">},</span> <span class="p">[]);</span>

  <span class="kd">var</span> <span class="nx">groupsWithInactiveMembers</span> <span class="o">=</span> <span class="nx">groups</span>
    <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">user.active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
    <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
    <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">acc</span><span class="p">,</span> <span class="nx">groupMember</span><span class="p">)</span> <span class="p">{</span>
      <span class="k">return</span> <span class="nx">acc</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">groupMember</span><span class="p">.</span><span class="nx">group</span><span class="p">.</span><span class="nx">group</span><span class="p">);</span>
    <span class="p">},</span> <span class="p">[]);</span>

  <span class="c1">// Define searches we want to perform from most to least concerning</span>
  <span class="kd">var</span> <span class="nx">searches</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Hardcoded inactive users</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">table</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">query</span><span class="p">:</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Hardcoded groups with no active members</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">table</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sys_user_group</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">query</span><span class="p">:</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_id</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">NOT IN</span><span class="dl">'</span><span class="p">,</span> <span class="nx">groupsWithActiveMembers</span><span class="p">)</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Hardcoded groups with at least one inactive member</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">table</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sys_user_group</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">query</span><span class="p">:</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_id</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">IN</span><span class="dl">'</span><span class="p">,</span> <span class="nx">groupsWithInactiveMembers</span><span class="p">)</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="na">description</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Hardcoded active users</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">table</span><span class="p">:</span> <span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">,</span>
      <span class="na">query</span><span class="p">:</span> <span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">()</span>
        <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">];</span>

  <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">searches</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Do each search in all three places and output the results</span>
    <span class="kd">var</span> <span class="nx">flowsResult</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">table</span><span class="p">,</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">query</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">findInFlows</span><span class="p">(</span><span class="k">new</span> <span class="nx">GlideQuery</span><span class="p">().</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">))</span>
      <span class="p">.</span><span class="nx">getDisplayValuesWithReferences</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">flowsResult</span><span class="p">).</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">description</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> in flows:</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">flowsResult</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="kd">var</span> <span class="nx">catalogWorkflowsResult</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">table</span><span class="p">,</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">query</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">findInWorkflows</span><span class="p">(</span><span class="nx">catalogWorkflowVersions</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">getDisplayValuesWithReferences</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">catalogWorkflowsResult</span><span class="p">).</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">description</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> in catalog workflows:</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">catalogWorkflowsResult</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
    <span class="p">}</span>

    <span class="kd">var</span> <span class="nx">otherWorkflowsResult</span> <span class="o">=</span> <span class="nx">HardcodedValuesLocator</span><span class="p">(</span><span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">table</span><span class="p">,</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">query</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">findInWorkflows</span><span class="p">(</span><span class="nx">otherWorkflowVersions</span><span class="p">)</span>
      <span class="p">.</span><span class="nx">getDisplayValuesWithReferences</span><span class="p">();</span>
    
    <span class="k">if</span> <span class="p">(</span><span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">otherWorkflowsResult</span><span class="p">).</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">searches</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">description</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> in other workflows:</span><span class="dl">'</span><span class="p">);</span>
      <span class="nx">gs</span><span class="p">.</span><span class="nx">print</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">otherWorkflowsResult</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">})();</span>
</code></pre></div></div>

<p>Note I’m using <code class="language-plaintext highlighter-rouge">gs.print()</code> in the above sample, which works fine in a background script or in Xplore, but you may need to use <code class="language-plaintext highlighter-rouge">gs.info()</code> instead depending on context.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I hope this helps you find and clean up some of the skeletons in your ServiceNow closet. If you do find it useful or have any questions, feel free to reach out on <a href="https://invite.sndevs.com/">SNDevs Slack</a> or <a href="https://social.sndevs.com/">Mastodon</a>. <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>]]></content><author><name>Joey</name></author><category term="Script Include" /><category term="Workflow" /><category term="Flow Designer" /><summary type="html"><![CDATA[I don’t know about your ServiceNow environment, but we’ve got a lot of technical debt built up in ours in the form of hardcoded values (users, groups, etc.) in legacy Workflows and Flow Designer Flows.]]></summary></entry><entry><title type="html">Python Script for Downloading Attachments</title><link href="https://glideguide.blog/2024/03/30/download-attachments.html" rel="alternate" type="text/html" title="Python Script for Downloading Attachments" /><published>2024-03-30T00:00:00+00:00</published><updated>2024-03-30T00:00:00+00:00</updated><id>https://glideguide.blog/2024/03/30/download-attachments</id><content type="html" xml:base="https://glideguide.blog/2024/03/30/download-attachments.html"><![CDATA[<p><span class="lead">I’ve been exporting a lot of data</span> from my ServiceNow instance lately for archival purposes (<abbr>IYKYK</abbr>) and one of the challenges I’ve encountered is properly preserving attachments.</p>

<h2 id="converting-from-base64">Converting from Base64?</h2>

<p>At first I looked for ways to “re-hydrate” the attachments from records exported in <abbr>XML</abbr> format. Attachment files are embedded in <abbr>XML</abbr> exports in a kind of <a href="https://en.wikipedia.org/wiki/Base64">Base64</a> encoding that’s probably not proprietary to ServiceNow, but I tried several different ways to convert that encoded data back into files with no luck. If you know a way to do this, please reach out and share, as I’m still curious to know if it can be done.</p>

<h2 id="server-side-script-andor-processor">Server-side script and/or Processor?</h2>

<p>The next approach I took was writing a server-side script (like a Fix Script or a Background Script) to initiate bulk downloading of attachments in my browser. I stumbled across a few promising Community posts going in a similar direction, including one that suggested creating a <abbr>UI</abbr> Action that utilized a custom <a href="https://docs.servicenow.com/bundle/washingtondc-application-development/page/script/processors/concept/c_Processors.html">Processor</a>. That and similar answers all seemed to be linking to a now-missing post on the <a href="https://servicenowguru.com">ServiceNow Guru</a> blog.</p>

<p>There’s apparently an out-of-box Processor called DownloadAllAttachmentsProcessor that will (what it says on the tin) download all attachments for a single record as a zip file. Handy! But I needed something that would download attachments in bulk, and custom Processors are not only deprecated but completely disabled (you need the <em>maint</em> role to create them and you can’t edit the <abbr>ACL</abbr>s) so all this was a dead end.</p>

<h2 id="attachment-api-and-python-to-the-rescue">Attachment <abbr>API</abbr> and Python to the rescue</h2>

<p>Fortunately, all that thinking about Processors got me on the related subject of <abbr>REST</abbr> <abbr>API</abbr>s. At first I thought I might have to create a Scripted <abbr>REST</abbr> <abbr>API</abbr>, but then I discovered there is an out-of-box <a href="https://docs.servicenow.com/bundle/washingtondc-api-reference/page/integrate/inbound-rest/concept/c_AttachmentAPI.html">Attachment <abbr>API</abbr></a>. The last missing piece fell into place when I found <a href="https://www.servicenow.com/community/developer-forum/download-file-using-rest-api/m-p/1824873#M481799">Hitoshi Ozawa using a Python script</a> to hit the Attachment <abbr>API</abbr> from the command line on his local machine.</p>

<p>Ozawa’s solution seemed promising, but he was still only downloading the attachments on a single record. So here’s my own version of a Python script that downloads all attachments for all records in a specified table matching a specified encoded query, organizing them in a nice folder hierarchy (don’t miss some notes on usage after the script, including a very important one about security):</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">re</span>

<span class="c1">### set options here ###
</span>
<span class="n">instance</span> <span class="o">=</span> <span class="s">'https://&lt;your-instance&gt;.service-now.com'</span>
<span class="n">username</span> <span class="o">=</span> <span class="s">''</span>
<span class="n">password</span> <span class="o">=</span> <span class="s">''</span>

<span class="n">table</span> <span class="o">=</span> <span class="s">'incident'</span>
<span class="n">display_field</span> <span class="o">=</span> <span class="s">'number'</span>
<span class="n">query</span> <span class="o">=</span> <span class="s">'active=true'</span>
<span class="n">limit</span> <span class="o">=</span> <span class="s">'5'</span> <span class="c1"># set to 'None' (without quotes) to remove limit
</span>
<span class="n">directory</span> <span class="o">=</span> <span class="s">'./Attachments'</span>

<span class="c1">### shouldn't have to edit anything below ###
</span>
<span class="c1"># helper dict for converting mime types to file extensions
</span><span class="n">mime_type_to_ext</span> <span class="o">=</span> <span class="p">{</span>
  <span class="s">'application/json'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.json'</span><span class="p">],</span>
  <span class="s">'application/xml'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.xml'</span><span class="p">],</span>
  <span class="s">'image/png'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.png'</span><span class="p">],</span>
  <span class="s">'image/jpeg'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.jpg'</span><span class="p">,</span> <span class="s">'.jpeg'</span><span class="p">],</span>
  <span class="s">'image/gif'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.gif'</span><span class="p">],</span>
  <span class="s">'text/csv'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.csv'</span><span class="p">],</span>
  <span class="s">'text/plain'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.txt'</span><span class="p">],</span>
  <span class="s">'application/pdf'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.pdf'</span><span class="p">],</span>
  <span class="s">'application/msword'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.doc'</span><span class="p">],</span>
  <span class="s">'application/vnd.openxmlformats-officedocument.wordprocessingml.document'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.docx'</span><span class="p">],</span>
  <span class="s">'application/vnd.ms-excel'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.xls'</span><span class="p">],</span>
  <span class="s">'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.xlsx'</span><span class="p">],</span>
  <span class="s">'application/application/vnd.ms-powerpoint'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.ppt'</span><span class="p">],</span>
  <span class="s">'application/vnd.openxmlformats-officedocument.presentationml.presentation'</span><span class="p">:</span> <span class="p">[</span><span class="s">'.pptx'</span><span class="p">],</span>
<span class="p">}</span>

<span class="c1"># helper function for hitting any endpoint, returns response object
</span><span class="k">def</span> <span class="nf">get_response</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">mime_type</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="p">{}):</span>
  <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span> <span class="s">"Accept"</span><span class="p">:</span> <span class="n">mime_type</span> <span class="p">}</span>
  <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">auth</span><span class="o">=</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="n">password</span><span class="p">),</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="n">params</span><span class="p">)</span>
  <span class="k">if</span> <span class="n">response</span><span class="p">.</span><span class="n">status_code</span> <span class="o">!=</span> <span class="mi">200</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="s">'Status:'</span><span class="p">,</span> <span class="n">response</span><span class="p">.</span><span class="n">status_code</span><span class="p">,</span> <span class="s">'headers:'</span><span class="p">,</span> <span class="n">response</span><span class="p">.</span><span class="n">headers</span><span class="p">,</span> <span class="s">'Error Response:'</span><span class="p">,</span> <span class="n">response</span><span class="p">.</span><span class="n">json</span><span class="p">())</span>
    <span class="nb">exit</span><span class="p">()</span>
  <span class="k">return</span> <span class="n">response</span>

<span class="c1"># helper function for table api, returns list of dicts
</span><span class="k">def</span> <span class="nf">get_records</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">limit</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
  <span class="n">endpoint</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">instance</span><span class="si">}</span><span class="s">/api/now/table/</span><span class="si">{</span><span class="n">table</span><span class="si">}</span><span class="s">'</span>
  <span class="n">params</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s">'sysparm_query'</span><span class="p">:</span> <span class="n">query</span><span class="p">,</span>
    <span class="s">'sysparm_display_value'</span><span class="p">:</span> <span class="s">'true'</span><span class="p">,</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="n">limit</span><span class="p">:</span>
    <span class="n">params</span><span class="p">[</span><span class="s">'sysparm_limit'</span><span class="p">]</span> <span class="o">=</span> <span class="n">limit</span>

  <span class="k">return</span> <span class="n">get_response</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="s">'application/json'</span><span class="p">,</span> <span class="n">params</span><span class="p">).</span><span class="n">json</span><span class="p">().</span><span class="n">get</span><span class="p">(</span><span class="s">'result'</span><span class="p">)</span>

<span class="c1"># helper function to make directory and file names safer
</span><span class="k">def</span> <span class="nf">sanitize_filename</span><span class="p">(</span><span class="n">filename</span><span class="p">):</span>
  <span class="n">filename</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s">'[&lt;&gt;:"/\\|?*]'</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span> <span class="c1"># remove invalid characters
</span>  <span class="n">filename</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s">'^\.+'</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">filename</span><span class="p">)</span> <span class="c1"># remove leading period
</span>  <span class="k">return</span> <span class="n">filename</span><span class="p">[:</span><span class="mi">255</span><span class="p">]</span> <span class="c1"># truncate to 255 characters
</span>  
<span class="c1"># Helper function to generate a unique filename if file exists
</span><span class="k">def</span> <span class="nf">unique_filename</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
    <span class="n">base</span><span class="p">,</span> <span class="n">extension</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">splitext</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
    <span class="n">counter</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">while</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
        <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">base</span><span class="si">}</span><span class="s">_</span><span class="si">{</span><span class="n">counter</span><span class="si">}{</span><span class="n">extension</span><span class="si">}</span><span class="s">"</span>
        <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="n">path</span>

<span class="c1"># helper function for attachment api, writes files to disk
</span><span class="k">def</span> <span class="nf">get_attachment</span><span class="p">(</span><span class="n">sys_id</span><span class="p">,</span> <span class="n">mime_type</span><span class="p">,</span> <span class="n">path</span><span class="p">):</span>
  <span class="n">path</span> <span class="o">=</span> <span class="n">unique_filename</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
  <span class="n">endpoint</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">instance</span><span class="si">}</span><span class="s">/api/now/attachment/</span><span class="si">{</span><span class="n">sys_id</span><span class="si">}</span><span class="s">/file'</span>
  <span class="n">response</span> <span class="o">=</span> <span class="n">get_response</span><span class="p">(</span><span class="n">endpoint</span><span class="p">,</span> <span class="n">mime_type</span><span class="p">)</span>

  <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
    <span class="k">for</span> <span class="n">chunk</span> <span class="ow">in</span> <span class="n">response</span><span class="p">.</span><span class="n">iter_content</span><span class="p">(</span><span class="n">chunk_size</span><span class="o">=</span><span class="mi">1024</span><span class="p">):</span>
      <span class="k">if</span> <span class="n">chunk</span><span class="p">:</span>
        <span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>

<span class="c1"># call table api to get specified table records and iterate over results
</span><span class="k">for</span> <span class="n">record</span> <span class="ow">in</span> <span class="n">get_records</span><span class="p">(</span><span class="n">table</span><span class="p">,</span> <span class="n">query</span><span class="p">,</span> <span class="n">limit</span><span class="p">):</span>
  <span class="n">class_name</span> <span class="o">=</span> <span class="n">sanitize_filename</span><span class="p">(</span><span class="n">record</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'sys_class_name'</span><span class="p">))</span>
  <span class="n">display_value</span> <span class="o">=</span> <span class="n">sanitize_filename</span><span class="p">(</span><span class="n">record</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">display_field</span><span class="p">))</span>
  <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">class_name</span><span class="si">}</span><span class="s">: </span><span class="si">{</span><span class="n">display_value</span><span class="si">}</span><span class="s">'</span><span class="p">)</span>

  <span class="c1"># call table api again to get attachment metadata and iterate over results
</span>  <span class="n">attachment_query</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"table_sys_id=</span><span class="si">{</span><span class="n">record</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'sys_id'</span><span class="p">)</span><span class="si">}</span><span class="s">"</span>
  <span class="n">attachments</span> <span class="o">=</span> <span class="n">get_records</span><span class="p">(</span><span class="s">'sys_attachment'</span><span class="p">,</span> <span class="n">attachment_query</span><span class="p">)</span>

  <span class="k">if</span> <span class="n">attachments</span><span class="p">:</span>
    <span class="c1"># make folders if they don't exist
</span>    <span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">directory</span><span class="p">,</span> <span class="n">class_name</span><span class="p">,</span> <span class="n">display_value</span><span class="p">)</span>
    <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">exist_ok</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>

    <span class="k">for</span> <span class="n">attachment</span> <span class="ow">in</span> <span class="n">attachments</span><span class="p">:</span>
      <span class="c1"># get mime type and file name
</span>      <span class="n">mime_type</span> <span class="o">=</span> <span class="n">attachment</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'content_type'</span><span class="p">)</span>
      <span class="n">file_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">sanitize_filename</span><span class="p">(</span><span class="n">attachment</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'file_name'</span><span class="p">)))</span>

      <span class="c1"># Append file extension to file name if not already
</span>      <span class="n">file_ext_list</span> <span class="o">=</span> <span class="n">mime_type_to_ext</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">mime_type</span><span class="p">,</span> <span class="p">[</span><span class="s">''</span><span class="p">])</span>
      <span class="n">file_ext</span> <span class="o">=</span> <span class="n">file_ext_list</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>  <span class="c1"># Choose first extension as default
</span>      <span class="k">if</span> <span class="ow">not</span> <span class="nb">any</span><span class="p">(</span><span class="n">file_path</span><span class="p">.</span><span class="n">endswith</span><span class="p">(</span><span class="n">ext</span><span class="p">)</span> <span class="k">for</span> <span class="n">ext</span> <span class="ow">in</span> <span class="n">file_ext_list</span><span class="p">):</span>
        <span class="n">file_path</span> <span class="o">+=</span> <span class="n">file_ext</span>

      <span class="c1"># call attachment api to download file and write to disk
</span>      <span class="n">get_attachment</span><span class="p">(</span><span class="n">attachment</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'sys_id'</span><span class="p">),</span> <span class="n">mime_type</span><span class="p">,</span> <span class="n">file_path</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="usage-notes">Usage notes</h2>

<p>Here’s how to use the script:</p>

<ol>
  <li>Copy and paste it into your favorite text editor (worth noting here: unlike in JavaScript, indentation matters in Python, so copy carefully)</li>
  <li>Update the settings at the top of the script to your liking</li>
  <li>Save it as <em>attachments.py</em> (or whatever you want)</li>
  <li>On the command line, navigate to the same folder where you saved the script</li>
  <li>Run it with the command <code class="language-plaintext highlighter-rouge">python attachments.py</code> (you might have to start the command with <em>python3</em> depending on your environment)</li>
</ol>

<p>When you run the script, it creates an “Attachments” folder in the same location. It then begins downloading the attachments into that folder, neatly organizing them in a folder hierarchy by table name and record display value. It also helpfully adds appropriate file extensions to any files that don’t have them.</p>

<p><em>I can’t put a fine enough point on this</em>: It’s super dangerous to put your ServiceNow username and password directly in a plain text file like this. If you’re using it as a one-off like I am, it may be fine, though I’d be sure to delete the password from the script (or just delete the whole script) when you’re done. If this script is going to be used on a regular basis you should investigate dynamically populating the username and password from environment variables or an encrypted password vault. Also, if you can get what you need from a sub-production instance, it’s always safer to go that route. If my script gets you reprimanded or fired over some kind of security breach, don’t @ me.</p>

<p>Lastly, you’ll (obviously) need <a href="https://www.python.org">Python</a> installed on your local machine to run this. Instructions for doing that are outside the scope of this article, partly since they’ll be different depending on your operating system. In addition to Python, I found I had to separately install the <em>requests</em> package using the <a href="https://packaging.python.org/en/latest/tutorials/installing-packages/">package installer for Python (pip)</a>. Google is your friend for figuring out how to get everything installed and running on your computer.</p>

<h2 id="questions-or-feedback">Questions or feedback?</h2>

<p>If you find a use case for downloading attachments in bulk, I hope the script helps. I’d love to hear about your experience and answer any questions you might have. Feel free to reach out on <a href="https://invite.sndevs.com/">SNDevs Slack</a> or <a href="https://social.sndevs.com/">Mastodon</a>. Cheers! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>]]></content><author><name>Joey</name></author><category term="Python" /><category term="Attachments" /><summary type="html"><![CDATA[I’ve been exporting a lot of data from my ServiceNow instance lately for archival purposes (IYKYK) and one of the challenges I’ve encountered is properly preserving attachments.]]></summary></entry><entry><title type="html">The GQ Script Include</title><link href="https://glideguide.blog/2024/03/06/gq-script-include.html" rel="alternate" type="text/html" title="The GQ Script Include" /><published>2024-03-06T00:00:00+00:00</published><updated>2024-03-06T00:00:00+00:00</updated><id>https://glideguide.blog/2024/03/06/gq-script-include</id><content type="html" xml:base="https://glideguide.blog/2024/03/06/gq-script-include.html"><![CDATA[<p><span class="lead">I wonder how many ServiceNow developers</span> know the <abbr>GQ</abbr> script include exists. Maybe you’ve heard of GlideQuery, but <abbr>GQ</abbr> is a bit of an undocumented feature. <!--more--> I first heard about it from Peter Bell himself (creator of GlideQuery) in an episode of <em>Creator Toolbox</em>, where he calls it, “a little undocumented magic, if you’re into that kind of stuff.”<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> Why, yes, yes I am.</p>

<h2 id="what-is-it">What is it?</h2>

<p>In a nutshell, <abbr>GQ</abbr> is a library of helper functions designed for use with GlideQuery, particularly with the filter, map, and reduce methods of the Stream object. In the video, Bell demonstrates the use of <code class="language-plaintext highlighter-rouge">GQ.jsonDebug()</code> with the map method to send whole records to the system log with pretty formatting, a bit like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nb">global</span><span class="p">.</span><span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">limit</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">caller_id$DISPLAY</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQ</span><span class="p">.</span><span class="nx">jsonDebug</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">incident</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// do something</span>
    <span class="c1">// ...</span>
  <span class="p">});</span>
</code></pre></div></div>

<p>Don’t miss this: the <code class="language-plaintext highlighter-rouge">jsonDebug()</code> method isn’t being invoked. Instead it’s being passed to the <code class="language-plaintext highlighter-rouge">map()</code> method as the callback function parameter. If you’re not familiar, JavaScript is a first-class functions language. Functions are also variables and can be passed in and out of other functions as parameters and return values.</p>

<h2 id="partially-applicable-functions">Partially-applicable functions</h2>

<p>The <abbr>GQ</abbr> script include has a really awesome feature called partially-applicable functions. The <code class="language-plaintext highlighter-rouge">labelDebug()</code> method is a great example. It takes two parameters, a string label and some value (usually a record object). If you call it with both, it system logs a prettified JSON string of the value parameter with the label parameter prepended. The system log can be pretty chatty, so a label can make it easier to find the debug statements you’re looking for.</p>

<p>But if you know how <code class="language-plaintext highlighter-rouge">map()</code> works, you know it only passes one argument to the callback function you pass in, so how do you get a version of <code class="language-plaintext highlighter-rouge">labelDebug()</code> that only takes one argument? This is the beauty of partially-applicable functions. If you call <code class="language-plaintext highlighter-rouge">labelDebug()</code> with just the first argument, the label, it doesn’t immediately send output to the debug log. Instead, it returns a new purpose-built function. The returned function, which has the label permanently baked in, only requires one parameter, the value.</p>

<p>So, unlike <code class="language-plaintext highlighter-rouge">jsonDebug()</code>, you do invoke this one when you use it with <code class="language-plaintext highlighter-rouge">map()</code>, but you only pass the first of the two arguments causing a new function to get returned and passed into <code class="language-plaintext highlighter-rouge">map()</code>. Each time through its own loop, the <code class="language-plaintext highlighter-rouge">map()</code> method will then supply the second argument to the new function.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">new</span> <span class="nb">global</span><span class="p">.</span><span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">where</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">,</span> <span class="kc">true</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">limit</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">caller_id$DISPLAY</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQ</span><span class="p">.</span><span class="nx">labelDebug</span><span class="p">(</span><span class="dl">'</span><span class="s1">Active Incident: </span><span class="dl">'</span><span class="p">))</span>
  <span class="p">.</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">incident</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ... });</span>
</code></pre></div></div>

<p>My favorite and probably the most useful method in the whole <abbr>GQ</abbr> utility is <code class="language-plaintext highlighter-rouge">get()</code>. It’s partially-applicable like <code class="language-plaintext highlighter-rouge">labelDebug()</code>, taking a field name as it’s first parameter and some value (again, usually a record object) as its second parameter. It takes the field name and returns the value of that property from the record object. Use it like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">names</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">global</span><span class="p">.</span><span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_user</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">whereNotNull</span><span class="p">(</span><span class="dl">'</span><span class="s1">first_name</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">(</span><span class="dl">'</span><span class="s1">first_name</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQ</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">first_name</span><span class="dl">'</span><span class="p">))</span>
  <span class="p">.</span><span class="nx">toArray</span><span class="p">(</span><span class="mi">20</span><span class="p">);</span>
</code></pre></div></div>

<p>If all you need is a list of sys_ids or a list of names, <code class="language-plaintext highlighter-rouge">get()</code> is a very handy way to convert a Stream of record objects into the simpler list you need.</p>

<h2 id="gqx">GQX</h2>

<p>The <abbr>GQ</abbr> utility has other functions like <code class="language-plaintext highlighter-rouge">merge()</code> and <code class="language-plaintext highlighter-rouge">values()</code> that I highly recommend you check out, but I also want to share with you another utility I made myself that complements <abbr>GQ</abbr> with a few helpful functions if its own. I call it <abbr>GQX</abbr>, and it has functions mainly for use with Stream’s <code class="language-plaintext highlighter-rouge">reduce()</code> method.</p>

<p>In my early experience using GlideQuery I got annoyed that Stream’s <code class="language-plaintext highlighter-rouge">toArray()</code> method would only handle up to 100 records. To avoid poorly-performing nested queries I’ll frequently pull data from one table and store it in a data structure before pulling related data from a different table and then mashing the two data sets up somehow in memory. Because of the record limit of <code class="language-plaintext highlighter-rouge">toArray()</code>, I found myself writing this pattern a lot to store records in an intermediate data structure:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">problemIDs</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">global</span><span class="p">.</span><span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">problem</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
  <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQ</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_id</span><span class="dl">'</span><span class="p">))</span>
  <span class="p">.</span><span class="nx">reduce</span><span class="p">((</span><span class="nx">problemIDs</span><span class="p">,</span> <span class="nx">problemID</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="nx">problemIDs</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">problemID</span><span class="p">);</span>
    <span class="k">return</span> <span class="nx">problemIDs</span><span class="p">;</span>
  <span class="p">},</span> <span class="p">[]);</span>
</code></pre></div></div>

<p>With the <abbr>GQX</abbr> utility, this same code can be shortened to:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">problemIDs</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">global</span><span class="p">.</span><span class="nx">GlideQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">problem</span><span class="dl">'</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">select</span><span class="p">()</span>
  <span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQ</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">sys_id</span><span class="dl">'</span><span class="p">))</span>
  <span class="p">.</span><span class="nx">reduce</span><span class="p">(</span><span class="nb">global</span><span class="p">.</span><span class="nx">GQX</span><span class="p">.</span><span class="nx">toArray</span><span class="p">,</span> <span class="p">[]);</span>
</code></pre></div></div>

<p>In addition to the <code class="language-plaintext highlighter-rouge">toArray()</code> method, <abbr>GQX</abbr> has methods for reducing a Stream to an object or even an <abbr>ES6</abbr> Set or Map. If you find a cool use for it, please let me know. Cheers! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<h4 id="settings">Settings</h4>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2024-03-06-gqx.png" alt="GQX Script Include settings" /></p>

<h4 id="script">Script</h4>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/**
 * GQX is a utility class intended for use with GlideQuery.
 * @namespace
 */</span>
<span class="kd">function</span> <span class="nx">GQX</span><span class="p">()</span> <span class="p">{}</span>

<span class="cm">/**
 * Returns the accumulator array after pushing the value.
 * Handy as a reduce function.
 * @example
 * var names = new GlideQuery('sys_user')
 *     .whereNotNull('first_name')
 *     .select('first_name')
 *     .reduce(GQX.toArray, []);
 * @param {array} accumulator
 * @param {any} value
 * @returns {array} accumulator
 */</span>
<span class="nx">GQX</span><span class="p">.</span><span class="nx">toArray</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toArray</span> <span class="p">(</span><span class="nx">accumulator</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">accumulator</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">accumulator</span><span class="p">;</span>
<span class="p">};</span>

<span class="cm">/**
 * Returns the accumulator Set after adding the value.
 * Handy as a reduce function.
 * @example
 * var names = new GlideQuery('sys_user')
 *     .whereNotNull('first_name')
 *     .select('first_name')
 *     .reduce(GQX.toSet, new Set());
 * @param {Set} accumulator
 * @param {any} value
 * @returns {Set} accumulator
 */</span>
<span class="nx">GQX</span><span class="p">.</span><span class="nx">toSet</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">toSet</span> <span class="p">(</span><span class="nx">accumulator</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">accumulator</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="nx">value</span><span class="p">);</span>
<span class="p">};</span>

<span class="cm">/**
 * Returns the accumulator object after adding the
 * specified key and value from the record.
 * Handy as a reduce function.
 * @example
 * var names = new GlideQuery('sys_user')
 *     .whereNotNull('first_name')
 *     .select('first_name')
 *     .reduce(GQX.toObject('sys_id', 'first_name'), {});
 * @param {string} key property name
 * @param {string} value property name
 * @param {object} accumulator
 * @param {object} record
 * @returns {object} accumulator
 */</span>
<span class="nx">GQX</span><span class="p">.</span><span class="nx">toObject</span> <span class="o">=</span> <span class="nx">GQ</span><span class="p">.</span><span class="nx">partial</span><span class="p">(</span><span class="kd">function</span> <span class="nx">toObject</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">accumulator</span><span class="p">,</span> <span class="nx">record</span><span class="p">)</span> <span class="p">{</span>
  <span class="nx">accumulator</span><span class="p">[</span><span class="nx">record</span><span class="p">[</span><span class="nx">key</span><span class="p">]]</span> <span class="o">=</span> <span class="nx">value</span> <span class="p">?</span> <span class="nx">record</span><span class="p">[</span><span class="nx">value</span><span class="p">]</span> <span class="p">:</span> <span class="nx">record</span><span class="p">;</span>
  <span class="k">return</span> <span class="nx">accumulator</span><span class="p">;</span>
<span class="p">});</span>

<span class="cm">/**
 * Returns the accumulator Map after adding the
 * specified key and value from the record.
 * Handy as a reduce function.
 * @example
 * var names = new GlideQuery('sys_user')
 *     .whereNotNull('first_name')
 *     .select('first_name')
 *     .reduce(GQX.toMap('sys_id', 'first_name'), new Map());
 * @param {string} key property name
 * @param {string} value property name
 * @param {Map} accumulator
 * @param {object} record
 * @returns {Map} accumulator
 */</span>
<span class="nx">GQX</span><span class="p">.</span><span class="nx">toMap</span> <span class="o">=</span> <span class="nx">GQ</span><span class="p">.</span><span class="nx">partial</span><span class="p">(</span><span class="kd">function</span> <span class="nx">toMap</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">,</span> <span class="nx">accumulator</span><span class="p">,</span> <span class="nx">record</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nx">accumulator</span><span class="p">.</span><span class="kd">set</span><span class="p">(</span><span class="nx">record</span><span class="p">[</span><span class="nx">key</span><span class="p">],</span> <span class="nx">value</span> <span class="p">?</span> <span class="nx">record</span><span class="p">[</span><span class="nx">value</span><span class="p">]</span> <span class="p">:</span> <span class="nx">record</span><span class="p">);</span>
<span class="p">});</span>
</code></pre></div></div>

<hr class="footnotes" />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>See the episode here: <a href="https://www.youtube.com/watch?v=IobUxnK3LDo">Creator Toolbox - GlideQuery</a> (my direct quote is from around 52 minutes into the video). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Joey</name></author><category term="GlideQuery" /><category term="GQ" /><category term="GQX" /><category term="Script Include" /><summary type="html"><![CDATA[I wonder how many ServiceNow developers know the GQ script include exists. Maybe you’ve heard of GlideQuery, but GQ is a bit of an undocumented feature.]]></summary></entry><entry><title type="html">BetterGetter</title><link href="https://glideguide.blog/2024/03/05/bettergetter.html" rel="alternate" type="text/html" title="BetterGetter" /><published>2024-03-05T00:00:00+00:00</published><updated>2024-03-05T00:00:00+00:00</updated><id>https://glideguide.blog/2024/03/05/bettergetter</id><content type="html" xml:base="https://glideguide.blog/2024/03/05/bettergetter.html"><![CDATA[<p><span class="lead">In keeping with my series about GlideQuery</span>, I’d like to share a tool Dan and I made that can slightly improve the experience when you’d prefer to use GlideQuery but have no choice but to use GlideRecord.</p>

<h2 id="the-problem">The problem</h2>

<p>Imagine you’re working in a before business rule and you need to interact with the current record. It’s possible earlier business rules have already changed some values, so using GlideQuery to retrieve the current record from the database will give you, at best, stale data or, at worst, no data at all (i.e. if the record is new and won’t even be inserted until after the before business rules finish). In this and other scenarios, your only option is to interact with the GlideRecord object you’ve been given.</p>

<p>The annoying thing about GlideRecord is what I’ve already been discussing over in my GlideQuery series: the property <code class="language-plaintext highlighter-rouge">gr.short_description</code> isn’t a native JavaScript string. It’s a weird Frankenstein of a Java GlideElement object and a Java string, and it behaves counterintuitively. Strict equality for comparisons fails in mysterious ways, and if you store the value for later use (say in an array) you’ll find it changes out from under you the next time you call <code class="language-plaintext highlighter-rouge">gr.next()</code>.</p>

<p>We all have our favorite tricks like using <code class="language-plaintext highlighter-rouge">gr.getValue()</code> to cast everything to strings, but that’s less than ideal for numeric and boolean fields.</p>

<h2 id="there-is-a-better-way">There is a better way</h2>

<p>Dan and I workshopped a tool and put it up on Share last year. We call it <a href="https://developer.servicenow.com/connect.do#!/share/contents/1148200_bettergetter">BetterGetter</a>. It’s a wrapper for GlideRecord with its own magic <code class="language-plaintext highlighter-rouge">getValue()</code> method that returns native JavaScript types rather than weird GlideElement objects or Java types.</p>

<p>Use it like this:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">bg</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">BetterGetter</span><span class="p">(</span><span class="nx">current</span><span class="p">);</span>
<span class="nx">bg</span><span class="p">.</span><span class="nx">getValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">short_description</span><span class="dl">'</span><span class="p">);</span>  <span class="c1">// string</span>
<span class="nx">bg</span><span class="p">.</span><span class="nx">getValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">priority</span><span class="dl">'</span><span class="p">);</span>       <span class="c1">// number</span>
<span class="nx">bg</span><span class="p">.</span><span class="nx">getValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">active</span><span class="dl">'</span><span class="p">);</span>         <span class="c1">// boolean</span>
<span class="nx">bg</span><span class="p">.</span><span class="nx">getValue</span><span class="p">(</span><span class="dl">'</span><span class="s1">watch_list</span><span class="dl">'</span><span class="p">);</span>       <span class="c1">// array of strings</span>
</code></pre></div></div>

<p>Note that BetterGetter is only a getter, not a setter, so if you need to set values you would just use <code class="language-plaintext highlighter-rouge">current.setValue()</code> as usual. The neat thing is your BetterGetter object becomes sort of quantum entangled with the GlideRecord object passed to it, so you can get some values with BetterGetter, set a value or two the normal way, then get them later with BetterGetter and you’ll get the updated values.</p>

<p>If you do happen to be iterating over a GlideRecord, you can make a single BetterGetter object outside your while loop, then refer to it inside the loop. Since it maintains a reference to the original GlideRecord object you’ll get appropriate values for each new record after calling <code class="language-plaintext highlighter-rouge">gr.next()</code>, but they’ll be native JavaScript values so you can safely store them for later use.</p>

<p>If this sounds awesome and you want to try it out, grab the Update Set from Share or copy/paste the Script Include below. Let us know if you find a cool use for this. Cheers! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<h4 id="settings">Settings:</h4>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2024-03-05-bettergetter.png" alt="BetterGetter Script Include settings" /></p>

<h4 id="script">Script:</h4>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Directly accessing GlideRecord elements gives you Java native types,</span>
<span class="c1">// and GlideRecord's getValue() method always returns JavaScript strings.</span>
<span class="c1">// Neither is very helpful! Using GlideQuery is the recommended best</span>
<span class="c1">// practice, but sometimes you are forced to work with GlideRecord</span>
<span class="c1">// objects, e.g. the `current` object in Business Rules.</span>
<span class="c1">// </span>
<span class="c1">// BetterGetter's getValue method returns the correct JavaScript native</span>
<span class="c1">// type based on the data type of an element's dictionary entry. BetterGetter</span>
<span class="c1">// also provides convenient methods for converting an element to any other</span>
<span class="c1">// preferred JavaScript native type, if possible.</span>
<span class="c1">// </span>
<span class="c1">// Use it like this:</span>
<span class="c1">//    var active = new BetterGetter(current).getValue('active');</span>
<span class="c1">// </span>
<span class="c1">// Or like this:</span>
<span class="c1">//    var getter = new BetterGetter(current);</span>
<span class="c1">//    var active = getter.getValue('active');</span>

<span class="kd">function</span> <span class="nx">BetterGetter</span><span class="p">(</span><span class="nx">gr</span><span class="p">)</span> <span class="p">{</span>
  <span class="c1">// In case caller omits the new keyword</span>
  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="k">this</span> <span class="k">instanceof</span> <span class="nx">BetterGetter</span><span class="p">))</span> <span class="k">return</span> <span class="k">new</span> <span class="nx">BetterGetter</span><span class="p">(</span><span class="nx">gr</span><span class="p">);</span>
  <span class="k">this</span><span class="p">.</span><span class="nx">_gr</span> <span class="o">=</span> <span class="nx">gr</span><span class="p">;</span>
<span class="p">}</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getValue</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">switch</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">].</span><span class="nx">getED</span><span class="p">().</span><span class="nx">getInternalType</span><span class="p">()</span> <span class="o">+</span> <span class="dl">''</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">boolean</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getBoolean</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">glide_date</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getDate</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">glide_date_time</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getDateTime</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">glide_list</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getList</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">decimal</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">float</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">integer</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">longint</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">long</span><span class="dl">'</span><span class="p">:</span>
    <span class="k">case</span> <span class="dl">'</span><span class="s1">numeric</span><span class="dl">'</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getNumber</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="nl">default</span><span class="p">:</span>
      <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">getString</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getBoolean</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="nb">Boolean</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]);</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getDate</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]</span> <span class="o">+</span> <span class="dl">''</span><span class="p">).</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="o">||</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getDateTime</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]</span> <span class="o">+</span> <span class="dl">''</span> <span class="o">||</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getList</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">].</span><span class="nx">nil</span><span class="p">())</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]</span> <span class="o">+</span> <span class="dl">''</span><span class="p">).</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">,</span><span class="dl">'</span><span class="p">);</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getListDisplay</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">].</span><span class="nx">nil</span><span class="p">())</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="k">return</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">.</span><span class="nx">getDisplayValue</span><span class="p">(</span><span class="nx">element</span><span class="p">)).</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">,</span><span class="dl">'</span><span class="p">);</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getNumber</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">].</span><span class="nx">nil</span><span class="p">())</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
  <span class="k">return</span> <span class="nb">Number</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]);</span>
<span class="p">};</span>

<span class="nx">BetterGetter</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">getString</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">element</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nx">_gr</span><span class="p">[</span><span class="nx">element</span><span class="p">]</span> <span class="o">+</span> <span class="dl">''</span> <span class="o">||</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">};</span>
</code></pre></div></div>]]></content><author><name>Joey</name></author><category term="BetterGetter" /><category term="GlideRecord" /><category term="GlideQuery" /><category term="Script Include" /><summary type="html"><![CDATA[In keeping with my series about GlideQuery, I’d like to share a tool Dan and I made that can slightly improve the experience when you’d prefer to use GlideQuery but have no choice but to use GlideRecord.]]></summary></entry><entry><title type="html">Scripted Due Dates in the Ask For Approval Action</title><link href="https://glideguide.blog/2023/05/10/approval-action-scripted-due-date.html" rel="alternate" type="text/html" title="Scripted Due Dates in the Ask For Approval Action" /><published>2023-05-10T00:00:00+00:00</published><updated>2023-05-10T00:00:00+00:00</updated><id>https://glideguide.blog/2023/05/10/approval-action-scripted-due-date</id><content type="html" xml:base="https://glideguide.blog/2023/05/10/approval-action-scripted-due-date.html"><![CDATA[<p><span class="lead">Something in Flow Designer</span> that has always bothered me is that I’ve been unable to get scripted due dates to work in the Ask For Approval action. If you’re unfamiliar, due dates provide the ability to specify how long the flow should wait for approval, as well as what action should be taken on the approval when the specified due date arrives. This prevents a flow from waiting endlessly for an approval response.</p>

<p>It is sometimes necessary to script the due date value in the Ask For Approval action. For example, if you wanted an approval to automatically cancel when left unresponded after 14 calendar days. At first glance this seems entirely possible using the relative due date configuration options, until you get to the “From” date/time value input that must be provided. How can you specify that you want the timer to start at the time of Approval record creation? To my knowledge you cannot, at least not without writing a script. As with all other inputs in Flow Designer it would like you to drag and drop a date/time pill value and move on. If this flow is based on a Catalog Item it can be tempting to drag and drop the Created On date/time pill from the RITM, and in some cases (depending on where this Action is in your Flow) this may not be much of a problem.</p>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2023-05-10-ask-for-approval-action.png" alt="Ask For Approval Action" /></p>

<p>One workaround that I have used after my failed attempts at using the script input has been to populate a flow variable with the desired date/time string value. I can populate the variable via script using the Set Flow Variables action. While I dislike this solution for a number of reasons it does work.</p>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2023-05-10-variable-workaround.png" alt="Set Flow Variable action" />
<img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2023-05-10-workaround-pill.png" alt="Add Variable Pill" /></p>

<p>I was recently building a flow using this workaround when I happened to see that a due date configured with pills generates a JSON configuration that you can view from the Flow Execution Context.</p>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2023-05-10-execution-context.png" alt="Execution Context" /></p>

<p>After finding this I immediately made some assumptions on other possible values and began to test my theories. The JSON object below is the result of my testing. I have documented each property’s purpose and possible values along with an example script.</p>

<h3 id="due-date-configuration-template">Due Date Configuration Template</h3>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
    </span><span class="nl">"action"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">approve</span><span class="p">,</span><span class="w"> </span><span class="err">cancel</span><span class="p">,</span><span class="w"> </span><span class="err">reject</span><span class="w">
    </span><span class="nl">"date_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">actual</span><span class="p">,</span><span class="w"> </span><span class="err">relative</span><span class="w">
    </span><span class="nl">"date"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Date/Time</span><span class="w"> </span><span class="err">string</span><span class="w"> </span><span class="err">used</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">static</span><span class="w"> </span><span class="err">date/time</span><span class="w"> </span><span class="err">value</span><span class="p">,</span><span class="w"> </span><span class="err">or</span><span class="w"> </span><span class="err">as</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">starting</span><span class="w"> </span><span class="err">date/time</span><span class="w"> </span><span class="err">value</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">relative</span><span class="w"> </span><span class="err">due</span><span class="w"> </span><span class="err">dates.</span><span class="w">
    </span><span class="err">//</span><span class="w"> </span><span class="err">The</span><span class="w"> </span><span class="err">following</span><span class="w"> </span><span class="err">can</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">ignored</span><span class="w"> </span><span class="err">for</span><span class="w"> </span><span class="err">static</span><span class="w"> </span><span class="err">due</span><span class="w"> </span><span class="err">date</span><span class="w"> </span><span class="err">configurations</span><span class="w">
    </span><span class="nl">"duration"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">The</span><span class="w"> </span><span class="err">duration</span><span class="w"> </span><span class="err">value</span><span class="w"> </span><span class="err">(e.g.</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="err">minute</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="err">hour</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="err">day</span><span class="p">,</span><span class="w"> </span><span class="err">etc.).</span><span class="w"> </span><span class="err">**</span><span class="w"> </span><span class="err">Default</span><span class="w"> </span><span class="err">value</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="err">**</span><span class="w">
    </span><span class="nl">"duration_type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"days"</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">minutes</span><span class="p">,</span><span class="w"> </span><span class="err">hours</span><span class="p">,</span><span class="w"> </span><span class="err">days</span><span class="p">,</span><span class="w"> </span><span class="err">weeks</span><span class="p">,</span><span class="w"> </span><span class="err">months</span><span class="p">,</span><span class="w"> </span><span class="err">quarters</span><span class="p">,</span><span class="w"> </span><span class="err">years.</span><span class="w"> </span><span class="err">**</span><span class="w"> </span><span class="err">Default</span><span class="w"> </span><span class="err">value</span><span class="w"> </span><span class="err">is</span><span class="w"> </span><span class="err">days</span><span class="w"> </span><span class="err">**</span><span class="w"> 
    </span><span class="nl">"schedule"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Sys</span><span class="w"> </span><span class="err">ID</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">Schedule</span><span class="w"> </span><span class="p">[</span><span class="err">cmn_schedule</span><span class="p">]</span><span class="w"> </span><span class="err">record</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">used</span><span class="w"> </span><span class="err">when</span><span class="w"> </span><span class="err">calculating</span><span class="w"> </span><span class="err">relative</span><span class="w"> </span><span class="err">due</span><span class="w"> </span><span class="err">dates</span><span class="w">
    </span><span class="nl">"schedule_label"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="w"> </span><span class="err">//</span><span class="w"> </span><span class="err">Label</span><span class="w"> </span><span class="err">of</span><span class="w"> </span><span class="err">the</span><span class="w"> </span><span class="err">Schedule</span><span class="w"> </span><span class="p">[</span><span class="err">cmn_schedule</span><span class="p">]</span><span class="w"> </span><span class="err">record</span><span class="w"> </span><span class="err">to</span><span class="w"> </span><span class="err">be</span><span class="w"> </span><span class="err">used</span><span class="w"> </span><span class="err">when</span><span class="w"> </span><span class="err">calculating</span><span class="w"> </span><span class="err">relative</span><span class="w"> </span><span class="err">due</span><span class="w"> </span><span class="err">dates</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<h3 id="scripted-due-date-example">Scripted Due Date Example</h3>

<p><img style="width: 100%; display: block !important; margin: auto;" src="/assets/images/2023-05-10-script-example.png" alt="Example Script Input" /></p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">gdt</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">GlideDateTime</span><span class="p">();</span>
<span class="nx">gdt</span><span class="p">.</span><span class="nx">addDaysLocalTime</span><span class="p">(</span><span class="mi">14</span><span class="p">);</span>
<span class="kd">var</span> <span class="nx">config</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">action</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">cancel</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// approve, cancel, reject</span>
    <span class="dl">"</span><span class="s2">date_type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">actual</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// actual, relative</span>
    <span class="dl">"</span><span class="s2">date</span><span class="dl">"</span><span class="p">:</span> <span class="nx">gdt</span><span class="p">.</span><span class="nx">getValue</span><span class="p">(),</span> <span class="c1">// Date/Time string used as the static date/time value, or as the starting date/time value for relative due dates.</span>
    <span class="c1">// The following can be ignored for static due date configurations</span>
    <span class="dl">"</span><span class="s2">duration</span><span class="dl">"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="c1">// The duration value (e.g. 1 minute, 1 hour, 1 day, etc.). ** Default value is 1 **</span>
    <span class="dl">"</span><span class="s2">duration_type</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">days</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// minutes, hours, days, weeks, months, quarters, years. ** Default value is days ** </span>
    <span class="dl">"</span><span class="s2">schedule</span><span class="dl">"</span><span class="p">:</span> <span class="dl">""</span><span class="p">,</span> <span class="c1">// Sys ID of the Schedule [cmn_schedule] record to be used when calculating relative due dates</span>
    <span class="dl">"</span><span class="s2">schedule_label</span><span class="dl">"</span><span class="p">:</span> <span class="dl">""</span> <span class="c1">// Label of the Schedule [cmn_schedule] record to be used when calculating relative due dates</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">config</span><span class="p">);</span>
</code></pre></div></div>

<p>While I am delighted to have solved this riddle, I can’t emphasize enough that to my knowledge all of this is completely undocumented, at least as of the <a href="https://docs.servicenow.com/bundle/utah-build-workflows/page/administer/flow-designer/reference/ask-approval-flow-designer.html">Utah release</a>, and is likely unrecommended as any instances of this action using a scripted input are subject to break in the event that the action is updated. I cannot guarantee that this will work for you or that it will continue to work. Now that I have seen how the action works I suspect that in the event you find yourself in a scenario like the one described above, the intended solution would be to use a flow variable or similar steps of scripting a dynamic date/time and provide that value via pill. <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>]]></content><author><name>Dan</name></author><category term="Flow Designer" /><summary type="html"><![CDATA[Something in Flow Designer that has always bothered me is that I’ve been unable to get scripted due dates to work in the Ask For Approval action. If you’re unfamiliar, due dates provide the ability to specify how long the flow should wait for approval, as well as what action should be taken on the approval when the specified due date arrives. This prevents a flow from waiting endlessly for an approval response.]]></summary></entry><entry><title type="html">GlideQuery Perks, Part 0.5: Resources</title><link href="https://glideguide.blog/2023/02/08/glidequery-perks-part-0.5.html" rel="alternate" type="text/html" title="GlideQuery Perks, Part 0.5: Resources" /><published>2023-02-08T00:00:00+00:00</published><updated>2023-02-08T00:00:00+00:00</updated><id>https://glideguide.blog/2023/02/08/glidequery-perks-part-0.5</id><content type="html" xml:base="https://glideguide.blog/2023/02/08/glidequery-perks-part-0.5.html"><![CDATA[<p><span class="lead">Before I start my GlideQuery series proper</span>, I’d be remiss if I didn’t take two steps back and recognize all the great content already out there on the topic. I certainly didn’t discover all the benefits of GlideQuery myself, though I do have direct experience with most of them. This article will be a complete rundown on every publicly available resource I can find.</p>

<p>Mind you, I’m not presenting any of the following as pre-requisites to my series. I won’t assume you’ve engaged with any of this material beforehand. But likewise, I’ll do my best not to just regurgitate what others have said. Though of necessity I’ll be covering a lot of the same ground, wherever possible I’ll try to give it my own unique spin and share my own original usage examples.</p>

<p>So feel free to engage with as much or as little of what follows, or ignore it for now and explore it later if the rest of my series piques your interest.</p>

<h2 id="official-documentation">Official documentation</h2>

<p>Of course, the best place to start is arguably the official documentation. There are Docs pages for <a href="https://docs.servicenow.com/bundle/xanadu-api-reference/page/app-store/dev_portal/API_reference/GlideQuery/concept/GlideQueryGlobalAPI.html">GlideQuery</a>, <a href="https://docs.servicenow.com/bundle/xanadu-api-reference/page/app-store/dev_portal/API_reference/Stream/concept/StreamGlobalAPI.html">Stream</a>, and <a href="https://docs.servicenow.com/bundle/xanadu-api-reference/page/app-store/dev_portal/API_reference/Optional/concept/OptionalGlobalAPI.html">Optional</a>. There are also mirrors of the same documentation on the Developer site (<a href="https://developer.servicenow.com/dev.do#!/reference/api/xanadu/server/no-namespace/GlideQueryGlobalAPI">GlideQuery</a>, <a href="https://developer.servicenow.com/dev.do#!/reference/api/xanadu/server/no-namespace/StreamGlobalAPI">Stream</a>, and <a href="https://developer.servicenow.com/dev.do#!/reference/api/xanadu/server/no-namespace/OptionalGlobalAPI">Optional</a>).</p>

<h2 id="content-featuring-peter-bell">Content featuring Peter Bell</h2>

<p>Peter Bell is the senior engineer within ServiceNow who created GlideQuery as a side project to help him with his normal day job on the <abbr>IT</abbr> Asset Management team. Naturally, his own content is very well worth checking out.</p>

<p>Bell’s original CreatorCon 2020 breakout session, <em><a href="https://www.servicenow.com/community/creatorcon-blogs/glidequery-a-modern-upgrade-to-gliderecord/ba-p/2331050">GlideQuery: A modern upgrade to GlideRecord (CCB3052)</a></em> (and accompanying <a href="/assets/files/2023-02-08-ccb3052-bell-glidequery.pdf">slide deck</a>), is, as far as I know, the first time GlideQuery was announced officially to the public. It’s a great introduction to the <abbr>API</abbr> and all of its benefits over GlideRecord.</p>

<p>From January to April 2021, Bell published a series of excellent blog posts explaining how to use GlideQuery. There’s a lot of overlap here with the official documentation, but, where the docs simply list methods alphabetically, these posts order the topics more pedagogically:</p>

<div class="column-left" style="width: 44%">
  <ul>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p1/">“GlideQuery Part 1”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p2/">“GlideQuery Part 2”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p3/">“GlideQuery Part 3”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p4/">“GlideQuery Part 4: Aggregates”</a></li>
  </ul>
</div>
<div class="column-right" style="width: 56%">
  <ul>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p5/">“GlideQuery Part 5: Dotwalking and Flags”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p6/">“GlideQuery: Stream Processing Part 1”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p7/">“GlideQuery: Stream Processing Part 2”</a></li>
    <li><a href="https://developer.servicenow.com/blog.do?p=/post/glidequery-p8/">“GlideQuery: Complex Queries”</a></li>
  </ul>
</div>

<p>In April 2021 Bell did an episode of <em>Creator Toolbox</em> on YouTube with Chuck Tomasi, Brad Tilton, and Andrew Barnes, <a href="https://www.youtube.com/live/IobUxnK3LDo">“GlideQuery”</a>. They work on refactoring some existing GlideRecord code to use GlideQuery instead, and along the way Bell shares a few interesting undocumented features.</p>

<p>In June 2021 Bell did another interview with Chuck Tomasi, this time on the <em>Breakpoint</em> podcast, <a href="https://developer.servicenow.com/blog.do?p=/post/break-point-025/">“GlideQuery with Peter Bell”</a>. This one is largely a review of the material in the original CreatorCon session, but he talks about some of the inspirations for GlideQuery and shares some useful info about the performance of GlideQuery compared to GlideRecord.</p>

<h2 id="community-resources">Community resources</h2>

<p>GlideQuery shipped in an early form as part of the <abbr>ITAM</abbr> suite in the Orlando release. A few people discovered it, but this post by Jace Benson is the only thing I can find that predates the official announcement: <a href="https://jace.pro/post/2020-04-28-what-is-glidequery/">“What is GlideQuery”</a>. After the official CreatorCon session came out (GlideQuery shipped officially in the Paris release), Benson followed his first post up with a full rundown of GlideQuery’s feature set: <a href="https://jace.pro/post/2020-05-24-glide-freaking-query-wow/">“Glide Freaking Query Wow”</a>.</p>

<p>Others have since posted cheat sheets for GlideQuery, such as Samuel Meylan’s <a href="https://www.snow-adventures.com/blog/glidequery-cheat-sheet/">“GlideQuery Cheat Sheet”</a>, Alberto Colombo’s <a href="https://web.archive.org/web/20231208033128/https://blog.kofko.xyz/glidequery-cheat-sheet">“GlideQuery Cheat Sheet”</a>, and Thiago Periera’s <a href="https://servicenowguru.com/scripting/glidequery-cheat-sheet/">“GlideQuery Cheat Sheet”</a>.</p>

<p>The above are the most important/useful bits of community content, but a number of other folks have blogged or published YouTube videos about GlideQuery (listing these in chronological order as far as dates could be ascertained):</p>

<ul>
  <li>Dhruv Gupta: <a href="https://dhruvsn.wordpress.com/2020/08/24/glidequery-not-gliderecord/">“GlideQuery Not GlideRecord”</a></li>
  <li>Lubos Strejcek: <a href="https://www.streyda.eu/post/gliderecordorglidequery">“GlideRecord or GlideQuery? That’s the question”</a></li>
  <li>John Skender: <a href="https://www.johnskender.com/articles/using-glidequery-to-check-if-a-single-record-exists">“GlideAggregate, GlideQuery or GlideRecord?”</a></li>
  <li>ServiceNow 911: <a href="https://www.youtube.com/watch?v=yY9YNe8nPfo">“GlideRecord vs. GlideQuery”</a></li>
  <li>Steven Bell: <a href="https://www.servicenow.com/community/developer-blog/glidequery-vs-gliderecord-a-comparison-part-1/ba-p/2267573">“GlideQuery vs. GlideRecord: A Comparison”, Part 1</a> and <a href="https://www.servicenow.com/community/developer-blog/glidequery-vs-gliderecord-a-comparison-part-2/ba-p/2268423">Part 2</a></li>
  <li>Ahmed Drar: <a href="https://ahmeddrar.me/2022/11/16/querying-with-glidequery/">Querying with GlideQuery</a></li>
  <li>ismr: <a href="https://web.archive.org/web/20230402062819/https://ismr.dev/posts/glidequery-main">“GlideQuery <abbr>API</abbr>”</a>, <a href="https://web.archive.org/web/20220717141747/https://ismr.dev/posts/glidequery-debug-post">“GlideQuery Debug Functions”</a>, and <a href="https://web.archive.org/web/20220607193820/https://ismr.dev/posts/glidequery-parse-post">“GlideQuery Parse()”</a></li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Well, there you have it. That’s every single resource I can find online about GlideQuery. Have you found some additional documentation or a blog post, YouTube video, TikTok, or some other kind of media that I missed? Find me in the ServiceNow Developers Slack workspace or on Mastodon and let me know. Next up in the series I’ll start to dive into GlideQuery’s many benefits. Stay tuned! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>

<p><em>Next in the series: <a href="/2024/08/18/glidequery-perks-part-1.html">Part 1: Unambiguous Boolean Logic →</a></em></p>]]></content><author><name>Joey</name></author><category term="GlideQuery" /><category term="GlideQuery Perks Series" /><summary type="html"><![CDATA[Before I start my GlideQuery series proper, I’d be remiss if I didn’t take two steps back and recognize all the great content already out there on the topic. I certainly didn’t discover all the benefits of GlideQuery myself, though I do have direct experience with most of them. This article will be a complete rundown on every publicly available resource I can find.]]></summary></entry><entry><title type="html">Friends Don’t Let Friends Use GlideAjax</title><link href="https://glideguide.blog/2023/02/02/friends-dont-let-friends-use-glideajax.html" rel="alternate" type="text/html" title="Friends Don’t Let Friends Use GlideAjax" /><published>2023-02-02T00:00:00+00:00</published><updated>2023-02-02T00:00:00+00:00</updated><id>https://glideguide.blog/2023/02/02/friends-dont-let-friends-use-glideajax</id><content type="html" xml:base="https://glideguide.blog/2023/02/02/friends-dont-let-friends-use-glideajax.html"><![CDATA[<p><span class="lead">I used to hate writing <a href="https://docs.servicenow.com/bundle/tokyo-application-development/page/app-store/dev_portal/API_reference/GlideAjax/concept/c_GlideAjaxAPI.html">GlideAjax</a> Script Includes</span>, but I haven’t written one in probably five years. How have I avoided it? Easy! Let me show you how to hit the <a href="https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/inbound-rest/concept/c_TableAPI.html">Table <abbr>API</abbr></a> in a Client Script using vanilla JavaScript.</p>

<p>The preferred way using modern JavaScript is with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch <abbr>API</abbr></a>. Here’s an example to pull the ten oldest Incident records:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">table</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">params</span> <span class="o">=</span> <span class="p">[</span>
  <span class="dl">'</span><span class="s1">sysparm_query=active=true^ORDERBYsys_created_on</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">sysparm_fields=number,short_description,description</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">sysparm_limit=10</span><span class="dl">'</span><span class="p">,</span>
  <span class="c1">// 'sysparm_display_value=all', // true for display values, all for both</span>
<span class="p">].</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">&amp;</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">const</span> <span class="nx">endpoint</span> <span class="o">=</span> <span class="s2">`/api/now/table/</span><span class="p">${</span><span class="nx">table</span><span class="p">}</span><span class="s2">?</span><span class="p">${</span><span class="nx">params</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>

<span class="nx">fetch</span><span class="p">(</span><span class="nx">endpoint</span><span class="p">,</span> <span class="p">{</span> <span class="na">headers</span><span class="p">:</span> <span class="p">{</span> <span class="dl">'</span><span class="s1">X-UserToken</span><span class="dl">'</span><span class="p">:</span> <span class="nx">g_ck</span> <span class="p">}</span> <span class="p">})</span> <span class="c1">// user auth token</span>
  <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span>
      <span class="k">throw</span> <span class="s2">`HTTP status </span><span class="p">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">status</span><span class="p">}</span><span class="s2"> on </span><span class="p">${</span><span class="nx">endpoint</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
      
    <span class="k">return</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
  <span class="p">})</span>
  <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">response</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
    <span class="c1">// Do stuff (e.g. number will be response.result[i].number)</span>
    <span class="c1">// ...</span>
  <span class="p">});</span>
</code></pre></div></div>

<p>I know what you’re gonna say, this looks like more code than a typical GlideAjax call. That actually depends on how many parameters you use and whether you need error handling, but keep in mind this is <em>all</em> the code. There’s no need for you to make any server-side component because the Table <abbr>API</abbr> does all the heavy lifting for you. One Client Script and you’re done.</p>

<p>Note, the <code class="language-plaintext highlighter-rouge">g_ck</code> variable is a client-side variable that stores the logged-in user’s session token. It’s the easiest way to authenticate the current user to the Table <abbr>API</abbr> so you don’t have to pass along any other credential. Be aware <abbr>ACL</abbr>s will be honored, so you may need to find another way to do this if your Client Script needs to fetch records the user can’t otherwise read.</p>

<p>Also note, the Fetch <abbr>API</abbr> has <a href="https://caniuse.com/?search=fetch">pretty broad support</a> (and don’t forget this is running in your end users’ browsers, so you’re not stuck on <abbr>ES</abbr>5 like you are most everywhere else in ServiceNow), but if your environment won’t allow it for whatever reason (say your company mandates Internet Explorer), here’s the same example using bog standard <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><abbr>XML</abbr>HttpRequest</a> instead:</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">table</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">params</span> <span class="o">=</span> <span class="p">[</span>
  <span class="dl">'</span><span class="s1">sysparm_query=active=true^ORDERBYsys_created_on</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">sysparm_fields=number,short_description,description</span><span class="dl">'</span><span class="p">,</span>
  <span class="dl">'</span><span class="s1">sysparm_limit=10</span><span class="dl">'</span><span class="p">,</span>
  <span class="c1">// 'sysparm_display_value=all', // true for display values, all for both</span>
<span class="p">];</span>

<span class="kd">var</span> <span class="nx">endpoint</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">/api/now/table/</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">table</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">?</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">params</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="s1">&amp;</span><span class="dl">'</span><span class="p">);</span>

<span class="kd">var</span> <span class="nx">xhr</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span> <span class="nb">encodeURI</span><span class="p">(</span><span class="nx">endpoint</span><span class="p">));</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="dl">'</span><span class="s1">X-UserToken</span><span class="dl">'</span><span class="p">,</span> <span class="nx">g_ck</span><span class="p">);</span> <span class="c1">// user auth token</span>
<span class="nx">xhr</span><span class="p">.</span><span class="nx">send</span><span class="p">();</span>

<span class="nx">xhr</span><span class="p">.</span><span class="nx">onreadystatechange</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">readyState</span> <span class="o">!==</span> <span class="nx">XMLHttpRequest</span><span class="p">.</span><span class="nx">DONE</span><span class="p">)</span>
    <span class="k">return</span><span class="p">;</span>
    
  <span class="k">if</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">&lt;</span> <span class="mi">200</span> <span class="o">||</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">&gt;=</span> <span class="mi">300</span><span class="p">)</span>
    <span class="k">throw</span> <span class="dl">'</span><span class="s1">HTTP status </span><span class="dl">'</span> <span class="o">+</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> on </span><span class="dl">'</span> <span class="o">+</span> <span class="nx">endpoint</span><span class="p">;</span>

  <span class="kd">var</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">responseText</span><span class="p">);</span>
  
  <span class="c1">// Do stuff (e.g. number will be response.result[i].number)</span>
  <span class="c1">// ...</span>
<span class="p">};</span>
</code></pre></div></div>

<p>Of course, you could refactor either of these examples to use any endpoint you like. Maybe you really need to crunch some data server-side and deliver it to the client in a way the Table <abbr>API</abbr> just can’t accommodate. You could create your own <a href="https://docs.servicenow.com/bundle/tokyo-application-development/page/integrate/custom-web-services/concept/c_CustomWebServices.html">Scripted <abbr>REST</abbr> <abbr>API</abbr></a> endpoint and ping that from the client instead. The world is your oyster, but please, and I say this as a friend, never use GlideAjax again. You’re welcome! <img src="/assets/images/endmark.png" width="14.4" height="14.4" style="width: 0.9em; height: 0.9em; vertical-align: -0.2em;" /></p>]]></content><author><name>Joey</name></author><category term="GlideaAax" /><category term="XMLHttpRequest" /><category term="REST" /><category term="Fetch" /><summary type="html"><![CDATA[I used to hate writing GlideAjax Script Includes, but I haven’t written one in probably five years. How have I avoided it? Easy! Let me show you how to hit the Table API in a Client Script using vanilla JavaScript.]]></summary></entry></feed>