Tag Archives: API

  • 0

FileMaker 17: Data API Multi-File Upload

Tags : 

The FileMaker 17 Data API, also known as fmrest, includes a new capability to upload data to a container field. Prior to 17 the Data API only allowed container data to be downloaded. Uploading data from a website to FileMaker had to be done using plugins, Base64 encoding, or by saving a temporary copy of the file in a spot where a FileMaker script could find it. Now we can upload to FileMaker container fields using only the Data API. This blog post describes how to use the new upload capability with HTML and JavaScript.

Before uploading to FileMaker, we need to obtain an access token. We’ll use this token to authenticate our requests to FileMaker. The 17 Data API allows you to obtain an access token using either a basic authentication header or an OAuth identity provider like Google. In this blog I’ll use basic authentication.

To simplify the code I’m using jQuery. Google makes jQuery easily available through their content delivery network. Load the library by including the following script tag at the top of your HTML:

<html>
   <head>
       <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   </head>

 

To configure basic authentication we prompt the web user for a username and password. The user needs to enter credentials for an account that has been assigned the “fmrest” extended privilege. Once the user submits this form, we concatenate their username and password, separated by a colon. Then we Base64 encode this concatenated string, which can be done in a web browser using the JavaScript method btoa. Here’s what this looks like:

   <form id="login">
       <label for="username">Username</label>
       <input name="username" type="text" />
       <label for="password">Password</label>
       <input name="password" type="password" />

       <input type="submit" value="Login" />
   </form>
  
   <script>
   let request;

   $("#login").submit(function(event){
       event.preventDefault();
       if(request) {
           request.abort();
       }

       let $form = $(this);

       let $inputs = $form.find("input");

       const auth = btoa($form.find('input[name="username"]').val() + ':' +  
$form.find('input[name="password"]').val());

       $inputs.prop('disabled', true);

 

With our newly encoded string, we’re ready to make our login request. If you used the Data API in FileMaker 16, the format of 17 API calls will be familiar to you. Note however that many of the URIs have changed. The URI for logging in has changed to “/fmi/data/v1/databases/filename/sessions”, for example.

Our login request looks like this:

       request = $.ajax({
           url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/sessions',
           type: 'post',
           headers: {
               'Content-Type': 'application/json',
               'Authorization': 'Basic ' + auth
           },
           data: {}
       });

 

If our login request is successful we store the access token in localStorage so we can use it in subsequent requests, then load the upload page. Otherwise we log an error message.

       request.done((response) => {
           localStorage.setItem('fmrest_token', response.response.token)
           $inputs.prop('disabled', false);
           location = 'blog-demo-upload.html'
       });

       request.fail((jqXHR, textStatus, errorThrown) => {
           console.log(errorThrown)
           $inputs.prop('disabled', false);
       })
   });
   </script>
</html>

 

Now that we have an access token saved in localStorage, we can begin making other requests to FileMaker, including uploading files to container fields.

My FileMaker demo file contains a simple table and layout called “Image”. The Image table contains 17’s default fields and two fields of my own: a container field named File and a text field named Name. We’ll upload a file by creating a new record in the Image table, obtaining the recordId in the response, then using the recordId in a call to insert the file into the container field.

To get started, let’s load jQuery and set up our form:

<html>
   <head>
       <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   </head>

   <form id="uploadForm">
       <label for="upload">Upload</label>
       <input name="upload" type="file" />

       <label for="name">Name</label>
       <input name="name" type="text" />

       <input type="submit" value="Upload" />
   </form>

 

Next let’s get our access token from localStorage. If we find that the token does not exist, we redirect the user back to the login page.

   <script>
       const token = localStorage.getItem('fmrest_token');
       if (token === null) {
           location = 'blog-demo-login.html'
       }

 

