<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[MaximoPlus]]></title><description><![CDATA[MaximoPlus mobile, GraphQL Server and Maximo]]></description><link>http://maximoplus.com/blog/</link><image><url>http://maximoplus.com/blog/favicon.png</url><title>MaximoPlus</title><link>http://maximoplus.com/blog/</link></image><generator>Ghost 2.25</generator><lastBuildDate>Sun, 12 Jul 2026 22:02:05 GMT</lastBuildDate><atom:link href="http://maximoplus.com/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Beyond the template]]></title><description><![CDATA[Use Google Maps or Apple Maps in MaximoPlus application.]]></description><link>http://maximoplus.com/blog/beyound-the-template/</link><guid isPermaLink="false">5df08d019eca9406f2146f42</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Fri, 07 Feb 2020 05:08:24 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1554341543-6eb780242777?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1554341543-6eb780242777?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Beyond the template"><p>Learn how to create the custom components with ease. We demonstrate how to wire-in Google Maps (or Apple Maps) into MaximoPlus.</p><p>In this tutorial, we will create the component that adapts the existing React Native MapView component to display the locations of the work orders. I assume you don't have the Maximo Spatial installed, so we will put the geo-coordinates in a location description field, to illustrate the point.</p><p>The Component Adapter is a MaximoPlus tool we use to create the component. For example:</p><!--kg-card-begin: code--><pre><code class="language-js">import {getComponentAdapter} from "mplus-react";

const TestComponent = getComponentAdapter(WrappedComponent);</code></pre><!--kg-card-end: code--><p><strong>WrappedComponent</strong> is a component that has the following properties, along with the properties passed to the <strong>TestComponent</strong>:</p><ul><li><em>maxrows</em> - data fetched into Component Adapter</li><li><em>fetchMore</em> - function to get more data into ComponentAdapter</li><li><em>setMaxRowValue</em> - function to change the Maximo data for a given row and column.</li><li><em>moveToRow</em> - a function to change the current row of an MboSet</li></ul><p>We use the above to bring the Maximo functionality to the component we adapt. </p><p>The resulting component, in our case <em>TestComponent</em>, has the following mandatory properties:</p><ul><li><em>container</em> - The id of the container</li><li><em>columns</em> - The array of required columns</li><li><em>norows</em> - Initial number of rows in the component</li></ul><p>Let's clarify it with an example.</p><p>First, we will create the Map component that displays the markers, and reacts when we tap on it.</p><!--kg-card-begin: code--><pre><code class="language-js">import React, { useState, useEffect } from "react";
import MapView, { Marker } from "react-native-maps";
import getInitialCoordinates from "../utils/map.js";
import { StyleSheet } from "react-native";

const style = StyleSheet.create({
  map: {
    ...StyleSheet.absoluteFillObject
  }
});
export default props =&gt; {
  const [region, setRegion] = useState(null);

  const asInitCoords = async props =&gt; {
    const coords = await getInitialCoordinates(props.coordinates);
    if (!region) {
      setRegion(coords);
    }
  };

  useEffect(() =&gt; {
    asInitCoords(props);
  });

  return (
    &lt;MapView style={style.map} region={region}&gt;
      {props.coordinates.map(marker =&gt; (
        &lt;Marker
          key={marker.key}
          coordinate={{
            latitude: marker.latitude,
            longitude: marker.longitude
          }}
          title={marker.title}
          onPress={ev =&gt; props.handlePress(marker)}
        /&gt;
      ))}
    &lt;/MapView&gt;
  );
};
</code></pre><!--kg-card-end: code--><p>We will not show the <em>getInitialCoordinates </em>function here for the sake of simplicity. The code above is almost the same as the official demo for the <a href="https://github.com/react-native-community/react-native-maps/blob/master/example/examples/DefaultMarkers.js">MapView</a> . Check it out if in doubt.</p><p>With this  in hand, let us create the MaximoPlus component. As already discussed, we  use the <strong>DESCRIPTION</strong> field in the work order location to put the latitude and longitude.  The format is <em>##latitude,longitude##:</em></p><!--kg-card-begin: code--><pre><code class="language-js">const regex = /##(.*),(.*)##/;
const WOMap = getComponentAdapter(props =&gt; {
  if (!props || !props.maxrows) return null;
  const { navigate } = useNavigation();
  const coords = props
    .filter(({ data }) =&gt; {
      const locDesc = data["LOCATION.DESCRIPTION"]; //required field for this to work
      return regex.test(locDesc);
    })
    .map(({ data, mxrow }) =&gt; {
      const locDesc = data["LOCATION.DESCRIPTION"];
      const [, latitude, longitude] = regex.exec(locDesc).map(parseFloat);
      return {
        latitude,
        longitude,
        key: data.key,
        title: data["DESCRIPTION"],
        rowNum: mxrow
      };
    });
  return (
    &lt;View&gt;
      &lt;CoordsMap
        coordinates={coords}
        handlePress={({ rowNum }) =&gt; {
          props.moveToRow(rowNum);
          navigate("WOSection");
        }}
      /&gt;
    &lt;/View&gt;
  );
});</code></pre><!--kg-card-end: code--><p>The component extracts the list of coordinates from the work orders in the list that have it and pass it to the CoordsMap component that we created in the previous step. If the user taps on the map marker, it will navigate him to the section page, and change the current Maximo row.</p><p>Our component is ready to be used. Let us create the dialog:</p><!--kg-card-begin: code--><pre><code class="language-js">const WOMapDialog = props =&gt; (
  &lt;View
    style={{
      flex: 1,
      flexDirection: "column",
      justifyContent: "flex-end"
    }}
  &gt;
    &lt;WOMap
      columns={["wonum", "description", "location.description"]}
      container="wotrack"
      norows={50}
    /&gt;
  &lt;/View&gt;
);

WOMapDialog.navigationOptions = ({ navigation }) =&gt; {
  const backButton = (
    &lt;HeaderActionButtons
      buttons={[{ key: "close", label: "Back", action: closeDialog }]}
      icons={{ close: "md-arrow-back" }}
      style={{ color: "white" }}
    /&gt;
  );
  return {
    headerTitle: (
      &lt;Text style={{ color: "white", fontSize: 18 }}&gt;Workorder Mape&lt;/Text&gt;
    ),
    headerTransparent: true,
    headerLeft: backButton,
    headerStyle: { borderBottomWidth: 0 }
  };
};

export default WOMapDialog;</code></pre><!--kg-card-end: code--><p>And that's it! As an exercise, create the button on the section screen that opens this dialog, as described in  <a href="https://maximoplus.com/blog/getting-more-from-maximo-with-maximoplus/">Calling Maximo Actions from MaximoPlus</a>.</p>]]></content:encoded></item><item><title><![CDATA[Offline]]></title><description><![CDATA[<p>This post is the sixth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/ .</a></p><p>MaximoPlus supports the reading and writing of Maximo data in the offline mode. To enable the offline mode on the application, add the <strong>offlineenabled</strong> property on the application container, for example:</p><!--kg-card-begin: code--><pre><code class="language-js">&lt;AppContainer
  mboname=</code></pre>]]></description><link>http://maximoplus.com/blog/offline/</link><guid isPermaLink="false">5dcbe2889eca9406f2146e55</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Fri, 15 Nov 2019 03:57:10 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1550697318-101edfec22b2?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1550697318-101edfec22b2?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Offline"><p>This post is the sixth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/ .</a></p><p>MaximoPlus supports the reading and writing of Maximo data in the offline mode. To enable the offline mode on the application, add the <strong>offlineenabled</strong> property on the application container, for example:</p><!--kg-card-begin: code--><pre><code class="language-js">&lt;AppContainer
  mboname="po"
  appname="po"
  id="pocont"
  offlineenabled={true}
