Skip to content

Commit

Permalink
Merge pull request #98 from sailpoint-oss/fning/PLTCONN-3577
Browse files Browse the repository at this point in the history
PLTCONN-3577: Customizer init command
  • Loading branch information
fangming-ning-sp committed Sep 12, 2023
2 parents 2cebcc9 + 671a8e7 commit 45bfaab
Show file tree
Hide file tree
Showing 20 changed files with 321 additions and 27 deletions.
18 changes: 14 additions & 4 deletions .github/workflows/prb_linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,21 @@ jobs:
- name: Install
run: make install

- name: Init connector project
run: sail conn init connTest
- name: Init connector and customizer project
run: |
sail conn init connectorTest
sail conn customizers init customizerTest
- name: Test connector project from init command
working-directory: ./connectorTest
run: |
npm install
npm run test
npm run pack-zip
- name: Build and package connector zip file
working-directory: ./connTest
- name: Test customizer project from init command
working-directory: ./customizerTest
run: |
npm install
npm run test
npm run pack-zip
18 changes: 14 additions & 4 deletions .github/workflows/prb_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,21 @@ jobs:
- name: Install
run: make install

- name: Init connector project
run: sail conn init connTest
- name: Init connector and customizer project
run: |
sail conn init connectorTest
sail conn customizers init customizerTest
- name: Test connector project from init command
working-directory: ./connectorTest
run: |
npm install
npm run test
npm run pack-zip
- name: Build and package connector zip file
working-directory: ./connTest
- name: Test customizer project from init command
working-directory: ./customizerTest
run: |
npm install
npm run test
npm run pack-zip
18 changes: 14 additions & 4 deletions .github/workflows/prb_windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,21 @@ jobs:
- name: Install
run: go build -o sail.exe

- name: Init connector project
run: ./sail.exe conn init connTest
- name: Init connector and customizer project
run: |
./sail.exe conn init connectorTest
./sail.exe conn customizers init customizerTest
- name: Test connector project from init command
working-directory: ./connectorTest
run: |
npm install
npm run test
npm run pack-zip
- name: Build and package connector zip file
working-directory: ./connTest
- name: Test customizer project from init command
working-directory: ./customizerTest
run: |
npm install
npm run test
npm run pack-zip
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 SailPoint
Copyright (c) 2023 SailPoint

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
21 changes: 11 additions & 10 deletions cmd/connector/conn_init.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import (
"github.com/spf13/cobra"
)

//go:embed static/*
var staticDir embed.FS
//go:embed static/connector/*
var connectorStaticDir embed.FS

const (
staticDirName = "static"
packageJsonName = "package.json"
connectorSpecName = "connector-spec.json"
connectorDirName = "connector"
connectorTemplatePath = "static/" + connectorDirName
packageJsonName = "package.json"
connectorSpecName = "connector-spec.json"
)

// newConnInitCmd is a connectors subcommand used to initialize a new connector project.
Expand Down Expand Up @@ -54,12 +55,12 @@ func newConnInitCommand() *cobra.Command {
return
}