When the user submits the form, we need to create a new record in the Image table and obtain it’s recordId from FileMaker’s response. First let’s grab what the user typed in the “Name” field:

       let request;
       $("#uploadForm").submit(function(event) {
           event.preventDefault();
           if(request) {
               request.abort();
           }

           let $form = $(this);

           let $inputs = $form.find("input");

           $inputs.prop('disabled', true);

           let name = $('#name').prop('value');

 

To create a record using the 17 Data API, we need to send a JSON object containing the fields and values we want to set. Let’s create our JSON object:

            const fieldData = {"fieldData": {"Name": name } }
            const data = JSON.stringify(fieldData)

 

With our JSON object created, we’re ready to make our request. The URI for creating records is in the format “/fmi/data/v1/databases/database-name/layouts/layout-name/records”. Notice that in our request, we include an Authorization header set to our token, with the prefix Bearer:

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records',
               type: 'post',
               headers: {
                   'Content-Type': 'application/json',
                   'Authorization': 'Bearer ' + token
               },
               data: data
           });

 

If our request succeeded, we obtain the recordId from FileMaker’s response and pass it to our insertFile function (see below). Otherwise, we display an error.

           request.done((response) => {
               insertFile(response.response.recordId)
               $inputs.prop('disabled', false);
           });

           request.fail((jqXHR, textStatus, errorThrown) => {
               console.log(errorThrown)
               $inputs.prop('disabled', false);
           })
       })

 

Our insertFile function builds a FormData object from the file upload input box:

        const insertFile = (recordId) => {
           let formData = new FormData();
           formData.append('upload', ($("#upload"))[0].files[0]);
           console.log(formData)
           event.preventDefault();
           if(request) {
               request.abort();
           }

 

Then it sends the object to FileMaker and uses recordId to identify which record it’s trying to modify:

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records/9/containers/File/1',
               type: 'post',
               headers: {
                   'Authorization': 'Bearer ' + token
               },
               data: formData,
               cache : false,
               contentType: false,
               processData: false
           });

       }

 

Knowing what we know now about uploading a single file to FileMaker, how can we change our code to allow us to upload multiple files at once? The answer lies in the HTML Drag and Drop API. This API is built into modern browsers and allows us to assign drag and drop event listeners to our DOM elements. A property of these events is dataTransfer, which is an object containing whatever files the user dropped on our DOM element.

The following chunk of code is borrowed from Mozilla’s documentation.

First let’s set up a div on the page where users can drop their files:

<div id="dropbox" style="height:200px; width:200px; background:rgba(0,0,0,0.2)">Drop files here.</div>

 

When “dropbox” hears a “drop” event, it assigns a dataTransfer object to dt. Then we select the files from dt and pass them to a handler function, handleFiles. Our handler function simply needs to iterate through our list of files and pass them off one by one to functions that call the Data API:

       const handleFiles = (files) => {
           for(i = 0; i < files.length; i++){
               createRecord(files[i], multiInsert)
           }
       }

 

Like our process when uploading a single file, we first create a record in the Image table using createRecord then call multiInsert to upload the file:

       const createRecord = (file, callback) => {

           const fieldData = {"fieldData": {"Name": file.name } }
           const data = JSON.stringify(fieldData)

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records',
               type: 'post',
               headers: {
                   'Content-Type': 'application/json',
                   'Authorization': 'Bearer ' + token
               },
               data: data
           });

           request.done((response) => {
               callback(file, response.response.recordId)
           });

           request.fail((jqXHR, textStatus, errorThrown) => {
               console.log(errorThrown)
           });

       }


       const multiInsert = (file, recordId) => {
           const formData = new FormData();
           formData.append('upload', file);
           event.preventDefault();

           request = $.ajax({
               url: 'https://17.app.works/fmi/data/v1/databases/MultiUpload/layouts/Image/records/' + recordId + '/containers/File/1',
               type: 'post',
               headers: {
                   'Authorization': 'Bearer ' + token
               },
               data: formData,
               cache : false,
               contentType: false,
               processData: false
           });
       }

 

These code snippets should hopefully get you started working with the Data API’s container features. They can be refined and built upon to do things like validation (only accept certain file types), relate an uploaded file to a record in another table, and even traverse a folder structure. If you’re interested in learning more about 17’s features, check out our other recent blog posts.


  • 0

