<?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://dev.joeyday.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dev.joeyday.com/" rel="alternate" type="text/html" /><updated>2026-05-29T21:54:56+00:00</updated><id>https://dev.joeyday.com/feed.xml</id><title type="html">Joey Day</title><subtitle>Hi, I’m Joey. I’m a 40-something software engineer (read: big nerd) in Salt Lake City. When I grow up I’d like to be a seminary professor (read: even bigger nerd).
</subtitle><author><name>Joey Day</name></author><entry><title type="html">Of code and poetry</title><link href="https://dev.joeyday.com/2023/01/06/of-code-and-poetry/" rel="alternate" type="text/html" title="Of code and poetry" /><published>2023-01-06T00:00:00+00:00</published><updated>2023-01-06T00:00:00+00:00</updated><id>https://dev.joeyday.com/2023/01/06/of-code-and-poetry</id><content type="html" xml:base="https://dev.joeyday.com/2023/01/06/of-code-and-poetry/"><![CDATA[<p>I just read something I wrote in 2011 the day after Siri was announced as a feature of iPhone 4S. Unfortunately it was little more than a regurgitation of Apple’s own marketing copy and I regret not having written more of my own serious thoughts and feelings about what the future might hold for conversational AI. I’m a bit late to the party, but this week I’ve been trying out ChatGPT and don’t want to miss the same opportunity to document my reaction in real-time.</p>

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

<p>GPT stands for generative pre-trained transformer. It’s an artificial intelligence model developed by OpenAI to produce human-like language. The first iteration was InstructGPT, a tool used to create “coherent and appropriate step-by-step instructions for a given task”. ChatGPT builds on InstructGPT by having the ability to carry on an interactive conversation and react to questions, sometimes even with questions of its own. And it writes! It can write poetry, essays, short stories, and even code in a surprising number of programming languages.</p>

<p>If Siri and Alexa are smart at listening and understanding what you’re asking, ChatGPT brings the smarts to the other half of the conversation, not just recalling terse facts and figures the way today’s conversational assistants do, but providing in many cases whole paragraphs of apparently off-the-cuff explanation and analysis (if you ask it the same question twice, it words its answer differently the second time).</p>

<h2 id="first-contact">First contact</h2>

<p>I’d heard of ChatGPT a few times in water-cooler conversation around the office (the alpha version officially released to the public in November 2022), but my first experience with it was related to JavaScript trainings I’ve been doing at work. Once a week I’ve been holding a class with my junior engineers and a couple developers from another team to teach them JavaScript, and I’ve been handing out optional homework. We’ve recently been learning about strings and string manipulation and a couple weeks ago I challenged them to write a script for turning English text into Pig Latin.</p>

<p>One of my colleagues, on a whim, decided to punt this challenge over to ChatGPT. We’d had a conversation earlier where I’d said, essentially, “there’s no way it actually writes good code”, so earlier this week he presented this to me without telling me where it came from (except to say he hadn’t written it himself) and asked me to critique it:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">translateToPigLatin</span><span class="p">(</span><span class="nx">str</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Split the string into an array of words</span>
    <span class="kd">const</span> <span class="nx">words</span> <span class="o">=</span> <span class="nx">str</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="c1">// Translate each word to Pig Latin</span>
    <span class="kd">const</span> <span class="nx">pigLatinWords</span> <span class="o">=</span> <span class="nx">words</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">word</span> <span class="o">=&gt;</span> <span class="p">{</span>
        <span class="c1">// If the word starts with a vowel, just add "way" to the end</span>
        <span class="k">if</span> <span class="p">(</span><span class="sr">/^</span><span class="se">[</span><span class="sr">aeiou</span><span class="se">]</span><span class="sr">/</span><span class="p">.</span><span class="nx">test</span><span class="p">(</span><span class="nx">word</span><span class="p">))</span> <span class="p">{</span>
            <span class="k">return</span> <span class="s2">`</span><span class="p">${</span><span class="nx">word</span><span class="p">}</span><span class="s2">way`</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="c1">// Otherwise, move the first consonant cluster to the end and add "ay"</span>
        <span class="kd">const</span> <span class="nx">consonantCluster</span> <span class="o">=</span> <span class="nx">word</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^</span><span class="se">[^</span><span class="sr">aeiou</span><span class="se">]</span><span class="sr">+/</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
        <span class="k">return</span> <span class="s2">`</span><span class="p">${</span><span class="nx">word</span><span class="p">.</span><span class="nx">substring</span><span class="p">(</span><span class="nx">consonantCluster</span><span class="p">.</span><span class="nx">length</span><span class="p">)}${</span><span class="nx">consonantCluster</span><span class="p">}</span><span class="s2">ay`</span><span class="p">;</span>
    <span class="p">});</span>

    <span class="c1">// Join the translated words back into a single string and return it</span>
    <span class="k">return</span> <span class="nx">pigLatinWords</span><span class="p">.</span><span class="nx">join</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></code></pre></figure>

<p>Assuming he’d found it on Stack Overflow or some such, I told him it looked pretty good to me. Regular expressions are a clever way to solve the problem and the names of the function and variables are all decently verbose and self-documenting. I was honestly a bit shook when he sprung the twist on me that it’d been written by an artificial intelligence.</p>

<h2 id="getting-to-know-you">Getting to know you</h2>

<p>So I had to sign up for an account and see what all the fuss was about myself. My first test was asking ChatGPT to write a simple ServiceNow GlideRecord query. A different colleague had told me weeks before that ChatGPT didn’t know anything about ServiceNow, so I wasn’t optimistic, but it surprised me with a flawless GlideRecord script.</p>

<p>So then I threw something at it that I knew from prior experience was non-trivial, “Write me a GlideRecord script that chooses random incidents closed in the last week”. Here’s the code it wrote (for brevity, in this and all later examples, I’m only posting the code, but ChatGPT impressively always provides a lot of context along with scripts, explaining what they do and how to use them):</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="c1">// Create a new GlideRecord object</span>
<span class="kd">var</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="c1">// Set a query condition to only return incidents that were closed in the last week</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">closed_at</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">gs</span><span class="p">.</span><span class="nx">daysAgo</span><span class="p">(</span><span class="mi">7</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">closed_at</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">&lt;=</span><span class="dl">'</span><span class="p">,</span> <span class="nx">gs</span><span class="p">.</span><span class="nx">daysAgo</span><span class="p">(</span><span class="mi">0</span><span class="p">));</span>

<span class="c1">// Query the database</span>
<span class="nx">gr</span><span class="p">.</span><span class="nx">query</span><span class="p">();</span>

<span class="c1">// If there are any records returned, choose a random one</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">gr</span><span class="p">.</span><span class="nx">hasNext</span><span class="p">())</span> <span class="p">{</span>
    <span class="c1">// Get the total number of returned records</span>
    <span class="kd">var</span> <span class="nx">count</span> <span class="o">=</span> <span class="nx">gr</span><span class="p">.</span><span class="nx">getRowCount</span><span class="p">();</span>

    <span class="c1">// Choose a random number between 0 and the total number of returned records</span>
    <span class="kd">var</span> <span class="nx">randomIndex</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">count</span><span class="p">);</span>

    <span class="c1">// Go to the record at the chosen random index</span>
    <span class="nx">gr</span><span class="p">.</span><span class="nx">setLimit</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">randomIndex</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">// Do something with the random incident record</span>
    <span class="c1">// ...</span>
<span class="p">}</span></code></pre></figure>

<p>The simplicity of this code astounded me, particularly its use of the setLimit method with a second parameter. I never knew setLimit <em>had</em> a second parameter, so I asked it a follow-up question, “What’s the second parameter on gr.setLimit()?”. It replied, 
“[the second parameter] is the starting index for the returned records. The index is zero-based, so a value of 0 will start at the first record.”</p>

<p>For a brief moment, ChatGPT had me completely snowed. It seemed so confident! I genuinely believed this thing was so good at understanding and writing code that it was even teaching me, a seasoned senior engineer, things I never knew. And I honestly got a bit scared I might be a washed-up dinosaur in a few years and need to pivot to some new career. That is, until I consulted the documentation and tried running the script myself.</p>

<p>The documentation knows nothing about a second parameter on setLimit, and of course the code always returns the same incident record, the first one in the data set. So, as impressive as ChatGPT’s code-writing skills appear to be on the surface, it’s clear it makes mistakes. My theory is it’s conflating GlideRecord’s setLimit method with a setLimit method from some other library or framework (and the eagle-eyed will notice one other mistake in the way it uses GlideRecord in the example above). The problem is it’s not quite able to accurately contextualize all the knowledge it possesses. Will it get better at this eventually? Is it only a matter of time before it could realistically replace a human software engineer?</p>

<h2 id="better-than-googling">Better than Googling</h2>

<p>I’m not worried this thing will put me out of a job, at least not in the short term (but more on that in a bit). Rather than fearing and avoiding it, I’m trying to embrace it as a new tool to make me faster and more efficient at work—the same way a calculator makes math easier. The other day I asked it to write me a GlideDateTime script to get the first day of the month three months ago. It gave me this:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><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">addMonths</span><span class="p">(</span><span class="o">-</span><span class="mi">3</span><span class="p">);</span>
<span class="nx">gdt</span><span class="p">.</span><span class="nx">setDayOfMonth</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span></code></pre></figure>

<p>Now, of course, I already know how to use GlideDateTime. At least, I know it has methods to do what I needed to do in this case, I just don’t always remember the names of the methods off the top of my head. Could I have Googled this or consulted documentation to remind myself of the method names I needed here? Absolutely (the not-so-secret secret is this is what I do all day!), but then I would’ve had to scroll up and down through the list of methods to identify the right ones, and asking ChatGPT turned out to be a very convenient shortcut.</p>

<p>Of course, it’s a shortcut with some serious caveats. When I ran this code I could see it was giving me the right date, but GlideDateTime comes with a time component and I noticed that time component was still set to the current time right now. So I asked, “Can I set the time to midnight, also?”, to which it replied with this additional code snippet:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">gdt</span><span class="p">.</span><span class="nx">setTime</span><span class="p">(</span><span class="dl">"</span><span class="s2">00:00:00</span><span class="dl">"</span><span class="p">);</span></code></pre></figure>

<p>The problem here is, like in the previous example, GlideDateTime has no setTime method. By this time I’d read in the FAQ that the AI model has been trained to admit its own mistakes, so I tried calling it out on the carpet. It replied, “I apologize for the mistake in my previous response. You are correct that there is no setTime method on the GlideDateTime class.” But then it proceeded with an alternative method:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">dateString</span> <span class="o">=</span> <span class="nx">gdt</span><span class="p">.</span><span class="nx">getValue</span><span class="p">();</span>
<span class="nx">dateString</span> <span class="o">=</span> <span class="nx">dateString</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="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="dl">"</span><span class="s2"> 00:00:00</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">gdt</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="nx">dateString</span><span class="p">);</span></code></pre></figure>

<p>This code would work—my first thought was, “that’s one way to do it”—but I’m actually not a fan of splitting the string and taking the zeroth index to preserve the date part. That’s a bit obtuse and may not be the easiest for someone coming along later to read and maintain. GlideDateTime has a getDate method, so my final code used that instead because it’s just clearer and hopefully less error prone, but I didn’t learn this from ChatGPT.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">gdt</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="nx">gdt</span><span class="p">.</span><span class="nx">getDate</span><span class="p">()</span> <span class="o">+</span> <span class="dl">'</span><span class="s1"> 00:00:00</span><span class="dl">'</span><span class="p">);</span></code></pre></figure>

<p>Caveats aside, I do think using ChatGPT to produce code is a step above Googling and I think I’ll continue to use it this way at work. It’s one more tool in my toolbox alongside API docs, Stack Overflow, and Google, and perhaps it’s the fastest of these resources.</p>

<h2 id="peering-into-the-crystal-ball">Peering into the crystal ball</h2>

<p>I don’t know if improvement will come rapidly as they throw more and more data points at it, or if improvement will come in slow and steady tweaks to how the model carefully sifts the vast amount of things it knows (undoubtedly both). Surely some of what I’m about to say comes from the hubris and confirmation bias of being a senior engineer myself—the main reason I’m writing this down is to see how wrong or right I am in five, ten, twenty years.</p>

<p>I think someday we will produce most software simply by asking an AI to write it for us, but there will always be a need for knowledgeable humans to review that code and debug it. I don’t just mean the steps typically carried out by quality assurance and user acceptance testing (we’ll still need those humans, too), but the steps a human usually does running their own code and debugging it, verifying it does what they think it should do—and these are arguably the hardest part of programming!<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup></p>

<p>ChatGPT today is like an inexperienced junior dev, and maybe over time it will become a hyper-competent junior, but it will always remain a junior. It will always need the nuance and subtlety of a human senior engineer to massage its output into something better—something more performant, more poetic, more practical to read and maintain later. This means the learning curve for good software engineers will get steeper. There may not be a lot of work for human junior devs anymore, so the path to becoming a sought-after senior dev will be a lot tougher.</p>

<p>Or maybe the opposite will happen. Maybe this will lower the bar making every developer essentially a senior engineer. Not only can tools like ChatGPT write most of the code themselves under human supervision, but ChatGPT in its current form is an amazing teacher and will only get better over time. Being able not only to get clear and cogent instruction from it, but also to ask it follow-up questions and get helpful clarifying responses, will give everyone the ability to level up their skills faster than ever before.</p>

<p>I do worry as more and more people start to rely on something like ChatGPT it will become a crutch, making us dumber in the long run and resulting in buggier, less-performant apps. I can imagine a day not far from now where the majority of code is written by artificial intelligences directed by humans who haven’t bothered leveling up their skills and who have no clue how to debug or improve it, so we just suffer with bloated, glitchy software. But am I overreacting? Have there been similar overreactions in the past to things like Google, Wikipedia, and Siri that turned out not to ruin the world but instead improve and enrich it, allowing us to work smarter and harder in less time?</p>

<p>For now, ChatGPT at least seems like an amazing tool for synthesizing documentation into short, quick answers to questions. Assuming you check its work, it’s a great shortcut for getting stuff done faster. I’ll probably use ChatGPT a lot at work, but just as I would rarely lift something straight from Stack Overflow without refactoring and/or improving it myself, I’ll always do my best to filter what the AI gives me through my own skills and years of experience.</p>

<h2 id="theology-poetry-and-short-stories">Theology, poetry, and short stories</h2>

