Skip to content

Commit a858faa

Browse files
add code examples for typescript
1 parent 31f11b4 commit a858faa

File tree

1 file changed

+162
-6
lines changed

1 file changed

+162
-6
lines changed

guides/build-plane-app.mdx

Lines changed: 162 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ Connect everything you already use. Your team probably uses 5-10 different tools
1818

1919
Build exactly what you need. Unlike rigid SaaS platforms, Plane's open-source nature means you can create integrations that fit your specific workflow.
2020

21-
### Promote Your Plane Integration
22-
Contact us to help promote your Plane integration and feature it within blogs and Plane itself: [email protected]
23-
2421
## Prerequisites
2522

2623
- A [Plane](https://app.plane.so) workspace
@@ -56,9 +53,15 @@ To build an OAuth application with Plane:
5653

5754
### Generating Consent URL
5855

59-
Before handling authentication, if your app manages installation, you must generate the consent (authorization) URL to initiate the OAuth flow. Below is a sample Python implementation:
56+
Before handling authentication, if your app manages installation, you must generate the consent (authorization) URL to initiate the OAuth flow. Below are sample implementations:
57+
58+
<Tabs>
59+
<Tab title="Python">
6060

6161
```python
62+
import os
63+
from urllib.parse import urlencode
64+
6265
params = {
6366
"client_id": os.getenv("PLANE_CLIENT_ID"),
6467
"response_type": "code",
@@ -69,6 +72,25 @@ params = {
6972
consent_url = f"https://api.plane.so/auth/o/authorize-app/?{urlencode(params)}"
7073
```
7174

75+
</Tab>
76+
<Tab title="TypeScript">
77+
78+
```typescript
79+
import { URLSearchParams } from 'url';
80+
81+
const params = new URLSearchParams({
82+
client_id: process.env.PLANE_CLIENT_ID!,
83+
response_type: "code",
84+
redirect_uri: process.env.PLANE_REDIRECT_URI!,
85+
// Optional: include state if needed
86+
});
87+
88+
const consentUrl = `https://api.plane.so/auth/o/authorize-app/?${params.toString()}`;
89+
```
90+
91+
</Tab>
92+
</Tabs>
93+
7294
There are two types of authenticated actions your application can perform:
7395

7496
1. **User-authorized actions**: Actions performed on behalf of a user after they grant permission to your app via OAuth.
@@ -82,6 +104,9 @@ When the app is installed, Plane will send an `app_installation_id` as part of t
82104

83105
#### Examples
84106

107+
<Tabs>
108+
<Tab title="Python">
109+
85110
```python
86111
import base64
87112
import requests
@@ -111,12 +136,53 @@ bot_token = response_data['access_token']
111136
expires_in = response_data["expires_in"]
112137
```
113138

139+
</Tab>
140+
<Tab title="TypeScript">
141+
142+
```typescript
143+
import axios from 'axios';
144+
145+
// Prepare basic auth header using client_id and client_secret
146+
const clientId = "your_client_id";
147+
const clientSecret = "your_client_secret";
148+
const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
149+
150+
// Prepare request data
151+
const payload = {
152+
grant_type: "client_credentials",
153+
app_installation_id: appInstallationId
154+
};
155+
156+
// Make a POST request to fetch bot token
157+
const response = await axios.post(
158+
"https://api.plane.so/auth/o/token/",
159+
payload,
160+
{
161+
headers: {
162+
Authorization: `Basic ${basicAuth}`,
163+
"Content-Type": "application/x-www-form-urlencoded"
164+
}
165+
}
166+
);
167+
168+
// Parse the response
169+
const responseData = response.data;
170+
const botToken = responseData.access_token;
171+
const expiresIn = responseData.expires_in;
172+
```
173+
174+
</Tab>
175+
</Tabs>
176+
114177
### User-Authorized Actions (Authorization Code Flow)
115178

116179
In this flow, your app exchanges the `code` received as a query parameter on the callback (to your `redirect_uri`) for an access token and refresh token. The access token is short-lived and must be refreshed using the refresh token when it expires. Both tokens should be securely stored.
117180

118181
#### Examples
119182

183+
<Tabs>
184+
<Tab title="Python">
185+
120186
```python
121187
import requests
122188

@@ -165,12 +231,77 @@ refresh_response_data = refresh_response.json()
165231
access_token = refresh_response_data["access_token"]
166232
```
167233

234+
</Tab>
235+
<Tab title="TypeScript">
236+
237+
```typescript
238+
import axios from 'axios';
239+
240+
// Exchange authorization code for access and refresh tokens
241+
const code = "authorization_code_from_callback";
242+
const clientId = "your_client_id";
243+
const clientSecret = "your_client_secret";
244+
const redirectUri = "your_redirect_uri";
245+
246+
const payload = {
247+
grant_type: "authorization_code",
248+
code: code,
249+
client_id: clientId,
250+
client_secret: clientSecret,
251+
redirect_uri: redirectUri
252+
};
253+
254+
const response = await axios.post(
255+
"https://api.plane.so/auth/o/token/",
256+
payload,
257+
{
258+
headers: {
259+
"Content-Type": "application/x-www-form-urlencoded"
260+
}
261+
}
262+
);
263+
264+
// Parse the response
265+
const responseData = response.data;
266+
const accessToken = responseData.access_token;
267+
const refreshToken = responseData.refresh_token;
268+
const expiresIn = responseData.expires_in;
269+
270+
// When access token expires, use refresh token to get a new access token
271+
const refreshPayload = {
272+
grant_type: "refresh_token",
273+
refresh_token: refreshToken,
274+
client_id: clientId,
275+
client_secret: clientSecret
276+
};
277+
278+
const refreshResponse = await axios.post(
279+
"https://api.plane.so/auth/o/token/",
280+
refreshPayload,
281+
{
282+
headers: {
283+
"Content-Type": "application/x-www-form-urlencoded"
284+
}
285+
}
286+
);
287+
288+
// Parse the refresh response
289+
const refreshResponseData = refreshResponse.data;
290+
const accessToken = refreshResponseData.access_token;
291+
```
292+
293+
</Tab>
294+
</Tabs>
295+
168296
### Fetching App Installation Details
169297

170298
In both user-authorized and app-authorized flows, the `app_installation_id` identifies the app's installation within a specific workspace. It is recommended that developers fetch workspace details after OAuth is successfully completed. Plane provides an `app-installation` endpoint that works with both types of tokens.
171299

172300
#### Examples
173301

302+
<Tabs>
303+
<Tab title="Python">
304+
174305
```python
175306
import requests
176307

@@ -188,6 +319,29 @@ response = requests.get(
188319
workspace_details = response.data[0]
189320
```
190321

322+
</Tab>
323+
<Tab title="TypeScript">
324+
325+
```typescript
326+
import axios from 'axios';
327+
328+
// Set authorization header with either access token or bot token
329+
const headers = {
330+
Authorization: `Bearer ${token}`,
331+
};
332+
333+
// Make GET request to fetch installation/workspace details
334+
const response = await axios.get(
335+
`https://api.plane.so/auth/o/app-installation/?id=${app_installation_id}`,
336+
{ headers }
337+
);
338+
339+
const workspaceDetails = response.data[0];
340+
```
341+
342+
</Tab>
343+
</Tabs>
344+
191345
#### Sample Response
192346

193347
```
@@ -217,8 +371,10 @@ workspace_details = response.data[0]
217371

218372
To simplify the OAuth flow and make it easier to build Plane apps, official SDKs are available:
219373

220-
- **Node.js**: `npm i @makeplane/plane-node-sdk`
221-
- **Python**: `pip install plane-sdk`
374+
| Language | Package Link | Source Code |
375+
|----------|---------|-------------|
376+
| Node.js | [npm i @makeplane/plane-node-sdk](https://www.npmjs.com/package/@makeplane/plane-node-sdk) | [plane-node-sdk](https://github.com/makeplane/plane-node-sdk) |
377+
| Python | [pip install plane-sdk](https://pypi.org/project/plane-sdk/) | [plane-python-sdk](https://github.com/makeplane/plane-python-sdk) |
222378

223379
These SDKs provide helpers for OAuth and other common tasks, allowing developers to implement the above flows efficiently.
224380

0 commit comments

Comments
 (0)