/&gt;</code></pre><!--kg-card-end: code--><p>The app with the enabled offline mode will automatically set up the database on the device.</p><p>Try setting the property as above in your example application and then deactivate the network connection on your device (note: in Expo development mode, you need to connect your device via USB or run the app in Emulator). If you perform any change while in offline mode, the data changes will be sent automatically to the server once you go online again.</p><h2 id="offline-lists">Offline Lists</h2><p>MaximoPlus stores the list items to offline storage when you open the list. To pick the value from that list while offline, you need to specify the <strong>offlineReturnColumn</strong> attribute in the column's metadata. The <strong>offlineReturnColumn</strong> is a list column that will be copied from the list once the user picks the row.  Maximo does it automatically in the online mode, but we need to set it explicitly for the offline:</p><!--kg-card-begin: code--><pre><code class="language-js"> metadata={{
          SHIPVIA: {
            hasLookup: "true",
            listTemplate: "valuelist",
            filterTemplate: "valuelist",
            offlineReturnColumn: "VALUE"
          }}</code></pre><!--kg-card-end: code--><p>By default, MaximoPlus stores offline only the data that has been visited by the user. That means that if you opened the list while online, it would store just the rows you had seen on the list. If you want to store all the items from the list, you need to add the <strong>preloadOffline</strong> attribute in metadata:</p><!--kg-card-begin: code--><pre><code class="language-js"> metadata={{
  SHIPVIA: {
    hasLookup: "true",
    listTemplate: "valuelist",
    filterTemplate: "valuelist",
    preloadOffline: "true",
    offlineReturnColumn: "VALUE"
  }
 }}</code></pre><!--kg-card-end: code--><p>Be careful when using this option because a value list may contain a vast number of elements, and storing all of them offline will be a time-consuming process with an increased load on the server.</p><h2 id="preloading-the-offline-data">Preloading the offline data</h2><p>If you have a use case that a user needs to have the complete set of data available offline, you need to use the <strong>preloadOffline</strong> <em>API function</em>. This function stores all the data defined by the filter criteria of the AppContainer and automatically loads the data for all the related containers as well. Here is the example of an action that preloads all the POs with the status WAPPR:</p><!--kg-card-begin: code--><pre><code class="language-js">const preload  = () =&gt; {
  setQbe("pocont", "status", "wappr")
    .then(() =&gt; reload("pocont"))
    .then(() =&gt; preloadOffline());
};</code></pre><!--kg-card-end: code--><p></p><p></p><p>This is the final post in the Getting Started series. Feel free to play with and make changes to the template, and let us <a href="https://maximoplus.com/contactus.html">know</a> what you think.</p>]]></content:encoded></item><item><title><![CDATA[Calling Maximo Actions from MaximoPlus]]></title><description><![CDATA[<p>Learn how to build the custom dialogs, process the workflow, and run Maximo actions from MaximoPlus.</p><p>This post is the fifth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/ .</a></p><p>Just like Maximo, MaximoPlus uses dialogs to execute the <strong>Maximo Actions </strong>that require user input. We will demonstrate</p>]]></description><link>http://maximoplus.com/blog/getting-more-from-maximo-with-maximoplus/</link><guid isPermaLink="false">5dca97809eca9406f2146c45</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Tue, 12 Nov 2019 17:27:04 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1512656182047-45353ebe62ef?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1512656182047-45353ebe62ef?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Calling Maximo Actions from MaximoPlus"><p>Learn how to build the custom dialogs, process the workflow, and run Maximo actions from MaximoPlus.</p><p>This post is the fifth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/ .</a></p><p>Just like Maximo, MaximoPlus uses dialogs to execute the <strong>Maximo Actions </strong>that require user input. We will demonstrate the concepts you need to know on the example of the <em>Change Status</em> dialog, but the same is still applicable for the more complex cases.</p><p>First, let's see how to add the dialogs in the template. From the point of React Navigation, a dialog is a screen like any else. By convention, we define the dialog in the <em>dialogs</em> folder. Let us make the empty dialog first, and connect it to our app. Create the file <em>POStatusHandler.js</em>, and add the following:</p><!--kg-card-begin: code--><pre><code class="language-js">import React,{PureComponent} from "react";

export default class extends PureComponent {
  static navigationOptions = ({ navigation }) =&gt; {
    return {
      headerTitle: "Change Status"
    };
  };
  render() {
    return (
      &lt;&gt;
      &lt;/&gt;
    );
  }
}</code></pre><!--kg-card-end: code--><p>Next, add the entry in the <em>DialogStack</em> const of <em>DialogNavigation.js </em></p><!--kg-card-begin: code--><pre><code class="language-js"> pochangestatus: { screen: POStatusHandler }</code></pre><!--kg-card-end: code--><p>To open the custom dialog, we use the API function <strong>openDialog</strong>. Add the import <em>openDialog</em> from <em>utils/utils.js</em> to our <em>screens/POSection.js</em> screen.</p><p>In the same file, define the open dialog action:</p><!--kg-card-begin: code--><pre><code class="language-js">const openStatusDialog = () =&gt; openDialog({ type: "pochangestatus" });
</code></pre><!--kg-card-end: code--><p>Following the steps from the previous blog post, add the button to the Navigation Header to execute that action. Now, let us add some logic to your dialog skeleton. Before we do that we need to  gloss over some important concepts:</p><h2 id="relcontainer">RelContainer</h2><p>The RelContainer is the Container defined on the Maximo relationship, as described in Maximo Database Configuration. For example, the following represents the Container on the MboSet set on the <em>poline</em> relationship of the <em>posingle</em> Container:</p><!--kg-card-begin: code--><pre><code class="language-js">&lt;RelContainer id="poline" container="posingle" relationship="poline" /&gt;</code></pre><!--kg-card-end: code--><p>We know from our previous cases that the <em>posingle</em> Container is bound to the PO Maximo object. That means that our RelContainer links to the child of the <em>poline</em> relationship, which is a <em>poline</em> object. Once the parent container changes its current row, the data in the RelContainer is reloading.</p><h3 id="non-persistent-containers">Non-persistent Containers</h3><p>In Maximo, most of the time, you need to specify the non-persistent MboSet before you can define the dialog for the action. Non-persistent MboSets are connected to the parent object using the Maximo Database Relationships. From the perspective of MaximoPlus, there is nothing special about the non-persistent containers; you need to use the RelContainer as well. For example, for the Container with the PO Status Change non-persistent MboSet, we define the RelContainer like this:</p><!--kg-card-begin: code--><pre><code class="language-js"> &lt;RelContainer
          id="pochangestatus"
          container="posingle"
          relationship="pochangestatus"
        /&gt;</code></pre><!--kg-card-end: code--><p>We can now connect the section to the container:</p><!--kg-card-begin: code--><pre><code class="language-js">&lt;Section
    container="pochangestatus"	
    columns={["status", "memo"]}
/&gt;</code></pre><!--kg-card-end: code--><h2 id="mbo-commands">Mbo Commands</h2><p>We have our Section connected to the Container now.  How does the user change the status once she fills in the data?</p><p>MaximoPlus Mbo commands execute the <strong>Java methods</strong> on the connected Mbo objects. You have two API functions at your disposal: <strong>mboCommand</strong> and <strong>mboSetCommand</strong>. The first executes the method on the current Mbo of the Container, while the latter runs the method on the MboSet bound to the Container. In our case we need the mboSetCommand:</p><!--kg-card-begin: code--><pre><code class="language-js">mboSetCommand("pochangestatus","execute")</code></pre><!--kg-card-end: code--><p>The full example:</p><!--kg-card-begin: code--><pre><code class="language-js">import React, { PureComponent } from "react";
import { RelContainer, mboSetCommand, reload, save } from "mplus-react";
import HeaderActionButtons from "../components/HeaderActionButtons";
import { closeDialog } from "../utils/utils";
import { Section } from "../components/Section";

const changeStatusAction = async () =&gt; {
  await mboSetCommand("pochangestatus", "execute");
  save("posingle");
  closeDialog();
};