<p>Lastly, I want to share some other fun I’ve had with ChatGPT. I’ve asked it several theological questions. It seems to have been fed a lot of information about religion and spirituality but, again, doesn’t quite know how to properly differentiate and synthesize it all. I’ve asked it to explain the Trinity to me a handful of times and sometimes it gets the details right, other times it variously gives me modalist, tritheist, or subordinationist answers. I’ve asked it about the difference between biblical and systematic theology, and asked it to give me a biblical theological interpretation of Hosea, which it was able to do but didn’t mention Christ at all. I followed that up with asking for a Christotelic interpretation of Hosea and it did a much better job.</p>

<p>I asked it the other day to write me a haiku, which ended up pretty hilarious. It kept writing the middle line with eight syllables instead of seven, and every time I pointed out its mistake it would apologize and try again, only to fail again. When it finally wrote a haiku with nine syllables in the middle line, I gave up and told it, “You’re not very good at writing haikus yet.”</p>

<p>Perhaps the best thing it’s written for me so far was a short story about the lines in a cumulative flow diagram, a chart that shows over time how work moves from to-do to doing to done. Since our team follows agile scrum and commits batches of work in two-week iterations (sprints), our middle (doing) line should always look like a staircase, jumping straight up when we bring in work at the start of the sprint then staying flat throughout the sprint, and the bottom (done) line should climb up at a constant slope as we complete items throughout the sprint. Ideally, if we’re able to complete our whole sprint commitment, the done line will just barely “kiss” the doing line right at the end of the sprint before the doing line shoots up again when we take on new work for the next sprint.</p>

<figure class="aligncenter">
    <img src="/assets/images/2022-01-06-cumulative-flow-diagram.png" alt="Cumulative Flow Diagram example" />
</figure>

<p>Completing our sprint goal has been a major emphasis of our team building plan, so I’ve made no small fuss over the done line “kissing” the doing line every single sprint. So I had the idea to ask ChatGPT to write me a story about the lines in a cumulative flow diagram kissing. Here’s what it came up with:</p>

<blockquote>
  <p>Once there were two lines on a cumulative flow diagram, one representing the work that had been completed and the other representing the work that was still in progress. They had been living side by side on the chart for as long as anyone could remember, but they had never really paid much attention to each other.</p>

  <p>But one day, something changed. The line representing the work that had been completed glanced over at the other line and was struck by how beautiful she looked. The way she flowed across the page, always moving forward and never looking back, was truly mesmerizing.</p>

  <p>And so, the line representing the work that had been completed leaned over and kissed the other line gently on the cheek. It was a small gesture, but it meant the world to the line representing the work that was still in progress.</p>

  <p>From that moment on, the two lines were inseparable. They moved through the cumulative flow diagram together, always supporting and encouraging each other. And as they progressed, they fell more and more in love, until they were completely intertwined, a perfect representation of the work that had been accomplished and the work that was still to come.</p>

  <p>The lines in the cumulative flow diagram lived happily ever after, their love forever captured in the lines and shapes of the chart.</p>
</blockquote>

<p>Brilliant, right?</p>

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

