src/index.js
function SDK(options = {}) {
  let token;
  let url;
  let project = "_";
  let localExp;
  let tokenExpiryTime = 5;

  if (options.storage) {
    let storedInfo = options.storage.getItem("directus-sdk-js");

    if (storedInfo) {
      storedInfo = JSON.parse(storedInfo);

      token = storedInfo.token;
      url = storedInfo.url;
      project = storedInfo.project;
      localExp = storedInfo.localExp;
    }
  }

  if (options.token) {
    token = options.token;
  }

  if (options.url) {
    url = options.url;
  }

  if (options.project) {
    project = options.project;
  }

  if (options.localExp) {
    localExp = options.localExp;
  }

  if (options.tokenExpiryTime) {
    tokenExpiryTime = options.tokenExpiryTime;
  }

  const SDK = {
    url: url,
    token: token,
    project: project,

    // The token will contain an expiry time based on the server time
    // In order to make sure we check the right expiry date, we need to
    // keep a version that's based on the browser time
    localExp: localExp,
    axios: axios.create({
      paramsSerializer: qs.stringify,
      timeout: 10 * 60 * 1000 // 10 min
    }),
    refreshInterval: null,
    onAutoRefreshError: null,
    onAutoRefreshSuccess: null,

    // The storage method to use. Has to support getItem and setItem to store and
    // retrieve the token
    storage: options.storage || null,

    // Defaults to 5 minutes. Once the API supports a custom, this option can be used to reflect that
    tokenExpiryTime: tokenExpiryTime,

    get payload() {
      if (!AV.isString(this.token)) return null;
      return getPayload(this.token);
    },

    get loggedIn() {
      if (
        AV.isString(this.token) &&
        AV.isString(this.url) &&
        AV.isString(this.project) &&
        AV.isObject(this.payload)
      ) {
        if (this.localExp > Date.now()) {
          return true;
        }
      }
      return false;
    },

    // REQUEST METHODS
    // -------------------------------------------------------------------------

    /**
     * Directus API request promise
     * @promise RequestPromise
     * @fulfill {object} Directus data
     * @reject {Error} Network error (if no connection to API)
     * @reject {Error} Directus error (eg not logged in or 404)
     */

    /**
     * Perform an API request to the Directus API
     * @param  {string} method      The HTTP method to use
     * @param  {string} endpoint    The API endpoint to request
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @param  {Object} [data={}]   The HTTP request body (non-GET only)
     * @param  {Boolean} noEnv      Don't use the project in the path
     * @param  {Boolean} ignoreJson Don't parse the API result into JSON
     * @return {RequestPromise}
     */
    request(
      method,
      endpoint,
      params = {},
      data = {},
      noEnv = false,
      headers = {},
      ignoreJson = false
    ) {
      AV.string(method, "method");
      AV.string(endpoint, "endpoint");
      AV.objectOrEmpty(params, "params");
      Array.isArray(data)
        ? AV.arrayOrEmpty(data, "data")
        : AV.objectOrEmpty(data, "data");

      AV.string(this.url, "this.url");

      let baseURL = `${this.url}/`;

      if (noEnv === false) {
        baseURL += `${this.project}/`;
      }

      const requestOptions = {
        url: endpoint,
        method,
        baseURL,
        params,
        data
      };

      if (
        this.token &&
        typeof this.token === "string" &&
        this.token.length > 0
      ) {
        requestOptions.headers = headers;
        requestOptions.headers.Authorization = `Bearer ${this.token}`;
      }

      return this.axios
        .request(requestOptions)
        .then(res => res.data)
        .then(data => {
          if (!data || data.length === 0) return data;

          if (ignoreJson) return data;

          if (typeof data !== "object") {
            try {
              return JSON.parse(data);
            } catch (error) {
              throw {
                json: true,
                error,
                data
              };
            }
          }

          return data;
        })
        .catch(error => {
          if (error.response) {
            throw error.response.data.error;
          } else if (error.json === true) {
            throw {
              // eslint-disable-line
              code: -2,
              message: "API returned invalid JSON",
              error: error.error,
              data: error.data
            };
          } else {
            throw {
              // eslint-disable-line
              code: -1,
              message: "Network Error",
              error
            };
          }
        });
    },

    /**
     * GET convenience method. Calls the request method for you
     * @param  {string} endpoint    The endpoint to get
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @return {RequestPromise}
     */
    get(endpoint, params = {}) {
      AV.string(endpoint, "endpoint");
      AV.objectOrEmpty(params, "params");

      return this.request("get", endpoint, params);
    },

    /**
     * POST convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    post(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("post", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    patch(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("patch", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    put(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("put", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @return {RequestPromise}
     */
    delete(endpoint) {
      AV.string(endpoint, "endpoint");

      return this.request("delete", endpoint);
    },

    // AUTHENTICATION
    // -------------------------------------------------------------------------

    /**
     * Logging in promise
     * @promise LoginPromise
     * @fulfill {Object} Object containing URL, ENV, and TOKEN
     * @reject {Error}   Network error (if no connection to API)
     * @reject {Error}   Directus error (eg not logged in or 404)
     */

    /**
     * Login to the API.
     *
     * Gets a new token from the API and stores it in this.token
     * @param  {Object} credentials
     * @param  {String} credentials.email     The user's email address
     * @param  {String} credentials.password  The user's password
     * @param  {String} [credentials.url]     The API to login to (overwrites this.url)
     * @param  {String} [credentials.project] The API project to login to (overwrites this.project)
     * @param  {String} [options.persist]     Auto-fetch a new token when it's about to expire
     * @param  {Boolean} [options.storage]    Where to store the token (survive refreshes)
     * @return {LoginPromise}
     */
    login(credentials, options = { persist: true }) {
      AV.object(credentials, "credentials");
      AV.keysWithString(credentials, ["email", "password"], "credentials");

      this.token = null;

      if (AV.hasKeysWithString(credentials, ["url"])) {
        this.url = credentials.url;
      }

      if (AV.hasKeysWithString(credentials, ["project"])) {
        this.project = credentials.project;
      }

      if (credentials.persist || options.persist) {
        this.startInterval();
      }

      return new Promise((resolve, reject) => {
        this.post("/auth/authenticate", {
          email: credentials.email,
          password: credentials.password
        })
          .then(res => res.data.token)
          .then(token => {
            this.token = token;

            // Expiry date is the moment we got the token + 5 minutes
            this.localExp = new Date(Date.now() + this.tokenExpiryTime * 60000).getTime();

            if (this.storage) {
              this.storage.setItem(
                "directus-sdk-js",
                JSON.stringify({
                  token: this.token,
                  url: this.url,
                  project: this.project,
                  localExp: this.localExp
                })
              );
            }

            resolve({
              url: this.url,
              project: this.project,
              token: this.token,
              localExp: this.localExp
            });
          })
          .catch(reject);
      });
    },

    /**
     * Logs the user out by "forgetting" the token, and clearing the refresh interval
     */
    logout() {
      this.token = null;

      if (this.refreshInterval) {
        this.stopInterval();
      }

      if (this.storage) {
        this.storage.removeItem("directus-sdk-js");
      }
    },

    /**
     * Resets the client instance by logging out and removing the URL and project
     */
    reset() {
      this.logout();
      this.url = null;
      this.project = null;
    },

    /**
     * Starts an interval of 10 seconds that will check if the token needs refreshing
     * @param {Boolean} fireImmediately Fire the refreshIfNeeded method directly
     */
    startInterval(fireImmediately) {
      if (fireImmediately) this.refreshIfNeeded();
      this.refreshInterval = setInterval(
        this.refreshIfNeeded.bind(this),
        10000
      );
    },

    /**
     * Clears and nullifies the token refreshing interval
     */
    stopInterval() {
      clearInterval(this.refreshInterval);
      this.refreshInterval = null;
    },

    /**
     * Refresh the token if it is about to expire (within 30 seconds of expiry date)
     *
     * Calls onAutoRefreshSuccess with the new token if the refreshing is successful
     * Calls onAutoRefreshError if refreshing the token fails for some reason
     */
    refreshIfNeeded() {
      if (!AV.hasStringKeys(this, ["token", "url", "project"])) return;
      if (!this.payload || !this.payload.exp) return;

      const timeDiff = this.localExp - Date.now();

      if (timeDiff <= 0) {
        if (AV.isFunction(this.onAutoRefreshError)) {
          this.onAutoRefreshError({
            message: "auth_expired_token",
            code: 102
          });
        }
        return;
      }

      if (timeDiff < 30000) {
        this.refresh(this.token)
          .then(res => {
            this.token = res.data.token;
            this.localExp = new Date(Date.now() + this.tokenExpiryTime * 60000).getTime();

            if (AV.isFunction(this.onAutoRefreshSuccess)) {
              this.onAutoRefreshSuccess({
                url: this.url,
                project: this.project,
                token: this.token,
                localExp: this.localExp
              });
            }

            if (this.storage) {
              this.storage.setItem(
                "directus-sdk-js",
                JSON.stringify({
                  token: this.token,
                  url: this.url,
                  project: this.project,
                  localExp: this.localExp
                })
              );
            }
          })
          .catch(error => {
            if (AV.isFunction(this.onAutoRefreshError)) {
              this.onAutoRefreshError(error);
            }
          });
      }
    },

    /**
     * Use the passed token to request a new one
     * @param  {String} token Active & Valid token
     * @return {RequestPromise}
     */
    refresh(token) {
      AV.string(token, "token");
      return this.post("/auth/refresh", { token });
    },

    /**
     * Request to reset the password of the user with the given email address
     *
     * The API will send an email to the given email address with a link to generate a new
     * temporary password.
     * @param {String} email The user's email
     */
    requestPasswordReset(email) {
      AV.string(email, "email");
      return this.post("/auth/password/request", {
        email: email
      });
    },

    // ACTIVITY
    // -------------------------------------------------------------------------

    /**
     * Get activity
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getActivity(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/activity", params);
    },

    // BOOKMARKS
    // -------------------------------------------------------------------------

    /**
     * Get the bookmarks of the current user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMyBookmarks(params = {}) {
      AV.string(this.token, "this.token");
      AV.objectOrEmpty(params);
      return Promise.all([
        this.get("/collection_presets", {
          "filter[title][nnull]": 1,
          "filter[user][eq]": this.payload.id
        }),
        this.get("/collection_presets", {
          "filter[title][nnull]": 1,
          "filter[role][eq]": this.payload.role,
          "filter[user][null]": 1
        })
      ]).then(values => {
        const [user, role] = values; // eslint-disable-line no-shadow
        return [...user.data, ...role.data];
      });
    },

    // COLLECTIONS
    // -------------------------------------------------------------------------

    /**
     * Get all available collections
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollections(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/collections", params);
    },

    /**
     * Get collection info by name
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollection(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");
      return this.get(`/collections/${collection}`, params);
    },

    /**
     * Create a collection
     * @param {Object} data Collection information
     * @return {RequestPromise}
     */
    createCollection(data) {
      AV.object(data, "data");
      return this.post("/collections", data);
    },

    /**
     * @param  {String} The collection to update
     * @param  {Object} The fields to update
     * @return {RequestPromise}
     */
    updateCollection(collection, data) {
      AV.string(collection, "collection");
      AV.object(data, "data");
      return this.patch(`/collections/${collection}`, data);
    },

    /**
     * @param  {String} collection The primary key of the collection to remove
     * @return {RequestPromise}
     */
    deleteCollection(collection) {
      AV.string(collection, "collection");
      return this.delete(`/collections/${collection}`);
    },

    // COLLECTION PRESETS
    // -------------------------------------------------------------------------

    /**
     * Create a new collection preset (bookmark / listing preferences)
     * @param  {Object} data The bookmark info
     * @return {RequestPromise}
     */
    createCollectionPreset(data) {
      AV.object(data);
      return this.post("/collection_presets", data);
    },

    /**
     * Update collection preset (bookmark / listing preference)
     * @param {String|Number} primaryKey
     * @param {RequestPromise} data
     */
    updateCollectionPreset(primaryKey, data) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(data, "data");

      return this.patch(`/collection_presets/${primaryKey}`, data);
    },

    /**
     * Delete collection preset by primarykey
     * @param {String|Number} primaryKey The primaryKey of the preset to delete
     */
    deleteCollectionPreset(primaryKey) {
      AV.notNull(primaryKey, "primaryKey");
      return this.delete(`/collection_presets/${primaryKey}`);
    },

    // DATABASE
    // ------------------------------------------------------------------------

    /**
     * This will update the database of the API instance to the latest version
     * using the migrations in the API
     * @return {RequestPromise}
     */
    updateDatabase() {
      return this.post("/update");
    },

    // EXTENSIONS
    // -------------------------------------------------------------------------

    /**
     * Get the meta information of all installed interfaces
     * @return {RequestPromise}
     */
    getInterfaces() {
      return this.request("get", "/interfaces", {}, {}, true);
    },

    /**
     * Get the meta information of all installed layouts
     * @return {RequestPromise}
     */
    getLayouts() {
      return this.request("get", "/layouts", {}, {}, true);
    },

    /**
     * Get the meta information of all installed pages
     * @return {RequestPromise}
     */
    getPages() {
      return this.request("get", "/pages", {}, {}, true);
    },

    // FIELDS
    // ------------------------------------------------------------------------

    /**
     * Get all fields that are in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getAllFields(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/fields", params);
    },

    /**
     * Get the fields that have been setup for a given collection
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getFields(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");
      return this.get(`/fields/${collection}`, params);
    },

    /**
     * Get the field information for a single given field
     * @param  {String} collection  Collection name
     * @param  {String} fieldName   Field name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getField(collection, fieldName, params = {}) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      AV.objectOrEmpty(params, "params");
      return this.get(`/fields/${collection}/${fieldName}`, params);
    },

    /**
     * Create a field in the given collection
     * @param  {String} collection Collection to add the field in
     * @param  {Object} fieldInfo  The fields info to save
     * @return {RequestPromise}
     */
    createField(collection, fieldInfo) {
      AV.string(collection, "collection");
      AV.object(fieldInfo, "fieldInfo");
      return this.post(`/fields/${collection}`, fieldInfo);
    },

    /**
     * Update a given field in a given collection
     * @param  {String} collection Field's parent collection
     * @param  {String} fieldName  Name of the field to update
     * @param  {Object} fieldInfo  Fields to update
     * @return {RequestPromise}
     */
    updateField(collection, fieldName, fieldInfo) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      AV.object(fieldInfo, "fieldInfo");
      return this.patch(`/fields/${collection}/${fieldName}`, fieldInfo);
    },

    /**
     * Update multiple fields at once
     * @param  {String} collection             Fields' parent collection
     * @param  {Array} fieldsInfoOrFieldNames  Array of field objects or array of field names
     * @param  {Object} [fieldInfo]            In case fieldsInfoOrFieldNames is an array of fieldNames, you need to provide the fields to update
     * @return {RequestPromise}
     *
     * @example
     *
     * // Set multiple fields to the same value
     * updateFields("projects", ["first_name", "last_name", "email"], {
     *   default_value: ""
     * })
     *
     * // Set multiple fields to different values
     * updateFields("projects", [
     *   {
     *     id: 14,
     *     sort: 1
     *   },
     *   {
     *     id: 17,
     *     sort: 2
     *   },
     *   {
     *     id: 912,
     *     sort: 3
     *   }
     * ])
     */
    updateFields(collection, fieldsInfoOrFieldNames, fieldInfo = null) {
      AV.string(collection, "collection");
      AV.array(fieldsInfoOrFieldNames, "fieldsInfoOrFieldNames");

      if (fieldInfo) {
        AV.object(fieldInfo);
      }

      if (fieldInfo) {
        return this.patch(
          `/fields/${collection}/${fieldsInfoOrFieldNames.join(",")}`,
          fieldInfo
        );
      }

      return this.patch(`/fields/${collection}`, fieldsInfoOrFieldNames);
    },

    /**
     * Delete a field from a collection
     * @param  {String} collection Name of the collection
     * @param  {String} fieldName  The name of the field to delete
     * @return {RequestPromise}
     */
    deleteField(collection, fieldName) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      return this.delete(`/fields/${collection}/${fieldName}`);
    },

    // FILES
    // ------------------------------------------------------------------------

    /**
     * Upload multipart files in multipart/form-data
     * @param  {Object} data FormData object containing files
     * @return {RequestPromise}
     */
    uploadFiles(data, onUploadProgress = () => {}) {
      const headers = {
        "Content-Type": "multipart/form-data",
        Authorization: `Bearer ${this.token}`
      };

      return this.axios
        .post(`${this.url}/${this.project}/files`, data, {
          headers,
          onUploadProgress
        })
        .then(res => res.data)
        .catch(error => {
          if (error.response) {
            throw error.response.data.error;
          } else {
            throw {
              // eslint-disable-line
              code: -1,
              message: "Network Error",
              error
            };
          }
        });
    },

    // ITEMS
    // -------------------------------------------------------------------------

    /**
     * Update an existing item
     * @param  {String} collection The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} body       The item's field values
     * @param  {Object} params     Query parameters
     * @return {RequestPromise}
     */
    updateItem(collection, primaryKey, body, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");

      if (collection.startsWith("directus_")) {
        return this.patch(
          `/${collection.substring(9)}/${primaryKey}`,
          body,
          params
        );
      }

      return this.patch(`/items/${collection}/${primaryKey}`, body, params);
    },

    /**
     * Update multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @param  {Object} params     Query Parameters
     * @return {RequestPromise}
     */
    updateItems(collection, body, params = {}) {
      AV.string(collection, "collection");
      AV.array(body, "body");

      if (collection.startsWith("directus_")) {
        return this.patch(`/${collection.substring(9)}`, body, params);
      }

      return this.patch(`/items/${collection}`, body, params);
    },

    /**
     * Create a new item
     * @param  {String} collection The collection to add the item to
     * @param  {Object} body       The item's field values
     * @return {RequestPromise}
     */
    createItem(collection, body) {
      AV.string(collection, "collection");
      AV.object(body, "body");

      if (collection.startsWith("directus_")) {
        return this.post(`/${collection.substring(9)}`, body);
      }

      return this.post(`/items/${collection}`, body);
    },

    /**
     * Create multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @return {RequestPromise}
     */
    createItems(collection, body) {
      AV.string(collection, "collection");
      AV.array(body, "body");

      if (collection.startsWith("directus_")) {
        return this.post(`/${collection.substring(9)}`, body);
      }

      return this.post(`/items/${collection}`, body);
    },

    /**
     * Get items from a given collection
     * @param  {String} collection The collection to add the item to
     * @param  {Object} [params={}]   Query parameters
     * @return {RequestPromise}
     */
    getItems(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(`/${collection.substring(9)}`, params);
      }

      return this.get(`/items/${collection}`, params);
    },

    /**
     * Get a single item by primary key
     * @param  {String} collection  The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getItem(collection, primaryKey, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(`/${collection.substring(9)}/${primaryKey}`, params);
      }

      return this.get(`/items/${collection}/${primaryKey}`, params);
    },

    /**
     * Delete a single item by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {String|Number} primaryKey Primary key of the item
     * @return {RequestPromise}
     */
    deleteItem(collection, primaryKey) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");

      if (collection.startsWith("directus_")) {
        return this.delete(`/${collection.substring(9)}/${primaryKey}`);
      }

      return this.delete(`/items/${collection}/${primaryKey}`);
    },

    /**
     * Delete multiple items by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {Array} primaryKey Primary key of the item
     * @return {RequestPromise}
     */
    deleteItems(collection, primaryKeys) {
      AV.string(collection, "collection");
      AV.array(primaryKeys, "primaryKeys");

      if (collection.startsWith("directus_")) {
        return this.delete(`/${collection.substring(9)}/${primaryKeys.join()}`);
      }

      return this.delete(`/items/${collection}/${primaryKeys.join()}`);
    },

    // LISTING PREFERENCES
    // -------------------------------------------------------------------------

    /**
     * Get the collection presets of the current user for a single collection
     * @param  {String} collection  Collection to fetch the preferences for
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMyListingPreferences(collection, params = {}) {
      AV.string(this.token, "this.token");
      AV.objectOrEmpty(params, "params");
      return Promise.all([
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][null]": 1,
          "filter[user][null]": 1,
          sort: "-id"
        }),
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][eq]": this.payload.role,
          "filter[user][null]": 1,
          sort: "-id"
        }),
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][eq]": this.payload.role,
          "filter[user][eq]": this.payload.id,
          sort: "-id"
        })
      ]).then(values => {
        const [collection, role, user] = values; // eslint-disable-line no-shadow
        if (user.data && user.data.length > 0) {
          return user.data[0];
        }
        if (role.data && role.data.length > 0) {
          return role.data[0];
        }
        if (collection.data && collection.data.length > 0) {
          return collection.data[0];
        }
        return {};
      });
    },

    // PERMISSIONS
    // -------------------------------------------------------------------------

    /**
     * Get permissions
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getPermissions(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.getItems("directus_permissions", params);
    },

    /**
     * Get the currently logged in user's permissions
     * @param  {Object} params Query parameters
     * @return {RequestPromise}
     */
    getMyPermissions(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/permissions/me", params);
    },

    /**
     * Create multiple new permissions
     * @param  {Array} data  Permission records to save
     * @return {RequestPromise}
     */
    createPermissions(data) {
      AV.array(data);
      return this.post("/permissions", data);
    },

    /**
     * Update multiple permission records
     * @param  {Array} data  Permission records to update
     * @return {RequestPromise}
     */
    updatePermissions(data) {
      AV.array(data);
      return this.patch("/permissions", data);
    },

    // RELATIONS
    // -------------------------------------------------------------------------

    /**
     * Get all relationships
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRelations(params = {}) {
      AV.objectOrEmpty(params);
      return this.get("/relations", params);
    },

    createRelation(data) {
      return this.post("/relations", data);
    },

    updateRelation(primaryKey, data) {
      return this.patch(`/relations/${primaryKey}`, data);
    },

    /**
     * Get the relationship information for the given collection
     * @param  {String} collection The collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollectionRelations(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params);

      return Promise.all([
        this.get("/relations", { "filter[collection_a][eq]": collection }),
        this.get("/relations", { "filter[collection_b][eq]": collection })
      ]);
    },

    // REVISIONS
    // -------------------------------------------------------------------------

    /**
     * Get a single item's revisions by primary key
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getItemRevisions(collection, primaryKey, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(
          `/${collection.substring(9)}/${primaryKey}/revisions`,
          params
        );
      }

      return this.get(`/items/${collection}/${primaryKey}/revisions`, params);
    },

    /**
     * revert an item to a previous state
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Number} revisionID The ID of the revision to revert to
     * @return {RequestPromise}
     */
    revert(collection, primaryKey, revisionID) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.number(revisionID, "revisionID");

      if (collection.startsWith("directus_")) {
        return this.patch(
          `/${collection.substring(9)}/${primaryKey}/revert/${revisionID}`
        );
      }

      return this.patch(
        `/items/${collection}/${primaryKey}/revert/${revisionID}`
      );
    },

    // ROLES
    // -------------------------------------------------------------------------

    /**
     * Get a single user role
     * @param  {Number} primaryKey  The id of the user rol to get
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRole(primaryKey, params = {}) {
      AV.number(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");
      return this.get(`/roles/${primaryKey}`, params);
    },

    /**
     * Get the user roles
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRoles(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/roles", params);
    },

    /**
     * Update a user role
     * @param  {Number} primaryKey The ID of the role
     * @param  {Object} body       The fields to update
     * @return {RequestPromise}
     */
    updateRole(primaryKey, body) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");
      return this.updateItem("directus_roles", primaryKey, body);
    },

    /**
     * Create a new user role
     * @param  {Object} body The role information
     * @return {RequestPromise}
     */
    createRole(body) {
      AV.object(body, "body");
      return this.createItem("directus_roles", body);
    },

    /**
     * Delete a user rol by primary key
     * @param  {Number | String} primaryKey Primary key of the user role
     * @return {RequestPromise}
     */
    deleteRole(primaryKey) {
      AV.notNull(primaryKey, "primaryKey");
      return this.deleteItem("directus_roles", primaryKey);
    },

    // SETTINGS
    // -------------------------------------------------------------------------

    /**
     * Get Directus' global settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getSettings(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/settings", params);
    },

    /**
     * Get the "fields" for directus_settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getSettingsFields(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/settings/fields", params);
    },

    // USERS
    // -------------------------------------------------------------------------

    /**
     * Get a list of available users in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getUsers(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/users", params);
    },

    /**
     * Get a single Directus user
     * @param  {String} primaryKey  The unique identifier of the user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getUser(primaryKey, params = {}) {
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");
      return this.get(`/users/${primaryKey}`, params);
    },

    /**
     * Get the user info of the currently logged in user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMe(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/users/me", params);
    },

    /**
     * Update a single user based on primaryKey
     * @param  {String|Number} primaryKey The primary key of the user
     * @param  {Object} body              The fields to update
     * @return {RequestPromise}
     */
    updateUser(primaryKey, body) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");
      return this.updateItem("directus_users", primaryKey, body);
    },

    // UTILS
    // -------------------------------------------------------------------------

    /**
     * Ping the API to check if it exists / is up and running
     * @return {RequestPromise}
     */
    ping() {
      return this.request("get", "/server/ping", {}, {}, true, true);
    },

    /**
     * Get the server info from the API
     * @return {RequestPromise}
     */
    serverInfo() {
      return this.request("get", "/", {}, {}, true);
    },

    /**
     * Get the server info from the project
     * @return {RequestPromise}
     */
    projectInfo() {
      return this.request("get", "/");
    },

    /**
     * Get all the setup third party auth providers
     * @return {RequestPromise}
     */
    getThirdPartyAuthProviders() {
      return this.get("/auth/sso");
    }
  };

  // Only start the auto refresh interval if the token exists and it's a JWT
  if (SDK.token && SDK.token.includes(".")) {
    SDK.startInterval(true);
  }

  return SDK;
}
function SDK(options = {}) {
  let token;
  let url;
  let project = "_";
  let localExp;
  let tokenExpiryTime = 5;

  if (options.storage) {
    let storedInfo = options.storage.getItem("directus-sdk-js");

    if (storedInfo) {
      storedInfo = JSON.parse(storedInfo);

      token = storedInfo.token;
      url = storedInfo.url;
      project = storedInfo.project;
      localExp = storedInfo.localExp;
    }
  }

  if (options.token) {
    token = options.token;
  }

  if (options.url) {
    url = options.url;
  }

  if (options.project) {
    project = options.project;
  }

  if (options.localExp) {
    localExp = options.localExp;
  }

  if (options.tokenExpiryTime) {
    tokenExpiryTime = options.tokenExpiryTime;
  }

  const SDK = {
    url: url,
    token: token,
    project: project,

    // The token will contain an expiry time based on the server time
    // In order to make sure we check the right expiry date, we need to
    // keep a version that's based on the browser time
    localExp: localExp,
    axios: axios.create({
      paramsSerializer: qs.stringify,
      timeout: 10 * 60 * 1000 // 10 min
    }),
    refreshInterval: null,
    onAutoRefreshError: null,
    onAutoRefreshSuccess: null,

    // The storage method to use. Has to support getItem and setItem to store and
    // retrieve the token
    storage: options.storage || null,

    // Defaults to 5 minutes. Once the API supports a custom, this option can be used to reflect that
    tokenExpiryTime: tokenExpiryTime,

    get payload() {
      if (!AV.isString(this.token)) return null;
      return getPayload(this.token);
    },

    get loggedIn() {
      if (
        AV.isString(this.token) &&
        AV.isString(this.url) &&
        AV.isString(this.project) &&
        AV.isObject(this.payload)
      ) {
        if (this.localExp > Date.now()) {
          return true;
        }
      }
      return false;
    },

    // REQUEST METHODS
    // -------------------------------------------------------------------------

    /**
     * Directus API request promise
     * @promise RequestPromise
     * @fulfill {object} Directus data
     * @reject {Error} Network error (if no connection to API)
     * @reject {Error} Directus error (eg not logged in or 404)
     */

    /**
     * Perform an API request to the Directus API
     * @param  {string} method      The HTTP method to use
     * @param  {string} endpoint    The API endpoint to request
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @param  {Object} [data={}]   The HTTP request body (non-GET only)
     * @param  {Boolean} noEnv      Don't use the project in the path
     * @param  {Boolean} ignoreJson Don't parse the API result into JSON
     * @return {RequestPromise}
     */
    request(
      method,
      endpoint,
      params = {},
      data = {},
      noEnv = false,
      headers = {},
      ignoreJson = false
    ) {
      AV.string(method, "method");
      AV.string(endpoint, "endpoint");
      AV.objectOrEmpty(params, "params");
      Array.isArray(data)
        ? AV.arrayOrEmpty(data, "data")
        : AV.objectOrEmpty(data, "data");

      AV.string(this.url, "this.url");

      let baseURL = `${this.url}/`;

      if (noEnv === false) {
        baseURL += `${this.project}/`;
      }

      const requestOptions = {
        url: endpoint,
        method,
        baseURL,
        params,
        data
      };

      if (
        this.token &&
        typeof this.token === "string" &&
        this.token.length > 0
      ) {
        requestOptions.headers = headers;
        requestOptions.headers.Authorization = `Bearer ${this.token}`;
      }

      return this.axios
        .request(requestOptions)
        .then(res => res.data)
        .then(data => {
          if (!data || data.length === 0) return data;

          if (ignoreJson) return data;

          if (typeof data !== "object") {
            try {
              return JSON.parse(data);
            } catch (error) {
              throw {
                json: true,
                error,
                data
              };
            }
          }

          return data;
        })
        .catch(error => {
          if (error.response) {
            throw error.response.data.error;
          } else if (error.json === true) {
            throw {
              // eslint-disable-line
              code: -2,
              message: "API returned invalid JSON",
              error: error.error,
              data: error.data
            };
          } else {
            throw {
              // eslint-disable-line
              code: -1,
              message: "Network Error",
              error
            };
          }
        });
    },

    /**
     * GET convenience method. Calls the request method for you
     * @param  {string} endpoint    The endpoint to get
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @return {RequestPromise}
     */
    get(endpoint, params = {}) {
      AV.string(endpoint, "endpoint");
      AV.objectOrEmpty(params, "params");

      return this.request("get", endpoint, params);
    },

    /**
     * POST convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    post(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("post", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    patch(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("patch", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */
    put(endpoint, body = {}, params = {}) {
      AV.string(endpoint, "endpoint");
      Array.isArray(body)
        ? AV.arrayOrEmpty(body, "body")
        : AV.objectOrEmpty(body, "body");

      return this.request("put", endpoint, params, body);
    },

    /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @return {RequestPromise}
     */
    delete(endpoint) {
      AV.string(endpoint, "endpoint");

      return this.request("delete", endpoint);
    },

    // AUTHENTICATION
    // -------------------------------------------------------------------------

    /**
     * Logging in promise
     * @promise LoginPromise
     * @fulfill {Object} Object containing URL, ENV, and TOKEN
     * @reject {Error}   Network error (if no connection to API)
     * @reject {Error}   Directus error (eg not logged in or 404)
     */

    /**
     * Login to the API.
     *
     * Gets a new token from the API and stores it in this.token
     * @param  {Object} credentials
     * @param  {String} credentials.email     The user's email address
     * @param  {String} credentials.password  The user's password
     * @param  {String} [credentials.url]     The API to login to (overwrites this.url)
     * @param  {String} [credentials.project] The API project to login to (overwrites this.project)
     * @param  {String} [options.persist]     Auto-fetch a new token when it's about to expire
     * @param  {Boolean} [options.storage]    Where to store the token (survive refreshes)
     * @return {LoginPromise}
     */
    login(credentials, options = { persist: true }) {
      AV.object(credentials, "credentials");
      AV.keysWithString(credentials, ["email", "password"], "credentials");

      this.token = null;

      if (AV.hasKeysWithString(credentials, ["url"])) {
        this.url = credentials.url;
      }

      if (AV.hasKeysWithString(credentials, ["project"])) {
        this.project = credentials.project;
      }

      if (credentials.persist || options.persist) {
        this.startInterval();
      }

      return new Promise((resolve, reject) => {
        this.post("/auth/authenticate", {
          email: credentials.email,
          password: credentials.password
        })
          .then(res => res.data.token)
          .then(token => {
            this.token = token;

            // Expiry date is the moment we got the token + 5 minutes
            this.localExp = new Date(Date.now() + this.tokenExpiryTime * 60000).getTime();

            if (this.storage) {
              this.storage.setItem(
                "directus-sdk-js",
                JSON.stringify({
                  token: this.token,
                  url: this.url,
                  project: this.project,
                  localExp: this.localExp
                })
              );
            }

            resolve({
              url: this.url,
              project: this.project,
              token: this.token,
              localExp: this.localExp
            });
          })
          .catch(reject);
      });
    },

    /**
     * Logs the user out by "forgetting" the token, and clearing the refresh interval
     */
    logout() {
      this.token = null;

      if (this.refreshInterval) {
        this.stopInterval();
      }

      if (this.storage) {
        this.storage.removeItem("directus-sdk-js");
      }
    },

    /**
     * Resets the client instance by logging out and removing the URL and project
     */
    reset() {
      this.logout();
      this.url = null;
      this.project = null;
    },

    /**
     * Starts an interval of 10 seconds that will check if the token needs refreshing
     * @param {Boolean} fireImmediately Fire the refreshIfNeeded method directly
     */
    startInterval(fireImmediately) {
      if (fireImmediately) this.refreshIfNeeded();
      this.refreshInterval = setInterval(
        this.refreshIfNeeded.bind(this),
        10000
      );
    },

    /**
     * Clears and nullifies the token refreshing interval
     */
    stopInterval() {
      clearInterval(this.refreshInterval);
      this.refreshInterval = null;
    },

    /**
     * Refresh the token if it is about to expire (within 30 seconds of expiry date)
     *
     * Calls onAutoRefreshSuccess with the new token if the refreshing is successful
     * Calls onAutoRefreshError if refreshing the token fails for some reason
     */
    refreshIfNeeded() {
      if (!AV.hasStringKeys(this, ["token", "url", "project"])) return;
      if (!this.payload || !this.payload.exp) return;

      const timeDiff = this.localExp - Date.now();

      if (timeDiff <= 0) {
        if (AV.isFunction(this.onAutoRefreshError)) {
          this.onAutoRefreshError({
            message: "auth_expired_token",
            code: 102
          });
        }
        return;
      }

      if (timeDiff < 30000) {
        this.refresh(this.token)
          .then(res => {
            this.token = res.data.token;
            this.localExp = new Date(Date.now() + this.tokenExpiryTime * 60000).getTime();

            if (AV.isFunction(this.onAutoRefreshSuccess)) {
              this.onAutoRefreshSuccess({
                url: this.url,
                project: this.project,
                token: this.token,
                localExp: this.localExp
              });
            }

            if (this.storage) {
              this.storage.setItem(
                "directus-sdk-js",
                JSON.stringify({
                  token: this.token,
                  url: this.url,
                  project: this.project,
                  localExp: this.localExp
                })
              );
            }
          })
          .catch(error => {
            if (AV.isFunction(this.onAutoRefreshError)) {
              this.onAutoRefreshError(error);
            }
          });
      }
    },

    /**
     * Use the passed token to request a new one
     * @param  {String} token Active & Valid token
     * @return {RequestPromise}
     */
    refresh(token) {
      AV.string(token, "token");
      return this.post("/auth/refresh", { token });
    },

    /**
     * Request to reset the password of the user with the given email address
     *
     * The API will send an email to the given email address with a link to generate a new
     * temporary password.
     * @param {String} email The user's email
     */
    requestPasswordReset(email) {
      AV.string(email, "email");
      return this.post("/auth/password/request", {
        email: email
      });
    },

    // ACTIVITY
    // -------------------------------------------------------------------------

    /**
     * Get activity
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getActivity(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/activity", params);
    },

    // BOOKMARKS
    // -------------------------------------------------------------------------

    /**
     * Get the bookmarks of the current user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMyBookmarks(params = {}) {
      AV.string(this.token, "this.token");
      AV.objectOrEmpty(params);
      return Promise.all([
        this.get("/collection_presets", {
          "filter[title][nnull]": 1,
          "filter[user][eq]": this.payload.id
        }),
        this.get("/collection_presets", {
          "filter[title][nnull]": 1,
          "filter[role][eq]": this.payload.role,
          "filter[user][null]": 1
        })
      ]).then(values => {
        const [user, role] = values; // eslint-disable-line no-shadow
        return [...user.data, ...role.data];
      });
    },

    // COLLECTIONS
    // -------------------------------------------------------------------------

    /**
     * Get all available collections
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollections(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/collections", params);
    },

    /**
     * Get collection info by name
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollection(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");
      return this.get(`/collections/${collection}`, params);
    },

    /**
     * Create a collection
     * @param {Object} data Collection information
     * @return {RequestPromise}
     */
    createCollection(data) {
      AV.object(data, "data");
      return this.post("/collections", data);
    },

    /**
     * @param  {String} The collection to update
     * @param  {Object} The fields to update
     * @return {RequestPromise}
     */
    updateCollection(collection, data) {
      AV.string(collection, "collection");
      AV.object(data, "data");
      return this.patch(`/collections/${collection}`, data);
    },

    /**
     * @param  {String} collection The primary key of the collection to remove
     * @return {RequestPromise}
     */
    deleteCollection(collection) {
      AV.string(collection, "collection");
      return this.delete(`/collections/${collection}`);
    },

    // COLLECTION PRESETS
    // -------------------------------------------------------------------------

    /**
     * Create a new collection preset (bookmark / listing preferences)
     * @param  {Object} data The bookmark info
     * @return {RequestPromise}
     */
    createCollectionPreset(data) {
      AV.object(data);
      return this.post("/collection_presets", data);
    },

    /**
     * Update collection preset (bookmark / listing preference)
     * @param {String|Number} primaryKey
     * @param {RequestPromise} data
     */
    updateCollectionPreset(primaryKey, data) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(data, "data");

      return this.patch(`/collection_presets/${primaryKey}`, data);
    },

    /**
     * Delete collection preset by primarykey
     * @param {String|Number} primaryKey The primaryKey of the preset to delete
     */
    deleteCollectionPreset(primaryKey) {
      AV.notNull(primaryKey, "primaryKey");
      return this.delete(`/collection_presets/${primaryKey}`);
    },

    // DATABASE
    // ------------------------------------------------------------------------

    /**
     * This will update the database of the API instance to the latest version
     * using the migrations in the API
     * @return {RequestPromise}
     */
    updateDatabase() {
      return this.post("/update");
    },

    // EXTENSIONS
    // -------------------------------------------------------------------------

    /**
     * Get the meta information of all installed interfaces
     * @return {RequestPromise}
     */
    getInterfaces() {
      return this.request("get", "/interfaces", {}, {}, true);
    },

    /**
     * Get the meta information of all installed layouts
     * @return {RequestPromise}
     */
    getLayouts() {
      return this.request("get", "/layouts", {}, {}, true);
    },

    /**
     * Get the meta information of all installed pages
     * @return {RequestPromise}
     */
    getPages() {
      return this.request("get", "/pages", {}, {}, true);
    },

    // FIELDS
    // ------------------------------------------------------------------------

    /**
     * Get all fields that are in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getAllFields(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/fields", params);
    },

    /**
     * Get the fields that have been setup for a given collection
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getFields(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");
      return this.get(`/fields/${collection}`, params);
    },

    /**
     * Get the field information for a single given field
     * @param  {String} collection  Collection name
     * @param  {String} fieldName   Field name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getField(collection, fieldName, params = {}) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      AV.objectOrEmpty(params, "params");
      return this.get(`/fields/${collection}/${fieldName}`, params);
    },

    /**
     * Create a field in the given collection
     * @param  {String} collection Collection to add the field in
     * @param  {Object} fieldInfo  The fields info to save
     * @return {RequestPromise}
     */
    createField(collection, fieldInfo) {
      AV.string(collection, "collection");
      AV.object(fieldInfo, "fieldInfo");
      return this.post(`/fields/${collection}`, fieldInfo);
    },

    /**
     * Update a given field in a given collection
     * @param  {String} collection Field's parent collection
     * @param  {String} fieldName  Name of the field to update
     * @param  {Object} fieldInfo  Fields to update
     * @return {RequestPromise}
     */
    updateField(collection, fieldName, fieldInfo) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      AV.object(fieldInfo, "fieldInfo");
      return this.patch(`/fields/${collection}/${fieldName}`, fieldInfo);
    },

    /**
     * Update multiple fields at once
     * @param  {String} collection             Fields' parent collection
     * @param  {Array} fieldsInfoOrFieldNames  Array of field objects or array of field names
     * @param  {Object} [fieldInfo]            In case fieldsInfoOrFieldNames is an array of fieldNames, you need to provide the fields to update
     * @return {RequestPromise}
     *
     * @example
     *
     * // Set multiple fields to the same value
     * updateFields("projects", ["first_name", "last_name", "email"], {
     *   default_value: ""
     * })
     *
     * // Set multiple fields to different values
     * updateFields("projects", [
     *   {
     *     id: 14,
     *     sort: 1
     *   },
     *   {
     *     id: 17,
     *     sort: 2
     *   },
     *   {
     *     id: 912,
     *     sort: 3
     *   }
     * ])
     */
    updateFields(collection, fieldsInfoOrFieldNames, fieldInfo = null) {
      AV.string(collection, "collection");
      AV.array(fieldsInfoOrFieldNames, "fieldsInfoOrFieldNames");

      if (fieldInfo) {
        AV.object(fieldInfo);
      }

      if (fieldInfo) {
        return this.patch(
          `/fields/${collection}/${fieldsInfoOrFieldNames.join(",")}`,
          fieldInfo
        );
      }

      return this.patch(`/fields/${collection}`, fieldsInfoOrFieldNames);
    },

    /**
     * Delete a field from a collection
     * @param  {String} collection Name of the collection
     * @param  {String} fieldName  The name of the field to delete
     * @return {RequestPromise}
     */
    deleteField(collection, fieldName) {
      AV.string(collection, "collection");
      AV.string(fieldName, "fieldName");
      return this.delete(`/fields/${collection}/${fieldName}`);
    },

    // FILES
    // ------------------------------------------------------------------------

    /**
     * Upload multipart files in multipart/form-data
     * @param  {Object} data FormData object containing files
     * @return {RequestPromise}
     */
    uploadFiles(data, onUploadProgress = () => {}) {
      const headers = {
        "Content-Type": "multipart/form-data",
        Authorization: `Bearer ${this.token}`
      };

      return this.axios
        .post(`${this.url}/${this.project}/files`, data, {
          headers,
          onUploadProgress
        })
        .then(res => res.data)
        .catch(error => {
          if (error.response) {
            throw error.response.data.error;
          } else {
            throw {
              // eslint-disable-line
              code: -1,
              message: "Network Error",
              error
            };
          }
        });
    },

    // ITEMS
    // -------------------------------------------------------------------------

    /**
     * Update an existing item
     * @param  {String} collection The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} body       The item's field values
     * @param  {Object} params     Query parameters
     * @return {RequestPromise}
     */
    updateItem(collection, primaryKey, body, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");

      if (collection.startsWith("directus_")) {
        return this.patch(
          `/${collection.substring(9)}/${primaryKey}`,
          body,
          params
        );
      }

      return this.patch(`/items/${collection}/${primaryKey}`, body, params);
    },

    /**
     * Update multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @param  {Object} params     Query Parameters
     * @return {RequestPromise}
     */
    updateItems(collection, body, params = {}) {
      AV.string(collection, "collection");
      AV.array(body, "body");

      if (collection.startsWith("directus_")) {
        return this.patch(`/${collection.substring(9)}`, body, params);
      }

      return this.patch(`/items/${collection}`, body, params);
    },

    /**
     * Create a new item
     * @param  {String} collection The collection to add the item to
     * @param  {Object} body       The item's field values
     * @return {RequestPromise}
     */
    createItem(collection, body) {
      AV.string(collection, "collection");
      AV.object(body, "body");

      if (collection.startsWith("directus_")) {
        return this.post(`/${collection.substring(9)}`, body);
      }

      return this.post(`/items/${collection}`, body);
    },

    /**
     * Create multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @return {RequestPromise}
     */
    createItems(collection, body) {
      AV.string(collection, "collection");
      AV.array(body, "body");

      if (collection.startsWith("directus_")) {
        return this.post(`/${collection.substring(9)}`, body);
      }

      return this.post(`/items/${collection}`, body);
    },

    /**
     * Get items from a given collection
     * @param  {String} collection The collection to add the item to
     * @param  {Object} [params={}]   Query parameters
     * @return {RequestPromise}
     */
    getItems(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(`/${collection.substring(9)}`, params);
      }

      return this.get(`/items/${collection}`, params);
    },

    /**
     * Get a single item by primary key
     * @param  {String} collection  The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getItem(collection, primaryKey, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(`/${collection.substring(9)}/${primaryKey}`, params);
      }

      return this.get(`/items/${collection}/${primaryKey}`, params);
    },

    /**
     * Delete a single item by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {String|Number} primaryKey Primary key of the item
     * @return {RequestPromise}
     */
    deleteItem(collection, primaryKey) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");

      if (collection.startsWith("directus_")) {
        return this.delete(`/${collection.substring(9)}/${primaryKey}`);
      }

      return this.delete(`/items/${collection}/${primaryKey}`);
    },

    /**
     * Delete multiple items by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {Array} primaryKey Primary key of the item
     * @return {RequestPromise}
     */
    deleteItems(collection, primaryKeys) {
      AV.string(collection, "collection");
      AV.array(primaryKeys, "primaryKeys");

      if (collection.startsWith("directus_")) {
        return this.delete(`/${collection.substring(9)}/${primaryKeys.join()}`);
      }

      return this.delete(`/items/${collection}/${primaryKeys.join()}`);
    },

    // LISTING PREFERENCES
    // -------------------------------------------------------------------------

    /**
     * Get the collection presets of the current user for a single collection
     * @param  {String} collection  Collection to fetch the preferences for
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMyListingPreferences(collection, params = {}) {
      AV.string(this.token, "this.token");
      AV.objectOrEmpty(params, "params");
      return Promise.all([
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][null]": 1,
          "filter[user][null]": 1,
          sort: "-id"
        }),
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][eq]": this.payload.role,
          "filter[user][null]": 1,
          sort: "-id"
        }),
        this.get("/collection_presets", {
          limit: 1,
          "filter[title][null]": 1,
          "filter[collection][eq]": collection,
          "filter[role][eq]": this.payload.role,
          "filter[user][eq]": this.payload.id,
          sort: "-id"
        })
      ]).then(values => {
        const [collection, role, user] = values; // eslint-disable-line no-shadow
        if (user.data && user.data.length > 0) {
          return user.data[0];
        }
        if (role.data && role.data.length > 0) {
          return role.data[0];
        }
        if (collection.data && collection.data.length > 0) {
          return collection.data[0];
        }
        return {};
      });
    },

    // PERMISSIONS
    // -------------------------------------------------------------------------

    /**
     * Get permissions
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getPermissions(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.getItems("directus_permissions", params);
    },

    /**
     * Get the currently logged in user's permissions
     * @param  {Object} params Query parameters
     * @return {RequestPromise}
     */
    getMyPermissions(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/permissions/me", params);
    },

    /**
     * Create multiple new permissions
     * @param  {Array} data  Permission records to save
     * @return {RequestPromise}
     */
    createPermissions(data) {
      AV.array(data);
      return this.post("/permissions", data);
    },

    /**
     * Update multiple permission records
     * @param  {Array} data  Permission records to update
     * @return {RequestPromise}
     */
    updatePermissions(data) {
      AV.array(data);
      return this.patch("/permissions", data);
    },

    // RELATIONS
    // -------------------------------------------------------------------------

    /**
     * Get all relationships
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRelations(params = {}) {
      AV.objectOrEmpty(params);
      return this.get("/relations", params);
    },

    createRelation(data) {
      return this.post("/relations", data);
    },

    updateRelation(primaryKey, data) {
      return this.patch(`/relations/${primaryKey}`, data);
    },

    /**
     * Get the relationship information for the given collection
     * @param  {String} collection The collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getCollectionRelations(collection, params = {}) {
      AV.string(collection, "collection");
      AV.objectOrEmpty(params);

      return Promise.all([
        this.get("/relations", { "filter[collection_a][eq]": collection }),
        this.get("/relations", { "filter[collection_b][eq]": collection })
      ]);
    },

    // REVISIONS
    // -------------------------------------------------------------------------

    /**
     * Get a single item's revisions by primary key
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getItemRevisions(collection, primaryKey, params = {}) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");

      if (collection.startsWith("directus_")) {
        return this.get(
          `/${collection.substring(9)}/${primaryKey}/revisions`,
          params
        );
      }

      return this.get(`/items/${collection}/${primaryKey}/revisions`, params);
    },

    /**
     * revert an item to a previous state
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Number} revisionID The ID of the revision to revert to
     * @return {RequestPromise}
     */
    revert(collection, primaryKey, revisionID) {
      AV.string(collection, "collection");
      AV.notNull(primaryKey, "primaryKey");
      AV.number(revisionID, "revisionID");

      if (collection.startsWith("directus_")) {
        return this.patch(
          `/${collection.substring(9)}/${primaryKey}/revert/${revisionID}`
        );
      }

      return this.patch(
        `/items/${collection}/${primaryKey}/revert/${revisionID}`
      );
    },

    // ROLES
    // -------------------------------------------------------------------------

    /**
     * Get a single user role
     * @param  {Number} primaryKey  The id of the user rol to get
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRole(primaryKey, params = {}) {
      AV.number(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");
      return this.get(`/roles/${primaryKey}`, params);
    },

    /**
     * Get the user roles
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getRoles(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/roles", params);
    },

    /**
     * Update a user role
     * @param  {Number} primaryKey The ID of the role
     * @param  {Object} body       The fields to update
     * @return {RequestPromise}
     */
    updateRole(primaryKey, body) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");
      return this.updateItem("directus_roles", primaryKey, body);
    },

    /**
     * Create a new user role
     * @param  {Object} body The role information
     * @return {RequestPromise}
     */
    createRole(body) {
      AV.object(body, "body");
      return this.createItem("directus_roles", body);
    },

    /**
     * Delete a user rol by primary key
     * @param  {Number | String} primaryKey Primary key of the user role
     * @return {RequestPromise}
     */
    deleteRole(primaryKey) {
      AV.notNull(primaryKey, "primaryKey");
      return this.deleteItem("directus_roles", primaryKey);
    },

    // SETTINGS
    // -------------------------------------------------------------------------

    /**
     * Get Directus' global settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getSettings(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/settings", params);
    },

    /**
     * Get the "fields" for directus_settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getSettingsFields(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/settings/fields", params);
    },

    // USERS
    // -------------------------------------------------------------------------

    /**
     * Get a list of available users in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getUsers(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/users", params);
    },

    /**
     * Get a single Directus user
     * @param  {String} primaryKey  The unique identifier of the user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getUser(primaryKey, params = {}) {
      AV.notNull(primaryKey, "primaryKey");
      AV.objectOrEmpty(params, "params");
      return this.get(`/users/${primaryKey}`, params);
    },

    /**
     * Get the user info of the currently logged in user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */
    getMe(params = {}) {
      AV.objectOrEmpty(params, "params");
      return this.get("/users/me", params);
    },

    /**
     * Update a single user based on primaryKey
     * @param  {String|Number} primaryKey The primary key of the user
     * @param  {Object} body              The fields to update
     * @return {RequestPromise}
     */
    updateUser(primaryKey, body) {
      AV.notNull(primaryKey, "primaryKey");
      AV.object(body, "body");
      return this.updateItem("directus_users", primaryKey, body);
    },

    // UTILS
    // -------------------------------------------------------------------------

    /**
     * Ping the API to check if it exists / is up and running
     * @return {RequestPromise}
     */
    ping() {
      return this.request("get", "/server/ping", {}, {}, true, true);
    },

    /**
     * Get the server info from the API
     * @return {RequestPromise}
     */
    serverInfo() {
      return this.request("get", "/", {}, {}, true);
    },

    /**
     * Get the server info from the project
     * @return {RequestPromise}
     */
    projectInfo() {
      return this.request("get", "/");
    },

    /**
     * Get all the setup third party auth providers
     * @return {RequestPromise}
     */
    getThirdPartyAuthProviders() {
      return this.get("/auth/sso");
    }
  };

  // Only start the auto refresh interval if the token exists and it's a JWT
  if (SDK.token && SDK.token.includes(".")) {
    SDK.startInterval(true);
  }

  return SDK;
}
function SDK ( options = { } ) { let token ; let url ; let project = "_" ; let localExp ; let tokenExpiryTime = 5 ; if ( options . storage ) { let storedInfo = options . storage . getItem ( "directus-sdk-js" ) ; if ( storedInfo ) { storedInfo = JSON . parse ( storedInfo ) ; token = storedInfo . token ; url = storedInfo . url ; project = storedInfo . project ; localExp = storedInfo . localExp ; } } if ( options . token ) { token = options . token ; } if ( options . url ) { url = options . url ; } if ( options . project ) { project = options . project ; } if ( options . localExp ) { localExp = options . localExp ; } if ( options . tokenExpiryTime ) { tokenExpiryTime = options . tokenExpiryTime ; } const SDK = { url : url , token : token , project : project , // The token will contain an expiry time based on the server time // In order to make sure we check the right expiry date, we need to // keep a version that's based on the browser time localExp : localExp , axios : axios . create ( { paramsSerializer : qs . stringify , timeout : 10 * 60 * 1000 // 10 min } ) , refreshInterval : null , onAutoRefreshError : null , onAutoRefreshSuccess : null , // The storage method to use. Has to support getItem and setItem to store and // retrieve the token storage : options . storage || null , // Defaults to 5 minutes. Once the API supports a custom, this option can be used to reflect that tokenExpiryTime : tokenExpiryTime , get payload ( ) { if ( ! AV . isString ( this . token ) ) return null ; return getPayload ( this . token ) ; } , get loggedIn ( ) { if ( AV . isString ( this . token ) && AV . isString ( this . url ) && AV . isString ( this . project ) && AV . isObject ( this . payload ) ) { if ( this . localExp > Date . now ( ) ) { return true ; } } return false ; } , // REQUEST METHODS // ------------------------------------------------------------------------- /**
     * Directus API request promise
     * @promise RequestPromise
     * @fulfill {object} Directus data
     * @reject {Error} Network error (if no connection to API)
     * @reject {Error} Directus error (eg not logged in or 404)
     */ /**
     * Perform an API request to the Directus API
     * @param  {string} method      The HTTP method to use
     * @param  {string} endpoint    The API endpoint to request
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @param  {Object} [data={}]   The HTTP request body (non-GET only)
     * @param  {Boolean} noEnv      Don't use the project in the path
     * @param  {Boolean} ignoreJson Don't parse the API result into JSON
     * @return {RequestPromise}
     */ request ( method , endpoint , params = { } , data = { } , noEnv = false , headers = { } , ignoreJson = false ) { AV . string ( method , "method" ) ; AV . string ( endpoint , "endpoint" ) ; AV . objectOrEmpty ( params , "params" ) ; Array . isArray ( data ) ? AV . arrayOrEmpty ( data , "data" ) : AV . objectOrEmpty ( data , "data" ) ; AV . string ( this . url , "this.url" ) ; let baseURL = ` ${ this . url } ` ; if ( noEnv === false ) { baseURL += ` ${ this . project } ` ; } const requestOptions = { url : endpoint , method , baseURL , params , data } ; if ( this . token && typeof this . token === "string" && this . token . length > 0 ) { requestOptions . headers = headers ; requestOptions . headers . Authorization = ` ${ this . token } ` ; } return this . axios . request ( requestOptions ) . then ( res => res . data ) . then ( data => { if ( ! data || data . length === 0 ) return data ; if ( ignoreJson ) return data ; if ( typeof data !== "object" ) { try { return JSON . parse ( data ) ; } catch ( error ) { throw { json : true , error , data } ; } } return data ; } ) . catch ( error => { if ( error . response ) { throw error . response . data . error ; } else if ( error . json === true ) { throw { // eslint-disable-line code : - 2 , message : "API returned invalid JSON" , error : error . error , data : error . data } ; } else { throw { // eslint-disable-line code : - 1 , message : "Network Error" , error } ; } } ) ; } , /**
     * GET convenience method. Calls the request method for you
     * @param  {string} endpoint    The endpoint to get
     * @param  {Object} [params={}] The HTTP query parameters (GET only)
     * @return {RequestPromise}
     */ get ( endpoint , params = { } ) { AV . string ( endpoint , "endpoint" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . request ( "get" , endpoint , params ) ; } , /**
     * POST convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */ post ( endpoint , body = { } , params = { } ) { AV . string ( endpoint , "endpoint" ) ; Array . isArray ( body ) ? AV . arrayOrEmpty ( body , "body" ) : AV . objectOrEmpty ( body , "body" ) ; return this . request ( "post" , endpoint , params , body ) ; } , /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */ patch ( endpoint , body = { } , params = { } ) { AV . string ( endpoint , "endpoint" ) ; Array . isArray ( body ) ? AV . arrayOrEmpty ( body , "body" ) : AV . objectOrEmpty ( body , "body" ) ; return this . request ( "patch" , endpoint , params , body ) ; } , /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @param  {Object} [body={}] The HTTP request body
     * @return {RequestPromise}
     */ put ( endpoint , body = { } , params = { } ) { AV . string ( endpoint , "endpoint" ) ; Array . isArray ( body ) ? AV . arrayOrEmpty ( body , "body" ) : AV . objectOrEmpty ( body , "body" ) ; return this . request ( "put" , endpoint , params , body ) ; } , /**
     * PATCH convenience method. Calls the request method for you
     * @param  {string} endpoint  The endpoint to get
     * @return {RequestPromise}
     */ delete ( endpoint ) { AV . string ( endpoint , "endpoint" ) ; return this . request ( "delete" , endpoint ) ; } , // AUTHENTICATION // ------------------------------------------------------------------------- /**
     * Logging in promise
     * @promise LoginPromise
     * @fulfill {Object} Object containing URL, ENV, and TOKEN
     * @reject {Error}   Network error (if no connection to API)
     * @reject {Error}   Directus error (eg not logged in or 404)
     */ /**
     * Login to the API.
     *
     * Gets a new token from the API and stores it in this.token
     * @param  {Object} credentials
     * @param  {String} credentials.email     The user's email address
     * @param  {String} credentials.password  The user's password
     * @param  {String} [credentials.url]     The API to login to (overwrites this.url)
     * @param  {String} [credentials.project] The API project to login to (overwrites this.project)
     * @param  {String} [options.persist]     Auto-fetch a new token when it's about to expire
     * @param  {Boolean} [options.storage]    Where to store the token (survive refreshes)
     * @return {LoginPromise}
     */ login ( credentials , options = { persist : true } ) { AV . object ( credentials , "credentials" ) ; AV . keysWithString ( credentials , [ "email" , "password" ] , "credentials" ) ; this . token = null ; if ( AV . hasKeysWithString ( credentials , [ "url" ] ) ) { this . url = credentials . url ; } if ( AV . hasKeysWithString ( credentials , [ "project" ] ) ) { this . project = credentials . project ; } if ( credentials . persist || options . persist ) { this . startInterval ( ) ; } return new Promise ( ( resolve , reject ) => { this . post ( "/auth/authenticate" , { email : credentials . email , password : credentials . password } ) . then ( res => res . data . token ) . then ( token => { this . token = token ; // Expiry date is the moment we got the token + 5 minutes this . localExp = new Date ( Date . now ( ) + this . tokenExpiryTime * 60000 ) . getTime ( ) ; if ( this . storage ) { this . storage . setItem ( "directus-sdk-js" , JSON . stringify ( { token : this . token , url : this . url , project : this . project , localExp : this . localExp } ) ) ; } resolve ( { url : this . url , project : this . project , token : this . token , localExp : this . localExp } ) ; } ) . catch ( reject ) ; } ) ; } , /**
     * Logs the user out by "forgetting" the token, and clearing the refresh interval
     */ logout ( ) { this . token = null ; if ( this . refreshInterval ) { this . stopInterval ( ) ; } if ( this . storage ) { this . storage . removeItem ( "directus-sdk-js" ) ; } } , /**
     * Resets the client instance by logging out and removing the URL and project
     */ reset ( ) { this . logout ( ) ; this . url = null ; this . project = null ; } , /**
     * Starts an interval of 10 seconds that will check if the token needs refreshing
     * @param {Boolean} fireImmediately Fire the refreshIfNeeded method directly
     */ startInterval ( fireImmediately ) { if ( fireImmediately ) this . refreshIfNeeded ( ) ; this . refreshInterval = setInterval ( this . refreshIfNeeded . bind ( this ) , 10000 ) ; } , /**
     * Clears and nullifies the token refreshing interval
     */ stopInterval ( ) { clearInterval ( this . refreshInterval ) ; this . refreshInterval = null ; } , /**
     * Refresh the token if it is about to expire (within 30 seconds of expiry date)
     *
     * Calls onAutoRefreshSuccess with the new token if the refreshing is successful
     * Calls onAutoRefreshError if refreshing the token fails for some reason
     */ refreshIfNeeded ( ) { if ( ! AV . hasStringKeys ( this , [ "token" , "url" , "project" ] ) ) return ; if ( ! this . payload || ! this . payload . exp ) return ; const timeDiff = this . localExp - Date . now ( ) ; if ( timeDiff <= 0 ) { if ( AV . isFunction ( this . onAutoRefreshError ) ) { this . onAutoRefreshError ( { message : "auth_expired_token" , code : 102 } ) ; } return ; } if ( timeDiff < 30000 ) { this . refresh ( this . token ) . then ( res => { this . token = res . data . token ; this . localExp = new Date ( Date . now ( ) + this . tokenExpiryTime * 60000 ) . getTime ( ) ; if ( AV . isFunction ( this . onAutoRefreshSuccess ) ) { this . onAutoRefreshSuccess ( { url : this . url , project : this . project , token : this . token , localExp : this . localExp } ) ; } if ( this . storage ) { this . storage . setItem ( "directus-sdk-js" , JSON . stringify ( { token : this . token , url : this . url , project : this . project , localExp : this . localExp } ) ) ; } } ) . catch ( error => { if ( AV . isFunction ( this . onAutoRefreshError ) ) { this . onAutoRefreshError ( error ) ; } } ) ; } } , /**
     * Use the passed token to request a new one
     * @param  {String} token Active & Valid token
     * @return {RequestPromise}
     */ refresh ( token ) { AV . string ( token , "token" ) ; return this . post ( "/auth/refresh" , { token } ) ; } , /**
     * Request to reset the password of the user with the given email address
     *
     * The API will send an email to the given email address with a link to generate a new
     * temporary password.
     * @param {String} email The user's email
     */ requestPasswordReset ( email ) { AV . string ( email , "email" ) ; return this . post ( "/auth/password/request" , { email : email } ) ; } , // ACTIVITY // ------------------------------------------------------------------------- /**
     * Get activity
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getActivity ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/activity" , params ) ; } , // BOOKMARKS // ------------------------------------------------------------------------- /**
     * Get the bookmarks of the current user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getMyBookmarks ( params = { } ) { AV . string ( this . token , "this.token" ) ; AV . objectOrEmpty ( params ) ; return Promise . all ( [ this . get ( "/collection_presets" , { "filter[title][nnull]" : 1 , "filter[user][eq]" : this . payload . id } ) , this . get ( "/collection_presets" , { "filter[title][nnull]" : 1 , "filter[role][eq]" : this . payload . role , "filter[user][null]" : 1 } ) ] ) . then ( values => { const [ user , role ] = values ; // eslint-disable-line no-shadow return [ ... user . data , ... role . data ] ; } ) ; } , // COLLECTIONS // ------------------------------------------------------------------------- /**
     * Get all available collections
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getCollections ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/collections" , params ) ; } , /**
     * Get collection info by name
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getCollection ( collection , params = { } ) { AV . string ( collection , "collection" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . get ( ` ${ collection } ` , params ) ; } , /**
     * Create a collection
     * @param {Object} data Collection information
     * @return {RequestPromise}
     */ createCollection ( data ) { AV . object ( data , "data" ) ; return this . post ( "/collections" , data ) ; } , /**
     * @param  {String} The collection to update
     * @param  {Object} The fields to update
     * @return {RequestPromise}
     */ updateCollection ( collection , data ) { AV . string ( collection , "collection" ) ; AV . object ( data , "data" ) ; return this . patch ( ` ${ collection } ` , data ) ; } , /**
     * @param  {String} collection The primary key of the collection to remove
     * @return {RequestPromise}
     */ deleteCollection ( collection ) { AV . string ( collection , "collection" ) ; return this . delete ( ` ${ collection } ` ) ; } , // COLLECTION PRESETS // ------------------------------------------------------------------------- /**
     * Create a new collection preset (bookmark / listing preferences)
     * @param  {Object} data The bookmark info
     * @return {RequestPromise}
     */ createCollectionPreset ( data ) { AV . object ( data ) ; return this . post ( "/collection_presets" , data ) ; } , /**
     * Update collection preset (bookmark / listing preference)
     * @param {String|Number} primaryKey
     * @param {RequestPromise} data
     */ updateCollectionPreset ( primaryKey , data ) { AV . notNull ( primaryKey , "primaryKey" ) ; AV . object ( data , "data" ) ; return this . patch ( ` ${ primaryKey } ` , data ) ; } , /**
     * Delete collection preset by primarykey
     * @param {String|Number} primaryKey The primaryKey of the preset to delete
     */ deleteCollectionPreset ( primaryKey ) { AV . notNull ( primaryKey , "primaryKey" ) ; return this . delete ( ` ${ primaryKey } ` ) ; } , // DATABASE // ------------------------------------------------------------------------ /**
     * This will update the database of the API instance to the latest version
     * using the migrations in the API
     * @return {RequestPromise}
     */ updateDatabase ( ) { return this . post ( "/update" ) ; } , // EXTENSIONS // ------------------------------------------------------------------------- /**
     * Get the meta information of all installed interfaces
     * @return {RequestPromise}
     */ getInterfaces ( ) { return this . request ( "get" , "/interfaces" , { } , { } , true ) ; } , /**
     * Get the meta information of all installed layouts
     * @return {RequestPromise}
     */ getLayouts ( ) { return this . request ( "get" , "/layouts" , { } , { } , true ) ; } , /**
     * Get the meta information of all installed pages
     * @return {RequestPromise}
     */ getPages ( ) { return this . request ( "get" , "/pages" , { } , { } , true ) ; } , // FIELDS // ------------------------------------------------------------------------ /**
     * Get all fields that are in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getAllFields ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/fields" , params ) ; } , /**
     * Get the fields that have been setup for a given collection
     * @param  {String} collection  Collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getFields ( collection , params = { } ) { AV . string ( collection , "collection" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . get ( ` ${ collection } ` , params ) ; } , /**
     * Get the field information for a single given field
     * @param  {String} collection  Collection name
     * @param  {String} fieldName   Field name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getField ( collection , fieldName , params = { } ) { AV . string ( collection , "collection" ) ; AV . string ( fieldName , "fieldName" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . get ( ` ${ collection } ${ fieldName } ` , params ) ; } , /**
     * Create a field in the given collection
     * @param  {String} collection Collection to add the field in
     * @param  {Object} fieldInfo  The fields info to save
     * @return {RequestPromise}
     */ createField ( collection , fieldInfo ) { AV . string ( collection , "collection" ) ; AV . object ( fieldInfo , "fieldInfo" ) ; return this . post ( ` ${ collection } ` , fieldInfo ) ; } , /**
     * Update a given field in a given collection
     * @param  {String} collection Field's parent collection
     * @param  {String} fieldName  Name of the field to update
     * @param  {Object} fieldInfo  Fields to update
     * @return {RequestPromise}
     */ updateField ( collection , fieldName , fieldInfo ) { AV . string ( collection , "collection" ) ; AV . string ( fieldName , "fieldName" ) ; AV . object ( fieldInfo , "fieldInfo" ) ; return this . patch ( ` ${ collection } ${ fieldName } ` , fieldInfo ) ; } , /**
     * Update multiple fields at once
     * @param  {String} collection             Fields' parent collection
     * @param  {Array} fieldsInfoOrFieldNames  Array of field objects or array of field names
     * @param  {Object} [fieldInfo]            In case fieldsInfoOrFieldNames is an array of fieldNames, you need to provide the fields to update
     * @return {RequestPromise}
     *
     * @example
     *
     * // Set multiple fields to the same value
     * updateFields("projects", ["first_name", "last_name", "email"], {
     *   default_value: ""
     * })
     *
     * // Set multiple fields to different values
     * updateFields("projects", [
     *   {
     *     id: 14,
     *     sort: 1
     *   },
     *   {
     *     id: 17,
     *     sort: 2
     *   },
     *   {
     *     id: 912,
     *     sort: 3
     *   }
     * ])
     */ updateFields ( collection , fieldsInfoOrFieldNames , fieldInfo = null ) { AV . string ( collection , "collection" ) ; AV . array ( fieldsInfoOrFieldNames , "fieldsInfoOrFieldNames" ) ; if ( fieldInfo ) { AV . object ( fieldInfo ) ; } if ( fieldInfo ) { return this . patch ( ` ${ collection } ${ fieldsInfoOrFieldNames . join ( "," ) } ` , fieldInfo ) ; } return this . patch ( ` ${ collection } ` , fieldsInfoOrFieldNames ) ; } , /**
     * Delete a field from a collection
     * @param  {String} collection Name of the collection
     * @param  {String} fieldName  The name of the field to delete
     * @return {RequestPromise}
     */ deleteField ( collection , fieldName ) { AV . string ( collection , "collection" ) ; AV . string ( fieldName , "fieldName" ) ; return this . delete ( ` ${ collection } ${ fieldName } ` ) ; } , // FILES // ------------------------------------------------------------------------ /**
     * Upload multipart files in multipart/form-data
     * @param  {Object} data FormData object containing files
     * @return {RequestPromise}
     */ uploadFiles ( data , onUploadProgress = ( ) => { } ) { const headers = { "Content-Type" : "multipart/form-data" , Authorization : ` ${ this . token } ` } ; return this . axios . post ( ` ${ this . url } ${ this . project } ` , data , { headers , onUploadProgress } ) . then ( res => res . data ) . catch ( error => { if ( error . response ) { throw error . response . data . error ; } else { throw { // eslint-disable-line code : - 1 , message : "Network Error" , error } ; } } ) ; } , // ITEMS // ------------------------------------------------------------------------- /**
     * Update an existing item
     * @param  {String} collection The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} body       The item's field values
     * @param  {Object} params     Query parameters
     * @return {RequestPromise}
     */ updateItem ( collection , primaryKey , body , params = { } ) { AV . string ( collection , "collection" ) ; AV . notNull ( primaryKey , "primaryKey" ) ; AV . object ( body , "body" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . patch ( ` ${ collection . substring ( 9 ) } ${ primaryKey } ` , body , params ) ; } return this . patch ( ` ${ collection } ${ primaryKey } ` , body , params ) ; } , /**
     * Update multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @param  {Object} params     Query Parameters
     * @return {RequestPromise}
     */ updateItems ( collection , body , params = { } ) { AV . string ( collection , "collection" ) ; AV . array ( body , "body" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . patch ( ` ${ collection . substring ( 9 ) } ` , body , params ) ; } return this . patch ( ` ${ collection } ` , body , params ) ; } , /**
     * Create a new item
     * @param  {String} collection The collection to add the item to
     * @param  {Object} body       The item's field values
     * @return {RequestPromise}
     */ createItem ( collection , body ) { AV . string ( collection , "collection" ) ; AV . object ( body , "body" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . post ( ` ${ collection . substring ( 9 ) } ` , body ) ; } return this . post ( ` ${ collection } ` , body ) ; } , /**
     * Create multiple items
     * @param  {String} collection The collection to add the item to
     * @param  {Array} body        The item's field values
     * @return {RequestPromise}
     */ createItems ( collection , body ) { AV . string ( collection , "collection" ) ; AV . array ( body , "body" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . post ( ` ${ collection . substring ( 9 ) } ` , body ) ; } return this . post ( ` ${ collection } ` , body ) ; } , /**
     * Get items from a given collection
     * @param  {String} collection The collection to add the item to
     * @param  {Object} [params={}]   Query parameters
     * @return {RequestPromise}
     */ getItems ( collection , params = { } ) { AV . string ( collection , "collection" ) ; AV . objectOrEmpty ( params , "params" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . get ( ` ${ collection . substring ( 9 ) } ` , params ) ; } return this . get ( ` ${ collection } ` , params ) ; } , /**
     * Get a single item by primary key
     * @param  {String} collection  The collection to add the item to
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getItem ( collection , primaryKey , params = { } ) { AV . string ( collection , "collection" ) ; AV . notNull ( primaryKey , "primaryKey" ) ; AV . objectOrEmpty ( params , "params" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . get ( ` ${ collection . substring ( 9 ) } ${ primaryKey } ` , params ) ; } return this . get ( ` ${ collection } ${ primaryKey } ` , params ) ; } , /**
     * Delete a single item by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {String|Number} primaryKey Primary key of the item
     * @return {RequestPromise}
     */ deleteItem ( collection , primaryKey ) { AV . string ( collection , "collection" ) ; AV . notNull ( primaryKey , "primaryKey" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . delete ( ` ${ collection . substring ( 9 ) } ${ primaryKey } ` ) ; } return this . delete ( ` ${ collection } ${ primaryKey } ` ) ; } , /**
     * Delete multiple items by primary key
     * @param  {String} collection  The collection to delete the item from
     * @param  {Array} primaryKey Primary key of the item
     * @return {RequestPromise}
     */ deleteItems ( collection , primaryKeys ) { AV . string ( collection , "collection" ) ; AV . array ( primaryKeys , "primaryKeys" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . delete ( ` ${ collection . substring ( 9 ) } ${ primaryKeys . join ( ) } ` ) ; } return this . delete ( ` ${ collection } ${ primaryKeys . join ( ) } ` ) ; } , // LISTING PREFERENCES // ------------------------------------------------------------------------- /**
     * Get the collection presets of the current user for a single collection
     * @param  {String} collection  Collection to fetch the preferences for
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getMyListingPreferences ( collection , params = { } ) { AV . string ( this . token , "this.token" ) ; AV . objectOrEmpty ( params , "params" ) ; return Promise . all ( [ this . get ( "/collection_presets" , { limit : 1 , "filter[title][null]" : 1 , "filter[collection][eq]" : collection , "filter[role][null]" : 1 , "filter[user][null]" : 1 , sort : "-id" } ) , this . get ( "/collection_presets" , { limit : 1 , "filter[title][null]" : 1 , "filter[collection][eq]" : collection , "filter[role][eq]" : this . payload . role , "filter[user][null]" : 1 , sort : "-id" } ) , this . get ( "/collection_presets" , { limit : 1 , "filter[title][null]" : 1 , "filter[collection][eq]" : collection , "filter[role][eq]" : this . payload . role , "filter[user][eq]" : this . payload . id , sort : "-id" } ) ] ) . then ( values => { const [ collection , role , user ] = values ; // eslint-disable-line no-shadow if ( user . data && user . data . length > 0 ) { return user . data [ 0 ] ; } if ( role . data && role . data . length > 0 ) { return role . data [ 0 ] ; } if ( collection . data && collection . data . length > 0 ) { return collection . data [ 0 ] ; } return { } ; } ) ; } , // PERMISSIONS // ------------------------------------------------------------------------- /**
     * Get permissions
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getPermissions ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . getItems ( "directus_permissions" , params ) ; } , /**
     * Get the currently logged in user's permissions
     * @param  {Object} params Query parameters
     * @return {RequestPromise}
     */ getMyPermissions ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/permissions/me" , params ) ; } , /**
     * Create multiple new permissions
     * @param  {Array} data  Permission records to save
     * @return {RequestPromise}
     */ createPermissions ( data ) { AV . array ( data ) ; return this . post ( "/permissions" , data ) ; } , /**
     * Update multiple permission records
     * @param  {Array} data  Permission records to update
     * @return {RequestPromise}
     */ updatePermissions ( data ) { AV . array ( data ) ; return this . patch ( "/permissions" , data ) ; } , // RELATIONS // ------------------------------------------------------------------------- /**
     * Get all relationships
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getRelations ( params = { } ) { AV . objectOrEmpty ( params ) ; return this . get ( "/relations" , params ) ; } , createRelation ( data ) { return this . post ( "/relations" , data ) ; } , updateRelation ( primaryKey , data ) { return this . patch ( ` ${ primaryKey } ` , data ) ; } , /**
     * Get the relationship information for the given collection
     * @param  {String} collection The collection name
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getCollectionRelations ( collection , params = { } ) { AV . string ( collection , "collection" ) ; AV . objectOrEmpty ( params ) ; return Promise . all ( [ this . get ( "/relations" , { "filter[collection_a][eq]" : collection } ) , this . get ( "/relations" , { "filter[collection_b][eq]" : collection } ) ] ) ; } , // REVISIONS // ------------------------------------------------------------------------- /**
     * Get a single item's revisions by primary key
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getItemRevisions ( collection , primaryKey , params = { } ) { AV . string ( collection , "collection" ) ; AV . notNull ( primaryKey , "primaryKey" ) ; AV . objectOrEmpty ( params , "params" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . get ( ` ${ collection . substring ( 9 ) } ${ primaryKey } ` , params ) ; } return this . get ( ` ${ collection } ${ primaryKey } ` , params ) ; } , /**
     * revert an item to a previous state
     * @param  {String} collection  The collection to fetch the revisions from
     * @param  {String|Number} primaryKey Primary key of the item
     * @param  {Number} revisionID The ID of the revision to revert to
     * @return {RequestPromise}
     */ revert ( collection , primaryKey , revisionID ) { AV . string ( collection , "collection" ) ; AV . notNull ( primaryKey , "primaryKey" ) ; AV . number ( revisionID , "revisionID" ) ; if ( collection . startsWith ( "directus_" ) ) { return this . patch ( ` ${ collection . substring ( 9 ) } ${ primaryKey } ${ revisionID } ` ) ; } return this . patch ( ` ${ collection } ${ primaryKey } ${ revisionID } ` ) ; } , // ROLES // ------------------------------------------------------------------------- /**
     * Get a single user role
     * @param  {Number} primaryKey  The id of the user rol to get
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getRole ( primaryKey , params = { } ) { AV . number ( primaryKey , "primaryKey" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . get ( ` ${ primaryKey } ` , params ) ; } , /**
     * Get the user roles
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getRoles ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/roles" , params ) ; } , /**
     * Update a user role
     * @param  {Number} primaryKey The ID of the role
     * @param  {Object} body       The fields to update
     * @return {RequestPromise}
     */ updateRole ( primaryKey , body ) { AV . notNull ( primaryKey , "primaryKey" ) ; AV . object ( body , "body" ) ; return this . updateItem ( "directus_roles" , primaryKey , body ) ; } , /**
     * Create a new user role
     * @param  {Object} body The role information
     * @return {RequestPromise}
     */ createRole ( body ) { AV . object ( body , "body" ) ; return this . createItem ( "directus_roles" , body ) ; } , /**
     * Delete a user rol by primary key
     * @param  {Number | String} primaryKey Primary key of the user role
     * @return {RequestPromise}
     */ deleteRole ( primaryKey ) { AV . notNull ( primaryKey , "primaryKey" ) ; return this . deleteItem ( "directus_roles" , primaryKey ) ; } , // SETTINGS // ------------------------------------------------------------------------- /**
     * Get Directus' global settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getSettings ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/settings" , params ) ; } , /**
     * Get the "fields" for directus_settings
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getSettingsFields ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/settings/fields" , params ) ; } , // USERS // ------------------------------------------------------------------------- /**
     * Get a list of available users in Directus
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getUsers ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/users" , params ) ; } , /**
     * Get a single Directus user
     * @param  {String} primaryKey  The unique identifier of the user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getUser ( primaryKey , params = { } ) { AV . notNull ( primaryKey , "primaryKey" ) ; AV . objectOrEmpty ( params , "params" ) ; return this . get ( ` ${ primaryKey } ` , params ) ; } , /**
     * Get the user info of the currently logged in user
     * @param  {Object} [params={}] Query parameters
     * @return {RequestPromise}
     */ getMe ( params = { } ) { AV . objectOrEmpty ( params , "params" ) ; return this . get ( "/users/me" , params ) ; } , /**
     * Update a single user based on primaryKey
     * @param  {String|Number} primaryKey The primary key of the user
     * @param  {Object} body              The fields to update
     * @return {RequestPromise}
     */ updateUser ( primaryKey , body ) { AV . notNull ( primaryKey , "primaryKey" ) ; AV . object ( body , "body" ) ; return this . updateItem ( "directus_users" , primaryKey , body ) ; } , // UTILS // ------------------------------------------------------------------------- /**
     * Ping the API to check if it exists / is up and running
     * @return {RequestPromise}
     */ ping ( ) { return this . request ( "get" , "/server/ping" , { } , { } , true , true ) ; } , /**
     * Get the server info from the API
     * @return {RequestPromise}
     */ serverInfo ( ) { return this . request ( "get" , "/" , { } , { } , true ) ; } , /**
     * Get the server info from the project
     * @return {RequestPromise}
     */ projectInfo ( ) { return this . request ( "get" , "/" ) ; } , /**
     * Get all the setup third party auth providers
     * @return {RequestPromise}
     */ getThirdPartyAuthProviders ( ) { return this . get ( "/auth/sso" ) ; } } ; // Only start the auto refresh interval if the token exists and it's a JWT if ( SDK . token && SDK . token . includes ( "." ) ) { SDK . startInterval ( true ) ; } return SDK ; }
Create a new SDK instance
@param       {object} [options]
@param       {string} [options.url]   The API url to connect to
@param       {string} [options.project]   The API project to connect to
@param       {string} [options.token] The access token to use for requests
@param       {number} [options.tokenExpiryTime] The time it takes for the API token to expire
@constructor
Create a new SDK instance