export default class extends PureComponent {
  static navigationOptions = ({ navigation }) =&gt; {
    const rightButtons = [
      { key: "ok", label: "OK", action: changeStatusAction },
      { key: "close", label: "Close", action: closeDialog }
    ];
    const headerActionButtons = &lt;HeaderActionButtons buttons={rightButtons} /&gt;;
    return {
      headerTitle: "Change Status",
      headerRight: headerActionButtons
    };
  };
  render() {
    return (
      &lt;&gt;
        &lt;RelContainer
          id="pochangestatus"
          container="posingle"
          relationship="pochangestatus"
        /&gt;
        &lt;Section
          container="pochangestatus"
          columns={["status", "memo"]}
          metadata={{
            STATUS: {
              hasLookup: "true",
              listTemplate: "valuelist",
              listColumns: ["value", "description"]
            }
          }}
        /&gt;
      &lt;/&gt;
    );
  }
}
</code></pre><!--kg-card-end: code--><h2 id="security">Security</h2><p>If you tried to change the status using the above dialog, you would've got the <em>Access Denied Exception</em>. The mboSetCommand attempts to run the Java code directly on the MboSet, so we need to protect our application via the security access check.</p><p>MaximoPlus uses the Maximo Security system to protect the application. However, protecting the individual method calls is not supported in the original Maximo Signature Security. MaximoPlus adds the additional security layer on top of Maximo Security, based on the following convention:</p><ul><li>The name of the option is the <strong>method name</strong> of Mbo or MboSet in upper case</li><li>If the description starts with the hash (#), the description is the <strong>name of the relationship </strong>on which the method is called</li><li>If there are more options with the same name (for example, more methods called execute on different relationships), you <strong>add the underscore and arbitrary symbol</strong> after it (example: EXECUTE, EXECUTE_1, EXECUTE_2...)</li></ul><p>For our case, the name of the option will be EXECUTE, and the description #POCHANGESTATUS</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/maximo_sig_sec.png" class="kg-image" alt="Calling Maximo Actions from MaximoPlus"></figure><!--kg-card-end: image--><p>Try to enter the above in Maximo Signature Security, assign the privilege to your group, and proceed with the status change.</p><h2 id="workflow">Workflow</h2><p>To route the workflow, you need to call the <strong>openWorkflowDialog</strong> API function, with the <strong>name of the Container and a workflow process name </strong>as arguments.</p><p>Example:</p><p>In the POSection.js screen, add the following action:</p><!--kg-card-begin: code--><pre><code class="language-js">const openWF = () =&gt; openWorkflowDialog("posingle", "POSTATUS");</code></pre><!--kg-card-end: code--><p>Now bind this action to the header button, as described in the previous blog post.</p><p></p><p>To learn how the Offline Mode works in MaximoPlus, read the <a href="https://maximoplus.com/blog/offline/">next</a> blog post.</p>]]></content:encoded></item><item><title><![CDATA[Get access to your phone features and apps from MaximoPlus]]></title><description><![CDATA[<p>Make a phone call, scan a barcode, upload the photo from a MaximoPlus app.<br></p><p>This post is a fourth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>Expo and React Native make accessing the phone API trivial. We will show here some built-in features in the MaximoPlus</p>]]></description><link>http://maximoplus.com/blog/get-access-to-your/</link><guid isPermaLink="false">5dc6ee809eca9406f2146b67</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Sat, 09 Nov 2019 18:13:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1487392882838-c9cd493de5eb?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1487392882838-c9cd493de5eb?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Get access to your phone features and apps from MaximoPlus"><p>Make a phone call, scan a barcode, upload the photo from a MaximoPlus app.<br></p><p>This post is a fourth in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>Expo and React Native make accessing the phone API trivial. We will show here some built-in features in the MaximoPlus template and also demonstrate how to create your integration.</p><h2 id="phone-calling">Phone calling</h2><p>To enable the phone calling in MaximoPlus, add the metadata attribute <strong>phonenum</strong> to the field containing the phone number. For example:</p><!--kg-card-begin: code--><pre><code class="language-js">  metadata={{
          "VENDOR.PHONE": { phonenum: true }
        }}</code></pre><!--kg-card-end: code--><p>Try adding this metadata to the <em>POSection.js </em>file of your tutorial app</p><h2 id="barcode-scanning">Barcode scanning</h2><p>If you add the metadata attribute <strong>barcode</strong> on the attribute, the barcode icon for activating the barcode scan will be added next to the field, and the barcode value copied to the field upon the scanning. Try this artificial example in your app (don't forget to add the <em>po9</em> field to the section columns first)</p><!--kg-card-begin: code--><pre><code class="language-js">metadata={{
          "VENDOR.PHONE": { phonenum: true },
          PO9: { barcode: true, title: "Barcode test1" }
        }}</code></pre><!--kg-card-end: code--><h2 id="take-a-photo-and-upload-to-maximo">Take a photo and upload to Maximo<br></h2><p>The upload will save your picture to the Maximo doclinks.  First, you need to import the utility function:</p><!--kg-card-begin: code--><pre><code class="language-js">import {openPhotoUpload} from "../utils/utils";</code></pre><!--kg-card-end: code--><p>This function has one argument - <strong>id</strong> of the application container where it should be uploaded.<br></p><p>In the example below, we will create one helper action and change the header to add the button for photo upload.</p><!--kg-card-begin: code--><pre><code class="language-js">const takePicture = () =&gt; openPhotoUpload("posingle");

 const buttons = [
      { key: "save", label: "Save", action: saveAction },
      { key: "photo", label: "Photo", action: takePicture }
    ];
    const iconMap = { save: "md-save", photo: "md-camera" };
</code></pre><!--kg-card-end: code--><h2 id="document-upload">Document upload</h2><p>You can also upload any document from your device (or Google Drive/Apple Cloud) in a similar vein:</p><!--kg-card-begin: code--><pre><code class="language-js">import { openPhotoUpload, openDocumentUpload } from "../utils/utils";

const uploadDocument = () =&gt; openDocumentUpload("posingle");

 const buttons = [
      { key: "save", label: "Save", action: saveAction },
      { key: "photo", label: "Phot", action: takePicture },
      { key: "docupload", label: "Upload Doc", action: uploadDocument }
    ];
    const iconMap = {
      save: "md-save",
      photo: "md-camera",
      docupload: "md-attach"
    };</code></pre><!--kg-card-end: code--><h2 id="document-viewer">Document Viewer</h2><p>Use document viewer to see the doclinks attachments:</p><!--kg-card-begin: code--><pre><code class="language-js">import {
  openPhotoUpload,
  openDocumentUpload,
  openDocumentViewer
} from "../utils/utils";

const openDocViewer = () =&gt; openDocumentViewer("posingle");

 const buttons = [
      { key: "save", label: "Save", action: saveAction },
      { key: "photo", label: "Phot", action: takePicture },
      { key: "docupload", label: "Upload Doc", action: uploadDocument },
      { key: "docview", label: "View Document", action: openDocViewer }
    ];
    const iconMap = {
      save: "md-save",
      photo: "md-camera",
      docupload: "md-attach",
      docview: "md-document"
    };
</code></pre><!--kg-card-end: code--><h3 id="full-example-">Full example:</h3><!--kg-card-begin: code--><pre><code class="language-js">import React, { Component } from "react";
import { Section } from "../components/Section";
import HeaderActionButtons from "../components/HeaderActionButtons";
import { save } from "mplus-react";
import {
  openPhotoUpload,
  openDocumentUpload,
  openDocumentViewer
} from "../utils/utils";

const saveAction = () =&gt; {
  save("posingle");
};

const takePicture = () =&gt; openPhotoUpload("posingle");

const uploadDocument = () =&gt; openDocumentUpload("posingle");

const openDocViewer = () =&gt; openDocumentViewer("posingle");

