Skip navigation

Developer Community

5 Posts authored by: Josh Nerius Employee

YouTube video: https://www.youtube.com/watch?v=NyFooBktJNE

 

The Live Coding Team: josh.nerius, dave.slusher, ctomasi

 

In last week’s Live Coding Happy Hour, we picked up where we left off previous week and figured out how to programmatically get an OAuth Access Token from the oauth_credential table for our Bit.ly integration.

 

Video Index

 

 

Useful Resources

 

 

Corrections

 

A Better Approach for Getting Access Tokens

 

In the Live Coding session, we used a manual approach to query the oauth_credential able to get an Access Token using GlideRecord, but there’s actually a much better way to accomplish this using GlideOAuthClient.

 

Before (manual GlideRecord lookup)

function getToken(profile, user) {   
    var gr = new GlideRecord('oauth_credential');    
    gr.addQuery('oauth_requestor_profile.oauth_entity_profile', profile);    
    gr.addQuery('user', user);
    gr.query();       

    if (gr.next()) {      
        gs.debug('found it');       
        return gr.getValue('token');   
    } else {      
        gs.debug(':(');    
    }
}

var token = getToken(oauthProfileId, userId); 

 

After (using GlideOAuthClient API)

// Initialize client and get a token object  
var oauthClient = new sn_auth.GlideOAuthClient();
var tokenObject = oauthClient.getToken(restMessageId, oauthProfileId);  
var accessToken = tokenObject.getAccessToken();  
gs.debug('Access Token: ' + accessToken);

 

 

Handling a non-expiring token

 

I mentioned that I’d show you how to handle the non-expiring token sent by Bit.ly, but didn’t get around to it during the broadcast.

To accomplish this, we need to add a single line to the postProcessAccessToken method in our custom OAuthBitlyHandler Script Include:

 

// Manually set the expire time to 1 year in seconds (60 * 60 * 24 * 365) 
paramMap.put(‘expires_in’, ‘31536000’); 

 

In this example, I’ve set the expiration time to 1 year, but you should set this to whatever value makes sense for you and your password rotation schedule (you should go and regenerate this periodically for security reasons, even if the the API doesn’t enforce token rotation).

Josh Nerius | Developer Evangelist | @NeriusNow | Get started at developer.servicenow.com

Last week, I attended the Integrate 2016 + API:World Conference & Expo (and Hackathon!) in San Jose. I met some great people, learned about some pretty interesting APIs and Integration products, and as a guy who spent his first several years with the ServiceNow platform working on APIs and building integrations, it was great getting to geek out on a subject I’ve grown to love.

 

Hackathon

 

hackathon.jpg

I spent Saturday/Sunday hacking on the HPE Haven Sentiment API, meeting other developers, and was thoroughly impressed by the resulting hackathon projects. The current abundance of publicly available APIs is allowing teams to build truly remarkable apps faster than ever before, and this hackathon was well positioned to demonstrate what a small team can build in just over a day.

 

I walked away inspired by the passion of the participants and the ambition of the projects they tackled. The winner: an augmented reality mobile app that uses the device’s camera to look at a food package and tell the user whether or not it’s safe for them to eat based on that user’s allergies. Its target audience is kids who may not know what’s safe to eat when away from their parents, and it was a pretty impressive use of machine learning APIs to provide a potentially life-saving function.

 

Sessions

 

I couldn’t attend them all, but here are some of my takeaways from the week.twilio.jpg

 

ServiceNow’s own Robert Duffner participated in a panel discussing the process of Integrating API Management Into Your Business Strategy. Robert discussed the high priority placed on APIs in the ServiceNow platform to enable customers to quickly integrate with their existing systems. The Cloud Elements blog published a great summary of this panel discussion.

 