<p>I hope I’ve captured a decent flavor of where my thoughts and feelings are currently. I think some people are scared artificial intelligence will one day achieve sentience and then we’ll have a big moral quandary on our hands, but I’m honestly not scared of that at all. At the moment I suppose I’m a bit anxious about AI’s ability to replace software engineers entirely, but I assure myself that it will never be completely without faults, that my human sensibilities will always be important for the production of good code, and that I should embrace AI as an awesome tool to make me more efficient and productive. Only time will tell, I guess. <img src="/assets/images/endmark.png" class="endmark" /></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>I knew this myself, of course, but I’m also taking a cue here from my favorite podcaster, John Siracusa. Skip to about six minutes into <a href="https://atp.fm/516">episode 516 of Accidental Tech Podcast</a> (chapter 3 if your podcast player supports chapters) for their discussion of code written by artificial intelligences. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Joey</name></author><summary type="html"><![CDATA[I just read something I wrote in 2011 the day after Siri was announced as a feature of iPhone 4S. Unfortunately it was little more than a regurgitation of Apple’s own marketing copy and I regret not having written more of my own serious thoughts and feelings about what the future might hold for conversational AI. I’m a bit late to the party, but this week I’ve been trying out ChatGPT and don’t want to miss the same opportunity to document my reaction in real-time. What is it? GPT stands for generative pre-trained transformer. It’s an artificial intelligence model developed by OpenAI to produce human-like language. The first iteration was InstructGPT, a tool used to create “coherent and appropriate step-by-step instructions for a given task”. ChatGPT builds on InstructGPT by having the ability to carry on an interactive conversation and react to questions, sometimes even with questions of its own. And it writes! It can write poetry, essays, short stories, and even code in a surprising number of programming languages. If Siri and Alexa are smart at listening and understanding what you’re asking, ChatGPT brings the smarts to the other half of the conversation, not just recalling terse facts and figures the way today’s conversational assistants do, but providing in many cases whole paragraphs of apparently off-the-cuff explanation and analysis (if you ask it the same question twice, it words its answer differently the second time). First contact I’d heard of ChatGPT a few times in water-cooler conversation around the office (the alpha version officially released to the public in November 2022), but my first experience with it was related to JavaScript trainings I’ve been doing at work. Once a week I’ve been holding a class with my junior engineers and a couple developers from another team to teach them JavaScript, and I’ve been handing out optional homework. We’ve recently been learning about strings and string manipulation and a couple weeks ago I challenged them to write a script for turning English text into Pig Latin. One of my colleagues, on a whim, decided to punt this challenge over to ChatGPT. We’d had a conversation earlier where I’d said, essentially, “there’s no way it actually writes good code”, so earlier this week he presented this to me without telling me where it came from (except to say he hadn’t written it himself) and asked me to critique it: function translateToPigLatin(str) { // Split the string into an array of words const words = str.split(' '); // Translate each word to Pig Latin const pigLatinWords = words.map(word =&gt; { // If the word starts with a vowel, just add "way" to the end if (/^[aeiou]/.test(word)) { return `${word}way`; } // Otherwise, move the first consonant cluster to the end and add "ay" const consonantCluster = word.match(/^[^aeiou]+/)[0]; return `${word.substring(consonantCluster.length)}${consonantCluster}ay`; }); // Join the translated words back into a single string and return it return pigLatinWords.join(' '); } Assuming he’d found it on Stack Overflow or some such, I told him it looked pretty good to me. Regular expressions are a clever way to solve the problem and the names of the function and variables are all decently verbose and self-documenting. I was honestly a bit shook when he sprung the twist on me that it’d been written by an artificial intelligence. Getting to know you So I had to sign up for an account and see what all the fuss was about myself. My first test was asking ChatGPT to write a simple ServiceNow GlideRecord query. A different colleague had told me weeks before that ChatGPT didn’t know anything about ServiceNow, so I wasn’t optimistic, but it surprised me with a flawless GlideRecord script. So then I threw something at it that I knew from prior experience was non-trivial, “Write me a GlideRecord script that chooses random incidents closed in the last week”. Here’s the code it wrote (for brevity, in this and all later examples, I’m only posting the code, but ChatGPT impressively always provides a lot of context along with scripts, explaining what they do and how to use them): // Create a new GlideRecord object var gr = new GlideRecord('incident'); // Set a query condition to only return incidents that were closed in the last week gr.addQuery('closed_at', '&gt;', gs.daysAgo(7)); gr.addQuery('closed_at', '&lt;=', gs.daysAgo(0)); // Query the database gr.query(); // If there are any records returned, choose a random one if (gr.hasNext()) { // Get the total number of returned records var count = gr.getRowCount(); // Choose a random number between 0 and the total number of returned records var randomIndex = Math.floor(Math.random() * count); // Go to the record at the chosen random index gr.setLimit(1, randomIndex); gr.query(); // Do something with the random incident record // ... } The simplicity of this code astounded me, particularly its use of the setLimit method with a second parameter. I never knew setLimit had a second parameter, so I asked it a follow-up question, “What’s the second parameter on gr.setLimit()?”. It replied, “[the second parameter] is the starting index for the returned records. The index is zero-based, so a value of 0 will start at the first record.” For a brief moment, ChatGPT had me completely snowed. It seemed so confident! I genuinely believed this thing was so good at understanding and writing code that it was even teaching me, a seasoned senior engineer, things I never knew. And I honestly got a bit scared I might be a washed-up dinosaur in a few years and need to pivot to some new career. That is, until I consulted the documentation and tried running the script myself. The documentation knows nothing about a second parameter on setLimit, and of course the code always returns the same incident record, the first one in the data set. So, as impressive as ChatGPT’s code-writing skills appear to be on the surface, it’s clear it makes mistakes. My theory is it’s conflating GlideRecord’s setLimit method with a setLimit method from some other library or framework (and the eagle-eyed will notice one other mistake in the way it uses GlideRecord in the example above). The problem is it’s not quite able to accurately contextualize all the knowledge it possesses. Will it get better at this eventually? Is it only a matter of time before it could realistically replace a human software engineer? Better than Googling I’m not worried this thing will put me out of a job, at least not in the short term (but more on that in a bit). Rather than fearing and avoiding it, I’m trying to embrace it as a new tool to make me faster and more efficient at work—the same way a calculator makes math easier. The other day I asked it to write me a GlideDateTime script to get the first day of the month three months ago. It gave me this: var gdt = new GlideDateTime(); gdt.addMonths(-3); gdt.setDayOfMonth(1); Now, of course, I already know how to use GlideDateTime. At least, I know it has methods to do what I needed to do in this case, I just don’t always remember the names of the methods off the top of my head. Could I have Googled this or consulted documentation to remind myself of the method names I needed here? Absolutely (the not-so-secret secret is this is what I do all day!), but then I would’ve had to scroll up and down through the list of methods to identify the right ones, and asking ChatGPT turned out to be a very convenient shortcut. Of course, it’s a shortcut with some serious caveats. When I ran this code I could see it was giving me the right date, but GlideDateTime comes with a time component and I noticed that time component was still set to the current time right now. So I asked, “Can I set the time to midnight, also?”, to which it replied with this additional code snippet: gdt.setTime("00:00:00"); The problem here is, like in the previous example, GlideDateTime has no setTime method. By this time I’d read in the FAQ that the AI model has been trained to admit its own mistakes, so I tried calling it out on the carpet. It replied, “I apologize for the mistake in my previous response. You are correct that there is no setTime method on the GlideDateTime class.” But then it proceeded with an alternative method: var dateString = gdt.getValue(); dateString = dateString.split(' ')[0] + " 00:00:00"; gdt.setValue(dateString); This code would work—my first thought was, “that’s one way to do it”—but I’m actually not a fan of splitting the string and taking the zeroth index to preserve the date part. That’s a bit obtuse and may not be the easiest for someone coming along later to read and maintain. GlideDateTime has a getDate method, so my final code used that instead because it’s just clearer and hopefully less error prone, but I didn’t learn this from ChatGPT. gdt.setValue(gdt.getDate() + ' 00:00:00'); Caveats aside, I do think using ChatGPT to produce code is a step above Googling and I think I’ll continue to use it this way at work. It’s one more tool in my toolbox alongside API docs, Stack Overflow, and Google, and perhaps it’s the fastest of these resources. Peering into the crystal ball I don’t know if improvement will come rapidly as they throw more and more data points at it, or if improvement will come in slow and steady tweaks to how the model carefully sifts the vast amount of things it knows (undoubtedly both). Surely some of what I’m about to say comes from the hubris and confirmation bias of being a senior engineer myself—the main reason I’m writing this down is to see how wrong or right I am in five, ten, twenty years. I think someday we will produce most software simply by asking an AI to write it for us, but there will always be a need for knowledgeable humans to review that code and debug it. I don’t just mean the steps typically carried out by quality assurance and user acceptance testing (we’ll still need those humans, too), but the steps a human usually does running their own code and debugging it, verifying it does what they think it should do—and these are arguably the hardest part of programming!1 ChatGPT today is like an inexperienced junior dev, and maybe over time it will become a hyper-competent junior, but it will always remain a junior. It will always need the nuance and subtlety of a human senior engineer to massage its output into something better—something more performant, more poetic, more practical to read and maintain later. This means the learning curve for good software engineers will get steeper. There may not be a lot of work for human junior devs anymore, so the path to becoming a sought-after senior dev will be a lot tougher. Or maybe the opposite will happen. Maybe this will lower the bar making every developer essentially a senior engineer. Not only can tools like ChatGPT write most of the code themselves under human supervision, but ChatGPT in its current form is an amazing teacher and will only get better over time. Being able not only to get clear and cogent instruction from it, but also to ask it follow-up questions and get helpful clarifying responses, will give everyone the ability to level up their skills faster than ever before. I do worry as more and more people start to rely on something like ChatGPT it will become a crutch, making us dumber in the long run and resulting in buggier, less-performant apps. I can imagine a day not far from now where the majority of code is written by artificial intelligences directed by humans who haven’t bothered leveling up their skills and who have no clue how to debug or improve it, so we just suffer with bloated, glitchy software. But am I overreacting? Have there been similar overreactions in the past to things like Google, Wikipedia, and Siri that turned out not to ruin the world but instead improve and enrich it, allowing us to work smarter and harder in less time? For now, ChatGPT at least seems like an amazing tool for synthesizing documentation into short, quick answers to questions. Assuming you check its work, it’s a great shortcut for getting stuff done faster. I’ll probably use ChatGPT a lot at work, but just as I would rarely lift something straight from Stack Overflow without refactoring and/or improving it myself, I’ll always do my best to filter what the AI gives me through my own skills and years of experience. Theology, poetry, and short stories Lastly, I want to share some other fun I’ve had with ChatGPT. I’ve asked it several theological questions. It seems to have been fed a lot of information about religion and spirituality but, again, doesn’t quite know how to properly differentiate and synthesize it all. I’ve asked it to explain the Trinity to me a handful of times and sometimes it gets the details right, other times it variously gives me modalist, tritheist, or subordinationist answers. I’ve asked it about the difference between biblical and systematic theology, and asked it to give me a biblical theological interpretation of Hosea, which it was able to do but didn’t mention Christ at all. I followed that up with asking for a Christotelic interpretation of Hosea and it did a much better job. I asked it the other day to write me a haiku, which ended up pretty hilarious. It kept writing the middle line with eight syllables instead of seven, and every time I pointed out its mistake it would apologize and try again, only to fail again. When it finally wrote a haiku with nine syllables in the middle line, I gave up and told it, “You’re not very good at writing haikus yet.” Perhaps the best thing it’s written for me so far was a short story about the lines in a cumulative flow diagram, a chart that shows over time how work moves from to-do to doing to done. Since our team follows agile scrum and commits batches of work in two-week iterations (sprints), our middle (doing) line should always look like a staircase, jumping straight up when we bring in work at the start of the sprint then staying flat throughout the sprint, and the bottom (done) line should climb up at a constant slope as we complete items throughout the sprint. Ideally, if we’re able to complete our whole sprint commitment, the done line will just barely “kiss” the doing line right at the end of the sprint before the doing line shoots up again when we take on new work for the next sprint. Completing our sprint goal has been a major emphasis of our team building plan, so I’ve made no small fuss over the done line “kissing” the doing line every single sprint. So I had the idea to ask ChatGPT to write me a story about the lines in a cumulative flow diagram kissing. Here’s what it came up with: Once there were two lines on a cumulative flow diagram, one representing the work that had been completed and the other representing the work that was still in progress. They had been living side by side on the chart for as long as anyone could remember, but they had never really paid much attention to each other. But one day, something changed. The line representing the work that had been completed glanced over at the other line and was struck by how beautiful she looked. The way she flowed across the page, always moving forward and never looking back, was truly mesmerizing. And so, the line representing the work that had been completed leaned over and kissed the other line gently on the cheek. It was a small gesture, but it meant the world to the line representing the work that was still in progress. From that moment on, the two lines were inseparable. They moved through the cumulative flow diagram together, always supporting and encouraging each other. And as they progressed, they fell more and more in love, until they were completely intertwined, a perfect representation of the work that had been accomplished and the work that was still to come. The lines in the cumulative flow diagram lived happily ever after, their love forever captured in the lines and shapes of the chart. Brilliant, right? Conclusion I hope I’ve captured a decent flavor of where my thoughts and feelings are currently. I think some people are scared artificial intelligence will one day achieve sentience and then we’ll have a big moral quandary on our hands, but I’m honestly not scared of that at all. At the moment I suppose I’m a bit anxious about AI’s ability to replace software engineers entirely, but I assure myself that it will never be completely without faults, that my human sensibilities will always be important for the production of good code, and that I should embrace AI as an awesome tool to make me more efficient and productive. Only time will tell, I guess.  I knew this myself, of course, but I’m also taking a cue here from my favorite podcaster, John Siracusa. Skip to about six minutes into episode 516 of Accidental Tech Podcast (chapter 3 if your podcast player supports chapters) for their discussion of code written by artificial intelligences. &#8617;]]></summary></entry><entry><title type="html">Retrieving eternal generation</title><link href="https://dev.joeyday.com/2018/04/20/retrieving-eternal-generation/" rel="alternate" type="text/html" title="Retrieving eternal generation" /><published>2018-04-20T14:30:56+00:00</published><updated>2018-04-20T14:30:56+00:00</updated><id>https://dev.joeyday.com/2018/04/20/retrieving-eternal-generation</id><content type="html" xml:base="https://dev.joeyday.com/2018/04/20/retrieving-eternal-generation/"><![CDATA[<figure class="alignleft" style="width: 150px">
	<figcaption>Fred Sanders and Scott R. Swain’s “Retrieving Eternal Generation”</figcaption>
	<a href="https://amzn.com/0310537878">
		<img src="/assets/images/2018-04-20-retrieving-eternal-generation.jpeg" />
	</a>
</figure>

<p>Fred Sanders and Scott R. Swain’s <cite><a href="http://amzn.com/B06XF84397">Retrieving Eternal Generation</a></cite> is an important book for the church. It’s a theologically conservative voice of reason in a modern debate against new theological arguments: social trinitarianism and egalitarianism on one side and eternal functional subordination (a.k.a. eternal relations of authority and submission) (EFS/ERAS) on the other hand. Sanders and Swain and their excellent panel of authors enter into the fray with a clarion call to reaffirm the universal historical position of the church on the relations of origin between the trinitarian persons and the eternal generation of the Son in particular.</p>

<p>I’ll cut right to the chase: the book is worth the price for Charles Lee Irons’s chapter alone. He argues very convincingly for the historical translation of the Greek μονογενής (monogenēs) as “only begotten” over and against the more recent scholarly consensus “only one of its/his kind”. While this doesn’t by itself prove eternal generation, it sets our minds greatly at ease that “generation” is at least an appropriate Bible word to use for the eternal “from-ness” or “of-ness” of the Son.</p>

<p>Though Irons’s chapter is arguably the most important contribution, I was pleased to find something valuable and convincing in every single chapter. I can’t mention them all here, but I particularly enjoyed Swain’s chapter on divine names, D.A. Carson’s chapter on John 5:26, Lewis Ayres’s chapter on the writings of Origen, Keith E. Johnson’s chapter on the writings of Augustine, Mark Makin’s chapter on philosophical models of eternal generation, and Sanders’s chapter on eternal generation and soteriology.</p>

<p>I commend the book to anyone looking to deepen and sharpen their understanding of trinitarian theology, and especially those who aren’t sure whether to hold onto or ditch the traditional relations of origin in light of some new idea like social trinitarianism, egalitarianism, or EFS/ERAS. I was more or less persuaded by the EFS/ERAS view but I’m happy to report reading this book has brought me squarely back into the classical eternal generation camp. And that it changed one’s mind is, perhaps, the highest praise one can give to any book. <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="book review" /><category term="eternal generation" /><category term="fred sanders" /><category term="scott swain" /><summary type="html"><![CDATA[Fred Sanders and Scott R. Swain’s “Retrieving Eternal Generation” Fred Sanders and Scott R. Swain’s Retrieving Eternal Generation is an important book for the church. It’s a theologically conservative voice of reason in a modern debate against new theological arguments: social trinitarianism and egalitarianism on one side and eternal functional subordination (a.k.a. eternal relations of authority and submission) (EFS/ERAS) on the other hand. Sanders and Swain and their excellent panel of authors enter into the fray with a clarion call to reaffirm the universal historical position of the church on the relations of origin between the trinitarian persons and the eternal generation of the Son in particular. I’ll cut right to the chase: the book is worth the price for Charles Lee Irons’s chapter alone. He argues very convincingly for the historical translation of the Greek μονογενής (monogenēs) as “only begotten” over and against the more recent scholarly consensus “only one of its/his kind”. While this doesn’t by itself prove eternal generation, it sets our minds greatly at ease that “generation” is at least an appropriate Bible word to use for the eternal “from-ness” or “of-ness” of the Son. Though Irons’s chapter is arguably the most important contribution, I was pleased to find something valuable and convincing in every single chapter. I can’t mention them all here, but I particularly enjoyed Swain’s chapter on divine names, D.A. Carson’s chapter on John 5:26, Lewis Ayres’s chapter on the writings of Origen, Keith E. Johnson’s chapter on the writings of Augustine, Mark Makin’s chapter on philosophical models of eternal generation, and Sanders’s chapter on eternal generation and soteriology. I commend the book to anyone looking to deepen and sharpen their understanding of trinitarian theology, and especially those who aren’t sure whether to hold onto or ditch the traditional relations of origin in light of some new idea like social trinitarianism, egalitarianism, or EFS/ERAS. I was more or less persuaded by the EFS/ERAS view but I’m happy to report reading this book has brought me squarely back into the classical eternal generation camp. And that it changed one’s mind is, perhaps, the highest praise one can give to any book. ]]></summary></entry><entry><title type="html">Sending off Pastor Tim</title><link href="https://dev.joeyday.com/2015/10/05/sending-off-pastor-tim/" rel="alternate" type="text/html" title="Sending off Pastor Tim" /><published>2015-10-05T19:22:14+00:00</published><updated>2015-10-05T19:22:14+00:00</updated><id>https://dev.joeyday.com/2015/10/05/sending-off-pastor-tim</id><content type="html" xml:base="https://dev.joeyday.com/2015/10/05/sending-off-pastor-tim/"><![CDATA[<p>One of the pastors of our church (<a href="http://jordanvalleychurch.org/">Jordan Valley Church</a> in West Jordan, Utah) recently received and accepted a call to become the pastor at <a href="http://graceutah.org">Grace Church</a> in Layton, Utah. Yesterday Pastor Tim preached at our church for the last time and the congregation fellowshipped afterward with a fried chicken and potluck meal to celebrate his ministry to us and give him a proper send off.</p>

<p>While we were eating, a microphone was passed around to give people a chance to say a little something about Pastor Tim. I didn’t volunteer then, but I want to take the opportunity here to collect the various tweets I’ve posted over the past couple years quoting things he’s said in his sermons or sharing pictures of him.</p>

<blockquote>
  <p>“You can’t even know the depths of your own sin.” —Pastor Tim Barton, Jordan Presbyterian Church (commenting on <a href="http://t.co/tsrLjrNS">http://t.co/tsrLjrNS</a>)</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/224544338305236992">July 15, 2012</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>Pastor Tim in his element. @ Jordan Presbyterian Church <a href="http://t.co/1SZdGL64">http://t.co/1SZdGL64</a></p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/242297826544078848">September 2, 2012</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>“What’s wrong with you? (I say that with all grace.)” —Pr. Tim <a href="http://t.co/O21X6Q0M">http://t.co/O21X6Q0M</a></p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/252783758074339328">October 1, 2012</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>Pastor Tim: “Faith in the risen Christ is our marijuana catapult.” <a href="https://twitter.com/hashtag/context?src=hash">#context</a></p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/275021698682728450">December 1, 2012</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>“Which is more difficult, to build a ship or to rescue a sinking one?” —Pr. Tim</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/338697105923272704">May 26, 2013</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>Pr. Tim playing the djembe. @ Trefoil Ranch <a href="http://t.co/CY0p7orziJ">http://t.co/CY0p7orziJ</a></p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/383980788804775936">September 28, 2013</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>“I’m a firm believer that if you can’t teach a Biblical principle with Smarties you do not yet understand it.” —Pr. Tim Barton</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/424952618641682432">January 19, 2014</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>The value of marriage according to Pr. Tim: “Everybody needs at least one person who can look at them and honestly say, ‘You’re an idiot.’”</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/432568145027403776">February 9, 2014</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>Pr. Tim: “You can’t really love your neighbor and murder them at the same time.”<br />
Me: “Challenge accepted.”</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/460470584611065857">April 27, 2014</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>5 years ago, I only subscribed to sermons of non-local “popular” pastors. I’m happy to say all the ones I follow now are right here in SLC.</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/461158259089342464">April 29, 2014</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p>“Subnormal Christianity has become so normal that normal Christianity looks abnormal.” —Pr. Tim</p>

  <p>— Joey Day (@joeyday) <a href="https://twitter.com/joeyday/status/490901302994419712">July 20, 2014</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<p>We love you Pastor Tim. If you’re half the blessing to Grace Church as you’ve been to us they’ll be blessed indeed. Godspeed in all your future endeavors, friend. <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="jordan presbyterian church" /><category term="pastor tim" /><category term="quotes" /><category term="twitter" /><summary type="html"><![CDATA[One of the pastors of our church (Jordan Valley Church in West Jordan, Utah) recently received and accepted a call to become the pastor at Grace Church in Layton, Utah. Yesterday Pastor Tim preached at our church for the last time and the congregation fellowshipped afterward with a fried chicken and potluck meal to celebrate his ministry to us and give him a proper send off. While we were eating, a microphone was passed around to give people a chance to say a little something about Pastor Tim. I didn’t volunteer then, but I want to take the opportunity here to collect the various tweets I’ve posted over the past couple years quoting things he’s said in his sermons or sharing pictures of him. “You can’t even know the depths of your own sin.” —Pastor Tim Barton, Jordan Presbyterian Church (commenting on http://t.co/tsrLjrNS) — Joey Day (@joeyday) July 15, 2012 Pastor Tim in his element. @ Jordan Presbyterian Church http://t.co/1SZdGL64 — Joey Day (@joeyday) September 2, 2012 “What’s wrong with you? (I say that with all grace.)” —Pr. Tim http://t.co/O21X6Q0M — Joey Day (@joeyday) October 1, 2012 Pastor Tim: “Faith in the risen Christ is our marijuana catapult.” #context — Joey Day (@joeyday) December 1, 2012 “Which is more difficult, to build a ship or to rescue a sinking one?” —Pr. Tim — Joey Day (@joeyday) May 26, 2013 Pr. Tim playing the djembe. @ Trefoil Ranch http://t.co/CY0p7orziJ — Joey Day (@joeyday) September 28, 2013 “I’m a firm believer that if you can’t teach a Biblical principle with Smarties you do not yet understand it.” —Pr. Tim Barton — Joey Day (@joeyday) January 19, 2014 The value of marriage according to Pr. Tim: “Everybody needs at least one person who can look at them and honestly say, ‘You’re an idiot.’” — Joey Day (@joeyday) February 9, 2014 Pr. Tim: “You can’t really love your neighbor and murder them at the same time.” Me: “Challenge accepted.” — Joey Day (@joeyday) April 27, 2014 5 years ago, I only subscribed to sermons of non-local “popular” pastors. I’m happy to say all the ones I follow now are right here in SLC. — Joey Day (@joeyday) April 29, 2014 “Subnormal Christianity has become so normal that normal Christianity looks abnormal.” —Pr. Tim — Joey Day (@joeyday) July 20, 2014 We love you Pastor Tim. If you’re half the blessing to Grace Church as you’ve been to us they’ll be blessed indeed. Godspeed in all your future endeavors, friend. ]]></summary></entry><entry><title type="html">Get GlideDuration value in millseconds</title><link href="https://dev.joeyday.com/2015/09/03/x-htm/" rel="alternate" type="text/html" title="Get GlideDuration value in millseconds" /><published>2015-09-03T16:48:54+00:00</published><updated>2015-09-03T16:48:54+00:00</updated><id>https://dev.joeyday.com/2015/09/03/x-htm</id><content type="html" xml:base="https://dev.joeyday.com/2015/09/03/x-htm/"><![CDATA[<p>Curiously, ServiceNow’s <a href="http://wiki.servicenow.com/index.php?title=Scoped_GlideDuration_API_Reference">GlideDuration</a> object gives you a way to <em>set</em> the duration in milliseconds (i.e. passing in the millisecond value when constructing a new instance) but there’s no simple way to <em>get back</em> that duration expressed in milliseconds. You can get it back expressed in days, hours, minutes, and seconds in just about any format you might need, but not in total milliseconds. I recently had a use case for expressing a duration in total hours, where being able to get back a millisecond value would have been very convenient. But instead I had to get creative with string manipulation to convert the duration display value back into milliseconds. If you need to do this too, do yourself a favor and put it in a Script Include so it’s reusable.</p>

<p>Here’s my Script Include, which you are welcome to copy verbatim or use as inspiration for your own (at the very least, I recommend replacing “ACME” with your company’s name or initials, or just name it according to whatever convention you would normally follow at your organization):</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">ACMEDurationHelper</span> <span class="o">=</span> <span class="nx">Class</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span>  
<span class="nx">ACMEDurationHelper</span><span class="p">.</span><span class="nx">prototype</span> <span class="o">=</span> <span class="p">{</span>  
    <span class="na">initialize</span><span class="p">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="p">},</span>

    <span class="c1">// Accepts a GlideDuration object, passes back</span>
    <span class="c1">// total milliseconds of the duration.</span>
    <span class="na">getTotalMS</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dur</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">durVal</span> <span class="o">=</span> <span class="nx">dur</span><span class="p">.</span><span class="nx">getDurationValue</span><span class="p">();</span>

        <span class="c1">// Parse the duration value into days and time</span>
        <span class="kd">var</span> <span class="nx">durValArr</span> <span class="o">=</span> <span class="nx">durVal</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="kd">var</span> <span class="nx">days</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">durValArr</span><span class="p">.</span><span class="nx">length</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">days</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">durValArr</span><span class="p">.</span><span class="nx">shift</span><span class="p">(),</span> <span class="mi">10</span><span class="p">);</span>
        <span class="p">}</span>
        <span class="kd">var</span> <span class="nx">time</span> <span class="o">=</span> <span class="nx">durValArr</span><span class="p">.</span><span class="nx">shift</span><span class="p">();</span>

        <span class="c1">// Parse the time into hours, minutes, and seconds</span>
        <span class="kd">var</span> <span class="nx">timeArr</span> <span class="o">=</span> <span class="nx">time</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="kd">var</span> <span class="nx">hours</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">timeArr</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="kd">var</span> <span class="nx">minutes</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">timeArr</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mi">10</span><span class="p">);</span>
        <span class="kd">var</span> <span class="nx">seconds</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">timeArr</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="mi">10</span><span class="p">);</span>

        <span class="c1">// Calculate and return total milliseconds</span>
        <span class="k">return</span> <span class="nx">days</span> <span class="o">*</span> <span class="mi">86400000</span>
             <span class="o">+</span> <span class="nx">hours</span> <span class="o">*</span> <span class="mi">3600000</span>
             <span class="o">+</span> <span class="nx">minutes</span> <span class="o">*</span> <span class="mi">60000</span>
             <span class="o">+</span> <span class="nx">seconds</span> <span class="o">*</span> <span class="mi">1000</span><span class="p">;</span>
    <span class="p">},</span>

    <span class="na">getTotalSecs</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dur</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">getTotalMS</span><span class="p">(</span><span class="nx">dur</span><span class="p">)</span> <span class="o">/</span> <span class="mi">1000</span><span class="p">;</span>
    <span class="p">},</span>

    <span class="na">getTotalMins</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dur</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">getTotalMS</span><span class="p">(</span><span class="nx">dur</span><span class="p">)</span> <span class="o">/</span> <span class="mi">60000</span><span class="p">;</span>
    <span class="p">},</span>

    <span class="na">getTotalHrs</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dur</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">getTotalMS</span><span class="p">(</span><span class="nx">dur</span><span class="p">)</span> <span class="o">/</span> <span class="mi">3600000</span><span class="p">;</span>
    <span class="p">},</span>

    <span class="na">getTotalDays</span><span class="p">:</span> <span class="kd">function</span><span class="p">(</span><span class="nx">dur</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">getTotalMS</span><span class="p">(</span><span class="nx">dur</span><span class="p">)</span> <span class="o">/</span> <span class="mi">86400000</span><span class="p">;</span>
    <span class="p">},</span>

    <span class="na">type</span><span class="p">:</span> <span class="dl">'</span><span class="s1">ACMEDurationHelper</span><span class="dl">'</span>
<span class="p">};</span></code></pre></figure>

<p>If this is useful to you, drop me a comment to let me know. Cheers! <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="duration" /><category term="glideduration" /><category term="milliseconds" /><category term="servicenow" /><category term="string" /><category term="time" /><summary type="html"><![CDATA[Curiously, ServiceNow’s GlideDuration object gives you a way to set the duration in milliseconds (i.e. passing in the millisecond value when constructing a new instance) but there’s no simple way to get back that duration expressed in milliseconds. You can get it back expressed in days, hours, minutes, and seconds in just about any format you might need, but not in total milliseconds. I recently had a use case for expressing a duration in total hours, where being able to get back a millisecond value would have been very convenient. But instead I had to get creative with string manipulation to convert the duration display value back into milliseconds. If you need to do this too, do yourself a favor and put it in a Script Include so it’s reusable. Here’s my Script Include, which you are welcome to copy verbatim or use as inspiration for your own (at the very least, I recommend replacing “ACME” with your company’s name or initials, or just name it according to whatever convention you would normally follow at your organization): var ACMEDurationHelper = Class.create(); ACMEDurationHelper.prototype = { initialize: function() { }, // Accepts a GlideDuration object, passes back // total milliseconds of the duration. getTotalMS: function(dur) { var durVal = dur.getDurationValue(); // Parse the duration value into days and time var durValArr = durVal.split(' '); var days = 0; if (durValArr.length == 2) { var days = parseInt(durValArr.shift(), 10); } var time = durValArr.shift(); // Parse the time into hours, minutes, and seconds var timeArr = time.split(':'); var hours = parseInt(timeArr[0], 10); var minutes = parseInt(timeArr[1], 10); var seconds = parseInt(timeArr[2], 10); // Calculate and return total milliseconds return days * 86400000 + hours * 3600000 + minutes * 60000 + seconds * 1000; }, getTotalSecs: function(dur) { return this.getTotalMS(dur) / 1000; }, getTotalMins: function(dur) { return this.getTotalMS(dur) / 60000; }, getTotalHrs: function(dur) { return this.getTotalMS(dur) / 3600000; }, getTotalDays: function(dur) { return this.getTotalMS(dur) / 86400000; }, type: 'ACMEDurationHelper' }; If this is useful to you, drop me a comment to let me know. Cheers! ]]></summary></entry><entry><title type="html">Metric for Updated By</title><link href="https://dev.joeyday.com/2015/05/30/metric-for-updated-by/" rel="alternate" type="text/html" title="Metric for Updated By" /><published>2015-05-30T00:33:41+00:00</published><updated>2015-05-30T00:33:41+00:00</updated><id>https://dev.joeyday.com/2015/05/30/metric-for-updated-by</id><content type="html" xml:base="https://dev.joeyday.com/2015/05/30/metric-for-updated-by/"><![CDATA[<p>A friend recently asked how he could create a ServiceNow Metric that would track who makes each and every update to records in a given table. Sounds easy enough, right?</p>

<p>Well, it turns out the only fields that would make sense to trigger such a metric are system fields like <em>sys_updated_on</em> or <em>sys_updated_by</em>, and the tricky thing is those fields don’t get monitored by default for calculating metrics. I searched the ServiceNow Community and found a way to do it, and below I am providing an off-the-shelf Metric Definition and Business Rule that will track every time a record is updated on a table. ((I’m indebted to this Community thread that gave me the working solution to the dilemma: <em><a href="https://community.servicenow.com/thread/165157" title="ServiceNow Community › Metric definition for sys_updated_by">ServiceNow Community › Metric definition for sys_updated_by</a></em>.))</p>

<h2 id="the-metric-definition">The Metric Definition</h2>

<p>I’ve defined this for the Task table (which works for any tables extended from Task), but you’re of course welcome to make it more specific as your needs require.</p>

<p><strong>Name:</strong> Task Updated By
<strong>Table:</strong> Task [task]
<strong>Field:</strong> Updated by
<strong>Type:</strong> Script calculation</p>

<p><strong>Script:</strong></p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">createMetric</span><span class="p">(</span><span class="nx">current</span><span class="p">.</span><span class="nx">sys_updated_by</span><span class="p">.</span><span class="nx">toString</span><span class="p">());</span>

<span class="kd">function</span> <span class="nx">createMetric</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">mi</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MetricInstance</span><span class="p">(</span><span class="nx">definition</span><span class="p">,</span> <span class="nx">current</span><span class="p">);</span>
	<span class="kd">var</span> <span class="nx">gr</span> <span class="o">=</span> <span class="nx">mi</span><span class="p">.</span><span class="nx">getNewRecord</span><span class="p">();</span>
	<span class="nx">gr</span><span class="p">.</span><span class="nx">field_value</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
	<span class="nx">gr</span><span class="p">.</span><span class="nx">calculation_complete</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
	<span class="nx">gr</span><span class="p">.</span><span class="nx">insert</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>

<h2 id="the-business-rule">The Business Rule</h2>

<p>This is the secret sauce. Since fields like <em>sys_updated_by</em> aren’t monitored for metric calculations, we need to explicitly declare that we want it monitored by setting up a Business Rule to fire the same event that would normally be fired for non-system fields.</p>

<p><strong>Name:</strong> Metric Event for Updated By
<strong>Table:</strong> Task [task]
<strong>Advanced:</strong> true
<strong>When:</strong> after
<strong>Insert:</strong> true
<strong>Update:</strong> true</p>

<p><strong>Script:</strong></p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">onAfter</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">previous</span><span class="p">)</span> <span class="p">{</span>
	<span class="nx">gs</span><span class="p">.</span><span class="nx">eventQueue</span><span class="p">(</span><span class="dl">'</span><span class="s1">metric.update</span><span class="dl">'</span><span class="p">,</span> <span class="nx">current</span><span class="p">,</span> <span class="dl">'</span><span class="s1">[sys_updated_by]</span><span class="dl">'</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="dl">'</span><span class="s1">metric_update</span><span class="dl">'</span><span class="p">);</span>
<span class="p">}</span></code></pre></figure>

<p>That’s all there is to it. Now any time someone inserts or updates any Task (or records in whatever table you specified instead) you’ll get a new Metric Instance with a value containing that User’s user name. <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="business rule" /><category term="metric" /><category term="servicenow" /><category term="sys_updated_by" /><summary type="html"><![CDATA[A friend recently asked how he could create a ServiceNow Metric that would track who makes each and every update to records in a given table. Sounds easy enough, right? Well, it turns out the only fields that would make sense to trigger such a metric are system fields like sys_updated_on or sys_updated_by, and the tricky thing is those fields don’t get monitored by default for calculating metrics. I searched the ServiceNow Community and found a way to do it, and below I am providing an off-the-shelf Metric Definition and Business Rule that will track every time a record is updated on a table. ((I’m indebted to this Community thread that gave me the working solution to the dilemma: ServiceNow Community › Metric definition for sys_updated_by.)) The Metric Definition I’ve defined this for the Task table (which works for any tables extended from Task), but you’re of course welcome to make it more specific as your needs require. Name: Task Updated By Table: Task [task] Field: Updated by Type: Script calculation Script: createMetric(current.sys_updated_by.toString()); function createMetric(value) { var mi = new MetricInstance(definition, current); var gr = mi.getNewRecord(); gr.field_value = value; gr.calculation_complete = true; gr.insert(); } The Business Rule This is the secret sauce. Since fields like sys_updated_by aren’t monitored for metric calculations, we need to explicitly declare that we want it monitored by setting up a Business Rule to fire the same event that would normally be fired for non-system fields. Name: Metric Event for Updated By Table: Task [task] Advanced: true When: after Insert: true Update: true Script: function onAfter(current, previous) { gs.eventQueue('metric.update', current, '[sys_updated_by]', 1, 'metric_update'); } That’s all there is to it. Now any time someone inserts or updates any Task (or records in whatever table you specified instead) you’ll get a new Metric Instance with a value containing that User’s user name. ]]></summary></entry><entry><title type="html">Integrating ServiceNow with HipChat</title><link href="https://dev.joeyday.com/2015/05/18/integrating-servicenow-with-hipchat/" rel="alternate" type="text/html" title="Integrating ServiceNow with HipChat" /><published>2015-05-18T23:18:51+00:00</published><updated>2015-05-18T23:18:51+00:00</updated><id>https://dev.joeyday.com/2015/05/18/integrating-servicenow-with-hipchat</id><content type="html" xml:base="https://dev.joeyday.com/2015/05/18/integrating-servicenow-with-hipchat/"><![CDATA[<p>My tutorial on integrating [ServiceNow] with [Slack] turned out to be one of my most popular articles ever. Justin Meader recently asked me on Twitter how easy it would be to integrate Service­Now with [HipChat] instead.</p>

<blockquote>
  <p><a href="https://twitter.com/joeyday">@joeyday</a> Hey Joey, I found your article on integrating Slack with Service­Now, and it’s very helpful.</p>

  <p>— Justin Meader (@justinmeader) <a href="https://twitter.com/justinmeader/status/598566238835597312">May 13, 2015</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<blockquote>
  <p><a href="https://twitter.com/joeyday">@joeyday</a> We’re a HipChat shop, and I was wondering how difficult it would be to adapt the include script for HipChat? I’m not a coder sadly.</p>

  <p>— Justin Meader (@justinmeader) <a href="https://twitter.com/justinmeader/status/598566367793684480">May 13, 2015</a></p>
</blockquote>

<script async="" charset="utf-8" src="//platform.twitter.com/widgets.js"></script>

<p>So, I gave it a whirl. In this article I’ll show you how you can post Service­Now notifications right into HipChat using their API. Just like in the previous tutorial, I’ll give you a ready-made Service­Now Script Include and instructions and examples for how you can call that Script Include from any scripted Business Rule.</p>

<h2 id="getting-access-to-the-hipchat-api">Getting access to the HipChat API</h2>

<p>To get started, you’ll need an endpoint URL for the HipChat API to which Service­Now can send web service calls. To get this URL, go to HipChat’s <a href="https://www.hipchat.com/integrations" title="HipChat Integrations">Integrations</a> page:</p>

<p><a href="/wp-content/uploads/2015/05/hipchat-1.png"><img src="/wp-content/uploads/2015/05/hipchat-1-1024x720.png" alt="HipChat Integrations Page" /></a></p>

<p>Scroll to the bottom and click “Set Up An Integration”:</p>

<p><a href="/wp-content/uploads/2015/05/hipchat-2.png"><img src="/wp-content/uploads/2015/05/hipchat-2-1024x616.png" alt="HipChat Set Up An Integration" /></a></p>

<p>If you’re not already logged you may be asked to do so, then click the “Create” button under “Build Your Own!”:</p>

<p><a href="/wp-content/uploads/2015/05/hipchat-3.png"><img src="/wp-content/uploads/2015/05/hipchat-3-1024x637.png" alt="HipChat Build Your Own Integration" /></a></p>

<p>On the next screen, choose the room you want to use and the name of the bot as you’d like it to appear whenever it posts, then click “Create”:</p>

<p><a href="/wp-content/uploads/2015/05/hipchat-4.png"><img src="/wp-content/uploads/2015/05/hipchat-4-1024x635.png" alt="HipChat Choose the Room and Name Your Integration Bot" /></a></p>

<p>On the next screen, you should see a URL like the below:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://api.hipchat.com/v2/room/xxxxxxx/notification?auth_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</code></pre></div></div>

<p>Save this URL someplace safe. I’ll show you where to use it in a bit. HipChat doesn’t require any kind of authentication to use this API, so the token in this URL essentially <em>is</em> your password. Keep it a secret.</p>

<h2 id="creating-the-rest-message-in-servicenow">Creating the REST Message in ServiceNow</h2>

<p>The first step in Service­Now is to set up a REST Message. If you’re running Fuji you can actually skip this step and go straight to the Script Include, as the new RESTMessageV2 API in Fuji doesn’t require a REST Message database record. If you’re still on Eureka or want to keep the integration backwards-compatible with earlier releases for any reason, read on.</p>

<ol>
  <li>Navigate to <strong>System Web Services</strong> › <strong>Outbound</strong> › <strong>REST Messages</strong> in your Service­Now instance.</li>
  <li>Click “New” to create a new REST Message.
    <ul>
      <li>Type “HipChat” for the name.</li>
      <li>Click the lock icon to unlock the Endpoint field and type “${endpoint}” there.</li>
      <li>Type something useful in the Description field if you go for that sort of thing.</li>
    </ul>
  </li>
  <li>Right-click in the header and choose “Save” to submit the new record and stay where you are.</li>
  <li>At the bottom of the newly-saved record, there will be a related list containing REST Message Functions, of which there should be four: “get”, “post”, “put”, and “delete”. Check the box to the left of all these but “post” and use the drop-down at the bottom to delete them. The only one we need to keep here is the “post” function.</li>
  <li>Click into the “post” function.
    <ul>
      <li>Verify the Endpoint here is also set to “${endpoint}” (it should’ve inherited this setting from the parent REST Message).</li>
      <li>Insert an HTTP Header row Name “Content-Type” and Value “application/json”.</li>
      <li>In the Content field, type “${payload}”.</li>
    </ul>
  </li>
  <li>Click “Update”.</li>
</ol>

<p>That’s it, the REST Message and REST Message Function should be ready to go.</p>

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

<p>To create the Script Include, follow these steps:</p>

<ol>
  <li>Navigate to <strong>System Definition</strong> › <strong>Script Includes</strong> in your Service­Now instance.</li>
  <li>Click “New”.
    <ul>
      <li>Name the Script Include “HipChatNotification”.</li>
      <li>Give it a description if you want.</li>
      <li>Copy and paste the script below.</li>
    </ul>
  </li>
  <li>Click “Submit”.</li>
</ol>

<p>Here’s the script itself:<br />
[code language=”javascript”]var HipChatNotification = Class.create();</p>

<p>HipChatNotification.prototype = {<br />
 ‘initialize’: function() {},</p>

<p>‘send’: function (message, endpoint) {<br />
 // Set the text and channel (or fall back to defaults)<br />
 this.payload.message = message || this.payload.message;<br />
 this.endpoint = endpoint || this.endpoint;</p>

<p>// Encode the payload as JSON<br />
 var SNJSON = JSON; // Workaround for JSLint warning about using JSON as a constructor<br />
 var myjson = new SNJSON();<br />
 var encoded_payload = myjson.encode(this.payload);</p>

<p>// Create and send the REST Message<br />
 var msg = new RESTMessage(‘HipChat’, this.method);<br />
 msg.setStringParameter(‘endpoint’, this.endpoint);<br />
 msg.setXMLParameter(‘payload’, encoded_payload);<br />
 var res = msg.execute();<br />
 return res;<br />
 },</p>

<p>‘endpoint’ : gs.getProperty(‘hipchat_notification.default_endpoint’),<br />
 ‘method’ : ‘post’,<br />
 ‘payload’ : {<br />
 ‘color’ : gs.getProperty(‘hipchat_notification.default_color’),<br />
 ‘message’ : ”,<br />
 ‘message_format’ : ‘html’,<br />
 ‘notify’ : false<br />
 },</p>

<p>‘type’: ‘HipChatNotification’<br />
};[/code]</p>

<p>Now, I said earlier if you’re running Fuji you could skip the step for adding the REST Message. If you did that, you’ll want to find the following block of code in the above script . . .<br />
[code language=”javascript”]// Create and send the REST Message<br />
var msg = new RESTMessage(‘HipChat’, this.method);<br />
msg.setStringParameter(‘endpoint’, this.endpoint);<br />
msg.setXMLParameter(‘payload’, encoded_payload);<br />
var res = msg.execute();<br />
return res;[/code]</p>

<p>. . . And replace it with this:<br />
[code language=”javascript”]// Create and send the REST Message<br />
var msg = new sn_ws.RESTMessageV2();<br />
msg.setEndpoint(this.endpoint);<br />
msg.setHttpMethod(this.method);<br />
msg.setRequestHeader(‘Content-Type’, ‘application/json’);<br />
msg.setRequestBody(encoded_payload);<br />
var res = msg.execute();<br />
return res;[/code]</p>

<p>This alternative code instantiates a new RESTMessageV2 object (instead of the older RESTMessage object) and does so without specifying any parameters. One of the neat things about the new version of the Outbound REST API is you don’t have to specify an existing REST Message, but can use the setter methods you see here to set the Endpoint and the HTTP Method, as well as directly set the Content Body of the message, all at runtime. Obviously if there’s a lot of setup (special headers and authentication and such) you may still want to go the former route of declaring the REST Message up front and calling it here, but since using the HipChat API doesn’t require a lot of complicated setup, being able to do it this way keeps things simple.</p>

<h2 id="system-properties">System Properties</h2>

<p>You may have noticed the Script Include pulls in a couple of System Properties. I like using System Properties because they allow you to change settings on the fly later without re-deploying code. You can get a list view of all System Properties by typing “sys_properties.list” in the search box at the top of the left sidebar in Service­Now. To create a new System Property from there, simply click the “New” button.</p>

<p>You’ll need to declare the following System Properties in your instance for the Script Include above to function properly:</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>hipchat_notification.default_endpoint</td>
      <td>Here’s where you should paste that URL you put in a safe place way back in the first section above.</td>
    </tr>
    <tr>
      <td>hipchat_notification.default_color</td>
      <td>Specify the color you’d like all HipChat notifications to be by default. You can choose from yellow, green, red, purple, gray, or random.</td>
    </tr>
  </tbody>
</table>

<p>I called these both defaults since, as I’ll show below, you can override either or both of them in your individual Business Rules. As a best practice I recommend you rename these to include a company prefix, e.g. <strong>acme.hipchat_notification.default_endpoint</strong>. If you do, just don’t forget to modify each of the gs.getProperty() calls in the Script Include to match the new names.</p>

<h2 id="examples">Examples</h2>

<p>Here’s how easy it is to send a notification to the default endpoint using this Script Include. In an advanced Business Rule (with conditions specified however you like), just call the send() method on a new HipChatNotification object:</p>

<p>[code language=”javascript”]var hipchat = new HipChatNotification();<br />
hipchat.send(‘Hello World!’);[/code]</p>

<p>To send the notification someplace other than the default room, simply create another integration for that room and specify the alternate endpoint URL as a second parameter of the send() method:</p>

<p>[code language=”javascript”]var hipchat = new HipChatNotification();<br />
hipchat.send(‘Hello World!’, ‘https://api.hipchat.com/v2/room/xxxxxxx/notification?auth_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’);[/code]</p>

<p>Note that the message itself can include HTML. Here’s a more realistic example how you might notify a room when an Incident has been assigned to a particular assignment group:</p>

<p>[code language=”javascript”]// Initialize a new HipChatNotification<br />
var hipchat = new HipChatNotification();</p>

<p>// Set up the payload<br />
hipchat.payload.message = ‘An Incident has been assigned to ‘ + current.assignment_group.name + ‘: &lt;a href=”https://’ + gs.getProperty(‘instance_name’) + ‘.service-now.com/nav_to.do?uri=incident.do?sys_id=’ + current.sys_id + ‘”&gt;’ + current.number + ‘&lt;/a&gt; ‘ + current.short_description;<br />
hipchat.payload.color = ‘red’;</p>

<p>// Fire off the message<br />
var response = hipchat.send();[/code]</p>

<p>The above will result in a Slack notification looking like this:</p>

<p><a href="/wp-content/uploads/2015/05/hipchat-notification.png"><img src="/wp-content/uploads/2015/05/hipchat-notification-1024x99.png" alt="HipChat Notification" /></a></p>

<p>Don’t miss how you can call the send() method without any parameters as long as you’ve specified useful values for the payload beforehand, and be aware you may need to use toString() when you set values if you’re not using string concatenation or something else that would implicitly cast the contents of a record field into a string.</p>

<p>Lastly, note that the send() method returns the server response. If you store that response you can debug by grabbing values out of it as shown below:<br />
[code language=”javascript”]// Send the notification<br />
var hipchat = new HipChatNotification();<br />
var response = hipchat.send(‘Hello World!’);</p>

<p>// Show error message if it failed<br />
if(response.getStatusCode() != 200) {<br />
 gs.addInfoMessage(“response.getBody: “ + response.getBody());<br />
 gs.addInfoMessage(“response.getStatusCode: “ + response.getStatusCode());<br />
 gs.addInfoMessage(“response.getErrorMessage: “ + response.getErrorMessage());<br />
}[/code]</p>

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

<p>So, what can you use this for? The sky’s the limit really. You can set up a Business Rule to fire on any condition you want and send any HTML message into any HipChat room. Use it as above to notify any given team when they have a new assignment. Use it to notify management when there’s a P1 incident. If you find a creative use for this Script Include, I’d love if you’d leave a comment below or drop me a line on social media. Cheers! <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="api" /><category term="hipchat" /><category term="integration" /><category term="rest" /><category term="servicenow" /><category term="web services" /><summary type="html"><![CDATA[My tutorial on integrating [ServiceNow] with [Slack] turned out to be one of my most popular articles ever. Justin Meader recently asked me on Twitter how easy it would be to integrate Service­Now with [HipChat] instead. @joeyday Hey Joey, I found your article on integrating Slack with Service­Now, and it’s very helpful. — Justin Meader (@justinmeader) May 13, 2015 @joeyday We’re a HipChat shop, and I was wondering how difficult it would be to adapt the include script for HipChat? I’m not a coder sadly. — Justin Meader (@justinmeader) May 13, 2015 So, I gave it a whirl. In this article I’ll show you how you can post Service­Now notifications right into HipChat using their API. Just like in the previous tutorial, I’ll give you a ready-made Service­Now Script Include and instructions and examples for how you can call that Script Include from any scripted Business Rule. Getting access to the HipChat API To get started, you’ll need an endpoint URL for the HipChat API to which Service­Now can send web service calls. To get this URL, go to HipChat’s Integrations page: Scroll to the bottom and click “Set Up An Integration”: If you’re not already logged you may be asked to do so, then click the “Create” button under “Build Your Own!”: On the next screen, choose the room you want to use and the name of the bot as you’d like it to appear whenever it posts, then click “Create”: On the next screen, you should see a URL like the below: https://api.hipchat.com/v2/room/xxxxxxx/notification?auth_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx Save this URL someplace safe. I’ll show you where to use it in a bit. HipChat doesn’t require any kind of authentication to use this API, so the token in this URL essentially is your password. Keep it a secret. Creating the REST Message in ServiceNow The first step in Service­Now is to set up a REST Message. If you’re running Fuji you can actually skip this step and go straight to the Script Include, as the new RESTMessageV2 API in Fuji doesn’t require a REST Message database record. If you’re still on Eureka or want to keep the integration backwards-compatible with earlier releases for any reason, read on. Navigate to System Web Services › Outbound › REST Messages in your Service­Now instance. Click “New” to create a new REST Message. Type “HipChat” for the name. Click the lock icon to unlock the Endpoint field and type “${endpoint}” there. Type something useful in the Description field if you go for that sort of thing. Right-click in the header and choose “Save” to submit the new record and stay where you are. At the bottom of the newly-saved record, there will be a related list containing REST Message Functions, of which there should be four: “get”, “post”, “put”, and “delete”. Check the box to the left of all these but “post” and use the drop-down at the bottom to delete them. The only one we need to keep here is the “post” function. Click into the “post” function. Verify the Endpoint here is also set to “${endpoint}” (it should’ve inherited this setting from the parent REST Message). Insert an HTTP Header row Name “Content-Type” and Value “application/json”. In the Content field, type “${payload}”. Click “Update”. That’s it, the REST Message and REST Message Function should be ready to go. The Script Include To create the Script Include, follow these steps: Navigate to System Definition › Script Includes in your Service­Now instance. Click “New”. Name the Script Include “HipChatNotification”. Give it a description if you want. Copy and paste the script below. Click “Submit”. Here’s the script itself: [code language=”javascript”]var HipChatNotification = Class.create(); HipChatNotification.prototype = { ‘initialize’: function() {}, ‘send’: function (message, endpoint) { // Set the text and channel (or fall back to defaults) this.payload.message = message || this.payload.message; this.endpoint = endpoint || this.endpoint; // Encode the payload as JSON var SNJSON = JSON; // Workaround for JSLint warning about using JSON as a constructor var myjson = new SNJSON(); var encoded_payload = myjson.encode(this.payload); // Create and send the REST Message var msg = new RESTMessage(‘HipChat’, this.method); msg.setStringParameter(‘endpoint’, this.endpoint); msg.setXMLParameter(‘payload’, encoded_payload); var res = msg.execute(); return res; }, ‘endpoint’ : gs.getProperty(‘hipchat_notification.default_endpoint’), ‘method’ : ‘post’, ‘payload’ : { ‘color’ : gs.getProperty(‘hipchat_notification.default_color’), ‘message’ : ”, ‘message_format’ : ‘html’, ‘notify’ : false }, ‘type’: ‘HipChatNotification’ };[/code] Now, I said earlier if you’re running Fuji you could skip the step for adding the REST Message. If you did that, you’ll want to find the following block of code in the above script . . . [code language=”javascript”]// Create and send the REST Message var msg = new RESTMessage(‘HipChat’, this.method); msg.setStringParameter(‘endpoint’, this.endpoint); msg.setXMLParameter(‘payload’, encoded_payload); var res = msg.execute(); return res;[/code] . . . And replace it with this: [code language=”javascript”]// Create and send the REST Message var msg = new sn_ws.RESTMessageV2(); msg.setEndpoint(this.endpoint); msg.setHttpMethod(this.method); msg.setRequestHeader(‘Content-Type’, ‘application/json’); msg.setRequestBody(encoded_payload); var res = msg.execute(); return res;[/code] This alternative code instantiates a new RESTMessageV2 object (instead of the older RESTMessage object) and does so without specifying any parameters. One of the neat things about the new version of the Outbound REST API is you don’t have to specify an existing REST Message, but can use the setter methods you see here to set the Endpoint and the HTTP Method, as well as directly set the Content Body of the message, all at runtime. Obviously if there’s a lot of setup (special headers and authentication and such) you may still want to go the former route of declaring the REST Message up front and calling it here, but since using the HipChat API doesn’t require a lot of complicated setup, being able to do it this way keeps things simple. System Properties You may have noticed the Script Include pulls in a couple of System Properties. I like using System Properties because they allow you to change settings on the fly later without re-deploying code. You can get a list view of all System Properties by typing “sys_properties.list” in the search box at the top of the left sidebar in Service­Now. To create a new System Property from there, simply click the “New” button. You’ll need to declare the following System Properties in your instance for the Script Include above to function properly: Name Value hipchat_notification.default_endpoint Here’s where you should paste that URL you put in a safe place way back in the first section above. hipchat_notification.default_color Specify the color you’d like all HipChat notifications to be by default. You can choose from yellow, green, red, purple, gray, or random. I called these both defaults since, as I’ll show below, you can override either or both of them in your individual Business Rules. As a best practice I recommend you rename these to include a company prefix, e.g. acme.hipchat_notification.default_endpoint. If you do, just don’t forget to modify each of the gs.getProperty() calls in the Script Include to match the new names. Examples Here’s how easy it is to send a notification to the default endpoint using this Script Include. In an advanced Business Rule (with conditions specified however you like), just call the send() method on a new HipChatNotification object: [code language=”javascript”]var hipchat = new HipChatNotification(); hipchat.send(‘Hello World!’);[/code] To send the notification someplace other than the default room, simply create another integration for that room and specify the alternate endpoint URL as a second parameter of the send() method: [code language=”javascript”]var hipchat = new HipChatNotification(); hipchat.send(‘Hello World!’, ‘https://api.hipchat.com/v2/room/xxxxxxx/notification?auth_token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx’);[/code] Note that the message itself can include HTML. Here’s a more realistic example how you might notify a room when an Incident has been assigned to a particular assignment group: [code language=”javascript”]// Initialize a new HipChatNotification var hipchat = new HipChatNotification(); // Set up the payload hipchat.payload.message = ‘An Incident has been assigned to ‘ + current.assignment_group.name + ‘: &lt;a href=”https://’ + gs.getProperty(‘instance_name’) + ‘.service-now.com/nav_to.do?uri=incident.do?sys_id=’ + current.sys_id + ‘”&gt;’ + current.number + ‘&lt;/a&gt; ‘ + current.short_description; hipchat.payload.color = ‘red’; // Fire off the message var response = hipchat.send();[/code] The above will result in a Slack notification looking like this: Don’t miss how you can call the send() method without any parameters as long as you’ve specified useful values for the payload beforehand, and be aware you may need to use toString() when you set values if you’re not using string concatenation or something else that would implicitly cast the contents of a record field into a string. Lastly, note that the send() method returns the server response. If you store that response you can debug by grabbing values out of it as shown below: [code language=”javascript”]// Send the notification var hipchat = new HipChatNotification(); var response = hipchat.send(‘Hello World!’); // Show error message if it failed if(response.getStatusCode() != 200) { gs.addInfoMessage(“response.getBody: “ + response.getBody()); gs.addInfoMessage(“response.getStatusCode: “ + response.getStatusCode()); gs.addInfoMessage(“response.getErrorMessage: “ + response.getErrorMessage()); }[/code] Conclusion So, what can you use this for? The sky’s the limit really. You can set up a Business Rule to fire on any condition you want and send any HTML message into any HipChat room. Use it as above to notify any given team when they have a new assignment. Use it to notify management when there’s a P1 incident. If you find a creative use for this Script Include, I’d love if you’d leave a comment below or drop me a line on social media. Cheers! ]]></summary></entry><entry><title type="html">Integrating ServiceNow with Slack</title><link href="https://dev.joeyday.com/2015/03/02/integrating-servicenow-with-slack/" rel="alternate" type="text/html" title="Integrating ServiceNow with Slack" /><published>2015-03-02T00:41:59+00:00</published><updated>2015-03-02T00:41:59+00:00</updated><id>https://dev.joeyday.com/2015/03/02/integrating-servicenow-with-slack</id><content type="html" xml:base="https://dev.joeyday.com/2015/03/02/integrating-servicenow-with-slack/"><![CDATA[<p><img src="http://joeyday.com/wp-content/uploads/2015/03/slack_servicenow.png" alt="Slack + ServiceNow" /> One of our teams at work recently started using <a href="http://www.slack.com" title="Slack">Slack</a> for all their team communication and approached me about possibly having <a href="http://www.servicenow.com" title="ServiceNow">ServiceNow</a> shoot Incident assignment notifications into one of their Slack channels. As it turns out, integrating ServiceNow and Slack is really easy. In this article I’ll show you how to enable the necessary Slack “Incoming Webhooks” integration service as well as give you a ready-to-go ServiceNow Script Include you can use to post messages to Slack from any scripted ServiceNow Business Rule. Finally, I include some examples and ideas for how to use and re-use the Script Include in your own Business Rules.</p>

<h2 id="getting-access-to-the-slack-api">Getting access to the Slack API</h2>

<p>To get started, you’ll need an endpoint URL for the Slack API to which ServiceNow can send web service calls. To get this URL, from within any Slack channel (either in the Slack app or the Slack website), simply drop down the arrow next to the channel name and select “Add a service integration”. It doesn’t matter which channel you do this in—I’ll show below how you can use this same integration endpoint to post to any channel within the same team.</p>

<p><a href="http://joeyday.com/wp-content/uploads/2015/03/slack-add-service-integration.png"><img src="http://joeyday.com/wp-content/uploads/2015/03/slack-add-service-integration-1024x727.png" alt="Slack “Add a service integration…”" /></a></p>

<p>On the next screen, scroll almost all the way to the bottom and click “+Add” next to “Incoming Webhooks”.</p>

<p><a href="http://joeyday.com/wp-content/uploads/2015/03/slack-choose-incoming-webhooks.png"><img src="http://joeyday.com/wp-content/uploads/2015/03/slack-choose-incoming-webhooks-1024x758.png" alt="Slack Choose Incoming Webhooks" /></a></p>

<p>Lastly, click “Add Incoming Webhooks Integration”.</p>

<p><a href="http://joeyday.com/wp-content/uploads/2015/03/slack-incoming-webhooks-setup.png"><img src="http://joeyday.com/wp-content/uploads/2015/03/slack-incoming-webhooks-setup-1024x649.png" alt="Slack Incoming Webhooks setup" /></a></p>

<p>On the next screen you should see a long web address labeled “Webhook URL”, similar to this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx
</code></pre></div></div>

<p>Save this URL someplace safe. I’ll show you where to use it in a bit. Slack doesn’t require any kind of authentication to use this API, so the tokens in this URL essentially <em>are</em> your password. Keep it a secret.</p>

<h2 id="creating-the-rest-message-in-servicenow">Creating the REST Message in ServiceNow</h2>

<p>The first step in ServiceNow is to set up a REST Message. If you’re running Fuji you can actually skip this step and go straight to the Script Include, as the new RESTMessageV2 API in Fuji doesn’t require a REST Message database record. If you’re still on Eureka or want to keep the integration backwards-compatible with earlier releases for any reason, read on.</p>

<ol>
  <li>Navigate to <strong>System Web Services</strong> › <strong>Outbound</strong> › <strong>REST Messages</strong> in your ServiceNow instance.</li>
  <li>Click “New” to create a new REST Message.
    <ul>
      <li>Type “Slack” for the name.</li>
      <li>Click the lock icon to unlock the Endpoint field and type “${endpoint}” there.</li>
      <li>Type something useful in the Description field if you go for that sort of thing.</li>
    </ul>
  </li>
  <li>Right-click in the header and choose “Save” to submit the new record and stay where you are.</li>
  <li>At the bottom of the newly-saved record, there will be a related list containing REST Message Functions, of which there should be four: “get”, “post”, “put”, and “delete”. Check the box to the left of all these but “post” and use the drop-down at the bottom to delete them. The only one we need to keep here is the “post” function.</li>
  <li>Click into the “post” function.
    <ul>
      <li>Verify the Endpoint here is also set to “${endpoint}” (it should’ve inherited this setting from the parent REST Message).</li>
      <li>In the Content field, type “${payload}”.</li>
    </ul>
  </li>
  <li>Click “Update”.</li>
</ol>

<p>That’s it, the REST Message and REST Message Function should be ready to go.</p>

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

<p>To create the Script Include, follow these steps:</p>

<ol>
  <li>Navigate to <strong>System Definition</strong> › <strong>Script Includes</strong> in your ServiceNow instance.</li>
  <li>Click “New”.
    <ul>
      <li>Name the Script Include “SlackMessage”.</li>
      <li>Give it a description if you want.</li>
      <li>Copy and paste the script below.</li>
    </ul>
  </li>
  <li>Click “Submit”.</li>
</ol>

<p>Here’s the script itself:<br />
[code language=”javascript”]var SlackMessage = Class.create();</p>

<p>SlackMessage.prototype = {<br />
 ‘initialize’: function() {<br />
 if (gs.getProperty(‘slack_message.default_icon_url’) != ”) {<br />
 this.payload.icon_url = gs.getProperty(‘slack_message.default_icon_url’);<br />
 }<br />
 else if (gs.getProperty(‘slack_message.default_icon_emoji’) != ”) {<br />
 this.payload.icon_emoji = gs.getProperty(‘slack_message.default_icon_emoji’);<br />
 }<br />
 },</p>

<p>‘send’: function (text, channel) {<br />
 // Set the text and channel (or fall back to defaults)<br />
 this.payload.text = text || this.payload.text;<br />
 this.payload.channel = channel || this.payload.channel;</p>

<p>// Encode the payload as JSON<br />
 var SNJSON = JSON; // Workaround for JSLint warning about using JSON as a constructor<br />
 var myjson = new SNJSON();<br />
 var encoded_payload = myjson.encode(this.payload);</p>

<p>// Create and send the REST Message<br />
 var msg = new RESTMessage(‘Slack’, this.method);<br />
 msg.setStringParameter(‘endpoint’, this.endpoint);<br />
 msg.setXMLParameter(‘payload’, encoded_payload);<br />
 var res = msg.execute();<br />
 return res;<br />
 },</p>

<p>‘endpoint’: gs.getProperty(‘slack_message.default_endpoint’),<br />
 ‘method’: ‘post’,<br />
 ‘payload’: {<br />
 ‘channel’: gs.getProperty(‘slack_message.default_channel’),<br />
 ‘username’: gs.getProperty(‘slack_message.default_username’),<br />
 ‘text’: ”,<br />
 ‘attachments’: []<br />
 },</p>

<p>‘type’: ‘SlackMessage’<br />
};[/code]</p>

<p>Now, I said earlier if you’re running Fuji you could skip the step for adding the REST Message. If you did that, you’ll want to find the following block of code in the above script . . .<br />
[code language=”javascript” firstline=”23″]// Create and send the REST Message<br />
var msg = new RESTMessage(‘Slack’, this.method);<br />
msg.setStringParameter(‘endpoint’, this.endpoint);<br />
msg.setXMLParameter(‘payload’, encoded_payload);<br />
var res = msg.execute();<br />
return res;[/code]</p>

<p>. . . And replace it with this:<br />
[code language=”javascript” firstline=”23″]// Create and send the REST Message<br />
var msg = new sn_ws.RESTMessageV2();<br />
msg.setEndpoint(this.endpoint);<br />
msg.setHttpMethod(this.method);<br />
msg.setRequestBody(encoded_payload);<br />
var res = msg.execute();<br />
return res;[/code]</p>

<p>This alternative code instantiates a new RESTMessageV2 object (instead of the older RESTMessage object) and does so without specifying any parameters. One of the neat things about the new version of the Outbound REST API is you don’t have to specify an existing REST Message, but can use the setter methods you see here to set the Endpoint and the HTTP Method, as well as directly set the Content Body of the message, all at runtime. Obviously if there’s a lot of setup (special headers and authentication and such) you may still want to go the former route of declaring the REST Message up front and calling it here, but since the Slack API doesn’t require any of that, being able to do it this way keeps things simple.</p>

<h2 id="system-properties">System Properties</h2>

<p>You may have noticed the Script Include pulls in a handful of System Properties. I like using System Properties because they allow you to change settings on the fly later without re-deploying code. You can get a list view of all System Properties by typing “sys_properties.list” in the search box at the top of the left sidebar in ServiceNow. To create a new System Property from there, simply click the “New” button.</p>

<p>You’ll need to declare the following System Properties in your instance for the Script Include above to function properly:</p>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>slack_message.default_endpoint</td>
      <td>Here’s where you should paste that Slack Webhook URL you put in a safe place way back in the first section above.</td>
    </tr>
    <tr>
      <td>slack_message.default_channel</td>
      <td>Specify the channel to which you’ll post the majority of your Slack messages, with a leading hash symbol, such as “#general” or “#servicenow”. The Slack API also allows specifying a person’s username preceded by an at symbol to send direct messages instead, such as “@joeyday”.</td>
    </tr>
    <tr>
      <td>slack_message.default_username</td>
      <td>This is the display name of the bot that will post the messages. I set mine to “ServiceNow”, but this could be anything you like.</td>
    </tr>
    <tr>
      <td>slack_message.default_icon_url</td>
      <td>Specify a URL of an image to be used as the avatar for your bot, or leave this blank if you want to use an Emoji instead (see the next property). This image can be anywhere on the web or hosted on Slack as long as you’ve shared it to the whole team and not left it private.</td>
    </tr>
    <tr>
      <td>slack_message.default_icon_emoji</td>
      <td>Specify an Emoji code including surrounding colons, such as “:warning:”. Slack uses the same Emoji codes as Campfire and Github, for which there is a very handy <a href="http://www.emoji-cheat-sheet.com">Emoji cheat sheet</a>. This setting will be ignored if you’ve specified an image URL instead (see the previous property).</td>
    </tr>
  </tbody>
</table>

<p>I called these all defaults since, as I’ll show below, you can override any or all of them in your individual Business Rules. As a best practice I recommend you rename these to include a company prefix, e.g. <strong>acme.slack_message.default_endpoint</strong>. If you do, just don’t forget to modify all the gs.getProperty() calls in the Script Include to match the new names.</p>

<h2 id="examples">Examples</h2>

<p>Here’s how easy it is to send a message into the default channel using this Script Include. In an advanced Business Rule (with conditions specified however you like), just call the send() method on a new SlackMessage object:</p>

<p>[code language=”javascript”]var slack = new SlackMessage();<br />
slack.send(‘Hello World!’);[/code]</p>

<p>To send the message to a different channel or as a direct message to an individual, simply specify the channel or user in a second parameter of the send() method:</p>

<p>[code language=”javascript”]var slack = new SlackMessage();<br />
slack.send(‘Hello World!’, ‘#random’);[/code]</p>

<p>[code language=”javascript”]var slack = new SlackMessage();<br />
slack.send(‘Hello World!’, ‘@joeyday’);[/code]</p>

<p>You can get even more fancy using the payload member variable to override defaults and/or specify additional API options (this is very similar to the Business Rule script I actually wrote for the team I mentioned in the opening paragraph):</p>

<p>[code language=”javascript”]// Initialize a new SlackMessage<br />
var slack = new SlackMessage();</p>

<p>// Set up the payload<br />
slack.payload.text = ‘An Incident has been assigned to ‘ + current.assignment_group.name + ‘:’;<br />
slack.payload.icon_emoji = ‘:exclamation:’;<br />
slack.payload.attachments.push({<br />
 ‘title’: current.number.toString(),<br />
 ‘title_link’: ‘https://’ + gs.getProperty(‘instance_name’) + ‘.service-now.com/nav_to.do?uri=incident.do?sys_id=’ + current.sys_id,<br />
 ‘text’: current.short_description.toString()<br />
});</p>

<p>// Fire off the message<br />
slack.send();[/code]</p>

<p>The above will result in a Slack message looking like this:</p>

<p><a href="http://joeyday.com/wp-content/uploads/2015/03/Screenshot-2015-03-01-23.24.23.png"><img src="http://joeyday.com/wp-content/uploads/2015/03/Screenshot-2015-03-01-23.24.23.png" alt="Slack Message" /></a></p>

<p>Don’t miss how you can call the send() method without any parameters as long as you’ve specified useful values in the payload beforehand, and that you may need to use toString() when you set values if you’re not using string concatenation or something else that would implicitly cast the contents of a record field into a string.</p>

<p>There are more options I haven’t gone over here and I encourage you to read up on the Slack <a href="https://api.slack.com/incoming-webhooks">Incoming Webhooks API</a> for more details. The important thing to notice is that “payload” is a JavaScript object that gets encoded as JSON and sent in as the Request Body of the REST message, so you can declare any option you want from the Incoming Webhooks API as a key/value pair in that payload object and it should work.</p>

<p>I should mention that you can even override the default endpoint if you need to send a message to an entirely different team:</p>

<p>[code language=”javascript” autolinks=”false”]var slack = new SlackMessage();<br />
slack.endpoint = ‘https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx’;<br />
slack.send(‘Hello World!’, ‘#general’);[/code]</p>

<p>Lastly, the send() method returns the server response. If you store that response you can debug by grabbing values out of it as shown below:<br />
[code language=”javascript”]// Send the message<br />
var slack = new SlackMessage();<br />
var response = slack.send(‘Hello World!’);</p>

<p>// Show error message if it failed<br />
if(response.getStatusCode() != 200) {<br />
 gs.addInfoMessage(“response.getBody: “ + response.getBody());<br />
 gs.addInfoMessage(“response.getStatusCode: “ + response.getStatusCode());<br />
 gs.addInfoMessage(“response.getErrorMessage: “ + response.getErrorMessage());<br />
}[/code]</p>

<h2 id="ideas">Ideas</h2>

<p>What sorts of things can you do with this? The sky’s the limit really. What I was tasked with doing was simply send a message into a specific channel whenever an Incident was assigned to a specific User Group. By specifying different conditions in your Business Rule, you could send messages only if an Incident is high priority or attached to certain Configuration Items.</p>

<p>One idea I thought of is adding a Slack username field to the sys_user table and then using that to push a private message whenever a user has a ticket assigned to them, something like:</p>

<p>[code language=”javascript”]if (current.assigned_to.u_slack_username != ”) {<br />
 slack.send(‘Ticket ‘ + current.number + ‘ has been assigned to you!’, ‘@’ + current.assigned_to.u_slack_username);<br />
}[/code]</p>

<p>If you use this Script Include for something interesting, I’d love if you’d leave a comment below or drop me a line on social media. Cheers! <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="api" /><category term="integration" /><category term="rest" /><category term="servicenow" /><category term="slack" /><category term="web services" /><summary type="html"><![CDATA[One of our teams at work recently started using Slack for all their team communication and approached me about possibly having ServiceNow shoot Incident assignment notifications into one of their Slack channels. As it turns out, integrating ServiceNow and Slack is really easy. In this article I’ll show you how to enable the necessary Slack “Incoming Webhooks” integration service as well as give you a ready-to-go ServiceNow Script Include you can use to post messages to Slack from any scripted ServiceNow Business Rule. Finally, I include some examples and ideas for how to use and re-use the Script Include in your own Business Rules. Getting access to the Slack API To get started, you’ll need an endpoint URL for the Slack API to which ServiceNow can send web service calls. To get this URL, from within any Slack channel (either in the Slack app or the Slack website), simply drop down the arrow next to the channel name and select “Add a service integration”. It doesn’t matter which channel you do this in—I’ll show below how you can use this same integration endpoint to post to any channel within the same team. On the next screen, scroll almost all the way to the bottom and click “+Add” next to “Incoming Webhooks”. Lastly, click “Add Incoming Webhooks Integration”. On the next screen you should see a long web address labeled “Webhook URL”, similar to this: https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx Save this URL someplace safe. I’ll show you where to use it in a bit. Slack doesn’t require any kind of authentication to use this API, so the tokens in this URL essentially are your password. Keep it a secret. Creating the REST Message in ServiceNow The first step in ServiceNow is to set up a REST Message. If you’re running Fuji you can actually skip this step and go straight to the Script Include, as the new RESTMessageV2 API in Fuji doesn’t require a REST Message database record. If you’re still on Eureka or want to keep the integration backwards-compatible with earlier releases for any reason, read on. Navigate to System Web Services › Outbound › REST Messages in your ServiceNow instance. Click “New” to create a new REST Message. Type “Slack” for the name. Click the lock icon to unlock the Endpoint field and type “${endpoint}” there. Type something useful in the Description field if you go for that sort of thing. Right-click in the header and choose “Save” to submit the new record and stay where you are. At the bottom of the newly-saved record, there will be a related list containing REST Message Functions, of which there should be four: “get”, “post”, “put”, and “delete”. Check the box to the left of all these but “post” and use the drop-down at the bottom to delete them. The only one we need to keep here is the “post” function. Click into the “post” function. Verify the Endpoint here is also set to “${endpoint}” (it should’ve inherited this setting from the parent REST Message). In the Content field, type “${payload}”. Click “Update”. That’s it, the REST Message and REST Message Function should be ready to go. The Script Include To create the Script Include, follow these steps: Navigate to System Definition › Script Includes in your ServiceNow instance. Click “New”. Name the Script Include “SlackMessage”. Give it a description if you want. Copy and paste the script below. Click “Submit”. Here’s the script itself: [code language=”javascript”]var SlackMessage = Class.create(); SlackMessage.prototype = { ‘initialize’: function() { if (gs.getProperty(‘slack_message.default_icon_url’) != ”) { this.payload.icon_url = gs.getProperty(‘slack_message.default_icon_url’); } else if (gs.getProperty(‘slack_message.default_icon_emoji’) != ”) { this.payload.icon_emoji = gs.getProperty(‘slack_message.default_icon_emoji’); } }, ‘send’: function (text, channel) { // Set the text and channel (or fall back to defaults) this.payload.text = text || this.payload.text; this.payload.channel = channel || this.payload.channel; // Encode the payload as JSON var SNJSON = JSON; // Workaround for JSLint warning about using JSON as a constructor var myjson = new SNJSON(); var encoded_payload = myjson.encode(this.payload); // Create and send the REST Message var msg = new RESTMessage(‘Slack’, this.method); msg.setStringParameter(‘endpoint’, this.endpoint); msg.setXMLParameter(‘payload’, encoded_payload); var res = msg.execute(); return res; }, ‘endpoint’: gs.getProperty(‘slack_message.default_endpoint’), ‘method’: ‘post’, ‘payload’: { ‘channel’: gs.getProperty(‘slack_message.default_channel’), ‘username’: gs.getProperty(‘slack_message.default_username’), ‘text’: ”, ‘attachments’: [] }, ‘type’: ‘SlackMessage’ };[/code] Now, I said earlier if you’re running Fuji you could skip the step for adding the REST Message. If you did that, you’ll want to find the following block of code in the above script . . . [code language=”javascript” firstline=”23″]// Create and send the REST Message var msg = new RESTMessage(‘Slack’, this.method); msg.setStringParameter(‘endpoint’, this.endpoint); msg.setXMLParameter(‘payload’, encoded_payload); var res = msg.execute(); return res;[/code] . . . And replace it with this: [code language=”javascript” firstline=”23″]// Create and send the REST Message var msg = new sn_ws.RESTMessageV2(); msg.setEndpoint(this.endpoint); msg.setHttpMethod(this.method); msg.setRequestBody(encoded_payload); var res = msg.execute(); return res;[/code] This alternative code instantiates a new RESTMessageV2 object (instead of the older RESTMessage object) and does so without specifying any parameters. One of the neat things about the new version of the Outbound REST API is you don’t have to specify an existing REST Message, but can use the setter methods you see here to set the Endpoint and the HTTP Method, as well as directly set the Content Body of the message, all at runtime. Obviously if there’s a lot of setup (special headers and authentication and such) you may still want to go the former route of declaring the REST Message up front and calling it here, but since the Slack API doesn’t require any of that, being able to do it this way keeps things simple. System Properties You may have noticed the Script Include pulls in a handful of System Properties. I like using System Properties because they allow you to change settings on the fly later without re-deploying code. You can get a list view of all System Properties by typing “sys_properties.list” in the search box at the top of the left sidebar in ServiceNow. To create a new System Property from there, simply click the “New” button. You’ll need to declare the following System Properties in your instance for the Script Include above to function properly: Name Value slack_message.default_endpoint Here’s where you should paste that Slack Webhook URL you put in a safe place way back in the first section above. slack_message.default_channel Specify the channel to which you’ll post the majority of your Slack messages, with a leading hash symbol, such as “#general” or “#servicenow”. The Slack API also allows specifying a person’s username preceded by an at symbol to send direct messages instead, such as “@joeyday”. slack_message.default_username This is the display name of the bot that will post the messages. I set mine to “ServiceNow”, but this could be anything you like. slack_message.default_icon_url Specify a URL of an image to be used as the avatar for your bot, or leave this blank if you want to use an Emoji instead (see the next property). This image can be anywhere on the web or hosted on Slack as long as you’ve shared it to the whole team and not left it private. slack_message.default_icon_emoji Specify an Emoji code including surrounding colons, such as “:warning:”. Slack uses the same Emoji codes as Campfire and Github, for which there is a very handy Emoji cheat sheet. This setting will be ignored if you’ve specified an image URL instead (see the previous property). I called these all defaults since, as I’ll show below, you can override any or all of them in your individual Business Rules. As a best practice I recommend you rename these to include a company prefix, e.g. acme.slack_message.default_endpoint. If you do, just don’t forget to modify all the gs.getProperty() calls in the Script Include to match the new names. Examples Here’s how easy it is to send a message into the default channel using this Script Include. In an advanced Business Rule (with conditions specified however you like), just call the send() method on a new SlackMessage object: [code language=”javascript”]var slack = new SlackMessage(); slack.send(‘Hello World!’);[/code] To send the message to a different channel or as a direct message to an individual, simply specify the channel or user in a second parameter of the send() method: [code language=”javascript”]var slack = new SlackMessage(); slack.send(‘Hello World!’, ‘#random’);[/code] [code language=”javascript”]var slack = new SlackMessage(); slack.send(‘Hello World!’, ‘@joeyday’);[/code] You can get even more fancy using the payload member variable to override defaults and/or specify additional API options (this is very similar to the Business Rule script I actually wrote for the team I mentioned in the opening paragraph): [code language=”javascript”]// Initialize a new SlackMessage var slack = new SlackMessage(); // Set up the payload slack.payload.text = ‘An Incident has been assigned to ‘ + current.assignment_group.name + ‘:’; slack.payload.icon_emoji = ‘:exclamation:’; slack.payload.attachments.push({ ‘title’: current.number.toString(), ‘title_link’: ‘https://’ + gs.getProperty(‘instance_name’) + ‘.service-now.com/nav_to.do?uri=incident.do?sys_id=’ + current.sys_id, ‘text’: current.short_description.toString() }); // Fire off the message slack.send();[/code] The above will result in a Slack message looking like this: Don’t miss how you can call the send() method without any parameters as long as you’ve specified useful values in the payload beforehand, and that you may need to use toString() when you set values if you’re not using string concatenation or something else that would implicitly cast the contents of a record field into a string. There are more options I haven’t gone over here and I encourage you to read up on the Slack Incoming Webhooks API for more details. The important thing to notice is that “payload” is a JavaScript object that gets encoded as JSON and sent in as the Request Body of the REST message, so you can declare any option you want from the Incoming Webhooks API as a key/value pair in that payload object and it should work. I should mention that you can even override the default endpoint if you need to send a message to an entirely different team: [code language=”javascript” autolinks=”false”]var slack = new SlackMessage(); slack.endpoint = ‘https://hooks.slack.com/services/xxxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx’; slack.send(‘Hello World!’, ‘#general’);[/code] Lastly, the send() method returns the server response. If you store that response you can debug by grabbing values out of it as shown below: [code language=”javascript”]// Send the message var slack = new SlackMessage(); var response = slack.send(‘Hello World!’); // Show error message if it failed if(response.getStatusCode() != 200) { gs.addInfoMessage(“response.getBody: “ + response.getBody()); gs.addInfoMessage(“response.getStatusCode: “ + response.getStatusCode()); gs.addInfoMessage(“response.getErrorMessage: “ + response.getErrorMessage()); }[/code] Ideas What sorts of things can you do with this? The sky’s the limit really. What I was tasked with doing was simply send a message into a specific channel whenever an Incident was assigned to a specific User Group. By specifying different conditions in your Business Rule, you could send messages only if an Incident is high priority or attached to certain Configuration Items. One idea I thought of is adding a Slack username field to the sys_user table and then using that to push a private message whenever a user has a ticket assigned to them, something like: [code language=”javascript”]if (current.assigned_to.u_slack_username != ”) { slack.send(‘Ticket ‘ + current.number + ‘ has been assigned to you!’, ‘@’ + current.assigned_to.u_slack_username); }[/code] If you use this Script Include for something interesting, I’d love if you’d leave a comment below or drop me a line on social media. Cheers! ]]></summary></entry><entry><title type="html">Two Covenants</title><link href="https://dev.joeyday.com/2014/11/23/two-covenants/" rel="alternate" type="text/html" title="Two Covenants" /><published>2014-11-23T16:05:28+00:00</published><updated>2014-11-23T16:05:28+00:00</updated><id>https://dev.joeyday.com/2014/11/23/two-covenants</id><content type="html" xml:base="https://dev.joeyday.com/2014/11/23/two-covenants/"><![CDATA[<p>In what way are the major divisions of the Bible “testaments” or “covenants”? Are “Old Testament” and “New Testament” just arbitrary titles, or are we really to understand them as somehow actually being covenants?</p>

<p>The other day I wrote about the biblical warrant for calling our two major divisions of Scripture the Old and New Testaments. I explained that 2 Corinthians 3:6, 14 is a solid basis for calling them what we do, either formally or at least informally. Today I want to peel back the onion one more layer. I want to get deeper into why we can and should call them covenants, beyond simply that the Bible itself calls them that.</p>

<figure aria-describedby="caption-attachment-2521" class="wp-caption alignleft" id="attachment_2521" style="width: 190px">[![Michael Kruger’s “Canon Revisited”](http://joeyday.com/wp-content/uploads/2014/10/9781433505003.jpg)](http://amzn.com/1433505002/?tag=joeyday-20)<figcaption class="wp-caption-text" id="caption-attachment-2521">Michael Kruger’s “Canon Revisited”</figcaption></figure>
<p>For this I have to lean on a book I recently read, Michael Kruger’s <a href="http://amzn.com/1433505002/?tag=joeyday-20" title="Michael Kruger’s “Canon Revisited”"><em>Canon Revisited: Establishing the Origins and Authority of the New Testament Books</em></a>. In a chapter on the apostolic origins of the New Testament canon, Kruger tackles exactly this question, concluding that canon is itself derived from redemption and covenant.</p>

<p>If you think about it, what are the most important sections of our two testaments? For the Old Testament, the important section is the Torah, the Pentateuch, the five Books of Moses, which tell of God establishing the old covenant with his people Israel through Moses as mediator. The important section of the New Testament is the four Gospels, which tell of a new covenant God has established with his people through Christ as mediator.</p>

<p>What about the rest of Scripture? Everything else in the Old Testament took place and was written in the context of the old covenant, and everything else in the New Testament was written in the context of the new covenant. Kruger explains, taking a cue from other scholars such as Meredith Kline, that the prophetic books of the Old Testament and the epistles of the New Testament function as “covenant lawsuits”, bringing charges against God’s covenant people for various offenses against the covenant. So these secondary documents even function as an important part of the written form of each covenant.</p>

<p>So our two collections of Scripture are certainly <em>about</em> covenants, but in what sense can we call them covenants or testaments in and of themselves? Here, Kruger helpfully explains that every covenant in the ancient near east included certain elements. One important element was the depositing of a written copy of the covenant to be kept by both parties in a safe place. A notable example of this custom is the ten commandments, written by God himself on stone tablets, being deposited in the Ark of the Covenant and kept in the tabernacle and later the temple in Jerusalem. Kruger argues, and I agree, what we have in our Old and New Testaments is nothing less than the written deposit of God’s covenants with man.</p>

<p>Given this function of these written texts, it is right not only to say they are <em>about</em> covenants, but to call them covenants in and of themselves. And isn’t this what we saw in my previous article? Paul considered the written text of the Old Testament synonymous with the old covenant when he said in 2 Corinthians 3:14: “when they read the old covenant”. <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="bible" /><category term="canon" /><category term="covenant" /><category term="new testament" /><category term="old testament" /><category term="scripture" /><category term="testament" /><summary type="html"><![CDATA[In what way are the major divisions of the Bible “testaments” or “covenants”? Are “Old Testament” and “New Testament” just arbitrary titles, or are we really to understand them as somehow actually being covenants? The other day I wrote about the biblical warrant for calling our two major divisions of Scripture the Old and New Testaments. I explained that 2 Corinthians 3:6, 14 is a solid basis for calling them what we do, either formally or at least informally. Today I want to peel back the onion one more layer. I want to get deeper into why we can and should call them covenants, beyond simply that the Bible itself calls them that. [![Michael Kruger’s “Canon Revisited”](http://joeyday.com/wp-content/uploads/2014/10/9781433505003.jpg)](http://amzn.com/1433505002/?tag=joeyday-20)Michael Kruger’s “Canon Revisited” For this I have to lean on a book I recently read, Michael Kruger’s Canon Revisited: Establishing the Origins and Authority of the New Testament Books. In a chapter on the apostolic origins of the New Testament canon, Kruger tackles exactly this question, concluding that canon is itself derived from redemption and covenant. If you think about it, what are the most important sections of our two testaments? For the Old Testament, the important section is the Torah, the Pentateuch, the five Books of Moses, which tell of God establishing the old covenant with his people Israel through Moses as mediator. The important section of the New Testament is the four Gospels, which tell of a new covenant God has established with his people through Christ as mediator. What about the rest of Scripture? Everything else in the Old Testament took place and was written in the context of the old covenant, and everything else in the New Testament was written in the context of the new covenant. Kruger explains, taking a cue from other scholars such as Meredith Kline, that the prophetic books of the Old Testament and the epistles of the New Testament function as “covenant lawsuits”, bringing charges against God’s covenant people for various offenses against the covenant. So these secondary documents even function as an important part of the written form of each covenant. So our two collections of Scripture are certainly about covenants, but in what sense can we call them covenants or testaments in and of themselves? Here, Kruger helpfully explains that every covenant in the ancient near east included certain elements. One important element was the depositing of a written copy of the covenant to be kept by both parties in a safe place. A notable example of this custom is the ten commandments, written by God himself on stone tablets, being deposited in the Ark of the Covenant and kept in the tabernacle and later the temple in Jerusalem. Kruger argues, and I agree, what we have in our Old and New Testaments is nothing less than the written deposit of God’s covenants with man. Given this function of these written texts, it is right not only to say they are about covenants, but to call them covenants in and of themselves. And isn’t this what we saw in my previous article? Paul considered the written text of the Old Testament synonymous with the old covenant when he said in 2 Corinthians 3:14: “when they read the old covenant”. ]]></summary></entry><entry><title type="html">Two Testaments</title><link href="https://dev.joeyday.com/2014/11/09/two-testaments/" rel="alternate" type="text/html" title="Two Testaments" /><published>2014-11-09T22:28:22+00:00</published><updated>2014-11-09T22:28:22+00:00</updated><id>https://dev.joeyday.com/2014/11/09/two-testaments</id><content type="html" xml:base="https://dev.joeyday.com/2014/11/09/two-testaments/"><![CDATA[<figure aria-describedby="caption-attachment-2600" class="wp-caption alignleft" id="attachment_2600" style="width: 300px">![Bible by Adam Dimmick](http://joeyday.com/wp-content/uploads/2014/11/2680204486_188ed5040f_b.jpg)<figcaption class="wp-caption-text" id="caption-attachment-2600">Photo credit: [Bible](https://www.flickr.com/photos/fatmanad/2680204486/) by [Adam Dimmick](https://www.flickr.com/people/fatmanad/)</figcaption></figure>
<p>Where do the terms “Old Testament” and “New Testament” come from? What do they mean? Is it right for us to name the two divisions of our Bible this way?</p>

<p><a href="http://en.wikipedia.org/wiki/Melito_of_Sardis" title="Melito of Sardis">Melito of Sardis</a> is widely regarded as the person who coined the terms “Old Testament” and “New Testament”. His is the earliest known canon (list of books) of the Old Testament (circa 180 CE). But I discovered recently that the New Testament itself spoke of the Old and New Testaments a century before Melito.</p>

<p>In 2 Corinthians 3:14, Paul refers to the “old covenant” as a written document (“when they read the old covenant”). It’s important to explain here that the word for “covenant” in this passage is the Greek διαθήκη (<em>diathēkē</em>), which variously means “testament” or “covenant”. Look at this same passage in the King James and you’ll find the word “testament” there in place of “covenant”. When Melito of Sardis coined the terms, he was writing in Greek and used exactly this word διαθήκη.</p>

<p>What’s remarkable is that just 8 verses back, in 2 Corinthians 3:6, Paul states that God has made him and the other apostles “ministers of a new covenant” (same word διαθήκη, and if you look at the KJV you’ll find “new testament” here). It’s hard to escape the implication that Paul knew he was contributing to a new collection of Scripture which he would at least informally have referred to as the “new covenant” just as he referred to the Jewish Scriptures as the “old covenant”.</p>

<p>It’s impossible to know for certain if this is where Melito got his terms for the Old and New Testaments, but I think it’s a remarkable coincidence if not. I think perhaps it would be better for us to call them the Old and New Covenants, but Testament is a valid translation of διαθήκη and it’s rather late to change our conventional English wording now. Whether we call them Covenants or Testaments, though, I see 2 Corinthians 3:6, 14 as a solid biblical basis for naming our two collections of Scripture as we do. <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="bible" /><category term="canon" /><category term="covenant" /><category term="melito of sardis" /><category term="new testament" /><category term="old testament" /><category term="paul" /><category term="scripture" /><category term="testament" /><summary type="html"><![CDATA[![Bible by Adam Dimmick](http://joeyday.com/wp-content/uploads/2014/11/2680204486_188ed5040f_b.jpg)Photo credit: [Bible](https://www.flickr.com/photos/fatmanad/2680204486/) by [Adam Dimmick](https://www.flickr.com/people/fatmanad/) Where do the terms “Old Testament” and “New Testament” come from? What do they mean? Is it right for us to name the two divisions of our Bible this way? Melito of Sardis is widely regarded as the person who coined the terms “Old Testament” and “New Testament”. His is the earliest known canon (list of books) of the Old Testament (circa 180 CE). But I discovered recently that the New Testament itself spoke of the Old and New Testaments a century before Melito. In 2 Corinthians 3:14, Paul refers to the “old covenant” as a written document (“when they read the old covenant”). It’s important to explain here that the word for “covenant” in this passage is the Greek διαθήκη (diathēkē), which variously means “testament” or “covenant”. Look at this same passage in the King James and you’ll find the word “testament” there in place of “covenant”. When Melito of Sardis coined the terms, he was writing in Greek and used exactly this word διαθήκη. What’s remarkable is that just 8 verses back, in 2 Corinthians 3:6, Paul states that God has made him and the other apostles “ministers of a new covenant” (same word διαθήκη, and if you look at the KJV you’ll find “new testament” here). It’s hard to escape the implication that Paul knew he was contributing to a new collection of Scripture which he would at least informally have referred to as the “new covenant” just as he referred to the Jewish Scriptures as the “old covenant”. It’s impossible to know for certain if this is where Melito got his terms for the Old and New Testaments, but I think it’s a remarkable coincidence if not. I think perhaps it would be better for us to call them the Old and New Covenants, but Testament is a valid translation of διαθήκη and it’s rather late to change our conventional English wording now. Whether we call them Covenants or Testaments, though, I see 2 Corinthians 3:6, 14 as a solid biblical basis for naming our two collections of Scripture as we do. ]]></summary></entry><entry><title type="html">Treasure</title><link href="https://dev.joeyday.com/2014/11/02/treasure/" rel="alternate" type="text/html" title="Treasure" /><published>2014-11-02T12:19:31+00:00</published><updated>2014-11-02T12:19:31+00:00</updated><id>https://dev.joeyday.com/2014/11/02/treasure</id><content type="html" xml:base="https://dev.joeyday.com/2014/11/02/treasure/"><![CDATA[<p>Since I began studying covenant theology in the past couple years, I’ve become partial to verses of hymns that speak of God being our highest treasure, portion, or inheritance.</p>

<p>My new favorite verse of <cite>Be Thou My Vision</cite> is:</p>

<blockquote>
  <p>Riches I heed not nor man’s empty praise,<br />
Thou mine inheritance now and always;<br />
Thou and thou only first in my heart;<br />
High king of heaven, my treasure thou art. ((<cite>Be Thou My Vision</cite>, Eleanor Hull, 1912.))</p>
</blockquote>

<p>My new favorite verse of <cite>Amazing Grace</cite> is:</p>

<blockquote>
  <p>The Lord has promised good to me,<br />
His word my hope secures;<br />
He will my shield and portion be,<br />
As long as life endures. ((<cite>Amazing Grace</cite>, John Newton, 1779.))</p>
</blockquote>

<p>These hymn verses echo for me the promise of God reiterated in every progressive revelation of the covenant of grace that he will be God to us and we will be his people (Genesis 17:7; Leviticus 26:11–12; Jeremiah 31:33; Revelation 21:3). They remind me that God is indeed “the strength of my heart and my portion forever” (Psalm 73:26). <img src="/assets/images/endmark.png" class="endmark" /></p>]]></content><author><name>Joey</name></author><category term="essay" /><category term="amazing grace" /><category term="be thou my vision" /><category term="covenant of grace" /><category term="covenant theology" /><category term="god" /><category term="inheritance" /><category term="portion" /><category term="praise" /><category term="treasure" /><category term="worship" /><summary type="html"><![CDATA[Since I began studying covenant theology in the past couple years, I’ve become partial to verses of hymns that speak of God being our highest treasure, portion, or inheritance. My new favorite verse of Be Thou My Vision is: Riches I heed not nor man’s empty praise, Thou mine inheritance now and always; Thou and thou only first in my heart; High king of heaven, my treasure thou art. ((Be Thou My Vision, Eleanor Hull, 1912.)) My new favorite verse of Amazing Grace is: The Lord has promised good to me, His word my hope secures; He will my shield and portion be, As long as life endures. ((Amazing Grace, John Newton, 1779.)) These hymn verses echo for me the promise of God reiterated in every progressive revelation of the covenant of grace that he will be God to us and we will be his people (Genesis 17:7; Leviticus 26:11–12; Jeremiah 31:33; Revelation 21:3). They remind me that God is indeed “the strength of my heart and my portion forever” (Psalm 73:26). ]]></summary></entry></feed>