export default class extends Component {
  static navigationOptions = ({ navigatiopn }) =&gt; {
    const buttons = [
      { key: "save", label: "Save", action: saveAction },
      { key: "photo", label: "Phot", action: takePicture },
      { key: "docupload", label: "Upload Doc", action: uploadDocument },
      { key: "docview", label: "View Document", action: openDocViewer }
    ];
    const iconMap = {
      save: "md-save",
      photo: "md-camera",
      docupload: "md-attach",
      docview: "md-document"
    };
    return {
      headerTitle: "Details",
      headerRight: &lt;HeaderActionButtons buttons={buttons} icons={iconMap} /&gt;
    };
  };
  render() {
    return (
      &lt;Section
        container="posingle"
        columns={[
          "ponum",
          "description",
          "status",
          "shipvia",
          "orderdate",
          "vendor",
          "vendor.phone",
          "po9"
        ]}
        metadata={{
          "VENDOR.PHONE": { phonenum: true },
          PO9: { barcode: true, title: "Barcode test1" }
        }}
      /&gt;
    );
  }
}
</code></pre><!--kg-card-end: code--><h2 id="make-a-new-functionality-using-the-metadata">Make a new functionality using the metadata</h2><p>MaximoPlus passes the metadata to every field of the section. We can use this feature to add the new phone integrations easily. You need to make changes to the <em>components/section/TextField.js</em>. We will not go in all the minute details of this class; we'll focus on the part utilizing the metadata. For example, the above phone calling metadata implements  as :</p><!--kg-card-begin: code--><pre><code class="language-js">    if (this.props.metadata &amp;&amp; this.props.metadata.phonenum) {
      const phoneF = () =&gt; Linking.openURL(`tel:${this.props.value}`);
      rightAction = { type: "font-awesome", name: "phone", onPress: phoneF };
    }</code></pre><!--kg-card-end: code--><p>As an exercise, create the metadata that enable the SMS sending for the field.</p><p></p><p>In the <a href="https://maximoplus.com/blog/getting-more-from-maximo-with-maximoplus/">next</a> blog post, learn how to get more of Maximo functionality in MaximoPlus by using the MaximoPlus Actions</p>]]></content:encoded></item><item><title><![CDATA[Saving the changes with MaximoPlus]]></title><description><![CDATA[<p>This post is a third in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>We already mentioned that MaximoPlus access Maximo directly from its server. Saving the Maximo data may be an expensive operation in Maximo, so MaximoPlus don't do it automatically. We need to use the standard</p>]]></description><link>http://maximoplus.com/blog/saving-the-changes-with-maximoplus/</link><guid isPermaLink="false">5dc6b44b9eca9406f2146aa7</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Sat, 09 Nov 2019 13:48:49 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1500472141701-084e6fa44840?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1500472141701-084e6fa44840?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Saving the changes with MaximoPlus"><p>This post is a third in a series that started with <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>We already mentioned that MaximoPlus access Maximo directly from its server. Saving the Maximo data may be an expensive operation in Maximo, so MaximoPlus don't do it automatically. We need to use the standard API <strong>save</strong> function from MaximoPlus.</p><p>To illustrate how the save works in MaximoPlus, we will create a s<strong>ave button</strong> on the toolbar of our section page.</p><p>First, add the save function to the import parts of the page:</p><!--kg-card-begin: code--><pre><code class="language-js">import { save } from "mplus-react";</code></pre><!--kg-card-end: code--><p>Then create a function to operate on the main po application container:</p><!--kg-card-begin: code--><pre><code class="language-js">const saveAction = () =&gt; {
  save("pocont");
};
</code></pre><!--kg-card-end: code--><p>Finally, add the Save button to the header of the screen:</p><!--kg-card-begin: code--><pre><code class="language-js">  static navigationOptions = ({ navigatiopn }) =&gt; {
    const buttons = [{ key: "save", label: "Save", action: saveAction }];
    const iconMap = { save: "md-save" };
    return {
      headerTitle: "Details",
      headerRight: &lt;HeaderActionButtons buttons={buttons} icons={iconMap} /&gt;
    };
  };</code></pre><!--kg-card-end: code--><p>Now, try to find some editable record in the list, change a value, and press the save button. What did happen? Our current record <em>went back to the first item in the list</em>.</p><p>To understand why this has happened, remember that Maximo resets the MboSet after the saving, and sets its current row to zero. To get a more Maximo-like behavior, we need to introduce the concept of the <strong>Single Mbo Container.</strong></p><p>Single Mbo container is the container bound to the MboSet with exactly one Mbo, the current Mbo of the parent container. Edit the Containers.js file and add the following:</p><!--kg-card-begin: code--><pre><code class="language-js">import { AppContainer, SingleMboContainer, RelContainer } from "mplus-react";
...
 &lt;SingleMboContainer id="posingle" container="pocont" /&gt;</code></pre><!--kg-card-end: code--><p>The <strong>container</strong> attribute of the SingleMboContainer is the parent MboSet. When the current row changes in the parent Mbo Set, the SingleMboContainer current Mbo changes. However, all the changes we do on the Single Mbo Container don't affect the parent container. Edit the container attribute of the section to posingle, and change the save action to :</p><!--kg-card-begin: code--><pre><code class="language-js">const saveAction = () =&gt; {
  save("posingle");
};</code></pre><!--kg-card-end: code--><p></p><p>The SingleMboContainer is a recommended way to operate on Maximo data. Some functions, like Maximo Status Change (more about that in another blog post), will not work as expected unless on SingleMboContainer.</p><p>In  the <a href="https://maximoplus.com/blog/get-access-to-your/">next</a> post, learn how to access the smart phone features from MaximoPlus.</p>]]></content:encoded></item><item><title><![CDATA[Going further with MaximoPlus : search and value lists]]></title><description><![CDATA[<p>Use MaximoPlus to add search forms and utilize Maximo domains through a list of values.</p><p>This is a part two of series, please read first the <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>Search is one of the basic functionalities in Maximo. In MaximoPlus, we use <strong>QBE Sections</strong> for that. They are</p>]]></description><link>http://maximoplus.com/blog/adding-more-functionalites-to-an-maximoplus-app-search-and-value-lists/</link><guid isPermaLink="false">5dbd60599eca9406f21469a6</guid><category><![CDATA[MaximoPlus]]></category><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Sat, 02 Nov 2019 13:09:01 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1488841714725-bb4c32d1ac94?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1488841714725-bb4c32d1ac94?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Going further with MaximoPlus : search and value lists"><p>Use MaximoPlus to add search forms and utilize Maximo domains through a list of values.</p><p>This is a part two of series, please read first the <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">https://maximoplus.com/blog/getting-started-with-maximoplus/</a>.</p><p>Search is one of the basic functionalities in Maximo. In MaximoPlus, we use <strong>QBE Sections</strong> for that. They are entirely equivalent to the Advanced Search Forms in Maximo itself and use the same Maximo QBE (Query by Example).</p><p>Create the search screen in <strong>screens/POQbeSection.js</strong> file, and paste the following:</p><!--kg-card-begin: code--><pre><code class="language-js">import React, { Component } from "react";
import { QbeSection } from "../components/Section";
import HeaderActionButtons from "../components/HeaderActionButtons";

export default class extends Component {
  static navigationOptions = ({ navigation }) =&gt; {
    const buttons = navigation.getParam("qbeButtons");
    const iconMap = { clear: "md-close", search: "md-checkmark" };
    return {
      headerTitle: "Search POs",
      headerRight: &lt;HeaderActionButtons buttons={buttons} icons={iconMap} /&gt;
    };
  };

  render() {
    return (
      &lt;QbeSection
        container="pocont"
        columns={["ponum", "description", "status", "shipvia"]}
        navigateTo="List"
      /&gt;
    );
  }
}
</code></pre><!--kg-card-end: code--><p>Once you finished editing the new file, don't forget to change the <strong>MainTabNavigation.js</strong> to include it:</p><!--kg-card-begin: code--><pre><code class="language-js">const QBESectionStack = createStackNavigator(
  { QBESection: POQbeSectionScreen },
  config
);</code></pre><!--kg-card-end: code--><p>As you can see from above, the <strong>QbeSection</strong> component looks almost the same as the <strong>Section</strong> we introduced before. It has one new attribute, <strong>navigateTo</strong> where you specify which page should be open after the search finishes.</p><p>You can spot one more difference in the navigation options. QBE section renders two buttons in the navigation header: one for performing the search, and another for clearing the QBE. You can read more about navigation header buttons on <a href="https://reactnavigation.org/docs/en/header-buttons.html">https://reactnavigation.org/docs/en/header-buttons.html</a>.</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/search.png" class="kg-image" alt="Going further with MaximoPlus : search and value lists"></figure><!--kg-card-end: image--><p>You can try to enter the search criteria as above and perform the search by clicking on the check icon.</p><!--kg-card-begin: html--><h3>Metadata</h3><!--kg-card-end: html--><p>We need briefly to talk about metadata before we introduce the value lists. In MaximoPlus, metadata is information about the attributes of the Mbo: its label, data type, description, domain id. Most of the time, the metadata comes automatically from Maximo, and this is how the captions were displayed in the Qbe Section above, even though we didn't specify them.</p><p><br>In addition to the automatic metadata, we can add the metadata in the definition of the component. For example:</p><!--kg-card-begin: code--><pre><code class="language-js"> render() {
    return (
      &lt;QbeSection
        container="pocont"
        columns={["ponum", "description", "status", "shipvia"]}
        navigateTo="POList"
        metadata={{
          SHIPVIA: {
            hasLookup: true,
            listTemplate: "qbevaluelist"
          },
          STATUS: {
            hasLookup: true,
            listTemplate: "qbevaluelist"
          }
        }}
      /&gt;
    );</code></pre><!--kg-card-end: code--><p>For this QBE Section, we appended the metadata attributes <strong>hasLookup</strong> and <strong>listTemplate</strong> to the attributes <em>SHIPVIA</em> and <em>STATUS.</em></p><p>MaximoPlus template uses the metadata to customize any field behavior. The most important ones are the value lists.</p><!--kg-card-begin: html--><h3>Value Lists</h3><!--kg-card-end: html--><p>The above example specifies the lookups on the SHIPVIA and STATUS fields of the QBE Section. If we define the hasLookup attribute, the Section adds the search to the text field. Since the value lists will show as lists in a separate screen, we need to define the listTemplate property as well. The <strong>valuelist</strong> and <strong>qbevaluelist</strong> are the built-in templates; you don't need to change the listTemplates.js file; this is for the reference only:</p><!--kg-card-begin: code--><pre><code class="language-js"> valuelist: ({ VALUE, DESCRIPTION }) =&gt; (
    &lt;ListItem title={VALUE} subtitle={DESCRIPTION} /&gt;
  ),
  qbevaluelist: ({ VALUE, DESCRIPTION, _SELECTED }) =&gt; (
    &lt;ListItem
      title={VALUE}
      subtitle={DESCRIPTION}
      leftIcon={
        _SELECTED === "Y" ? &lt;Icon name="check" type="font-awesome" /&gt; : null
      }
    /&gt;
  ),</code></pre><!--kg-card-end: code--><p>You can place the list of values on both the Section and the QbeSection. There is the difference in behavior, same as in Maximo: you can pick only one value for the Section, but you can choose multiple lines in Qbe Section for more complicated searches. That is the reason why we utilize the virtual field _<strong>SELECTED</strong>, it indicates when the user selects a row.</p><p>Change your POQbeSection.js file, as per the example in the metadata section, and try to search for multiple values:</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/qbevaluelist.png" class="kg-image" alt="Going further with MaximoPlus : search and value lists"></figure><!--kg-card-end: image--><p>Read the <a href="https://maximoplus.com/blog/saving-the-changes-with-maximoplus/">next post</a> in the series to learn more Container concepts, and how to save the MaximoPlus data changes to Maximo.</p>]]></content:encoded></item><item><title><![CDATA[Getting started with MaximoPlus]]></title><description><![CDATA[How to create Maximo Mobile app with MaximoPlus and React Native]]></description><link>http://maximoplus.com/blog/getting-started-with-maximoplus/</link><guid isPermaLink="false">5dbd20959eca9406f2146928</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Sat, 02 Nov 2019 09:06:31 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1488402024618-0658e4c2b1f0?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" medium="image"/><content:encoded><![CDATA[<img src="https://images.unsplash.com/photo-1488402024618-0658e4c2b1f0?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ" alt="Getting started with MaximoPlus"><p>How to create native Maximo mobile application with MaximoPlus and React Native</p><p>Before we get started, make sure you installed the MaximoPlus server and all the dependencies as described in the <a href="https://maximoplus.com/manual/html/#/?id=installation-steps">MaximoPlus Server installation</a>, or the <a href="https://maximoplus.com/blog/maximoplus-installation-with-docker/">MaximoPlus Server installation with Docker</a> post. You also need the standard JavaScript tools - node/npm and yarn, and the Expo application (<a href="https://expo.io/learn">https://expo.io/learn</a>) installed on your mobile or emulator.</p><p>If you want to try it out without installing anything,  checkout <a href="https://maximoplus.com/blog/see-maximoplus-in-action/">See MaximoPlus in action</a></p><p>First, install the Expo CLI and MaximoPlus CLI if not already installed:</p><!--kg-card-begin: code--><pre><code>npm install expo-cli -g
