! This is a fork to fix an issue #33
PHP package to interface with ClickUp. We strongly encourage you to review ClickUp's API docs to get a feel for what this package can do, as we are just wrapping their API.
We solely use Laravel for our applications, so this package is written with Laravel in mind. We have tried to make it work outside of Laravel. If there is a request from the community to split this package into 2 parts, then we will consider doing that work.
Branch | Status | Coverage | Code Quality |
---|---|---|---|
Develop | |||
Master |
Install ClickUp PHP Package via Composer:
$ composer require spinen/laravel-clickup
The package uses the auto registration feature of Laravel.
-
You will need to make your
User
object implement includes theSpinen\ClickUp\Concerns\HasClickUp
trait which will allow it to access the Client as an attribute like this:$user->clickup
<?php namespace App; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Spinen\ClickUp\Concerns\HasClickUp; class User extends Authenticatable { use HasClickUp, Notifiable; // ... }
-
Add the appropriate values to your
.env
fileCLICKUP_CLIENT_ID=<Application ID, if using OAuth to generate user tokens> CLICKUP_CLIENT_SECRET=<Application Secret, if using OAuth to generate user tokens> CLICKUP_OAUTH_URL=<url to clickup OAuth flow, default is v2> CLICKUP_URL=<url to clickup API, default is v2>
-
[Optional] Publish config & migration
A configuration file named
clickup.php
can be published toconfig/
by running...php artisan vendor:publish --tag=clickup-config
Migrations files can be published by running...
php artisan vendor:publish --tag=clickup-migrations
You'll need the migration to set the ClickUp API token on your
User
model.
To get a Spinen\ClickUp\Api\Client
instance...
$ psysh
Psy Shell v0.9.9 (PHP 7.3.11 — cli) by Justin Hileman
>>> $configs = [
"oauth" => [
"id" => "<client_id>", // if using OAuth
"secret" => "<client_secret>", // If using OAuth
"url" => "https://app.clickup.com/api",
],
"route" => [
"enabled" => true,
"middleware" => [
"web",
],
"sso" => "clickup/sso",
],
"url" => "https://api.clickup.com/api/v2",
];
>>> $guzzle = new GuzzleHttp\Client();
=> GuzzleHttp\Client {#2379}
>>> $clickup = new Spinen\ClickUp\Api\Client($configs, $guzzle) // Optionally, pass the token as 3rd parameter
=> Spinen\ClickUp\Api\Client {#2363}
>>> $clickup->setToken('<a token>') // Skip if passed in via constructor
=> Spinen\ClickUp\Api\Client {#2363}
The $clickup
instance will work exactly like all of the examples below, so if you are not using Laravel, then you can use the package once you bootstrap the client.
ClickUp has 2 ways to authenticate when making API calls... 1) OAuth token or 2) Personal Token. Either method uses a token that is saved to the clickup_token
property on the User
model.
There is a middleware named clickup
that you can apply to any route that verifies that the user has a clickup_token
, and if the user does not, then it redirects the user to ClickUp's OAuth page with the client_id
where the user selects the team(s) to link with your application. Upon selecting the team(s), the user is redirected to /clickup/sso/<user_id>?code=<OAuth Code>
where the system converts the code
to a token & saves it to the user. Upon saving the clickup_token
, the user is redirected to the initial page that was protected by the middleware.
NOTE: You will need to have the
auth
middleware on the routes as theUser
is needed to see if there is aclickup_token
.
If you do not want to use the clickup
middleware to start the OAuth flow, then you can use the oauthUri
on the Client
to generate the link for the user...
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.11 — cli) by Justin Hileman
>>> $clickup = app(Spinen\ClickUp\Api\Client::class)
=> Spinen\ClickUp\Api\Client {#3035}
>>> $clickup->oauthUri(route('clickup.sso.redirect_url', <user_id>))
=> "https://app.clickup.com/api?client_id=<client_id>&redirect_uri=https%3A%2F%2F<your.host>2Fclickup%2Fsso%2F<user_id>"
>>>
NOTE: At this time, there is not a way to remove a token that has been invalidated, so you will need to delete the
clickup_token
on the user to restart the flow.
If you do not want to use the OAuth flow, then you can allow the user to provide you a personal token that you can save on the User
.
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.3.11 — cli) by Justin Hileman
>>> $user = App\User::find(1)
=> App\User {#3040
id: 1,
first_name: "Bob",
last_name: "Tester",
email: "bob.tester@example.com",
email_verified_at: null,
created_at: "2019-11-15 19:49:01",
updated_at: "2019-11-15 19:49:01",
logged_in_at: "2019-11-15 19:49:01",
deleted_at: null,
}
>>> $user->clickup_token = '<personal token>';
=> "<personal token>"
>>> $user->save()
=> true
The primary class is Spinen\ClickUp\Client
. It gets constructed with 3 parameters...
-
array $configs
- Configuration properties. See theclickup.php
file in the./config
directory for a documented list of options. -
Guzzle $guzzle
- Instance ofGuzzleHttp\Client
-
Token $token
- [Optional] String of the user's token
Once you new up a Client
instance, you have the following methods...
-
delete($path)
- Shortcut to therequest()
method with 'DELETE' as the last parameter -
get($path)
- Shortcut to therequest()
method with 'GET' as the last parameter -
oauthRequestTokenUsingCode($code)
- Request a token from the OAuth code -
oauthUri($url)
- Build the URI to the OAuth page with the redirect_url set to$url
-
post($path, array $data)
- Shortcut to therequest()
method with 'POST' as the last parameter -
put($path, array $data)
- Shortcut to therequest()
method with 'PUT' as the last parameter -
request($path, $data = [], $method = 'GET')
- Make an API call to ClickUp to$path
with the$data
using the JWT for the logged in user. -
setConfigs(array $configs)
- Allow overriding the$configs
on theClient
instance. -
setToken($token)
- Set the token for the ClickUp API -
uri($path = null, $url = null)
- Generate a full uri for the path to the ClickUp API.
The Client is meant to emulate Laravel's models with Eloquent. When working with ClickUp resources, you can access properties and relationships just like you would in Laravel.
By running the migration included in this package, your User
class will have a clickup_token
column on it. When you set the user's token, it is encrypted in your database with Laravel's encryption methods. After setting the ClickUp API token, you can access the Client object through $user->clickup
.
$ php artisan tinker
Psy Shell v0.9.9 (PHP 7.2.19 — cli) by Justin Hileman
>>> $user = User::find(1);
=> App\User {#3631
id: 1,
first_name: "Bob",
last_name: "Tester",
email: "bob.tester@example.com",
email_verified_at: null,
created_at: "2019-11-15 19:49:01",
updated_at: "2019-11-15 19:49:01",
logged_in_at: "2019-11-15 19:49:01",
deleted_at: null,
}
>>> // NOTE: Must have a clickup_token via one of the 2 ways in the Authentication section
>>> $user->clickup;
=> Spinen\ClickUp\Api\Client {#3635}
The API responses are cast into models with the properties cast into the types as defined in the ClickUp API documentation. You can review the models in the src/
folder. There is a property named casts
on each model that instructs the Client on how to cast the properties from the API response. If the casts
property is empty, then the properties are not defined in the API docs, so an array is returned.
>>> $team = $user->clickUp()->teams->first();
=> Spinen\ClickUp\Team {#3646
+exists: true,
+incrementing: false,
+parentModel: null,
+timestamps: false,
}
>>> $team->toArray(); // Calling toArray() is allowed just like in Laravel
=> [
"id" => <7 digit ClickUp ID>,
"name" => "SPINEN",
"color" => "#2980B9",
"avatar" => <URL to avatar>,
"members" => [
[
// Keeps going
Some of the responses have links to the related resources. If a property has a relationship, you can call it as a method and the additional calls are automatically made & returned. The value is stored in place of the original data, so once it is loaded it is cached.
$folder = $team->spaces->first()->folders->first();
=> Spinen\ClickUp\Folder {#3632
+exists: true,
+incrementing: false,
+parentModel: Spinen\ClickUp\Space {#3658
+exists: true,
+incrementing: false,
+parentModel: Spinen\ClickUp\Team {#3645
+exists: true,
+incrementing: false,
+parentModel: null,
+timestamps: false,
},
+timestamps: false,
},
+timestamps: false,
}
>>> $folder->lists->count();
=> 5
>>> $folder->lists->first()->name;
=> "Test Folder"
You may also call these relationships as attributes, and the Client will return a Collection
for you (just like Eloquent).
>>> $folder->lists;
=> Spinen\ClickUp\Support\Collection {#3650
all: [
Spinen\ClickUp\TaskList {#3636
+exists: true,
+incrementing: false,
+parentModel: Spinen\ClickUp\Space {#3658
+exists: true,
+incrementing: false,
+parentModel: Spinen\ClickUp\Team {#3645
+exists: true,
+incrementing: false,
+parentModel: null,
+timestamps: false,
},
+timestamps: false,
},
+timestamps: false,
},
// Keeps going
You can do advanced filters by using where
on the models
>>> $team->tasks()->where('space_ids', ['space_id_1', 'space_id_2'])->where('assignees', ['assignee1', 'assignee2'])->get()->count();
=> 100
// If there are more than 100 results, they will be paginated. Pass in another parameter to get another page:
>>> $team->tasks()->where....->where('page', 2)->get();
NOTE: The API has a page size of
100
records, so to get to the next page you use thewhere
method...->where('page', 3)
>>> $team = $user->clickUp()->teams->first();
=> Spinen\ClickUp\Team {#3646
+exists: true,
+incrementing: false,
+parentModel: null,
+timestamps: false,
}
>>> $first_space = $team->spaces->first();
=> Spinen\ClickUp\Space {#3695
+exists: true,
+incrementing: false,
+parentModel: Spinen\ClickUp\Team {#3646
+exists: true,
+incrementing: false,
+parentModel: null,
+timestamps: false,
},
+timestamps: false,
}
>>> $folder = $first_space->folders->first()->toArray();
=> [
"id" => <7 digit ClickUp ID>,
"name" => "Test folder",
"orderindex" => 3.0,
"override_statuses" => true,
"hidden" => false,
"task_count" => 79,
"archived" => false,
"lists" => [
// Keeps going
// TODO: Document known issues as we find them