Skip to main content
Version: Current

Basic Application

In previous tutorial we've create a simple connector without any authorization. Connector requiring authorizations are usually supported via Application class.

The Application is a means of authorising calls. It provides a form for setting access credentials and any other user settings. Above that, it can contain everything that is common to its connectors.

In this tutorial we'll create a BasicAuthorization application which will prepare HTTP request filling up Authorization header. Also we'll create a simple UI form for authorization settings. This time we'll connect to GitHub.

Prerequisites

Creating application

First, we create an application class in the src folder, inheriting from ABasicApplication.

import { ABasicApplication } from '@orchesty/nodejs-sdk/dist/lib/Authorization/Type/Basic/ABasicApplication';

export const NAME = 'git-hub';

export default class GitHubApplication extends ABasicApplication {

public getName(): string {
return NAME;
}

public getPublicName(): string {
return 'Git Hub';
}

public getDescription(): string {
return 'Git Hub application';
}
}

The methods in this part of the code are required. The name attribute serves as a unique identifier for the application. The publicName and description are displayed in Orchesty marketplace.

Form

For each application, we can create any number of forms for user communication settings. In our example, we will create one form for entering GitHub account credentials. In the form, we need to enter a token that we can generate in our GitHub account.

import CoreFormsEnum, { getFormName } from '@orchesty/nodejs-sdk/dist/lib/Application/Base/CoreFormsEnum';
import Field from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Field';
import FieldType from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FieldType';
import Form from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Form';
import FormStack from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FormStack';
import {
ABasicApplication,
TOKEN,
} from '@orchesty/nodejs-sdk/dist/lib/Authorization/Type/Basic/ABasicApplication';

// ...
export default class GitHubApplication extends ABasicApplication {

// ...
public getFormStack(): FormStack {
const form = new Form(CoreFormsEnum.AUTHORIZATION_FORM, getFormName(CoreFormsEnum.AUTHORIZATION_FORM))
.addField(new Field(FieldType.TEXT, TOKEN, ' Token', undefined, true));

return new FormStack().addForm(form);
}
// ...

}

Request

Next step is finishing method for setting up RequestDto for connectors. This method will fill authorization header and returns fully built RequestDto object ready to be send. URL, method and body provides connector calling this Application.

// ...
import { ApplicationInstall } from '@orchesty/nodejs-sdk/dist/lib/Application/Database/ApplicationInstall';
import RequestDto from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/RequestDto';
import AProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/AProcessDto';
import { CommonHeaders, JSON_TYPE } from '@orchesty/nodejs-sdk/dist/lib/Utils/Headers';
import { HttpMethods } from '@orchesty/nodejs-sdk/dist/lib/Transport/HttpMethods';
// ...

export default class GitHubApplication extends ABasicApplication {

// ...
public getRequestDto(
dto: AProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
uri?: string,
data?: unknown,
): RequestDto {
const request = new RequestDto(`https://api.github.com${uri}`, method, dto);
if (!this.isAuthorized(applicationInstall)) {
throw new Error(`Application [${this.getPublicName()}] is not authorized!`);
}
const form = applicationInstall.getSettings()[CoreFormsEnum.AUTHORIZATION_FORM] ?? {};
request.setHeaders({
[CommonHeaders.CONTENT_TYPE]: JSON_TYPE,
[CommonHeaders.ACCEPT]: 'application/vnd.github+json',
[CommonHeaders.AUTHORIZATION]: `Bearer ${form[TOKEN]}`,
});

if (data) {
request.setJsonBody(data);
}

return request;
}
// ...

}

Full application code

That's all. Below you can see the full application code.

import CoreFormsEnum, { getFormName } from '@orchesty/nodejs-sdk/dist/lib/Application/Base/CoreFormsEnum';
import { ApplicationInstall } from '@orchesty/nodejs-sdk/dist/lib/Application/Database/ApplicationInstall';
import Field from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Field';
import FieldType from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FieldType';
import Form from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/Form';
import FormStack from '@orchesty/nodejs-sdk/dist/lib/Application/Model/Form/FormStack';
import {
ABasicApplication,
TOKEN,
} from '@orchesty/nodejs-sdk/dist/lib/Authorization/Type/Basic/ABasicApplication';
import RequestDto from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/RequestDto';
import { HttpMethods } from '@orchesty/nodejs-sdk/dist/lib/Transport/HttpMethods';
import AProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/AProcessDto';
import { CommonHeaders, JSON_TYPE } from '@orchesty/nodejs-sdk/dist/lib/Utils/Headers';

export const NAME = 'git-hub';

export default class GitHubApplication extends ABasicApplication {

public getName(): string {
return NAME;
}

public getPublicName(): string {
return 'Git Hub';
}

public getDescription(): string {
return 'Git Hub application';
}

public getFormStack(): FormStack {
const form = new Form(CoreFormsEnum.AUTHORIZATION_FORM, getFormName(CoreFormsEnum.AUTHORIZATION_FORM))
.addField(new Field(FieldType.TEXT, TOKEN, ' Token', undefined, true));

return new FormStack().addForm(form);
}

public getRequestDto(
dto: AProcessDto,
applicationInstall: ApplicationInstall,
method: HttpMethods,
uri?: string,
data?: unknown,
): RequestDto {
const request = new RequestDto(`https://api.github.com${uri}`, method, dto);
if (!this.isAuthorized(applicationInstall)) {
throw new Error(`Application [${this.getPublicName()}] is not authorized!`);
}
const form = applicationInstall.getSettings()[CoreFormsEnum.AUTHORIZATION_FORM] ?? {};
request.setHeaders({
[CommonHeaders.CONTENT_TYPE]: JSON_TYPE,
[CommonHeaders.ACCEPT]: 'application/vnd.github+json',
[CommonHeaders.AUTHORIZATION]: `Bearer ${form[TOKEN]}`,
});

if (data) {
request.setJsonBody(data);
}

return request;
}

}