err := fs.WalkDir(staticDir, staticDirName, func(path string, d fs.DirEntry, err error) error {
err := fs.WalkDir(connectorStaticDir, connectorTemplatePath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if d.Name() == staticDirName {
if d.Name() == connectorDirName {
return nil
}

Expand All @@ -68,9 +69,9 @@ func newConnInitCommand() *cobra.Command {
return err
}
} else {
fileName := filepath.Join(projName, strings.TrimPrefix(path, staticDirName))
fileName := filepath.Join(projName, strings.TrimPrefix(path, connectorTemplatePath))

data, err := staticDir.ReadFile(path)
data, err := connectorStaticDir.ReadFile(path)
if err != nil {
return err
}
Expand All @@ -81,7 +82,7 @@ func newConnInitCommand() *cobra.Command {
}

if d.Name() == packageJsonName || d.Name() == connectorSpecName {
fileAbsPath, err := filepath.Abs(filepath.Join(projName, strings.TrimPrefix(path, staticDirName)))
fileAbsPath, err := filepath.Abs(filepath.Join(projName, strings.TrimPrefix(path, connectorTemplatePath)))
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/connector/customizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func newConnCustomizersCmd(client client.Client) *cobra.Command {
}

cmd.AddCommand(
newCustomizerInitCmd(),
newCustomizerListCmd(client),
newCustomizerCreateCmd(client),
newCustomizerGetCmd(client),
Expand Down
101 changes: 101 additions & 0 deletions cmd/connector/customizer_init.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2023, SailPoint Technologies, Inc. All rights reserved.
package connector

import (
"embed"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"

"github.com/spf13/cobra"
)

//go:embed static/customizer/*
var customizerStaticDir embed.FS

const (
customizerDirName = "customizer"
customizerTemplatePath = "static/" + customizerDirName
)

func newCustomizerInitCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "init <customizer-name>",
Short: "Initialize new connector customizer project",
Long: `init sets up a new TypeScript project with sample connector customizer included for reference.`,
Example: "sail conn customizers init \"My Customizer\"",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
projName := args[0]
if projName == "" {
printError(cmd.ErrOrStderr(), errors.New("connector customizer name cannot be empty"))
return
}

if f, err := os.Stat(projName); err == nil && f.IsDir() && f.Name() == projName {
printError(cmd.ErrOrStderr(), fmt.Errorf("Error: project '%s' already exists.\n", projName))
return
}

if err := createDir(projName); err != nil {
_ = os.RemoveAll(projName)
printError(cmd.ErrOrStderr(), err)
return
}

err := fs.WalkDir(customizerStaticDir, customizerTemplatePath, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}

if d.Name() == customizerDirName {
return nil
}

if d.IsDir() {
if err := createDir(filepath.Join(projName, d.Name())); err != nil {
return err
}
} else {
fileName := filepath.Join(projName, strings.TrimPrefix(path, customizerTemplatePath))

data, err := customizerStaticDir.ReadFile(path)
if err != nil {
return err
}

if err := createFile(fileName, data); err != nil {
return err
}
}

if d.Name() == packageJsonName {
fileAbsPath, err := filepath.Abs(filepath.Join(projName, strings.TrimPrefix(path, customizerTemplatePath)))
if err != nil {
return err
}

if err := createFileFromTemplate(projName, d.Name(), fileAbsPath); err != nil {
return err
}
return nil
}

return nil
})
if err != nil {
_ = os.RemoveAll(projName)
printError(cmd.ErrOrStderr(), err)
return
}

printDir(cmd.OutOrStdout(), projName, 0)
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Successfully created project '%s'.\nRun `npm install` to install dependencies.\n", projName)
},
}

return cmd
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { connector } from './index'
import { StandardCommand } from '@sailpoint/connector-sdk'
import { Connector, RawResponse, ResponseType, StandardCommand } from '@sailpoint/connector-sdk'
import { PassThrough } from 'stream'

const mockConfig: any = {
Expand All @@ -9,16 +9,16 @@ process.env.CONNECTOR_CONFIG = Buffer.from(JSON.stringify(mockConfig)).toString(

describe('connector unit tests', () => {

it('connector SDK major version should be 0', async () => {
expect((await connector()).sdkVersion).toStrictEqual(0)
it('connector SDK major version should be the same as Connector.SDK_VERSION', async () => {
expect((await connector()).sdkVersion).toStrictEqual(Connector.SDK_VERSION)
})

it('should execute stdTestConnectionHandler', async () => {
await (await connector())._exec(
StandardCommand.StdTestConnection,
{},
undefined,
new PassThrough({ objectMode: true }).on('data', (chunk) => expect(chunk).toStrictEqual({}))
new PassThrough({ objectMode: true }).on('data', (chunk) => expect(chunk).toStrictEqual(new RawResponse ({}, ResponseType.Output)))
)
})
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
19 changes: 19 additions & 0 deletions cmd/connector/static/customizer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# macOS General
.DS_Store
.AppleDouble
.LSOverride

# Visual Studio Code
.vscode/
.history/

# Intellij
.idea/
*.iml

# Dependency directories
node_modules/

# Compiled source
dist/
coverage/
51 changes: 51 additions & 0 deletions cmd/connector/static/customizer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"name": "{{$.ProjectName}}",
"version": "0.1.0",
"main": "dist/index.js",
"scripts": {
"clean": "shx rm -rf ./dist",
"prebuild": "npm run clean",
"build": "npx ncc build ./src/index.ts -o ./dist -m -C",
"dev": "cross-env NODE_OPTIONS=--enable-source-maps spcx run dist/index.js",
"debug": "spcx run dist/index.js",
"prettier": "npx prettier --write .",
"test": "jest --coverage",
"prepack-zip": "npm ci && npm run build",
"pack-zip": "spcx package"
},
"private": true,
"dependencies": {
"@sailpoint/connector-sdk": "^1.0.0"
},
"devDependencies": {
"@types/jest": "^27.0.1",
"@vercel/ncc": "^0.34.0",
"jest": "^27.0.6",
"prettier": "^2.3.2",
"shx": "^0.3.3",
"ts-jest": "^27.0.5",
"typescript": "4.3.5",
"cross-env": "7.0.3"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"clearMocks": true,
"collectCoverage": true,
"coverageThreshold": {
"global": {
"statements": 60,
"branches": 50,
"functions": 40,
"lines": 60
}
}
},
"prettier": {
"printWidth": 120,
"trailingComma": "es5",
"tabWidth": 4,
"semi": false,
"singleQuote": true
}
}
43 changes: 43 additions & 0 deletions cmd/connector/static/customizer/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { connectorCustomizer } from './index'
import { CustomizerType, StandardCommand } from '@sailpoint/connector-sdk'

const mockConfig: any = {
token: 'xxx123'
}
process.env.CONNECTOR_CONFIG = Buffer.from(JSON.stringify(mockConfig)).toString('base64')

describe('connector customizer unit tests', () => {

it('should not change input from beforeStdAccountReadHandler', async () => {
let customizer = await connectorCustomizer()
let input = {
identity: 'john.doe',
}
let updatedInput = await customizer._exec(
customizer.handlerKey(CustomizerType.Before, StandardCommand.StdAccountRead),
{},
input
)

expect(input).toStrictEqual(updatedInput)
})

it('should add location attribute from afterStdAccountReadHandler', async () => {
let customizer = await connectorCustomizer()
let output = await customizer._exec(
customizer.handlerKey(CustomizerType.After, StandardCommand.StdAccountRead),
{},
{
identity: '',
attributes: {
username: 'john.doe',
firstName: 'john',
lastName: 'doe',
email: 'john.doe@example.com',
}
}
)

expect(output.attributes.location).toStrictEqual('Austin')
})
})
Loading

0 comments on commit 45bfaab

Please sign in to comment.