npm install create-mp-app -g</code></pre><!--kg-card-end: code--><p>Run the following command, and then enter the required information:</p><!--kg-card-begin: code--><pre><code>create-mp-app</code></pre><!--kg-card-end: code--><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2020/02/mpcli-min.png" class="kg-image" alt="Getting started with MaximoPlus"></figure><!--kg-card-end: image--><p>Follow the instructions from the console, and then open the Expo application on your device or emulator and scan the QR code to start your app.<br></p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/initial.png" class="kg-image" alt="Getting started with MaximoPlus"></figure><!--kg-card-end: image--><p>So far, our app is just an empty shell.  Let's start adding some MaximoPlus components. We have first to define the <strong>Application Container</strong>. Containers are the bridge between your application and the MaximoPlus server. They send and receive data and function calls.</p><p>Add the following to the <strong>Containers.js</strong> file in the template:</p><!--kg-card-begin: code--><pre><code class="language-js">export default props =&gt; (
  &lt;&gt;
    &lt;AppContainer id="pocont" mboname="po" appname="po" /&gt;
  &lt;/&gt;
);
</code></pre><!--kg-card-end: code--><p>The <strong>id</strong> attribute is the arbitrary identification of the container. You will reference it from the components that depend on the container.<br>Application Containers refer to the main Mbo of the Maximo application. In our example, we connect to the <strong>PO</strong>  app, and the name of its object is also <strong>PO</strong>.<br></p><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/login.png" class="kg-image" alt="Getting started with MaximoPlus"></figure><!--kg-card-end: image--><p><br>When you enter your Maximo credentials in the login screen, you will come back to the empty template. Let us add the first screen. MaximoPlus uses the standard React Navigation library and its recommended location of the files. Create the POList.js file in the screens directory, and paste the following:</p><!--kg-card-begin: code--><pre><code class="language-js">import React, { Component } from "react";
import NavigationService from "../navigation/NavigationService";
import MaxList from "../components/Mlist";

export default class extends Component {
  static navigationOptions = {
    title: "POs"
  };
  render() {
    return (
      &lt;MaxList
        listTemplate="po"
        container="pocont"
        columns={["ponum", "description", "status"]}
        selectableF={_ =&gt; NavigationService.navigate("POSection")}
        norows={20}
        initdata={true}
      /&gt;
    );
  }
}

</code></pre><!--kg-card-end: code--><p>The <strong>container</strong> attribute refers to the <strong>PO</strong> Application container. With <strong>columns</strong>, we define the Mbo attributes we need from the PO. The <strong>norows</strong> specify the initial number of rows in the list, and the <strong>initdata</strong> tells the component to load the data automatically. The attribute <strong>selectableF</strong> is a function that will execute once the user taps on the list item, and after Maximo moves to that row. In most of the cases, it will open the new screen (like above).</p><p>To display something in the list, we have to define how the list render the list items Open the <strong>coponents/listTemplates.js</strong>  file and edit the entry for the <strong>po</strong> template:</p><!--kg-card-begin: code--><pre><code class="language-js">  po: ({ DESCRIPTION, PONUM, STATUS }) =&gt; (
    &lt;ListItem title={DESCRIPTION} subtitle={PONUM + " " + STATUS} /&gt;
  ),
</code></pre><!--kg-card-end: code--><p>Finally, we need to inform the React Navigation, about the location of our new file. Open the <strong>navigation/MainTabNavigation.js</strong> and add the following to the import section:</p><!--kg-card-begin: code--><pre><code class="language-js">import POListScreen from "../screens/POListScreen";
</code></pre><!--kg-card-end: code--><p>Now, replace the existing list screen with this:</p><!--kg-card-begin: code--><pre><code class="language-js">const ListStack = createStackNavigator({ List: POListScreen }, config);
</code></pre><!--kg-card-end: code--><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/List.png" class="kg-image" alt="Getting started with MaximoPlus"></figure><!--kg-card-end: image--><p>If you tap on the list now, nothing will happen, because we still don't have the <strong>POSection</strong> screen. Let's fix that. Create the <strong>screens/POSection.js</strong> file, and paste the following:</p><!--kg-card-begin: code--><pre><code class="language-js">import React, { Component } from "react";
import { Section } from "../components/Section";

export default class extends Component {
  static navigationOptions = ({ navigatiopn }) =&gt; {
    return {
      headerTitle: "Details"
    };
  };
  render() {
    return (
      &lt;Section
        container="pocont"
        columns={[
          "ponum",
          "description",
          "status",
          "shipvia",
          "orderdate",
          "vendor",
          "vendor.phone",
          "revcomments"
        ]}
      /&gt;
    );
  }
}

