Skip to content

Commit

Permalink
Add "(Gitlab Project) Token" to commit file directly to GitLab (#307)
Browse files Browse the repository at this point in the history
* Add "Gitlab Project" Auth Type on exportUrl UI

* implement gitlab api

* fix base64 and upload issue

* address comments on package.json and urlExport.ts

* Delete dist/plugin.js

---------

Co-authored-by: Lukas Oppermann <[email protected]>
  • Loading branch information
tatashidayat and lukasoppermann authored Jun 28, 2024
1 parent 2861fa9 commit 954e74a
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 21 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
"version": "6.10.5",
"description": "Export design tokens from Figma",
"main": "plugin.js",
"engines": {
"node": "~18.20.3"
},
"repository": {
"type": "git",
"url": "https://github.com/lukasoppermann/design-tokens.git"
Expand Down
1 change: 1 addition & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export default {
authType: {
token: 'token',
gitlabToken: 'gitlab_token',
gitlabCommit: 'gitlab_commit',
basic: 'Basic',
bearer: 'Bearer'
}
Expand Down
25 changes: 24 additions & 1 deletion src/ui/components/UrlExportSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const UrlExportSettings = () => {
type='text'
required
pattern='^https://.*'
placeholder='https://api.github.com/repos/:username/:repo/dispatches'
placeholder= {(settings.authType === config.key.authType.gitlabCommit && 'https://gitlab.com/api/v4/projects/:projectId')|| 'https://api.github.com/repos/:username/:repo/dispatches'}
value={settings.serverUrl}
onChange={value => updateSettings(draft => { draft.serverUrl = value })}
/>
Expand Down Expand Up @@ -156,6 +156,10 @@ export const UrlExportSettings = () => {
label: '(Gitlab) token',
value: config.key.authType.gitlabToken
},
{
label: '(Gitlab) Project Token',
value: config.key.authType.gitlabCommit
},
{
label: 'Basic authentication',
value: config.key.authType.basic
Expand Down Expand Up @@ -198,6 +202,25 @@ export const UrlExportSettings = () => {
</Row>
</>}

{config.key.authType.gitlabCommit === settings.authType &&
<>
<h3>Branch<Info
width={150}
label='The branch where the file will be committed. Only used when Gitlab Project selected for "Auth type"'
/>
</h3>
<Row fill>
<Input
type='text'
required
pattern='\S+'
placeholder='main'
value={settings.reference}
onChange={value => updateSettings(draft => { draft.reference = value })}
/>
</Row>
</>}

<Separator />
<Title size='xlarge' weight='bold'>About This Export</Title>
<h3>Commit Message<Info width={200} label='Typically this will be a "commit message" for Git. Your organization may require a specific convention for these messages.' /></h3>
Expand Down
122 changes: 122 additions & 0 deletions src/ui/modules/gitlabRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { utf8ToBase64 } from "@src/utilities/base64";
import {
urlExportRequestBody,
urlExportSettings,
} from "@typings/urlExportData";

export class GitlabRepository {
baseUrl: string;
token: string;

constructor(props: { baseUrl: string; token: string }) {
this.baseUrl = props.baseUrl;
this.token = props.token;
}

async upload(
{ client_payload: clientPayload }: urlExportRequestBody,
{ reference: branch }: urlExportSettings,
responseHandler: {
onError: () => void;
onLoaded: (request: XMLHttpRequest) => void;
}
) {
const encodedContent = utf8ToBase64(clientPayload.tokens);
const encodedFilepath = encodeURIComponent(clientPayload.filename);

let isFileExist: boolean;
try {
isFileExist = await this._checkFile(encodedFilepath, branch);
} catch (error) {
if (error && error.request && error.code === 401) {
responseHandler.onLoaded(error.request);
}
return;
}

const uploadRequest = new XMLHttpRequest();
uploadRequest.onerror = (_err) => responseHandler.onError();
uploadRequest.onload = (event) =>
responseHandler.onLoaded(event.target as XMLHttpRequest);

this._uploadFile({
request: uploadRequest,
content: encodedContent,
commitMessage: clientPayload.commitMessage,
filepath: encodedFilepath,
branch: branch,
isFileExist: isFileExist,
});
}

private _checkFile(
encodedFilepath: string,
branch: string
): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
const request = new XMLHttpRequest();
request.open(
"GET",
`${this.baseUrl}/repository/files/${encodedFilepath}?ref=${branch}`
);
this._setRequestHeader(request);

request.onreadystatechange = (_ev: ProgressEvent) => {
if (request.readyState !== XMLHttpRequest.DONE) {
return;
}

const statusCode = request.status;
if (statusCode === 200) {
resolve(true);
return;
}

if (statusCode === 404) {
resolve(false);
return;
}

reject({
code: statusCode,
message: request.response,
request: request,
});
};

request.send();
});
}

private _uploadFile(args: {
request: XMLHttpRequest;
filepath: string;
content: string;
commitMessage: string;
branch: string;
isFileExist: boolean;
}) {
const { isFileExist, request, branch, content, commitMessage, filepath } = args;

const body = {
branch: branch,
content: content,
commit_message: commitMessage || `Design token update at ${Date.now()}`,
encoding: "base64",
};
const encodedFilepath = encodeURIComponent(filepath);

request.open(
isFileExist ? "PUT" : "POST",
`${this.baseUrl}/repository/files/${encodedFilepath}`
);
this._setRequestHeader(request);

request.send(JSON.stringify(body));
}

private _setRequestHeader(request: XMLHttpRequest) {
request.setRequestHeader("Authorization", `Bearer ${this.token}`);
request.setRequestHeader("Content-Type", `application/json`);
}
}
68 changes: 48 additions & 20 deletions src/ui/modules/urlExport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { commands } from '@config/commands'
import config from '@config/config'
import { PluginMessage } from '@typings/pluginEvent'
import { urlExportRequestBody, urlExportSettings } from '@typings/urlExportData'
import { GitlabRepository } from './gitlabRepository'

const responeHandler = (request: XMLHttpRequest): string => {
const responseHandler = (request: XMLHttpRequest): string => {
// 401
if (request.status === 401) {
return '🚨 401: Check your access token'
Expand Down Expand Up @@ -40,31 +41,45 @@ const addUrlExportRequestHeaders = (request: XMLHttpRequest, exportSettings: url
}
}

const addUrlExportRequestEvents = (request: XMLHttpRequest) => {
// on error
request.onerror = (event) => {
// @ts-ignore
parent.postMessage({
function requestErrorHandler() {
parent.postMessage(
{
pluginMessage: {
command: commands.closePlugin,
payload: {
notification: '🚨 An error occurred while sending the tokens: check your settings & your server.'
}
} as PluginMessage
}, '*')
}
// show message on successful push
request.onload = (progressEvent: ProgressEvent) => {
// @ts-ignore
parent.postMessage({
notification:
"🚨 An error occurred while sending the tokens: check your settings & your server.",
},
} as PluginMessage,
},
"*"
);
}

function requestLoadedHandler(request: XMLHttpRequest) {
// @ts-ignore
parent.postMessage(
{
pluginMessage: {
command: commands.closePlugin,
payload: {
notification: responeHandler(progressEvent.target as XMLHttpRequest)
}
} as PluginMessage
}, '*')
}
notification: responseHandler(request),
},
} as PluginMessage,
},
"*"
);
}

const addUrlExportRequestEvents = (request: XMLHttpRequest) => {
// on error
request.onerror = (_event) => {
requestErrorHandler();
};
// show message on successful push
request.onload = (progressEvent: ProgressEvent) => {
requestLoadedHandler(progressEvent.target as XMLHttpRequest);
};
}

const generateUrlExportRequestBody = (exportSettings: urlExportSettings, requestBody: urlExportRequestBody) => {
Expand Down Expand Up @@ -96,6 +111,19 @@ const urlExport = (parent, exportSettings: urlExportSettings, requestBody: urlEx
} as PluginMessage
}, '*')
}

if (exportSettings.authType === config.key.authType.gitlabCommit) {
const gitlabRepo = new GitlabRepository({
baseUrl: exportSettings.url,
token: exportSettings.accessToken,
});
gitlabRepo.upload(requestBody, exportSettings, {
onError: requestErrorHandler,
onLoaded: requestLoadedHandler,
});
return;
}

// init request
const request = new XMLHttpRequest()
// send to user defined url
Expand Down
9 changes: 9 additions & 0 deletions src/utilities/base64.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const utf8ToBase64 = (text: string): string => {
const utf8EncodedBytes = new TextEncoder().encode(text)
const binString = Array.from(utf8EncodedBytes, (byte) =>
String.fromCodePoint(byte)
).join('')
return btoa(binString)
}

export { utf8ToBase64 }

0 comments on commit 954e74a

Please sign in to comment.