Skip to content
Merged
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
5 changes: 5 additions & 0 deletions examples/sdk_example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
"nsf:get": "ts-node src/nestedShareFolders/get_nsf.ts",
"nsf:ln": "ts-node src/nestedShareFolders/link_nsf.ts",
"nsf:rm": "ts-node src/nestedShareFolders/remove_nsf.ts",
"nsf:record-details": "ts-node src/nestedShareFolders/get_nsf_record_details.ts",
"nsf:record-update": "ts-node src/nestedShareFolders/update_nsf_record.ts",
"nsf:record-add": "ts-node src/nestedShareFolders/add_nsf_record.ts",
"nsf:mkdir": "ts-node src/nestedShareFolders/mkdir_nsf.ts",
"nsf:rmdir": "ts-node src/nestedShareFolders/rmdir_nsf.ts",
"teams:list": "ts-node src/teams/list_teams.ts",
"teams:view": "ts-node src/teams/view_team.ts",
"teams:add": "ts-node src/teams/add_team.ts",
Expand Down
137 changes: 137 additions & 0 deletions examples/sdk_example/src/nestedShareFolders/add_nsf_record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import {
cleanup,
extractErrorMessage,
login,
logger,
parseNsfFieldSpaceInput,
prompt,
type AddNsfRecordInput,
type AddNsfRecordResult,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { splitCommaSeparated, withSuppressedLogs } from '../utils/format'
import { promptChoice, promptRequiredList, promptYesNo, yesNoPrompt } from '../utils/promptCommands'

type AddMode = 'single' | 'bulk' | 'multi'

const MODE_CHOICES: Record<string, AddMode> = {
'': 'single',
'1': 'single',
'2': 'bulk',
'3': 'multi',
single: 'single',
bulk: 'bulk',
multi: 'multi',
}

async function promptSharedRecordOptions(): Promise<{
recordType: string
folder?: string
notes?: string
fieldEntries?: AddNsfRecordInput['fieldEntries']
customEntries?: AddNsfRecordInput['customEntries']
hasFileFields?: boolean
force: boolean
}> {
const recordType = (await prompt('Record type (e.g. login, general): ')).trim()
const folder = (await prompt('Folder name or UID (optional, Enter for root): ')).trim()
const notes = (await prompt('Notes (optional): ')).trim()
const fieldsInput = (await prompt('Fields (field=value, space-separated, optional): ')).trim()
const force = await promptYesNo(yesNoPrompt('Ignore warnings (e.g. attachments)?'))
const parsed = fieldsInput ? parseNsfFieldSpaceInput(fieldsInput) : undefined

return {
recordType,
folder: folder || undefined,
notes: notes || undefined,
fieldEntries: parsed?.fieldEntries,
customEntries: parsed?.customEntries,
hasFileFields: parsed?.hasFileFields,
force,
}
}

async function promptOneRecord(shared?: Awaited<ReturnType<typeof promptSharedRecordOptions>>): Promise<AddNsfRecordInput> {
const title = (await prompt('Record title: ')).trim()
if (!title) {
throw new Error('Record title is required.')
}

if (shared) {
return { title, ...shared }
}

const options = await promptSharedRecordOptions()
return { title, ...options }
}

function printAddResults(added: AddNsfRecordResult[], revision?: number): void {
logger.info('')
for (const item of added) {
logger.info(`Record: ${item.recordUid}`)
logger.info(` Status: ${item.status}`)
if (item.message) logger.info(` Message: ${item.message}`)
if (item.revision != null) logger.info(` Revision: ${item.revision}`)
logger.info('')
}
if (revision != null) {
logger.info(`Batch revision: ${revision}`)
}
logger.info(`Total: ${added.length} record(s) added`)
logger.info('')
}

async function addSingleRecord(vault: Awaited<ReturnType<typeof login>>): Promise<void> {
const input = await promptOneRecord()
const result = await withSuppressedLogs(() => vault.addNestedShareRecord(input))
printAddResults([result], result.revision)
}

async function addBulkSameTemplate(vault: Awaited<ReturnType<typeof login>>): Promise<void> {
const titles = await promptRequiredList('Record title(s), comma-separated: ', splitCommaSeparated)
const shared = await promptSharedRecordOptions()

const records = titles.map((title) => ({ title, ...shared }))
const result = await withSuppressedLogs(() => vault.addNestedShareRecords({ records }))
printAddResults(result.added, result.revision)
}

async function addMultipleRecords(vault: Awaited<ReturnType<typeof login>>): Promise<void> {
const records: AddNsfRecordInput[] = []

logger.info('Enter each record (different title, type, folder, or fields allowed).')
while (true) {
logger.info(`--- Record ${records.length + 1} ---`)
records.push(await promptOneRecord())
if (!(await promptYesNo(yesNoPrompt('Add another record?')))) {
break
}
}

const result = await withSuppressedLogs(() => vault.addNestedShareRecords({ records }))
printAddResults(result.added, result.revision)
}

async function addNsfRecord() {
const vault = await login()

try {
logger.info('Mode: 1) single 2) bulk (same type/folder/fields, multiple titles) 3) multiple (enter each)')
const mode = await promptChoice('Choose [1]: ', MODE_CHOICES)

if (mode === 'single') {
await addSingleRecord(vault)
} else if (mode === 'bulk') {
await addBulkSameTemplate(vault)
} else {
await addMultipleRecords(vault)
}
} catch (err) {
logger.error(`Record add failed: ${extractErrorMessage(err)}`)
process.exitCode = 1
} finally {
cleanup(vault)
}
}

runExample(addNsfRecord)
19 changes: 8 additions & 11 deletions examples/sdk_example/src/nestedShareFolders/get_nsf.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import {
cleanup,
extractErrorMessage,
formatNsfDetail,
formatNsfJson,
GetNsfFormat,
login,
logger,
prompt,
suppressLogs,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { isYes } from '../utils/format'
import { promptRequired, promptYesNo, yesNoPrompt } from '../utils/promptCommands'

async function getNsf() {
const vault = await login()

try {
const identifier = (await prompt('Record UID, folder UID, or title: ')).trim()
if (!identifier) {
logger.info('No UID or title given.')
return
}
const identifier = await promptRequired('Record UID, folder UID, or title: ', 'No UID or title given.')

const asJson = isYes(await prompt('Output as JSON? [y/N]: '))
const verbose = isYes(await prompt('Verbose permissions? [y/N]: '))
const unmask = isYes(await prompt('Unmask secrets? [y/N]: '))
const asJson = await promptYesNo(yesNoPrompt('Output as JSON?'))
const verbose = await promptYesNo(yesNoPrompt('Verbose permissions?'))
const unmask = await promptYesNo(yesNoPrompt('Unmask secrets?'))

const restore = suppressLogs()
let result
Expand All @@ -37,7 +34,7 @@ async function getNsf() {
}

logger.info('')
const output = asJson ? JSON.stringify(result.view, null, 2) : vault.formatNsfDetail(result, verbose)
const output = asJson ? formatNsfJson(result) : formatNsfDetail(result, verbose)
process.stdout.write(`${output}\n`)
logger.info('')
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
cleanup,
extractErrorMessage,
formatNsfRecordDetailsOutput,
GetNsfRecordDetailsFormat,
login,
logger,
prompt,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { splitCommaSeparated, withSuppressedLogs } from '../utils/format'

async function getNsfRecordDetails() {
const vault = await login()

try {
const records = splitCommaSeparated(await prompt('Record UID(s) or title(s), comma-separated: '))
if (records.length === 0) {
logger.info('At least one record is required.')
return
}

const formatInput = (await prompt('Output format (table/json) [table]: ')).trim().toLowerCase()
const format =
formatInput === 'json' ? GetNsfRecordDetailsFormat.JSON : GetNsfRecordDetailsFormat.Table

const result = await withSuppressedLogs(() => vault.getNestedShareRecordDetails({ records, format }))

logger.info('')
logger.info(formatNsfRecordDetailsOutput(result, format))
logger.info('')
} catch (err) {
logger.error(`Record details failed: ${extractErrorMessage(err)}`)
process.exitCode = 1
} finally {
cleanup(vault)
}
}

runExample(getNsfRecordDetails)
10 changes: 3 additions & 7 deletions examples/sdk_example/src/nestedShareFolders/link_nsf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,17 @@ import {
extractErrorMessage,
login,
logger,
prompt,
suppressLogs,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { promptRequired } from '../utils/promptCommands'

async function linkNsf() {
const vault = await login()

try {
const recordIdentifier = (await prompt('Record UID or title: ')).trim()
const folderIdentifier = (await prompt('Destination folder UID or name: ')).trim()
if (!recordIdentifier || !folderIdentifier) {
logger.info('Both record and folder are required.')
return
}
const recordIdentifier = await promptRequired('Record UID or title: ')
const folderIdentifier = await promptRequired('Destination folder UID or name: ')

const restore = suppressLogs()
let result
Expand Down
20 changes: 8 additions & 12 deletions examples/sdk_example/src/nestedShareFolders/list_nsf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ import fs from 'fs/promises'
import {
cleanup,
extractErrorMessage,
formatListNsfOutput,
ListNsfFormat,
login,
logger,
prompt,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { isYes } from '../utils/format'
import { promptChoice, promptOptional, promptYesNo, yesNoPrompt } from '../utils/promptCommands'

type ListMode = 'all' | 'folders' | 'records'

const MODE_BY_INPUT: Record<string, ListMode> = {
const LIST_MODE_CHOICES: Record<string, ListMode> = {
'': 'all',
'1': 'all',
'2': 'folders',
Expand All @@ -22,19 +22,15 @@ const MODE_BY_INPUT: Record<string, ListMode> = {
records: 'records',
}

function parseMode(input: string): ListMode {
return MODE_BY_INPUT[input.trim().toLowerCase()] ?? 'all'
}

async function listNsf() {
const vault = await login()

try {
logger.info('Show: 1) all 2) folders only 3) records only')
const mode = parseMode(await prompt('Choose [1]: '))
const asJson = isYes(await prompt('Output as JSON? [y/N]: '))
const asCsv = !asJson && isYes(await prompt('Output as CSV? [y/N]: '))
const outputPath = (await prompt('Output file path (Enter for stdout): ')).trim()
const mode = await promptChoice('Choose [1]: ', LIST_MODE_CHOICES)
const asJson = await promptYesNo(yesNoPrompt('Output as JSON?'))
const asCsv = !asJson && (await promptYesNo(yesNoPrompt('Output as CSV?')))
const outputPath = await promptOptional('Output file path (Enter for stdout): ')

const format = asJson ? ListNsfFormat.JSON : asCsv ? ListNsfFormat.CSV : ListNsfFormat.Table
const rows = vault.listNestedShareFolders({
Expand All @@ -47,7 +43,7 @@ async function listNsf() {
return
}

const output = vault.formatListNsfOutput(rows, format)
const output = formatListNsfOutput(rows, format)
if (outputPath && format !== ListNsfFormat.Table) {
await fs.writeFile(outputPath, output, 'utf-8')
logger.info(`Wrote ${rows.length} row(s) to ${outputPath}`)
Expand Down
58 changes: 58 additions & 0 deletions examples/sdk_example/src/nestedShareFolders/mkdir_nsf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {
cleanup,
extractErrorMessage,
login,
logger,
NSF_FOLDER_COLORS,
prompt,
type MkdirNsfInput,
} from '@keeper-security/keeper-sdk-javascript'
import { runExample } from '../utils/runner'
import { withSuppressedLogs } from '../utils/format'

type Vault = Awaited<ReturnType<typeof login>>

async function runMkdirNsf(vault: Vault): Promise<void> {
const folder = (await prompt('Folder name or path (e.g. Team/Projects/Q1): ')).trim()
if (!folder) {
logger.info('Folder name is required.')
return
}

logger.info(`Colors: ${NSF_FOLDER_COLORS.join(', ')}`)
const colorInput = (await prompt('Color (optional, leaf only) [none]: ')).trim().toLowerCase()
const color =
colorInput && NSF_FOLDER_COLORS.some((candidate) => candidate === colorInput)
? (colorInput as MkdirNsfInput['color'])
: undefined
const noInherit = (await prompt('Do not inherit parent permissions? [y/N]: ')).trim().toLowerCase() === 'y'

const result = await withSuppressedLogs(() =>
vault.mkdirNestedShareFolder({
folder,
color,
noInheritPermissions: noInherit,
})
)

logger.info('')
if (result.message) logger.info(result.message)
logger.info(`Folder UID: ${result.folderUid}`)
logger.info(`Created: ${result.created ? 'Yes' : 'No'}`)
logger.info('')
}

async function mkdirNsf() {
const vault = await login()

try {
await runMkdirNsf(vault)
} catch (err) {
logger.error(`Folder create failed: ${extractErrorMessage(err)}`)
process.exitCode = 1
} finally {
cleanup(vault)
}
}

runExample(mkdirNsf)
Loading
Loading