</code></pre><!--kg-card-end: code--><p>The <strong>Section</strong> component displays the current row in Maximo. Let us add it to the React Navigation in MainTabNavigation.js.<br>Add to the following to the import part of the file:</p><!--kg-card-begin: code--><pre><code class="language-js">import POSectionScreen from "../screens/POSection";
</code></pre><!--kg-card-end: code--><p>and then change the route:</p><!--kg-card-begin: code--><pre><code class="language-javascript">const SectionStack = createStackNavigator({ Section: POSectionScreen }, config);
</code></pre><!--kg-card-end: code--><!--kg-card-begin: image--><figure class="kg-card kg-image-card"><img src="http://maximoplus.com/blog/content/images/2019/11/section.png" class="kg-image" alt="Getting started with MaximoPlus"></figure><!--kg-card-end: image--><p>In the <a href="https://maximoplus.com/blog/adding-more-functionalites-to-an-maximoplus-app-search-and-value-lists/">next post</a>, you will learn how to add search and list functionality to your app.</p>]]></content:encoded></item><item><title><![CDATA[Simple React Native Application with GraphQL Server for Maximo]]></title><description><![CDATA[Use GraphQL Server for Maximo to create the simple React Native based mobile app]]></description><link>http://maximoplus.com/blog/simple-react-native-application-with-graphql-server-for-maximo/</link><guid isPermaLink="false">5d0f419d646862505b52f6bc</guid><category><![CDATA[GraphQL Server]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Fri, 28 Jun 2019 09:38:34 GMT</pubDate><media:content url="http://maximoplus.com/blog/content/images/2019/06/banter-snaps-ciee5oe8ewi-unspl.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://maximoplus.com/blog/content/images/2019/06/banter-snaps-ciee5oe8ewi-unspl.jpg" alt="Simple React Native Application with GraphQL Server for Maximo"><p>In this tutorial, we will create the very rudimentary React Native application - it will display the list of the POs in the WAPPR status, and if the user taps on the PO in the list, it will open the page with the PO details. </p><h2 id="install-expo">Install Expo </h2><p>Expo is probably the easiest way to get started with the React Native.  Follow the instructions on <a href="https://docs.expo.io/versions/v33.0.0/introduction/installation/">https://docs.expo.io/versions/v33.0.0/introduction/installation/</a> and install the expo-cli on your computer, and the Expo application on your mobile device.</p><p>Use the expo init command to create the new project Choose the bare project template, without the tab or drawer navigation.</p><h2 id="basic-structure">Basic structure</h2><p>You can see the full source on: <a href="https://bitbucket.org/maximoplusteam/graphql-native-demo.git">https://bitbucket.org/maximoplusteam/graphql-native-demo.git</a></p><p>As already discussed, our application will have two pages: one for the list of POs, and one for the details. We obviously need one more page for the login. For navigation between these pages, we will use the standard <strong>React Navigation</strong> library.</p><!--kg-card-begin: markdown--><pre><code class="language-shell">yarn add react-navigation
</code></pre>
<!--kg-card-end: markdown--><p>Create the file <strong>Main.js,</strong> it contains the app navigation logic:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React, { Component } from &quot;react&quot;;
import {
  createSwitchNavigator,
  createStackNavigator,
  createAppContainer
} from &quot;react-navigation&quot;;
import LoginScreen from &quot;./Login.js&quot;;
import ListScreen from &quot;./List.js&quot;;
import DetailsScreen from &quot;./Details.js&quot;;
import NavigationService from &quot;./NavigationService.js&quot;;

const AppStack = createStackNavigator({
  List: ListScreen,
  Details: DetailsScreen
});

const AuthStack = createStackNavigator({ Login: LoginScreen });

const AppContainer = createAppContainer(
  createSwitchNavigator(
    { App: AppStack, Auth: AuthStack },
    { initialRouteName: &quot;App&quot; }
  )
);

export default class App extends Component {
  render() {
    return (
      &lt;AppContainer/&gt;
    );
  }
}
</code></pre>
<!--kg-card-end: markdown--><p>In this phase, we will not introduce the GraphQL yet, but we need the dummy pages to check is our navigation working correctly or not.</p><p>List.js:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React from &quot;react&quot;;
import { View, Text, Button } from &quot;react-native&quot;;

export default props =&gt; (
  &lt;View&gt;
    &lt;Text&gt;List&lt;/Text&gt;
    &lt;Button onPress={props.navigation.navigate(&quot;Details&quot;)} /&gt;
  &lt;/View&gt;
);
</code></pre>
<!--kg-card-end: markdown--><p>Details.js:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React from &quot;react&quot;;
import { View, Text} from &quot;react-native&quot;;

export default props =&gt; (
  &lt;View&gt;
    &lt;Text&gt;Details&lt;/Text&gt;
  &lt;/View&gt;
);

</code></pre>
<!--kg-card-end: markdown--><p>Login.js:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React from &quot;react&quot;;
import { View, Text } from &quot;react-native&quot;;

export default props =&gt; (
  &lt;View&gt;
    &lt;Text&gt;Login&lt;/Text&gt;
  &lt;/View&gt;
);
</code></pre>
<!--kg-card-end: markdown--><p>Finally, edit the <strong>App.js </strong>that was created during the project init.</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React from &quot;react&quot;;
import Main from &quot;./Main&quot;;

export default function App() {
  return &lt;Main/&gt;;
}
</code></pre>
<!--kg-card-end: markdown--><p>Test the application by running the <strong>yarn start.</strong></p><h2 id="adding-the-graphql-logic">Adding the GraphQL logic</h2><p>So far, our application didn't do anything except navigating the empty pages. We will use <strong>Apollo Client </strong>library to connect the app to the GraphQL server. We also need to install the <strong>react-native-elements</strong> library,  and use its ready-made components:</p><!--kg-card-begin: markdown--><pre><code class="language-shell">yarn add react-native-elements apollo-cache-memory apolo-client apollo-link apollo-link-context apollo-link-error apollo-link-http base-64 graphql graphql-tag react-apollo react-scripts
</code></pre>
<!--kg-card-end: markdown--><p>Apollo Client is using the mechanism called "Links" to communicate with the GraphQL servers. GraphQL server for Maximo uses the Basic HTTP authentication, and it expects the standard <strong>base64 encoded authorization token</strong>, together with the API request. Our login page will store the token in the AsyncStorage (React Native default storage mechanism), and the link will read it from there and send with every request.  We also need to navigate to the login page if there is no token. The file containing the link logic is GraphQLLink.js:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import NavigationService from &quot;./NavigationService.js&quot;;
import { ApolloClient } from &quot;apollo-client&quot;;
import { ApolloProvider } from &quot;react-apollo&quot;;
import { HttpLink, createHttpLink } from &quot;apollo-link-http&quot;;
import { ApolloLink } from &quot;apollo-link&quot;;
import { onError } from &quot;apollo-link-error&quot;;
import { InMemoryCache } from &quot;apollo-cache-inmemory&quot;;
import { setContext } from &quot;apollo-link-context&quot;;

//import AsyncStorage from &quot;@react-native-community/async-storage&quot;;
import { AsyncStorage } from &quot;react-native&quot;;

const httpLink = createHttpLink({
  uri: &quot;http://192.168.1.107:4001/graphql&quot;,
  credentials: &quot;include&quot;
});

