View this page in the latest version of Appian. Design Considerations Share Share via LinkedIn Reddit Email Copy Link Print On This Page Overview This page explains features, concepts, and considerations to use when building your connected system plug-in. Branding Good logos and descriptions will help users find and use your plug-in. Add logos to your Connected System to show custom branding Add a description that tells us how to use your system. Why would I use it? What is exciting about it? For example, our Google Cloud Natural Language Connected System has the following description: Easily connect to Google Cloud Natural Language machine learning models. Extract structure and information from text, including both sentiment and key subjects such as people, places, and events. Integrations using this connected system will allow you to provide a text value and select which types of information to analyze. Check out Appian's Connected Systems for more examples. Authentication When authentication is required to send and receive information, fields can be configured to securely store credentials. Appian supports the use of the following authentication types for API requests: System username/password Basic authentication NTLM Digest Kerberos SOAP request body Prebuilt OAuth 2.0 (Authorization Code grant) - storage and retrieval of refresh and access tokens See How to work with OAuth Custom - you can configure many different kinds of auth. Below are a few examples API Key AWS Authentication Known unsupported authentication mechanisms Currently, you cannot build a plug-in that authenticates using: Anything Involving Certificate-Based Signing OAuth 2.0 (JWT Bearer Token) Per use credentials for anything other than OAuth 2.0 (for example, per-user creds for Basic auth) Secure storage of Short-lived token authentication OAuth 2.0 (Client Credentials) Workaround: You can configure your Connected System Template to request a token on each request. Be sure to review the policies and rate limits for the service you are connecting to. File transfer Sending files supported: How to send files Suite API Injecting services As with Suite API plug-ins, public *Service interfaces (such as ProcessAnalyticsService and ContentService) are available for injection into connected system, integration, and client API templates, enabling access to Appian data and application context. There are a couple limitations to be aware of as you build your plug-in: EncryptionService is not available for Integration SDK plug-ins. Deprecated services, such as com.appiancorp.suiteapi.collaboration.DocumentService, are not available for use. Note: This is the only valid method for obtaining a *Service within custom plug-in code. Do not use ServiceLocator.getXService()*. Plug-ins using *Service interfaces from the public API are compatible with Appian 26.1 and above. To update an existing function plug-in to use these interfaces, do the following: Make sure you are using the appian-plug-in-sdk.jar from a site running version 26.1 or above. In the appian-plugin.xml file, change the plug-in's minimum Appian version: <application-version min="26.1.0">. Update the plug-in's version to a new major <version>. For example, if your plug-in is currently at v1.5.2, you'd update it to v2.0.0. Update the plug-in's release notes in the AppMarket to make a note of the change. Access data sources with injection The javax.naming.Context can be injected to provide access to data sources. Use the lookup() method to access to the objects that are registered in the JNDI tree, such as data sources. The following Spring injection patterns are supported across connected system, integration, and client API templates: Constructor injection: Inject services into a template constructor following the same pattern as Smart Service Plug-ins. Method injection: Inject services into setter functions. Field injection: Inject services into fields within method signatures of a connected system, integration, or client API template. Constructor injection example The following example shows a Client API that creates a document using text provided by the component's end user. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @TemplateId(name = "CreateDocument") public class CreateDocument extends SimpleClientApi { private final ContentService contentService; public CreateDocument(ContentService contentService) { this.contentService = contentService; } @Override protected ClientApiResponse execute(SimpleClientApiRequest simpleClientApiRequest, ExecutionContext executionContext) { Folder ruleFolder=new RulesFolder(); contentService.getIdByUuid("uploadFolderUuid"); Document document = new Document(); document.setName("Sample Document"); document.setExtension("txt"); document.setParent(contentService.getIdByUuid("FOLDER_UUID")); ruleFolder.setName("Text Document Folder"); ContentUploadOutputStream cos = null; try { cos = contentService.uploadDocument(document, ContentConstants.UNIQUE_NONE); String contentFromUser = simpleClientApiRequest.getPayload().get("content"); cos.write(content.getBytes()); } catch (InvalidContentException | StorageLimitException | PrivilegeException | InsufficientNameUniquenessException | DuplicateUuidException | AppianStorageException | IOException e) { throw new RuntimeException(e); } Map<String,Object> resultMap = new HashMap<>(); resultMap.put("Document ID", cos.getContentId()); return new ClientApiResponse(resultMap); } } Method injection example The following example shows a connected system plug-in that fetches a document that stores authentication information and verifies proper configuration. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @TemplateId(name = "Basic Authentication Connected System Template") public class BasicAuthenticationConnectedSystemTemplate extends SimpleTestableConnectedSystemTemplate{ public static final String PRIVATE_KEY_DOCUMENT_PROP = "privatekeydoc"; ContentService contentService; @Inject public void setContentService(ContentService contentService){ this.contentService=contentService; } @Override protected SimpleConfiguration getConfiguration(SimpleConfiguration simpleConfiguration, ExecutionContext executionContext){ return simpleConfiguration.setProperties( textProperty(PRIVATE_KEY_DOCUMENT_PROP).label("Private Key Document ID") .isRequired(true) .isImportCustomizable(true) .build()); } @Override protected TestConnectionResult testConnection(SimpleConfiguration connectedSystem, ExecutionContext executionContext) { try{ long privateKeyDocId=connectedSystem.getValue(PRIVATE_KEY_DOCUMENT_PROP); DocumentInputStream document=contentService.getDocumentInputStream(privateKeyDocId); String privateKey = new String(document.readAllBytes()); HttpResponse sessionTokenResponse = Http.getToken(privateKey, executionContext, connectedSystem); if(sessionTokenResponse.getStatusCode() == 200 && sessionTokenResponse.getResponse().get("responseStatus").equals("SUCCESS")){ return TestConnectionResult.success(); } return TestConnectionResult.error("Authentication failed"); } catch (Exception e){ return TestConnectionResult.error("Error in connecting: "+e.getMessage()); } } } Field injection example The following example shows an integration that fetches data from a third-party service, starts a process with that data, and then returns the results. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @TemplateId(name = "Add Insurance Process Integration") @IntegrationTemplateType(IntegrationTemplateRequestPolicy.READ) public class StartProcessInstance extends SimpleIntegrationTemplate { @Inject private ProcessDesignService processDesignService; @Inject private ProcessExecutionService processExecutionService; @Override protected SimpleConfiguration getConfiguration( SimpleConfiguration simpleConfiguration, SimpleConfiguration simpleConfiguration1, PropertyPath propertyPath, ExecutionContext executionContext) { return null; } @Override protected IntegrationResponse execute( SimpleConfiguration simpleConfiguration, SimpleConfiguration simpleConfiguration1, ExecutionContext executionContext) { // ... insert code which integrates with third-party service... Map<String,Object> resultMap = new HashMap<>(); ProcessVariable[] processVariables = { new ProcessVariable(new NamedTypedValue("insuranceRecord", 0L, "REPLACE_WITH_ACTUAL_VALUE")) }; try { ProcessModelPermissions permissions = processDesignService.getPermissionsForProcessModel(0L); if (!permissions.isInitiateProcess()){ resultMap.put("Error", "Current user doesn't have permissions to initiate the process"); } long instanceId = processDesignService.initiateProcess(0L, new ProcessStartConfig(processVariables)); if(instanceId != 0){ ProcessDetails processDetails = processExecutionService.getProcessDetails(instanceId); resultMap.put("Process Status",processDetails.getStatusName()); } } catch (Exception e) { throw new RuntimeException(e); } return IntegrationResponse.forSuccess(resultMap).build(); } } System wide configurations Proxy support If an HTTP proxy has been enabled by an Appian administrator, you can build the plug-in to use the provided configuration data when making HTTP requests. Note: Connected system plug-ins won't support using the configured proxy unless you specifically develop them to do so. For an example, see Configuring HttpClient when a proxy is enabled. SSL Plug-ins can use trusted server certificates uploaded in the admin console. Give feedback on errors Validating credentials and connection information Validating a single input (for example, length of a text input) Indicating a failed execution Diagnostics Adding debugging information about the request Adding debugging information about the response Timing Adding timing information Import customizable fields Ensuring fields can change between environments Use sensitive values on connected systems Creating a secured input field Connected system plug-ins restrictions Connected systems Must not implement both TestableConnectedSystemTemplate and OAuthConnectedSystemTemplate. Fields must have a RefreshPolicy of NEVER. Fields must not be expressionable. Fields must not be LIST type. Fields must not have deeply nested Local Types. Don’t call setErrors(), as errors are ignored. Integrations isImportCustomizable should not be set to true. Fields must not be encrypted. Local Types must be expressionable. ` @TemplateId.majorVersion` annotation is ignored. Choices Each Choice must have a name and value. PropertyDescriptors Fields that are DisplayHint.EXPRESSION must be expressionable. Required fields must not be hidden. EncryptedTextPropertyDescriptors isImportCustomizable should be set to true. DocumentPropertyDescriptors isImportCustomizable should not be set to true. ListTypePropertyDescriptors Must be expressionable. Must not have a placeholder. ` displayHint() must be set to DisplayHint.Expression`. LocalTypePropertyDescriptor Must not have a null type isImportCustomizable should not be set to true. Feedback Was this page helpful? SHARE FEEDBACK Loading...