diff --git a/src/components/CodeSnippet.tsx b/src/components/CodeSnippet.tsx index a11742ed8..a0209d88c 100644 --- a/src/components/CodeSnippet.tsx +++ b/src/components/CodeSnippet.tsx @@ -63,7 +63,7 @@ export const CodeSnippet = memo( variant="secondary" startIcon="duplicate" onClick={() => { - copyToClipboard(code) + copyToClipboard(code, { ignoreComment: true }) addToast({ severity: 'info', diff --git a/src/core/utils/__tests__/copyToClipboard.test.ts b/src/core/utils/__tests__/copyToClipboard.test.ts index f1c4864bf..c793fce09 100644 --- a/src/core/utils/__tests__/copyToClipboard.test.ts +++ b/src/core/utils/__tests__/copyToClipboard.test.ts @@ -14,4 +14,14 @@ describe('copyToClipboard', () => { 'the text that needs to be copied', ) }) + + it('should filter out comments', () => { + const value = `# comment + the text that needs to be copied` + + copyToClipboard(value, { ignoreComment: true }) + expect(window.navigator.clipboard.writeText).toHaveBeenCalledWith( + 'the text that needs to be copied', + ) + }) }) diff --git a/src/core/utils/__tests__/snippetBuilder.test.ts b/src/core/utils/__tests__/snippetBuilder.test.ts new file mode 100644 index 000000000..7fef586aa --- /dev/null +++ b/src/core/utils/__tests__/snippetBuilder.test.ts @@ -0,0 +1,127 @@ +import { snippetBuilder } from '~/core/utils/snippetBuilder' + +describe('SnippetBuilder', () => { + it('should work properly', () => { + const result = snippetBuilder({ + title: 'My title', + url: `url`, + method: 'POST', + headers: [{ Authorization: 'Bearer ' }, { 'Content-Type': 'application/json' }], + data: { + snippet: { + name: 'name', + code: 'code', + nested: { + nestedName: 'nestedName', + nestedCode: 'nestedCode', + }, + nestedArray: [ + { + id: '1', + name: 'test', + }, + ], + }, + }, + footerComment: 'To use the snippet, don’t forget to edit your __YOUR_API_KEY__', + }) + + expect(result).toBe(`# My title +curl --location --request POST "url" \\ + --header "Authorization: Bearer " \\ + --header "Content-Type: application/json" \\ + --data-raw '{ + "snippet": { + "name": "name", + "code": "code", + "nested": { + "nestedName": "nestedName", + "nestedCode": "nestedCode" + }, + "nestedArray": [ + { + "id": "1", + "name": "test" + } + ] + } + }' + +# To use the snippet, don’t forget to edit your __YOUR_API_KEY__`) + }) + + it('should work properly with no data', () => { + const result = snippetBuilder({ + title: 'My title', + url: `url`, + method: 'POST', + headers: [{ Authorization: 'Bearer ' }, { 'Content-Type': 'application/json' }], + data: {}, + }) + + expect(result).toBe(`# My title +curl --location --request POST "url" \\ + --header "Authorization: Bearer " \\ + --header "Content-Type: application/json" \\ + --data-raw '{}' +`) + }) + + it('should work properly with conditional data and undefined values', () => { + const shouldRender: boolean = false + const state: string = '' + + const result = snippetBuilder({ + title: 'My title', + url: `url`, + method: 'POST', + headers: [{ Authorization: 'Bearer ' }, { 'Content-Type': 'application/json' }], + data: { + snippet: { + ...(!!state && { state: 'active' }), + ...(!!state && { state: null }), + name: undefined, + title: 'title', + nested: { + nestedName: 'nestedName', + nestedCode: 'nestedCode', + }, + nestedArray: [ + { + id: '1', + name: 'test', + }, + ], + ...(shouldRender ? { withFalse: 'true' } : false), + ...(shouldRender ? { withEmptyObj: 'true' } : {}), + ...(shouldRender ? { withText: 'true' } : ''), + ...(shouldRender ? { withUndefined: 'true' } : undefined), + ...(shouldRender ? { withNull: 'true' } : null), + }, + }, + footerComment: 'To use the snippet, don’t forget to edit your __YOUR_API_KEY__', + }) + + expect(result).toBe(`# My title +curl --location --request POST "url" \\ + --header "Authorization: Bearer " \\ + --header "Content-Type: application/json" \\ + --data-raw '{ + "snippet": { + "title": "title", + "nested": { + "nestedName": "nestedName", + "nestedCode": "nestedCode" + }, + "nestedArray": [ + { + "id": "1", + "name": "test" + } + ] + } + }' + +# To use the snippet, don’t forget to edit your __YOUR_API_KEY__`) + }) +}) diff --git a/src/core/utils/copyToClipboard.ts b/src/core/utils/copyToClipboard.ts index e8a782cdb..9303caeb2 100644 --- a/src/core/utils/copyToClipboard.ts +++ b/src/core/utils/copyToClipboard.ts @@ -1,8 +1,20 @@ import { addToast } from '~/core/apolloClient' -export const copyToClipboard: (value: string) => void = (value) => { +const filterComment = (value: string) => { + return value + .split('\n') + .filter((line) => !line.startsWith('#')) + .join('\n') +} + +export const copyToClipboard: (value: string, options?: { ignoreComment?: boolean }) => void = ( + value, + ignoreComment, +) => { + const serializedValue = ignoreComment ? filterComment(value) : value + try { - navigator.clipboard.writeText(value) + navigator.clipboard.writeText(serializedValue) } catch (error) { addToast({ severity: 'danger', diff --git a/src/core/utils/snippetBuilder.ts b/src/core/utils/snippetBuilder.ts new file mode 100644 index 000000000..530837ea7 --- /dev/null +++ b/src/core/utils/snippetBuilder.ts @@ -0,0 +1,50 @@ +const serializeDataToString = (data: DataType): string => { + const extractedDataString = JSON.stringify(data, null, 2) + const extractedDataStringIndented = extractedDataString.replace(/^(?=.)/gm, ' ').trimStart() + + return extractedDataStringIndented +} + +type DataType = Record + +interface CurlCommand { + title: string + url: string + method: 'POST' | 'PUT' + headers: Array> + data: DataType + footerComment?: string +} + +export enum SnippetVariables { + EXTERNAL_CUSTOMER_ID = '__EXTERNAL_CUSTOMER_ID__', + MUST_BE_DEFINED = '__MUST_BE_DEFINED__', + API_KEY = '__YOUR_API_KEY__', +} + +/** + * Helper function to build a curl command snippet + * @returns string + */ +export const snippetBuilder = (curlCommand: CurlCommand): string => { + const { title, url, method, headers, data, footerComment } = curlCommand + + return `\ +# ${title} +curl --location --request ${method} "${url}" \\ +${headers + .map((header) => { + const [key] = Object.keys(header) + + return ` --header "${key}: ${header[key]}" \\` + }) + .join('\n')} + --data-raw '${serializeDataToString(data)}' +${ + footerComment + ? ` +# ${footerComment}` + : '' +}\ +` +}