const authLink = setContext(async (_, { headers }) =&gt; {
  // get the authentication token from local storage if it exists
  //  const token = localStorage.getItem(&quot;token&quot;);
  const token = await AsyncStorage.getItem(&quot;token&quot;);
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Basic ${token}` : &quot;&quot;
    }
  };
});

const errorHandlerLink = onError(({ response, networkError }) =&gt; {
  if (networkError) {
    if (networkError.statusCode === 401) {
      AsyncStorage.removeItem(&quot;token&quot;);
      NavigationService.navigate(&quot;Login&quot;);
    } else {
      NavigationService.navigate(&quot;Error&quot;, { error: networkError });
      //display the error (for example network error)
    }
  }
});

export default new ApolloClient({
  link: errorHandlerLink.concat(authLink).concat(httpLink),
  cache: new InMemoryCache()
});
</code></pre>
<!--kg-card-end: markdown--><p>In the snippet above, replace the URI of the HTTP link with the IP address of your computer.  The above link uses the <em>NavigationService.js</em> to navigate to the login page in case of the wrong username and password. Below is its source code:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import { NavigationActions } from &quot;react-navigation&quot;;

let _navigator;

function setTopLevelNavigator(navigatorRef) {
  _navigator = navigatorRef;
}

function navigate(routeName, params) {
  _navigator.dispatch(
    NavigationActions.navigate({
      routeName,
      params
    })
  );
}

// add other navigation functions that you need and export them

export default {
  navigate,
  setTopLevelNavigator
};
</code></pre>
<!--kg-card-end: markdown--><p>We have everything ready now to create our first GraphQL - powered component. Change the List.js to following:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React, { Component } from &quot;react&quot;;
import { View, Text, FlatList } from &quot;react-native&quot;;
import gql from &quot;graphql-tag&quot;;
import { Query } from &quot;react-apollo&quot;;
import styles from &quot;./Styles.js&quot;;
import { ListItem } from &quot;react-native-elements&quot;;

const poQuery = gql`
  query($qbe: POQBE) {
    po(fromRow: 0, numRows: 20, qbe: $qbe) {
      ponum
      description
      status
      purchaseagent
      vendor
      receipts
      totalcost
      id
      _handle
      
    }
  }
`;

const qbe = { status: &quot;=WAPPR&quot; };

const POList = props =&gt; (
  &lt;Query query={poQuery} variables={{ qbe }}&gt;
    {({ loading, error, data }) =&gt; {
      if (loading) return &lt;Text styles={styles.text}&gt;Loading...&lt;/Text&gt;;
      if (error) return &lt;Text styles={styles.text}&gt;Error :(&lt;/Text&gt;;
      return (
        &lt;FlatList
          data={data.po}
          keyExtractor={item =&gt; item.id}
          renderItem={({ item }) =&gt; (
            &lt;ListItem
              title={item.ponum}
              subtitle={item.description}
              onPress={() =&gt;
                props.navigation.navigate(&quot;Details&quot;, { data: item })
              }
            /&gt;
          )}
        /&gt;
      );
    }}
  &lt;/Query&gt;
);

export default POList;

</code></pre>
<!--kg-card-end: markdown--><p>The <em>poQuery </em>const is the actual GraphQL query. It has one variable $qbe, that we use to limit the results to WAPPR POs. You can try out this query in the GraphQL playground, before running this app. The Apollo client <strong>Query </strong>component feeds the result data to React Native's standard <strong>FlatList </strong>component. The <strong>ListItem </strong>is a<strong> </strong>component from the react-native-elements library with the pre-defined styling. Note that there is no usage of the Apollo link we defined in the GraphQL.js. How then Apollo client runs the query?</p><p>Connecting to the actual Apollo HTTP link is the rule of the <strong>ApolloProvider</strong>. Typically, this is a root component that provides the connection to all the nested queries and mutations. Change the App.js to following:</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React from &quot;react&quot;;
import { ApolloProvider } from &quot;react-apollo&quot;;
import Main from &quot;./Main&quot;;
import client from &quot;./GraphQLLink&quot;;

export default function App() {
  return (
    &lt;ApolloProvider client={client}&gt;
      &lt;Main /&gt;
    &lt;/ApolloProvider&gt;
  );
}
</code></pre>
<!--kg-card-end: markdown--><p>Finally let's create the Details.js :</p><!--kg-card-begin: markdown--><pre><code class="language-js">import React, { Component } from &quot;react&quot;;
import { View, Text } from &quot;react-native&quot;;
import { Input, Button } from &quot;react-native-elements&quot;;

class Details extends Component {
  constructor(props) {
    super(props);
    this.state = props.navigation.state.params.data;
  }
  render() {
    return (
      &lt;View&gt;
        &lt;Input lebel=&quot;PO&quot; value={this.state.ponum} editable={false} /&gt;
        &lt;Input label=&quot;Status&quot; value={this.state.status} editable={false} /&gt;
        &lt;Input
          label=&quot;Buyer&quot;
          value={this.state.purchaseagent}
          editable={false}
        /&gt;
        &lt;Input label=&quot;Vendor&quot; value={this.state.vendor} editable={false} /&gt;
        &lt;Input label=&quot;Receipts&quot; value={this.state.receipts} editable={false} /&gt;
        &lt;Input
          label=&quot;Total Cost&quot;
          value={this.state.totalcost &amp;&amp; String(this.state.totalcost)}
          editable={false}
        /&gt;
        &lt;Button
          title=&quot;Workflow&quot;
          onPress={() =&gt; this.props.navigation.navigate(&quot;Workflow&quot;)}
        /&gt;
      &lt;/View&gt;
    );
  }
}

export default Details;

</code></pre>
<!--kg-card-end: markdown--><p>Notice that the Details page is not connected to the GraphQL data. Our application uses the React Navigation mechanism to pass the data from the List.js page. We are using the state instead of props here so that you can change the data through the mutation to the server if you want (Not covered in the tutorial). The button at the end of the form doesn't do anything yet. To see it in action, read the next blog post: <strong>React Native Workflow Routing with GraphQL Server</strong></p>]]></content:encoded></item><item><title><![CDATA[Get more results from AppDynamics in Maximo]]></title><description><![CDATA[Use the AppDymamics Bisoness Transactions properly in Maximo]]></description><link>http://maximoplus.com/blog/get-more-results-from-appdynamics-in-maximo/</link><guid isPermaLink="false">5d135a5a646862505b52f8f9</guid><category><![CDATA[Maximo]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Wed, 26 Jun 2019 19:33:24 GMT</pubDate><media:content url="http://maximoplus.com/blog/content/images/2019/06/labyrinth.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://maximoplus.com/blog/content/images/2019/06/labyrinth.jpg" alt="Get more results from AppDynamics in Maximo"><p>If you ever configured AppDynamics to monitor the Maximo performance, you must have noticed that it is not apparent which Maximo application generates the load. I will demonstrate the easy way to setup AppDynamics to get the most from the Maximo performance data.</p><p>One of the most useful features in AppDynamics is the concept of the <strong>business transactions,</strong> that is an end to end performance metrics for specific actions performed by the user. For very complex systems like Maximo it is impossible to cover all the possible business use cases. It would be good, though if it were possible to get the performance metrics breakdown by the Maximo application. In the picture below, you see the performance metrics coming from different Maximo applications: rfq, pr, wotrack, plusgptw_g, inventor, item...</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/businesstrannacios.PNG" class="kg-image" alt="Get more results from AppDynamics in Maximo"><figcaption>AppDynamics capturing Maximo business transactions</figcaption></figure><!--kg-card-end: image--><p>Take a look at the following network request coming to Maximo:</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/laodapp.PNG" class="kg-image" alt="Get more results from AppDynamics in Maximo"><figcaption>Network request to Maximo</figcaption></figure><!--kg-card-end: image--><p>When the user navigates to the new application in Maximo, the application name is sent to the Maximo server.  We will use this fact to create the cookie that stores the application name between the requests and use this cookie to get the business transactions in AppDynamics. We use the <strong>Servlet Filter</strong> for that purpose:</p><!--kg-card-begin: markdown--><pre><code class="language-java">package custom.webclient.servlet;

import javax.servlet.*;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * The filter should add the custom cookie once the app load event comes (i.e. the new app is loaded)
 * The purpose of that is to separate business transactions in AppDynamics
 */
public class AddCookieFilter implements Filter {

    @Override
    public void destroy() {

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        final String event = servletRequest.getParameter(&quot;event&quot;);
        final String value = servletRequest.getParameter(&quot;value&quot;);
        if (event == null || value == null || !event.equals(&quot;loadapp&quot;)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }
        Cookie appCookie = new Cookie(&quot;maximoapp&quot;, value);
        appCookie.setPath(&quot;/&quot;);
        ((HttpServletResponse) servletResponse).addCookie(appCookie);
        filterChain.doFilter(servletRequest, servletResponse);
    }
}

</code></pre>
<!--kg-card-end: markdown--><p>Don't forget to define the filter in web.xml:</p><!--kg-card-begin: markdown--><pre><code class="language-xml">&lt;filter&gt;
	  &lt;filter-name&gt;AddCookieFilter&lt;/filter-name&gt;
	  &lt;filter-class&gt;custom.webclient.servlet.AddCookieFilter&lt;/filter-class&gt;
&lt;/filter&gt;
</code></pre>
<!--kg-card-end: markdown--><p>and configure where it is used(also in web.xml):</p><!--kg-card-begin: markdown--><pre><code class="language-xml">&lt;filter-mapping&gt;
		  &lt;filter-name&gt;AddCookieFilter&lt;/filter-name&gt;
		  &lt;url-pattern&gt;/ui/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
</code></pre>
<!--kg-card-end: markdown--><p>When restarting the application, make sure the cookie is set:</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/cookies.PNG" class="kg-image" alt="Get more results from AppDynamics in Maximo"><figcaption>maximoapp cookie</figcaption></figure><!--kg-card-end: image--><p>In the AppDynamics Instrumentation define the new Transaction Detection:</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/appdynamics1.PNG" class="kg-image" alt="Get more results from AppDynamics in Maximo"><figcaption>Create new Transaction discovery in Instrumentation</figcaption></figure><!--kg-card-end: image--><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/appdynamics2.PNG" class="kg-image" alt="Get more results from AppDynamics in Maximo"><figcaption>Define the cookie splitting</figcaption></figure><!--kg-card-end: image--><p>Within several minutes, you will have the business transactions data coming to AppDynamics.</p>]]></content:encoded></item><item><title><![CDATA[MaximoPlus Server installation with Docker]]></title><description><![CDATA[More flexible development and deployment with the MaximoPlus running in Docker ]]></description><link>http://maximoplus.com/blog/maximoplus-installation-with-docker/</link><guid isPermaLink="false">5d122e63646862505b52f86b</guid><category><![CDATA[MaximoPlus]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Wed, 26 Jun 2019 18:10:23 GMT</pubDate><media:content url="http://maximoplus.com/blog/content/images/2019/06/library.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://maximoplus.com/blog/content/images/2019/06/library.jpg" alt="MaximoPlus Server installation with Docker"><p>Use Docker to easily install and manage your MaximoPlus Server.</p><p>If you have been installing MaximoPlus Server, you know that the installation process is straightforward: download, unzip, run the prepare  script and finally start the server. While this is still a fast and viable option, you may want to consider running the MaximoPlus server in the Docker container.</p><h3 id="why-use-docker-for-maximoplus">Why use Docker for MaximoPlus?</h3><p>First of all, you don't need to worry about the Java and OS versions. The MaximoPlus process runs isolated in the container.  You can run smoothly many Docker container instances in parallel without having to change the ports in the configuration file. If you have any changes in Maximo, deploying them in MaximoPlus means just creating a new instance of MaximoPlus Docker container. Finally, Docker containers are immutable, and you can run many Docker instances with the different versions of Maximo application, simply create the new instance when you want to test some change.</p><p><strong>Installation</strong></p><p>To install the MaximoPlus Docker container, do the following:</p><ul><li>Create a new directory</li><li>Copy the <strong>maximo.ear</strong> and optionally <strong>maximo.properties</strong> file inside it</li><li>Create the file named <strong>Dockerfile</strong>, and paste the following snippet inside</li></ul><!--kg-card-begin: markdown--><pre><code class="language-script">FROM maximoplus/maximoplus-server
WORKDIR /mp
COPY maximo.ear .
RUN /mp/prepare.sh inst maximo.ear
RUN rm /mp/maximo.ear
RUN cp startdocker.sh deployment/inst/
COPY . deployment/inst/
WORKDIR /mp/deployment/inst
CMD ./startdocker.sh
EXPOSE 8080
</code></pre>
<!--kg-card-end: markdown--><p>Now build the image:</p><!--kg-card-begin: markdown--><pre><code>docker build -t maximoplus_image1 .
</code></pre>
<!--kg-card-end: markdown--><p>Run the MaximoPlus server and expose port 8080 to Docker instance:</p><!--kg-card-begin: markdown--><pre><code class="language-sh">docker run --name maximoplus-instance -d -p 8080:8080 maximoplus_image1
</code></pre>
<!--kg-card-end: markdown--><p>Once you have the server up and running, it is time to <a href="https://maximoplus.com/blog/getting-started-with-maximoplus/">create your first MaximoPlus app</a>.</p>]]></content:encoded></item><item><title><![CDATA[Installing GraphQL Server for Maximo]]></title><description><![CDATA[<p>We will describe here how to install the free development version of GraphQL Server for Maximo. It is the same as a production one, just limited to five concurrent users.</p><h2 id="prerequisites">Prerequisites</h2><ul><li>Git </li><li>docker</li><li>docker-compose</li><li>Working maximo.ear file</li></ul><h2 id="installation">Installation</h2><p>First, clone the server template with git:</p><!--kg-card-begin: markdown--><pre><code class="language-sh">git clone https://bitbucket.</code></pre>]]></description><link>http://maximoplus.com/blog/installing-graphql-server-for-maximo/</link><guid isPermaLink="false">5d0e24d0646862505b52f502</guid><category><![CDATA[GraphQL Server]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Mon, 24 Jun 2019 18:11:07 GMT</pubDate><media:content url="http://maximoplus.com/blog/content/images/2019/06/train.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://maximoplus.com/blog/content/images/2019/06/train.jpg" alt="Installing GraphQL Server for Maximo"><p>We will describe here how to install the free development version of GraphQL Server for Maximo. It is the same as a production one, just limited to five concurrent users.</p><h2 id="prerequisites">Prerequisites</h2><ul><li>Git </li><li>docker</li><li>docker-compose</li><li>Working maximo.ear file</li></ul><h2 id="installation">Installation</h2><p>First, clone the server template with git:</p><!--kg-card-begin: markdown--><pre><code class="language-sh">git clone https://bitbucket.org/maximoplusteam/graphql-server-template.git
</code></pre>
<!--kg-card-end: markdown--><p>Go to the graphql-server-template directory. Copy the <strong>maximo.ear</strong> file inside the folder. Make sure that the <strong>db.url</strong> in maximo.properties inside the ear file point to the IP of the network adapter, not to the localhost. You may optionally put the<strong> maximo.properties</strong> file inside the directory as well, it will override the one from the ear file. If you are using Windows, open the <strong>docker-compose.yml</strong> file, and put the full path of the schema directory in the <strong>source</strong> property.</p><p>You are ready now to create and start the server. Open your shell or command prompt in graphql-server-template directory and run:</p><!--kg-card-begin: markdown--><pre><code class="language-sh">docker-compose up -d
</code></pre>
<!--kg-card-end: markdown--><p>To verify the installation run:</p><!--kg-card-begin: code--><pre><code>docker-compose logs -f</code></pre><!--kg-card-end: code--><h2 id="try-it-out">Try it out</h2><p>GrapqQL Server is built upon the Apollo GraphQL server, and comes with its <strong>GraphQL Playground</strong>. Open the browser and navigate to <a href="http://localhost:4001/graphql">http://localhost:4001/graphq</a>l, and then enter the <em>credentials </em>of a Maximo user. If you click on the SCHEMA button, you can see there are already some types defined in the schema. GraphQL server reads all the schema definitions from all the files inside the <strong>schema </strong>directory, and automatically exposes the types and creates the resolvers.</p><!--kg-card-begin: image--><figure class="kg-card kg-image-card kg-card-hascaption"><img src="http://maximoplus.com/blog/content/images/2019/06/g1.PNG" class="kg-image" alt="Installing GraphQL Server for Maximo"><figcaption>GraphQL Playground</figcaption></figure><!--kg-card-end: image--><p>Now let's run our first query:</p><!--kg-card-begin: code--><pre><code>query {
  po(fromRow:0, numRows:2){
    ponum
    description
    id
  }
}</code></pre><!--kg-card-end: code--><h3 id="where-to-go-from-here">Where to go from here?</h3><p>If you want to start learning how to write Maximo  types, queries and mutations in GraphQL SDL,  then immerse yourself in our <strong>manual</strong>. If you feel you would like to see how GraphQL works in action, read our next blog post: <strong>Simple React Native application with GraphQL for Maximo. </strong></p>]]></content:encoded></item><item><title><![CDATA[Introducing GraphQL Server for Maximo]]></title><description><![CDATA[GraphQL server for Maximo, no programming required]]></description><link>http://maximoplus.com/blog/test-blog/</link><guid isPermaLink="false">5d0e250b646862505b52f506</guid><category><![CDATA[GraphQL Server]]></category><dc:creator><![CDATA[Dusan Miloradovic]]></dc:creator><pubDate>Mon, 24 Jun 2019 17:56:40 GMT</pubDate><media:content url="http://maximoplus.com/blog/content/images/2019/06/work1.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://maximoplus.com/blog/content/images/2019/06/work1.jpg" alt="Introducing GraphQL Server for Maximo"><p>Is your company in the process of migrating(or considering it) from isolated applications architecture to the open API? If the answer is yes, then choosing the GraphQL instead or REST might be the right choice for you. The benefits of GraphQL over REST are numerous, and we will not repeat them here, you may find many resources online (for example, <a href="http://maximoplus.com/blog/test-blog/Top 5 reasons to use GraphQL">Top Five Reasons to use GraphQL)</a>.</p><h2 id="graphql-suits-maximo-naturally">GraphQL suits Maximo naturally</h2><p>The basic building blocks of Maximo are its business objects (Mbos),  business objects fields, and the relationships between them. If you have even the slightest idea about the GraphQL, you can easily spot the similarities: Mbos - object types, Mbo fields - object fields, and the relationship - nested object types.</p><h2 id="why-graphql-server-for-maximo">Why GraphQL server for Maximo?</h2><p>The GrapqhQL Server for Maximo requires no programming at all. You simply define the schema in the GraphQL schema definition language (<strong>SDL</strong>), and the server automatically creates the resolvers. The server covers the complete  Maximo functionality: you can use the GraphQL <strong>mutations</strong> to change the data, route the workflow, or execute any action available in Maximo (for example: change the status, change the balance of item inventory, create RFQ from PR...)<br>Want to try it out? Check out the documentation, or the next blog post - <strong>Installing GraphQL server for Maximo</strong></p>]]></content:encoded></item></channel></rss>