Microservices microservices microservices. I heard this term so many times, it no longer sounded like a word. The term is used to describe a methodology of software development in which an application is broken down into “micro” components and each dev team owns all aspects of that component. The goal is to reduce friction between development teams, making it easier to deploy features independently, and in theory, there are quite a few benefits to the approach. A few observations:

 

  • The term is increasingly used when describing the methodologies some companies have been using since long before the word existed (some even asked: is this SOA revisited?).
  • Not everyone fully agreed on the definition of what a microservice actually is.
  • Before you use the term to describe something you are building, it’s probably a good idea to spend some time reading Martin Fowler’s excellent overview of the subject.
  • Microservices aren't for everyone. Don't develop this way just because it's a popular thing on Hacker News - make sure your organization will benefit from the approach.

 

Twilio’s Patrick Malatack gave a great session on the consequences of an unhealthy API, and

raised some key points that really resonated well. Summarized:

 

  • APIs are for Humans, should be human readable, should allow developers to opt-in to complexity
  • Diamonds are forever, APIs are forever, be careful when changing APIs and don’t break things
  • Invest heavily in API documentation to keep developers happy

 

Expo

 

I gained the most value from meeting people in the expo area, and learned about some pretty interesting products that I was previously unaware of. It was particularly gratifying to walk up to a booth, introduce myself as a Developer Evangelist from ServiceNow, and see the person’s face light up and respond “oh yeah, I’ve heard about ServiceNow, we want to build an integration with you guys” (some already have!).

 

Major theme: products that simplify the process of building integrations between a myriad of APIs/products.

 

built-io.jpg

Built.io is an Integration-Platform-as-a-Service product that simplifies the process of building integrations, bots, interacting with IoT devices and more. I spent some time chatting with these guys, and even set up a simple integration with my ServiceNow developer instance using their built-in ServiceNow connector. Cool stuff.

 

Stamplay is another automation/integration framework that makes it easy to chain API calls and pull multiple services together without having to build each integration point from scratch. I haven’t had a chance to play with this yet, but fully intend to see if I can make this talk to my ServiceNow instance in the near future.

 

Microsoft Azure Logic Apps is also an integration platform (see the trend here?) that simplifies the process of pulling multiple APIs together, and provides many connectors to existing services/systems. Also on my list of things to explore further as an integration geek.

 

Amazon API Gateway is a managed service that allows developers to publish/maintain/monitor their APIs centrally, even if those APIs are backed by many disparate services. Expect a future post from me about setting this up with ServiceNow REST APIs if you need the capabilities provided by Amazon.

 

Summary

 

I’m not endorsing any of these products or advertising for these companies, but wanted to share some of the more interesting findings from my time at this conference. I’m excited by the trends I saw throughout the week and the promise of better tools to make hard problems easier to solve.

 

For ServiceNow customers, offerings like these promise to make it increasingly simple to leverage the power of dozens of APIs and services in a fraction of the time it would traditionally take learn the underlying APIs and build the requisite integration logic. Querying APIs that enrich your business processes, "eBonding" with other systems and similar use cases become much easier than ever before.

 

For ServiceNow developers, these offerings open up a world of possibilities when building new apps, and make it easier to build complex API mashups to augment your application's business logic or enable connectivity with the 3rd party services your customers expect.

 

Stay tuned as I explore some of these tools in more depth!

Josh Nerius | Developer Evangelist | @NeriusNow | Get started at developer.servicenow.com

I know it's late in the day, but I couldn't pass up the opportunity to blog about International Talk Like a Pirate Day.

 

In this quick tutorial, we'll create a simple scoped integration app that uses ARRPI, the Talk like a Pirate translation API.

 

I've already created a skeleton (arrrrrr, see what I did there?) scoped app where I'll write my code, and I'll assume you know how to do the same (if not, check out Building a ServiceNow Application on the Developer Portal).

 

1. Configure a REST Message

 

From Studio, Create New Application File > REST Message with the following details:

 

 

Open the automatically generated get method:

Talk_Like_a_Pirate___Unlinked_get_method.png

 

Create two HTTP Query Parameters:

Talk_Like_a_Pirate___Unlinked.png

 

Navigate back to the REST Message, and from the Related Lists section, create a new Variable Substitution so we can test our call:

Talk_Like_a_Pirate___var_sub.png

 

Test everything out by clicking the Test related link:

Talk_Like_a_Pirate___test.png

With any luck, we should see a result that looks like this:

Talk_Like_a_Pirate___test_run.png

 

We're now ready to start consuming the API elsewhere in the platform.

 

2. Create a Business Rule

 

Now we'll create a business rule to execute our piratey logic.

 

From Studio, Create New Application File > Business Rule with the following details:

 

  • Name: Translate, Arrrrrrrr
  • Table: Task
  • Advanced: checked
  • When: async
  • Insert: checked
  • Condition: new GlideDate().getByFormat('yyyy-MM-dd').endsWith('09-19')

 

Script:

 

Note: you'll need to update lines 3 and 22 depending on the name of your application scope. Replace x_48785_tlap with your application's scope name.

 

(function executeRule(current, previous /*null when async*/) {
  // Let's decide which fields to translate
  var fields = gs.getProperty('x_48785_tlap.fields_to_translate').split(',');
  var value;
  var translated;

  // Use some ECMA5 goodness to loop through the fields
  fields.forEach(function(field) {
      var value = current.getValue(field);

      // If the field holds a value, translate it! 
      if (value) {
          translated = englishToPirate(current.getValue(field));
          current.work_notes += 'Pirate ' + current[field].getLabel() + ': ' + translated + '\n';
      }
  });

  current.update();

})(current, previous);

// Encapsulate our translation logic so it's easy to reuse
function englishToPirate(text) {
  try {
      var r = new sn_ws.RESTMessageV2('x_48785_tlap.Talk Like a Pirate API', 'get');
      r.setStringParameter('text', text);

      var response = r.execute();
      var responseBody = response.getBody();
      var httpStatus = response.getStatusCode();
      var responseObj = JSON.parse(responseBody);
      return responseObj.translation.pirate || '';
  } catch(ex) {
      var message = ex.getMessage();
  }
}

 

3. Create a System Property

 

Finally, let's create a system property to control which fields get translated.

 

From Studio, Create New Application File > System Property with the following values:

 

  • Suffix: fields_to_translate
  • Type: string
  • Value: short_description,description

 

Let's try it out!

 

Go create a new incident and give it a short description of "Hello my friend, do you know where I can find a printer around here?". If everything worked correctly, we should see a work note  after a few moments:

 

INC0010016___ServiceNow_translated.png

 

Conclusion

 

This may seem like a silly example, but it demonstrates a fairly common integration scenario: make a call to an external API when a record is created and do something with the result. It also shows that it really is this easy to set up an integration with an external REST API. It took me about an hour to create the app and write this blog post, and while talking like a pirate requires less care and planning than a production integration, setting up a simple call to an external API may not be as hard as as you think.

 

If you'd like to play with this on your own, my sample code is available in a public github repo here. Feel free to fork this and talk like a pirate to your heart's content.

 

Arrrrrrr.

 

 

/**

* Josh Nerius | Developer Evangelist

* ServiceNow | The Enterprise Cloud Company

* (m) 312-600-4994 | Chicago, IL | Central Time

* Get started at developer.servicenow.com

*/

Josh Nerius | Developer Evangelist | @NeriusNow | Get started at developer.servicenow.com

One of the things dave.slusher and I do every day is read/respond to the feedback you submit on developer.servicenow.com. We recently noticed that a number of developers have been running into an odd dictionary issue. This issue could occur in any app, but I'll focus on fixing it in the Marketing Events application we build as part of the Building a sample ServiceNow application course.

 

The Problem

 

Applies to: Helsinki

 

If we:

 

  1. Create a table that extends task
  2. Change our minds and delete the table we just created
  3. Change our minds again and create a table with the same name

 

This happens:

 

blog_aug15_img1.png

 

For the benefit of search engines:

 

Error 1: Invalid 'Table' selected on the Module record. The 'Task' table is in application 'Global', but the current application is 'Table Extension Issue'.

