Skip to content

New Components - sailpoint #14935

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions components/sailpoint/.gitignore

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import sailpoint from "../../sailpoint.app.mjs";

export default {
key: "sailpoint-list-certification-campaigns",
name: "List Certification Campaigns",
description: "Retrieves multiple certification campaigns in IdentityNow. [See the documentation](https://developer.sailpoint.com/docs/api/v2024/get-active-campaigns)",
version: "0.0.1",
type: "action",
props: {
sailpoint,
filter: {
propDefinition: [
"sailpoint",
"filter",
],
optional: true,
},
},
async run({ $ }) {
const response = this.sailpoint.paginate({
fn: this.sailpoint.listCertificationCampaigns,
params: {
detail: "full",
},
});

const responseArray = [];

for await (const item of response) {
responseArray.push(item);
}

$.export("$summary", `Successfully retrieved ${responseArray.length} certification campaigns`);
return responseArray;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { REQUEST_TYPE_OPTIONS } from "../../common/constants.mjs";
import sailpoint from "../../sailpoint.app.mjs";

export default {
key: "sailpoint-submit-access-request",
name: "Submit Access Request",
description: "Sends an access request to IdentityNow. [See the documentation](https://developer.sailpoint.com/docs/api/v2024/create-access-request)",
version: "0.0.1",
type: "action",
props: {
sailpoint,
requestedFor: {
propDefinition: [
sailpoint,
"requestedFor",
],
},
requestType: {
type: "string",
label: "Request Type",
description: "Type of access request.",
options: REQUEST_TYPE_OPTIONS,
default: REQUEST_TYPE_OPTIONS[0].value,
},
requestedItems: {
type: "string[]",
label: "Requested Items",
description: "List of requested items as JSON strings. **Example: [{\"type\": \"ROLE\",\"id\": \"2c9180835d2e5168015d32f890ca1581\",\"comment\": \"Requesting access profile for John Doe\",\"clientMetadata\": {\"requestedAppId\":\"2c91808f7892918f0178b78da4a305a1\",\"requestedAppName\":\"test-app\"},\"removeDate\": \"2020-07-11T21:23:15.000Z\"}]**. [See the documentation](https://developer.sailpoint.com/docs/api/v2024/create-access-request) for forther information.",
},
clientMetadata: {
type: "object",
label: "Client Metadata",
description: "Arbitrary key-value pairs. They will never be processed by the IdentityNow system but will be returned on associated APIs such as /account-activities. **Example: {\"requestedAppId\":\"2c91808f7892918f0178b78da4a305a1\",\"requestedAppName\":\"test-app\"}**.",
optional: true,
},
},
async run({ $ }) {
const response = await this.sailpoint.submitAccessRequest({
$,
data: {
requestedFor: this.requestedFor,
requestType: this.requestType,
resquestItems: this.resquestItems,
clientMetadata: this.clientMetadata,
},
});
Comment on lines +38 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Fix typo in request payload property name

There's a typo in the request payload: resquestItems should be requestedItems. This doesn't match:

  1. The prop name defined in the component (requestedItems)
  2. The documentation link provided in the component's description

Additionally, the property name in the payload doesn't match the actual prop name defined in the component's configuration, which could cause the request to fail.

🔗 Analysis chain

Verify API implementation against latest documentation

Please verify that the implementation matches the latest SailPoint API documentation, especially the request payload structure.


Let me search the codebase for more information about the API implementation.


Let me check the specific implementation of the submit-access-request component.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the API documentation URL is valid and accessible
curl -I "https://developer.sailpoint.com/docs/api/v2024/create-access-request"

Length of output: 1032


Script:

#!/bin/bash
# Search for SailPoint API related files and documentation
fd sailpoint --type f --exec cat {}

# Also search for any test files that might contain example payloads
fd "sailpoint.*test|test.*sailpoint" --type f --exec cat {}

Length of output: 3605


Script:

#!/bin/bash
# Check the submit-access-request component implementation
cat components/sailpoint/actions/submit-access-request/submit-access-request.mjs

# Search for any related test files
fd "submit-access-request.*test" --type f --exec cat {}

Length of output: 2217

$.export("$summary", "Access request submitted successfully.");
return response;
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import FormData from "form-data";
import fs from "fs";
import { checkTmp } from "../../common/utils.mjs";
import sailpoint from "../../sailpoint.app.mjs";

export default {
key: "sailpoint-upload-account-source-file",
name: "Upload Account Source File",
description: "Uploads a CSV-formatted account source file to IdentityNow. [See the documentation]()",
version: "0.0.1",
type: "action",
props: {
sailpoint,
sourceId: {
propDefinition: [
sailpoint,
"sourceId",
],
},
filePath: {
type: "string",
label: "File Path",
description: "The path to a file in the `/tmp` directory. [See the documentation on working with files](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp).",
},
},
async run({ $ }) {
const data = new FormData();

data.append("file", fs.createReadStream(checkTmp(this.filePath)));

const response = await this.sailpoint.uploadSourceAccountFile({
$,
sourceId: this.sourceId,
data,
headers: data.getHeaders(),
});

$.export("$summary", `Successfully uploaded file for source account: ${response.id} (${response.name})`);
return response;
},
};
13 changes: 0 additions & 13 deletions components/sailpoint/app/sailpoint.app.ts

This file was deleted.

20 changes: 20 additions & 0 deletions components/sailpoint/common/constants.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const LIMIT = 100;

export const REQUEST_TYPE_OPTIONS = [
{
label: "Grant Access",
value: "GRANT_ACCESS",
},
{
label: "Revoke Access",
value: "REVOKE_ACCESS",
},
];

export const WEBHOOK_TYPE_OPTIONS = [
"HTTP",
"EVENTBRIDGE",
"INLINE",
"SCRIPT",
"WORKFLOW",
];
8 changes: 8 additions & 0 deletions components/sailpoint/common/utils.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default {
checkTmp(filename) {
if (!filename.startsWith("/tmp")) {
return `/tmp/${filename}`;
}
return filename;
},
};
9 changes: 6 additions & 3 deletions components/sailpoint/package.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
{
"name": "@pipedream/sailpoint",
"version": "0.0.3",
"version": "0.1.0",
"description": "Pipedream SailPoint Components",
"main": "dist/app/sailpoint.app.mjs",
"main": "sailpoint.app.mjs",
"keywords": [
"pipedream",
"sailpoint"
],
"files": ["dist"],
"homepage": "https://pipedream.com/apps/sailpoint",
"author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
"publishConfig": {
"access": "public"
},
"dependencies": {
"@pipedream/platform": "^3.0.3"
}
}

145 changes: 145 additions & 0 deletions components/sailpoint/sailpoint.app.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { axios } from "@pipedream/platform";
import { LIMIT } from "./common/constants.mjs";

export default {
type: "app",
app: "sailpoint",
propDefinitions: {
requestedFor: {
type: "string[]",
label: "Requested For",
description: "List of Identity IDs for whom the Access is requested.",
async options({ page }) {
const data = await this.listIdentityIds({
params: {
limit: LIMIT,
offset: LIMIT * page,
},
});

return data.map(({
id: value, name: label,
}) => ({
label,
value,
}));
},
},
sourceId: {
type: "string",
label: "Source ID",
description: "ID of the source to upload the account file.",
async options({ page }) {
const data = await this.listSoruceIds({
params: {
limit: LIMIT,
offset: LIMIT * page,
},
});

return data.map(({
id: value, name: label,
}) => ({
label,
value,
}));
},
},
},
methods: {
_baseUrl() {
return "https://sailpoint.api.identitynow.com/v2024";
},
_headers(headers = {}) {
return {
...headers,
Accept: "application/json",
Authorization: `Bearer ${this.$auth.oauth_access_token}`,
};
},
_makeRequest({
$ = this, path, headers, ...opts
}) {
return axios($, {
url: this._baseUrl() + path,
headers: this._headers(headers),
...opts,
});
},
listIdentityIds(opts = {}) {
return this._makeRequest({
path: "/identities",
headers: {
"X-SailPoint-Experimental": true,
},
...opts,
});
},
listSoruceIds(opts = {}) {
return this._makeRequest({
path: "/sources",
...opts,
});
},
submitAccessRequest(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/access-requests",
...opts,
});
},
uploadSourceAccountFile({
sourceId, ...opts
}) {
return this._makeRequest({
method: "POST",
path: `/sources/${sourceId}/schemas/accounts`,
...opts,
});
},
createWebhook(opts = {}) {
return this._makeRequest({
method: "POST",
path: "/trigger-subscriptions",
headers: {
"X-SailPoint-Experimental": true,
},
...opts,
});
},
deleteWebhook(webhookId) {
return this._makeRequest({
method: "POST",
path: `/trigger-subscriptions/${webhookId}`,
headers: {
"X-SailPoint-Experimental": true,
},
});
},
},
async *paginate({
fn, params = {}, maxResults = null, ...opts
}) {
let hasMore = false;
let count = 0;
let page = 0;

do {
params.page = ++page;
const data = await fn({
params,
...opts,
});
for (const d of data) {
yield d;

if (maxResults && ++count === maxResults) {
return count;
}
}

hasMore = data.length;

} while (hasMore);
},
};
Loading
Loading