Tutorial 1 - Setting up your local environment and initial app

In this tutorial, you will set up your local simulator environment using CLI tools to simulate your first blueprint running in its own Datastore service, and configure the JavaScript Software Development Kit (JS SDK) to work with this blueprint.

You will also examine the initial blueprint files and JavaScript files (for the event application), which are provided for you from the tutorial source file package.

This tutorial takes around 30-40 minutes to complete.

Install the CLI tools

Set up the Datastore command line interface (CLI) tools locally on your computer to run the local simulator, which allows you to develop and test your blueprint and event management application locally.

  1. Ensure the software requirements for this tutorial series have been installed and updated.

  2. Install the Experience Cloud CLI tool, which provides the base set of commands for all Squiz DXP products:

    $ npm install --global @squiz/dxp-cli
    If you experience EACCES permission errors when running this command, follow the recommendations in this troubleshooting article to resolve these issues.
  3. Install the Datastore CLI tool, which is installed as a plugin to the Experience Cloud CLI tool:

    $ dxp plugin add @squiz/dxp-plugin-datastore

Prepare your local workspace and obtain the tutorial source files

Before starting the tutorials, set up your local working directory for your blueprint and application files, and download and extract the tutorial source file package.

  1. Run the following commands to set up your tutorial’s workspace and to create an empty blueprint ready for simulation:

    1. Create and enter the datastore-tutorial directory:

      $ mkdir datastore-tutorial
      $ cd datastore-tutorial
    2. Obtain the tutorial source files and extract them:

      $ curl https://blueprints.datastore.squiz.cloud/tutorialv2.tar.gz --output tutorial.tar.gz
      $ tar -xvf tutorial.tar.gz
      $ rm tutorial.tar.gz
    3. Create and enter the working directory for your event application (event-app):

      $ mkdir event-app
      $ cd event-app
    4. Within the event-app directory, create the initial file structures for your event application’s blueprint:

      $ mkdir api
      $ touch api/api.yaml
  2. Ensure your local datastore-tutorial directory and file structure matches this structure:

    datastore-tutorial
      |_ event-app
        |_ api
          |_ api.yaml
      |_ tutorial
        |_ ... contains all downloaded and extracted tutorial source files

Add your initial blueprint to your local simulator

Use the newly installed Datastore CLI tool to start running your local Datastore simulator and add your initial blueprint file to it.

  1. Ensure you are still in the event-app directory.

  2. Run the following command to simulate the empty (api.yaml) initial blueprint that was created when you prepared your local workspace:

    $ dxp datastore simulator add --blueprint api/api.yaml

    which if run successfully, should provide a result like this:

    ✔ Done! Use these details for local querying:
    
        URL: http://0.0.0.0:7001/aBCDEFGh
        JWT URL: http://0.0.0.0:7001/__JWT/issueToken
    
    ...

The command will take a few moments to run while the simulation environment is prepared.
The details for your Datastore service URL (indicated just by "URL") and JWT URL will differ for each set-up.

Configure the local JS SDK to use your simulated blueprint

This tutorial uses a local JS SDK which provides query and filter methods that interact with your blueprint’s API in the Datastore simulator. These methods prevent the need for developers to write bespoke code to add, find, update and delete document data and collections within their Datastore service.

The settings to configure the interactions between the JS SDK and your Datastore simulator are defined in a settings object.

This configuration is often defined in your app’s main.js file. However, since the main.js file in this set of tutorials is overwritten during most of the subsequent tutorials, the settings object is defined in a separate file, to prevent you having to reconfigure its Datastore service URL value each time.

To define the settings object:

  1. Create a settings.js file in the event-app directory, with the following source code:

    const settings = {
        serviceURL: '<replace-with-your-datastore-service-url>'
    };
  2. Replace the serviceURL value with the URL value provided in the CLI output when you added the blueprint above.

    You can always retrieve your Datastore service’s URL value with the command:

    $ dxp datastore simulator list
  3. Save your updated settings.js file.

Copy the 'tutorial 1' files over to your event-app directory and test the app

