# The Application Installer

Most of the apps will require some level of configuration from the Deskpro administrators. For this purpose, the application manifest contains a `settings` property which is a list of setting names and their associated form field types.

But this requires maintaining some sort of UI to interact with them which would only distract you from developing your actual applications, so we provide an out-of-the-box installer for this purpose.

The installer is built as a regular app and it is available here: <https://github.com/deskpro/apps-installer> .

## How does it work?

During the compile and package phases, the build tool (dpat) bundles its version of [apps-installer](https://github.com/deskpro/apps-installer) installer in the same package with your app. If you are curious, you can inspect the contents of the `dist` folder of your application (provided you use the [apps-boilerplate](https://github.com/deskpro/apps-boilerplate) for your app) and check the installer files in the `dist/html` and `dist/assets` folder.

The end result is that in the distribution bundle, your application manifest will contain a new target named `install` which loads the file `html/install.html` from your application bundle into an iframe.

The `install` target is only available in the Administration section when you install or update an application. The installer application from your application bundle is loaded in an iframe, then reads the `settings` property from your application manifest to determine if it should display a settings form and renders it. Once the form is submitted the installer reads the rest of configuration settings and proceeds with the actual installation.

## Declaring settings

You declare settings using the `settings` property of the application manifest:

```javascript
{
  "deskpro" : {
    "settings": [
      {
        "name": "clientId",
        "defaultValue": "",
        "title": "The client id",
        "required": true,
        "type": "text"
      },    
      {
        "name": "clientDescription",
        "defaultValue": "",
        "title": "A brief description of your client",
        "required": true,
        "type": "textarea"
      }      
    ]
  }
}
```

Each setting can have the following properties:

* [name](/apps-developer-guide-old/sdk/the-application-installer.md#name)
* [defaultValue](/apps-developer-guide-old/sdk/the-application-installer.md#defaultvalue)
* [title](/apps-developer-guide-old/sdk/the-application-installer.md#title)
* [required](/apps-developer-guide-old/sdk/the-application-installer.md#required)
* [type](/apps-developer-guide-old/sdk/the-application-installer.md#type)
* [items](/apps-developer-guide-old/sdk/the-application-installer.md#type)

### name

* Required
* Type: `string`

The name of your setting. Must be a valid object key

### defaultValue

* Optional
* Type: `string|boolean`

### title

* Required
* Type: `string`

The label displayed in the settings form for this item

### required

* Optional
* Default value: `false`
* Type: `boolean`

If this is set to `true` then the setting can not be empty and the field will be marked required in the settings form

### type

* Required
* Type: `string`
* Value: `"text" | "textarea" | "boolean" | "choice"`

The type of form field that should be displayed for this setting

### items

* Required only when `type` is set to `choice`
* Type: `array`

The list of choices for this setting. Each item in the list is an object with the following properties:

* title: a `string`, the title of this item
* value: a `string`, the setting value of this item

Example:

```javascript
{
  "deskpro" : {
    "settings": [
      {
        "name": "authStrategy",
        "defaultValue": "oauth2",
        "title": "Authentication method",
        "type": "choice",
        "items": [
           {
              "title": "Oauth2 Authentication",
              "value": "oauth2"            
            }, 
            {
              "title": "API Key",
              "value": "apikey"
            }            
        ]
      }      
    ]
  }
}
```

## Customising the installer

There are cases where you applying simple validation to your settings is not enough. For instance, if you are requiring OAuth configuration and you want to make sure the credentials actually work.

For these cases, we have provided a way to create a custom settings form component, which allows you to intercept the settings collection step in the install process and add your custom logic to it. You can also add your own UI elements, or links to help pages.

The first thing you need to do is declare a dev-dependency to the the latest version of [apps-boilerplate](https://github.com/deskpro/apps-boilerplate) from the github branch in your `package.json` :

```javascript
{
  "devDependencies": {
    "@deskpro/apps-installer": "github:deskpro/apps-installer#v0.4.5"
  }
}
```

At the time of writing this article, the latest version is [0.4.5](https://github.com/deskpro/apps-installer/tree/v0.4.5). This tells the build tool to look for your custom settings form component and use it to build the a custom installer.

The next thing is create a module at `src/installer/index.js`. This module should have as a default export your custom settings form component.

Your component will act as a container for the actual settings form component, which you will have to show in your component's `render` method. Your component will receive as props a reference to the settings form constructor and a callback that should be invoked when you have finished processing the settings values to allow the installer to continue.

Here's how a custom settings form component that is functionally equivalent to the default one might look like:

```jsx
import React from 'react';
import PropTypes from 'prop-types';

class ScreenSettings extends React.Component {

  static propTypes = {
    /**
     * @type function a callback to invoke to finish the installation
     */
    finishInstall: PropTypes.func.isRequired,

    /**
     * @type string they type of installation: install or update
     */
    installType: PropTypes.string.isRequired,

    /**
     * @type {Array<Object>} the list of settings defined in the application manifest
     */
    settings: PropTypes.array.isRequired,

    /**
     * @type {Object} a map of setting name and value
     */
    values: PropTypes.object.isRequired,

    /**
     * @type {function} the settings form constructor
     */
    settingsForm: PropTypes.func.isRequired,

    /**
     * @type {AppClient} the Deskpro Application Client
     * @see https://deskpro.github.io/apps-sdk-core/reference/AppClient.html
     */
    dpapp: PropTypes.object.isRequired
  };

  constructor() {
    super();
    // initialize our state
    this.state = { values: null, error: null };
  }

  /**
  * Submit hook, add your custom logic for values here
  *
  * @param {Object} values
  */
  onBeforeSubmit(values) { return Promise.resolve(values); }

  /**
   * Callback, invoked when the settings form is submitted
   *
   * @param {Object} values
   */
  onSubmit = (values) => {
    const { finishInstall } = this.props;

    this.onBeforeSubmit(values)
      .then(values => finishInstall(values).then(({ onStatus }) => onStatus())) // finish install
      .catch(error => this.setState({ error })) // display an error message
    ;
  };

  /**
   * Displays the settings screen
   *
   * @returns {XML}
   */
  render() {

    const { settings, settingsForm: SettingsForm } = this.props;
    const values = this.state.values || this.props.values;

    let formRef;
    return (
      <div>
        <SettingsForm settings={ settings } values={ values } onSubmit={this.onSubmit} ref={ref => formRef = ref} />
        <button className={'btn-action'} onClick={() => formRef.submit()}>Update Settings</button>

        { this.renderError() }

      </div>
    );
  }

  /**
   * Renders an error message
   *
   * @returns {XML|null}
   */
  renderError()
  {
    if (this.state.error) {
      return (<div style={{backgroundColor: "red", color:"white", padding: "2%"}}>{ this.state.error }</div>);
    }

    return null;
  }
}

export default ScreenSettings;
```

As you can see, your component will receive a list of properties:

### installType

This a string with only two value: `install` and `update`. You should use this value if you need to treat some setting differently if the admin is performing an update, for instance when the admin wants only to change some settings.

### settings

This is a list of settings as defined in your application manifest

### values

This is a map of setting name - setting value, containing the initial values.

### settingsForm

This is a reference to the constructor for the component which actually renders the form and fields from settings and values. In your render method you should pass it the settings, values and a submit method as props:

```jsx
<SettingsForm settings={ settings } values={ values } onSubmit={this.onSubmit}/>
```

You should attach a `ref` property like in the example above if you want to be able to submit the form.

### finishInstall

An async callback to be invoked when you are done processing the values. After the values are saved, the callback returns with a promise which resolves with a final callback that returns the control back to the installer when invoked.

This is how the you should use this callback:

```javascript
    /**
    * @param {Object} values
    */
    onFinish = (values) => {
      const { finishInstall } = this.props;
      finishInstall(values)
        .then( installer => installer.onStatus()) // on success return control to installer
        .catch(error => this.setState({ error })) // display an error message
    };
```

### dpapp

This is an instance of [AppClient](https://deskpro.github.io/apps-sdk-core/reference/AppClient.html) and it is injected into every app.

You can use it as you would use it in your application, for instance to make calls to the Deskpro REST API. The only thing to remember is that the authenticated user has admin privileges instead of agent privileges and your API calls will be made using admin privileges.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://deskpro.gitbook.io/apps-developer-guide-old/sdk/the-application-installer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
