Skip to content
Open
Show file tree
Hide file tree
Changes from 7 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 identitynow from "../../identitynow.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: {
identitynow,
requestedFor: {
propDefinition: [
identitynow,
"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.identitynow.submitAccessRequest({
$,
data: {
requestedFor: this.requestedFor,
requestType: this.requestType,
resquestItems: this.resquestItems,
clientMetadata: this.clientMetadata,
},
});
$.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 <[email protected]> (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