Now copy the initial stage of your app’s functionality from the source tutorial repository over to your event-app directory.

  1. From the event-app directory, copy the content of the step1 directory to the event-app directory:

    $ cp -r ../tutorial/step1/* .
  2. Test that the initial application loads by opening the index.html file in the event-app directory, directly in a web browser. This web application is designed to run without a web server.

    • Click the Create an event button to begin creating an event.

      1. Specify an event’s Name and Description (in the Create an event dialog box).

      2. Click Create event to save the event.

If you find that creating an event does not work, or you receive an "Internal server error" upon opening the index.html file, then you may need to upgrade your Datastore simulator. This is likely to happen if you had previously run through (part of) this tutorial series, and Datastore had been updated between then and now.

To resolve these issues, try upgrading your local Datastore simulation environment first, and then re-try the 'test' step above.

Examine how the API endpoints of your blueprint are defined

In this section, you will examine the file content that was copied over from the previous step.

This will provide you with an insight and foundation into how your blueprint’s API specifications and data models are defined and constructed.

In subsequent tutorials, you will add to these definitions yourself.

Examine the API specification (api.yaml) file

As explained in detail in Data organization within collections and documents, the api.yaml file in the api/ directory defines the initial API endpoints for:

  • A collection named events (accessible through the /events endpoint), which defines GET and POST requests.

  • Individual event documents (contained within the events collection), whose names are dynamic IDs assigned by Datastore (accessible through the /events/{eventid} endpoint), which defines an initial GET request, where the name of the event document is the parameter itself.

In the event-app/api/ directory, open the api.yaml file to examine its content:

paths:
  /events:
    get: (1)
      description: Gets all events for a user
        ...
    post: (2)
      description: Adds a new event
        ...
  /events/{eventid}:
    parameters: (3)
      - in: path
        name: eventid
        description: ID of an event
        required: true
        schema:
          type: string
    get: (4)
      description: Get an event by ID
        ...
1 Definition for the GET request for the /events collection endpoint.
Note: If a GET request is defined on a collection endpoint (like this), then the collection’s document endpoint (below) must also define a GET request.
2 Definition for the POST request for the /events collection endpoint.
3 Definition for the characteristics of the /events/{eventid} document endpoint’s parameter value, where the parameter value is the {eventid} name itself.
Note: Although the app will not yet utilize this endpoint until Tutorial 3, this definition in the API specification (YAML) file is required whenever a GET request is defined on the parent collection endpoint (i.e. /events above).
4 Definition for the GET request for the /events/{eventid} document endpoint.
Note: As explained above, a GET request must be defined on this document endpoint when a GET request is defined on its parent collection endpoint.

This simple API structure allows for URLs like /events and /events/0cad4697-8422-47c0-9d4b-5f16d7f5baf3 (as an example) to represent the main collection and individual events within it, respectively.

Examine the JSON schema (data model) files

The JSON schema files (in the schemas/ directory) define the data that each endpoint accepts.

Examine the event.json file

The event.json file defines the data that the /events/{eventid} endpoint sends and/or receives. This is explained in more detail below.

In the event-app/api/schemas/ directory, open the event.json file to examine its content:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",
    "properties": {
        "name": { (1)
            "description": "The name of the event.",
            "type": "string",
            "example": "Music Festival"
        },
        "description": { (2)
            "description": "The name of the event.",
            "type": "string",
            "example": "A festival of music, circus and magic."
        }
    }
}
1 Definition for the /events/{eventid} endpoint’s name string property.
2 Definition for the same endpoint’s description string property.
Other than strings, properties can also be integers, numbers, booleans, dates/times and nulls. A property could also be an array containing multiple values of any of these supported types.

Examine the events.json file

The events.json file defines the data that the /events endpoint receives.

In the event-app/api/schemas/ directory, open the events.json file to examine its content:

{
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "array",
    "items": {
        "$ref": "./event.json" (1)
    }
}
1 Definition for the /events endpoint’s array property, which is an array of objects defined by the event.json file in the same directory. This array represents a collection of documents, where each document’s properties are defined by the event.json file. This type of definition is required if a method’s request to a document (i.e. the event via the /events/{eventid} endpoint) within a collection (i.e. events) requires event properties to be either passed in its payload, or provided in the response from Datastore.

Examine the remainder of the api.yaml file

Examining the remainder of the api.yaml file shows how the properties defined in the event.json and events.json files (above) relate to the API endpoint definitions.

paths:
  /events:
    get:
      description: Gets all events for a user
      x-datastore-acl: public
      responses: (1)
        '200':
          description: Array of events
          content:
            application/json:
              schema:
                $ref: './schemas/events.json'
    post:
      description: Adds a new event
      x-datastore-acl: public
      responses: (2)
        '201':
          description: The event info is returned
          content:
            application/json:
              schema:
                $ref: './schemas/event.json'
      requestBody: (2)
        content:
          application/json:
            schema:
              $ref: './schemas/event.json'
  /events/{eventid}:
    parameters:
      - in: path
        name: eventid
        description: ID of an event
        required: true
        schema:
          type: string
    get:
      description: Get an event by ID
      x-datastore-acl: public
      responses: (3)
        '200':
          description: The event info is returned
          content:
            application/json:
              schema:
                $ref: './schemas/event.json'
1 The GET response from the /events endpoint returns properties defined by the events.json object. This response retrieves a collection (i.e. an array) of individual event documents, whose properties are defined by the event.json file.
2 The POST request to (and response from) the /events endpoint returns properties defined by the event.json object. The request creates a new event document, whose properties are defined by the event.json object. The response is also an event document, whose properties are defined by event.json.
3 The GET response from the /events/{eventid} endpoint returns properties defined by the event.json object. This response retrieves an event document, whose name is {eventid}, and whose properties are defined by the event.json object.

Examine how the JS SDK is used

To ensure your web application uses the local JS SDK simulator, incorporate its library towards the end of your web application’s HTML file.

The local JS SDK and Datastore configuration in the index.html file

In the event-app directory, open the index.html file and scroll towards the end of the file.

The following code snippet shows how the local JS SDK and Datastore are configured in this file.

<html>
    ....
    <body>
        ....
        <!-- Datastore JS -->
        <script src="./src/datastoresim.js"></script> (1)
        <script src="./settings.js"></script> (2)
        <script>
            // Datastore JS SDK object.
            const datastore = new Datastore(settings); (3)
        </script>
        <script src="./main.js"></script>
    </body>
</html>
1 Incorporates the Datastore JS SDK simulator class into the web application.
2 References the settings.js file you set above to configure Datastore within your app. Note that configuring Datastore details for the JS SDK would normally be a part of the main.js file (and optionally instantiated there), as depicted in the code snippet below. However, since this tutorial uses the cp command to re-write the existing main.js file with more code and functionality, the definition for the settings constant has been moved into a separate settings.js file to avoid you having to re-specify the Datastore URL in main.js each time.
3 Instantiates the Datastore object.
Configuring and optionally instantiating Datastore within your main.js itself
const settings = {
    serviceURL: '<Your URL here>'
};

const datastore = new Datastore(settings);

Examine the getEvents function in the main.js file

In the event-app directory, open the main.js file and examine the const getEvents …​ function definition towards the top of the file.

The following code snippet shows the JS SDK call to retrieve a list of events (i.e. from a collection).

/**
 * Gets all events for My Event Manager
 */