FileMaker Portland – Integrating Web Services

Tags : 

Thank you to everyone who attended the January FileMaker Portland meetup. The evening was packed with presentations about how people are leveraging FileMaker 16’s JSON support to connect to web services.

Some of the integrations we discussed were:

  • Magento – This popular e-commerce platform has REST and SOAP APIs. Use them to connect data about products, customers, and orders to FileMaker.
  • SmartyStreets – Supercharge your address entry fields with five different APIs.
  • Google Charts – A Javascript library for creating interactive HTML5/SVG charts.
  • FileMaker – Why not the other way around? Make POST, GET, PUT, and DELETE calls to FileMaker using the Data API.

Have something you’d like to share or discuss with other FileMaker developers? Consider attending the next meetup.


  • 0

Generating Google Charts in Web Viewer

Tags : 

Google deprecated Google Image Charts in 2012. While that API can still be used to create charts with a URL, Google currently recommends that people use the similarly named Google Charts. This blog post will go over how to format data for the Google Charts library and generate a chart within a Web Viewer object.

Classic vs Material

Google Charts offers two versions: Classic and Material. The two versions differ in how charts look and feel, and how customization options are defined. Material charts are redesigns of core charts that follow Material Design guidelines. They offer marked style improvements over Classic charts, and will appear familiar to users of Google products. However, they are currently in beta and do not support all of the features of Classic charts. If you are having trouble implementing a Material chart, make sure that you are not trying to include an unsupported customization option by referencing the open Feature Parity issue.

 

When you need to use an option that is only available for Classic charts, you can still style your chart like a Material chart by including the “theme : material” option.

 

 

DataTables

Information to be charted must be sent as a DataTable. The simplest approaches to generating DataTables within Web Viewer are by combining addColumn() and addRows(), or by using arrayToDataTable().

The addColumn() method requires either a type, or an object describing the column’s type and role. You can optionally pass labels as parameters as well.

In the example below, the x and y axes of a scatter plot are defined by calling data.addColumn(‘number’, ‘Revenue’) and data.addColumn(‘number’, ‘Gross Margin’). The third column is a tooltip, which displays when a user hovers over a datapoint.

Material-Themed Scatter Chart

 

The addRows() method accepts a nested array as a parameter. In the above example, a FileMaker script was used to assign a list of arrays to global variable $$A. When inspected using Data Viewer, $$A looks like this:

 

While these two methods do the job, if you are adding more than a few columns it will likely be more helpful to use arrayToDataTable(). This method converts a nested array to a DataTable and can infer the data types of each column, meaning you do not need to manually define them using addColumn().

Material Bar Chart

 

The global variable $$B referenced in the above code was generated using a FileMaker script and looks like this:

Trying It Out

  1. Add a Web Viewer object to a layout and open Web Viewer Setup. Set the website to Custom Web Address and select Specify…
  2. Begin your calculation with the following line: “data:text/html;charset=UTF-8,” & “
  3. Prepare your own html code or sample code from Google’s Chart Gallery in a text editor. Specifically, replace any double quotes within the html with single quotation marks. This makes inserting FileMaker variables and fields into html code easier, since you can define a block of html between double quotes, concatenate a variable or field, then concatenate another block of html between another pair of double quotes.
  4. Following the quotation mark, paste in the prepared html code.
  5. End your calculation with a double quotation mark.

Questions?

If you’re interested in integrating Google Charts within a FileMaker solution but could use some more guidance, please feel free to contact us. We have experience with various web services, and have even created a free downloadable FileMaker module, fmMapping, using the Google Maps API.


  • 0

Podcast Episode 134 :: Vince Menanno discusses Tableau

Tags : 

 

For FileMaker Talk Podcast Episode 134 Vince Menanno discusses Tableau and the integration within FileMaker 16 with Matt Navarre.

Like this? Check out more podcast at FileMaker Talk.