generated from salesforcecli/lerna-template
-
Notifications
You must be signed in to change notification settings - Fork 15
feat: file upload #922
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
Merged
Merged
feat: file upload #922
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
179c640
style: linter did this
mshanemc f710228
feat: upload and attach files
mshanemc a5d2098
chore: snapshot
mshanemc add2d77
fix: edit messages for "data create file" (#923)
jshackell-sfdc c055ca3
test: nut
mshanemc d6a22af
docs: update package.json
mshanemc c03107e
refactor: change flag name => title
mshanemc e39538e
Merge branch 'sm/file-upload' of https://github.com/salesforcecli/plu…
mshanemc e1b41d8
docs: fix example
mshanemc edc6e88
chore: snapshot
mshanemc 3729932
refactor: pr feedback
mshanemc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# summary | ||
|
||
Upload a local file to an org. | ||
|
||
# description | ||
|
||
This command always creates a new file in the org; you can't update an existing file. After a successful upload, the command displays the ID of the new ContentDocument record which represents the uploaded file. | ||
|
||
By default, the uploaded file isn't attached to a record; in the Salesforce UI the file shows up in the Files tab. You can optionally attach the file to an existing record, such as an account, as long as you know its record ID. | ||
|
||
You can also give the file a new name after it's been uploaded; by default its name in the org is the same as the local file name. | ||
|
||
# flags.title.summary | ||
|
||
New title given to the file (ContentDocument) after it's uploaded. | ||
|
||
# examples | ||
|
||
- Upload the local file "resources/astro.png" to your default org: | ||
|
||
<%= config.bin %> <%= command.id %> --file resources/astro.png | ||
|
||
- Give the file a different filename after it's uploaded to the org with alias "my-scratch": | ||
|
||
<%= config.bin %> <%= command.id %> --file resources/astro.png --title AstroOnABoat.png --target-org my-scratch | ||
|
||
- Attach the file to a record in the org: | ||
|
||
<%= config.bin %> <%= command.id %> --file path/to/astro.png --parent-id a03fakeLoJWPIA3 | ||
|
||
# flags.file.summary | ||
|
||
Path of file to upload. | ||
|
||
# flags.parent-id.summary | ||
|
||
ID of the record to attach the file to. | ||
|
||
# createSuccess | ||
|
||
Created file with ContentDocumentId %s. | ||
|
||
# attachSuccess | ||
|
||
File attached to record with ID %s. | ||
|
||
# attachFailure | ||
|
||
The file was successfully uploaded, but we weren't able to attach it to the record. | ||
|
||
# insufficientAccessActions | ||
|
||
- Check that the record ID is correct and that you have access to it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright (c) 2023, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import { readFile } from 'node:fs/promises'; | ||
import { basename } from 'node:path'; | ||
import { Connection } from '@salesforce/core'; | ||
import { Record, SaveResult } from '@jsforce/jsforce-node'; | ||
import FormData from 'form-data'; | ||
|
||
export type ContentVersion = { | ||
Title: string; | ||
FileExtension: string; | ||
VersionData: string; | ||
/** this could be undefined outside of our narrow use case (created files) */ | ||
ContentDocumentId: string; | ||
} & Record; | ||
|
||
type ContentVersionCreateRequest = { | ||
PathOnClient: string; | ||
Title?: string; | ||
}; | ||
|
||
export async function file2CV(conn: Connection, filepath: string, title?: string): Promise<ContentVersion> { | ||
const req: ContentVersionCreateRequest = { | ||
PathOnClient: filepath, | ||
Title: title, | ||
}; | ||
|
||
const form = new FormData(); | ||
form.append('VersionData', await readFile(filepath), { filename: title ?? basename(filepath) }); | ||
form.append('entity_content', JSON.stringify(req), { contentType: 'application/json' }); | ||
|
||
// POST the multipart form to Salesforce's API, can't use the normal "create" action because it doesn't support multipart | ||
const CV = await conn.request<SaveResult>({ | ||
url: '/sobjects/ContentVersion', | ||
headers: { ...form.getHeaders() }, | ||
body: form.getBuffer(), | ||
method: 'POST', | ||
}); | ||
|
||
return conn.singleRecordQuery<ContentVersion>( | ||
`Select Id, ContentDocumentId, Title, FileExtension from ContentVersion where Id='${CV.id}'` | ||
); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
* Copyright (c) 2023, salesforce.com, inc. | ||
* All rights reserved. | ||
* Licensed under the BSD 3-Clause license. | ||
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause | ||
*/ | ||
|
||
import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; | ||
import { Messages, SfError } from '@salesforce/core'; | ||
import { ContentVersion, file2CV } from '../../../api/file/fileToContentVersion.js'; | ||
|
||
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); | ||
const messages = Messages.loadMessages('@salesforce/plugin-data', 'data.create.file'); | ||
|
||
type CDLCreateRequest = { | ||
ContentDocumentId: string; | ||
LinkedEntityId: string; | ||
ShareType: string; | ||
}; | ||
|
||
export default class DataCreateFile extends SfCommand<ContentVersion> { | ||
public static readonly summary = messages.getMessage('summary'); | ||
public static readonly description = messages.getMessage('description'); | ||
public static readonly examples = messages.getMessages('examples'); | ||
|
||
public static readonly flags = { | ||
'target-org': Flags.requiredOrg(), | ||
'api-version': Flags.orgApiVersion(), | ||
title: Flags.string({ | ||
summary: messages.getMessage('flags.title.summary'), | ||
char: 't', | ||
required: false, | ||
}), | ||
file: Flags.file({ | ||
summary: messages.getMessage('flags.file.summary'), | ||
char: 'f', | ||
required: true, | ||
exists: true, | ||
}), | ||
// it really could be most any valid ID | ||
// eslint-disable-next-line sf-plugin/id-flag-suggestions | ||
'parent-id': Flags.salesforceId({ | ||
summary: messages.getMessage('flags.parent-id.summary'), | ||
char: 'i', | ||
length: 'both', | ||
}), | ||
}; | ||
|
||
public async run(): Promise<ContentVersion> { | ||
const { flags } = await this.parse(DataCreateFile); | ||
const conn = flags['target-org'].getConnection(flags['api-version']); | ||
const cv = await file2CV(conn, flags.file, flags.title); | ||
this.logSuccess(messages.getMessage('createSuccess', [cv.ContentDocumentId])); | ||
|
||
if (!flags['parent-id']) { | ||
return cv; | ||
} | ||
|
||
const CDLReq = { | ||
ContentDocumentId: cv.ContentDocumentId, | ||
LinkedEntityId: flags['parent-id'], | ||
ShareType: 'V', | ||
} satisfies CDLCreateRequest; | ||
try { | ||
const CDLCreateResult = await conn.sobject('ContentDocumentLink').create(CDLReq); | ||
|
||
if (CDLCreateResult.success) { | ||
this.logSuccess(messages.getMessage('attachSuccess', [flags['parent-id']])); | ||
return cv; | ||
} else { | ||
throw SfError.create({ | ||
message: messages.getMessage('attachFailure'), | ||
data: CDLCreateResult.errors, | ||
}); | ||
} | ||
} catch (e) { | ||
const error = SfError.wrap(e); | ||
if (error.name === 'INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY') { | ||
error.actions = messages.getMessages('insufficientAccessActions'); | ||
} | ||
throw error; | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
3PP approved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
used by jsforce
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, yes.