const getEvents = () => {
    datastore.collection('events').get().then( (1)
        (events) => {
            if (events.length === 0) {
                ...
                // Code to handle what happens when there are no events.
                ...
            } else {
                events.forEach((event) => {
                    printEvent(event); (2)
                });
            }
        }
    );
};
1 The JS SDK call to get a list of events.
The datastore.collection() method prepares a collection request on the top-level collection (i.e. events) defined in the API specification above.
The get() method is then called on this collection() to send a GET request (on the prepared collection request) to retrieve the events collection’s array of individual event document objects.
This method uses the blueprint’s get definition within /events, which is also defined in the API specification above, to retrieve the requested collection of event documents from Datastore.
Then, if all goes well …​
2 Outputs a list of each event document in the events collection.

Examine the addEvent function

Further down in the main.js file, the following code snippet shows the JS SDK call to add an event (i.e. document).

/**
 * Adds a new event to My Event Manager
 */
const addEvent = () => {
    const data = getData('#createEvent'); (1)

    datastore.collection('events').add(data).then((event) => { (2)
        printEvent(event); (3)
        ...
    });
};
1 Contains JavaScript code to retrieve the properties' values (from the app) for the newly created event, temporarily held by the data constant.
2 The JS SDK call to add the event to the events collection.
The datastore.collection() method prepares a collection request on the top-level collection (i.e. events) defined in the API specification above.
The add() method is then called on this collection() to send a POST request (on the prepared collection request) to add the new event properties' values in the data constant, as a new document of this events collection.
This method uses the API’s post definition within /events, which is also defined in the API specification above.
Then, if all goes well …​
3 Outputs the event data that was added to the events collection.

Now that you understand the fundamentals of how to define API endpoints and work with the JS SDK to use these API endpoints, you can now extend the functionality of your events management app to handle more structured data.

© 2015- Squiz Pty Ltd