Registering an application in a container

The last step is to register our Application into container. This is once again done in index.ts file.

// ...
import { container } from '@orchesty/nodejs-sdk';
import GitHubApplication from './GitHubApplication';
// ...

export default function prepare(): void {
// ...
const gitHubApplication = new GitHubApplication();
container.setApplication(gitHubApplication);
// ...
}

View the app in the marketplace

Applications created in any worker are displayed in the Orchesty marketplace. This is where we install them for use in topologies, and where we also have forms that we have prepared in the applications for user settings.

So if we have done everything correctly, we will now see our new application in the Applications tab in the Admin.

GitHub application

When we install the application, the form we have created will also be available.

GitHub form

Connector creation

Now we will create a connector that will be used by the application. The connector will download a specific repository and will expect input data to complete the URL.

import AConnector from '@orchesty/nodejs-sdk/dist/lib/Connector/AConnector';
import { HttpMethods } from '@orchesty/nodejs-sdk/dist/lib/Transport/HttpMethods';
import ProcessDto from '@orchesty/nodejs-sdk/dist/lib/Utils/ProcessDto';
import ResultCode from '@orchesty/nodejs-sdk/dist/lib/Utils/ResultCode';

export const NAME = 'github-get-repository';

export default class GitHubGetRepositoryConnector extends AConnector {

public getName(): string {
return NAME;
}

public async processAction(dto: ProcessDto<IInput>): Promise<ProcessDto> {
const data = dto.getJsonData();
const appInstall = await this.getApplicationInstallFromProcess(dto);

if (!data.org || !data.repo) {
dto.setStopProcess(ResultCode.STOP_AND_FAILED, 'Connector has no required data.');
} else {
const request = await this.getApplication().getRequestDto(dto, appInstall, HttpMethods.GET, `/repos/${data.org}/${data.repo}`);
const response = await this.getSender().send(request, {
success: '<400',
stopAndFail: ['400-500'],
repeat: '>=500',
});

dto.setData(response.getBody());
}
return dto;
}

}

export interface IInput {
org: string;
repo: string;
}

In the connector you can see the handling of error responses in the second argument of the send method. In the configuration are ranges of response status codes. For example when the status code will be 404 the process will stop and fail. For all the connector error handling options, we recommend to study the Results evaluation page.

Application connector registration

Now we need to register a new connector in the container. Application connectors require access to the database and to the application they use, so we must not forget to set them up.

// ...
import { initiateContainer, container } from '@orchesty/nodejs-sdk';
import DbClient from '@orchesty/nodejs-sdk/dist/lib/Storage/Database/Client';
import CurlSender from '@orchesty/nodejs-sdk/dist/lib/Transport/Curl/CurlSender';
import GitHubApplication from './GitHubApplication';
import GitHubGetRepositoryConnector from './GitHubGetRepositoryConnector';
// ...

export default function prepare(): void {

// ...
const curlSender = container.get(CurlSender);
const databaseClient = container.get(DbClient);

const gitHubApplication = new GitHubApplication();
container.setApplication(gitHubApplication);

const gitHubGetRepositoryConnector = new GitHubGetRepositoryConnector()
.setSender(curlSender)
.setDb(databaseClient)
.setApplication(gitHubApplication);

container.setConnector(gitHubGetRepositoryConnector);
// ...
}

Note that connector is calling getRequestDto of an application (given to it by setApplication in index.ts), which will fill required authorization header.

Using a connector with an application in the topology

We will now test our new application. First, we must remember to install our application in the Applications tab. For authorization, we need to fill in our GitHub API accesses in the application. You can generate an authorization token in the developer settings of your account at https://github.com/settings/tokens and insert it into the authorization form of our new application. Then we still must activate the application.

Authorize application

In Orchesty Admin we will create a new topology. Again, we'll include a user task at the end to better control the output of the connector.

GitHub topology

We publish and activate the topology. This time, the topology will expect data to be inserted for submission, namely the owner and the repository name. Since we have handled error situations, we can first test how the process behaves if we don't insert the correct data.

Error handling

So let's run the topology without data first. If we now look at the Processes tab of the topology, we can see that the process ended with an error.

Failed process

If the process ends with an error, we can look at the Log list tab for more information about the error.

Error log

The error status was handled by a code in the connector, which says that the process should be terminated and the message should be catched in the trash. So we go to the Trash tab, where we can look at the message and even fix it.

Failed message in trash

Among the headers we can also see the result message that we have set in the connector, which tells us that the connector has not received the necessary data. In this case, we can correct the data directly, or insert and drop the message into the connector again.

Update in trash

We save the message and use the Approve button to send it again to the connector. If we have entered the correct name and owner of the repository, we can now see that the message has successfully reached the user task node behind the connector.

We can also try running the process directly with the correct data. We have defined the keys to insert the data in the connector interface.

Run topology

Congratulations! This is our first application with basic authorization. The application can now be used by any number of connectors.