Error 2: Invalid 'Table' selected on the Dictionary Entry record. The 'Task' table is in application 'Global', but the current application is 'Table Extension Issue'. The 'Table' field can only select 'Global' tables with read access enabled.

 

In addition to these errors, the Label is forcefully reverted to Task, and it it initially seems like we can't create a table with this name ever again.

 

But There's a Workaround!


The good news is that we discovered a workaround. It's a bit weird, but should let you create the table with the name you want, and get you back onto a path of productivity.

 

  1. If you haven't already, delete the bad table
  2. Create a new table with exactly the same name, but do not extend task this time
  3. Delete the table you just created
  4. Now, try creating the table as you originally intended, extending task

 

Steps 2 and 3 are enough to flush out whatever was interfering before, and we're back on track.

 

We've logged an INT to track this, but hopefully this will help anyone currently stuck on the error.

 

Let me know in the comments if this workaround does or doesn't work for you!

Josh Nerius | Developer Evangelist | @NeriusNow | Get started at developer.servicenow.com

Applies to: Geneva, Helsinki and beyond.

 

Have you ever needed to:

 

  1. Retrieve something using RESTMessageV2
  2. Save that thing as an attachment on a record
  3. Do something with the newly generated attachment?

 

This recently came up as a question over in the sndevs slack channel, and I realized that I knew how to accomplish #1 and #2, but didn't know how to actually identify the attachment to achieve #3 without some ugly hacks. But as it turns out, there's a right way to do this.

 

First, some code

 

Here's a quick refresher on steps 1 and 2: retrieving and saving an attachment.

 

// This is where we'll save the attachment
var tablename   = 'incident';
var recordSysId = '8d6353eac0a8016400d8a125ca14fc1f';
var filename    = 'snlogo.png';

// Let's download the ServiceNow Logo
var logoUrl = 'https://instance.service-now.com/images/logos/logo_service-now.png';
var request = new sn_ws.RESTMessageV2();
request.setHttpMethod('get');
request.setEndpoint(logoUrl);

// Configure the request to save the response as an attachment
request.saveResponseBodyAsAttachment(tablename, recordSysId, filename);

// When we execute the request, the attachment will automatically be
// saved to the record we specified
var response = request.execute();
var httpResponseStatus = response.getStatusCode();
var httpResponseContentType = response.getHeader('Content-Type');
gs.debug("http response status_code: " + httpResponseStatus);
gs.debug("http response content-type: " + httpResponseContentType);

 

Now what?

 

If all you're concerned about is saving the attachment data to a record, you're done. No further actions are necessary. But what if you want to do something else with the attachment you just generated? Copy it somewhere, process its contents, etc?

 

The call to saveResponseBodyAsAttachment() returns void, so we can't get a reference to the attachment from there. We could do something super ugly like go query the sys_attachment table and find the latest attachment associated with our target record, but this is far from ideal and prone to bugs.

 

I was happy to learn that there's a correct way to do this.

 

Using getResponseAttachmentSysid()

 

The response object returned by request.execute() provides a method called getResponseAttachmentSysid(). As the method name suggests, this returns the sys_id of the attachment generated by the REST call.

 

To use this, we simply call the method after executing the request. We can append the following lines of code to the earlier example:

 

// Get the sys_id of the newly created attachment
var newAttachmentSysId = response.getResponseAttachmentSysid();

// Do something useful with it
var grAttach = new GlideRecord('sys_attachment');
if (grAttach.get(newAttachmentSysId)) {
  // Note: we're using the scoped version of GlideSysAttachment
  // for this example.
  var gsa = new GlideSysAttachment()
  var content = gsa.getContent(grAttach);
  // Now we have the data...the rest is up to you
}

 

You can view/download the complete script here. This can be executed from Scripts - Background, just remember to update the instance URL and target table/record.

 

Happy attaching!

Josh Nerius | Developer Evangelist | @NeriusNow | Get started at developer.servicenow.com

Filter Blog

By date: By tag: