diff --git a/.github/workflows/staging-tests.yml b/.github/workflows/staging-tests.yml new file mode 100644 index 0000000..f008ded --- /dev/null +++ b/.github/workflows/staging-tests.yml @@ -0,0 +1,42 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: Dart run tests when activity on staging + +on: + push: + branches: [ "staging" ] + pull_request: + branches: [ "staging" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + # Note: This workflow uses the latest stable version of the Dart SDK. + # You can specify other versions if desired, see documentation here: + # https://github.com/dart-lang/setup-dart/blob/main/README.md + # - uses: dart-lang/setup-dart@v1 + - uses: dart-lang/setup-dart@9a04e6d73cca37bd455e0608d7e5092f881fd603 + + - name: Install dependencies + run: dart pub get + + # Uncomment this step to verify the use of 'dart format' on each commit. + # - name: Verify formatting + # run: dart format --output=none --set-exit-if-changed . + + # Consider passing '--fatal-infos' for slightly stricter analysis. + - name: Analyze project source + run: dart analyze + + # Your project will need to have tests in test/ and a dependency on + # package:test for this step to succeed. Note that Flutter projects will + # want to change this to 'flutter test'. + - name: Run tests + run: dart test diff --git a/CHANGELOG.md b/CHANGELOG.md index b43026d..24a9109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.4.0 + +- Implemented endpoints for Goals, Guests and Task Checklists + +## 0.3.2 + +- Refactoring and minor fixes. + ## 0.3.0 - Implemented endpoints for Tasks. diff --git a/lib/src/clickup_dart_base.dart b/lib/src/clickup_dart_base.dart index 19dab20..e7efb4f 100644 --- a/lib/src/clickup_dart_base.dart +++ b/lib/src/clickup_dart_base.dart @@ -59,56 +59,29 @@ class ClickUp { void initialize({required String authToken}) async { httpClient = Client(); - auth = ClickUpAuth( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - attachments = ClickUpAttachments( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - comments = ClickUpComments( - endPoint: apiEndpoint, - authToken: auth.authToken, - httpClient: httpClient); - customFields = ClickUpCustomFields( - endPoint: apiEndpoint, - authToken: auth.authToken, - httpClient: httpClient); - folders = ClickUpFolders( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - goals = ClickUpGoals( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - guests = ClickUpGuests( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - lists = ClickUpLists( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - members = ClickUpMembers( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - roles = ClickUpRoles( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - sharedHierarchy = ClickUpSharedHierarchy( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - spaces = ClickUpSpaces( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - tags = ClickUpTags( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - tasks = ClickUpTasks( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - taskChecklists = ClickUpTaskChecklists( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - taskRelationships = ClickUpTaskRelationships( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - taskTemplates = ClickUpTaskTemplates( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - teams = ClickUpTeams( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - timeTrackingLegacy = ClickUpTimeTrackingLegacy( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - timeTrackingV2 = ClickUpTimeTrackingV2( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - users = ClickUpUsers( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - views = ClickUpViews( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); - webhooks = ClickUpWebhooks( - endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + auth = ClickUpAuth(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + attachments = ClickUpAttachments(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + comments = ClickUpComments(endPoint: apiEndpoint, authToken: auth.authToken, httpClient: httpClient); + customFields = ClickUpCustomFields(endPoint: apiEndpoint, authToken: auth.authToken, httpClient: httpClient); + folders = ClickUpFolders(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + goals = ClickUpGoals(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + guests = ClickUpGuests(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + lists = ClickUpLists(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + members = ClickUpMembers(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + roles = ClickUpRoles(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + sharedHierarchy = ClickUpSharedHierarchy(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + spaces = ClickUpSpaces(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + tags = ClickUpTags(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + tasks = ClickUpTasks(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + taskChecklists = ClickUpTaskChecklists(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + taskRelationships = ClickUpTaskRelationships(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + taskTemplates = ClickUpTaskTemplates(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + teams = ClickUpTeams(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + timeTrackingLegacy = ClickUpTimeTrackingLegacy(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + timeTrackingV2 = ClickUpTimeTrackingV2(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + users = ClickUpUsers(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + views = ClickUpViews(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); + webhooks = ClickUpWebhooks(endPoint: apiEndpoint, authToken: authToken, httpClient: httpClient); print("ClickUp Initialized.."); } } diff --git a/lib/src/core/endpoints/auth.dart b/lib/src/core/endpoints/auth.dart index 774e79a..32340d9 100644 --- a/lib/src/core/endpoints/auth.dart +++ b/lib/src/core/endpoints/auth.dart @@ -7,10 +7,7 @@ class ClickUpAuth { late String authToken; late Client httpClient; - ClickUpAuth( - {required this.endPoint, - required this.authToken, - required this.httpClient}); + ClickUpAuth({required this.endPoint, required this.authToken, required this.httpClient}); /// Get access token based on your credentials. // Future getAccessToken( @@ -31,32 +28,28 @@ class ClickUpAuth { /// Get the user bound to the token. Future> getAuthorizedUser() async { try { - final response = await httpClient.get(Uri.parse("$endPoint/user"), - headers: {"Authorization": authToken}); + final response = await httpClient.get(Uri.parse("$endPoint/user"), headers: { + "Authorization": authToken + }); final user = jsonDecode(response.body); return user; } catch (e) { print(e.toString()); - throw ClickUpException( - exceptionType: ClickUpExceptionType.requestError, - exceptionMessage: - "An error occured while making the request. Error is ${e.toString()}"); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); } } /// Get the teams bound to authorized user. Future> getAuthorizedTeams() async { try { - final response = await httpClient.get(Uri.parse("$endPoint/team"), - headers: {"Authorization": authToken}); + final response = await httpClient.get(Uri.parse("$endPoint/team"), headers: { + "Authorization": authToken + }); final teams = jsonDecode(response.body); return teams; } catch (e) { print(e.toString()); - throw ClickUpException( - exceptionType: ClickUpExceptionType.requestError, - exceptionMessage: - "An error occured while making the request. Error is ${e.toString()}"); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); } } } diff --git a/lib/src/core/endpoints/goals.dart b/lib/src/core/endpoints/goals.dart index c55deb7..d392e23 100644 --- a/lib/src/core/endpoints/goals.dart +++ b/lib/src/core/endpoints/goals.dart @@ -1,12 +1,226 @@ +import 'dart:convert'; + import 'package:http/http.dart'; +import '../clickup_exception.dart'; + class ClickUpGoals { late String endPoint; late String authToken; late Client httpClient; - ClickUpGoals( - {required this.endPoint, - required this.authToken, - required this.httpClient}); + ClickUpGoals({required this.endPoint, required this.authToken, required this.httpClient}); + + /// View the Goals available in a Workspace. + + Future> getGoals({required double teamID, bool includeCompleted = false}) async { + try { + final response = await httpClient.get(Uri.parse(includeCompleted ? "$endPoint/team/$teamID/goal?include_completed=true" : "$endPoint/team/$teamID/goal"), headers: { + "Authorization": authToken, + }); + final goals = jsonDecode(response.body); + return goals; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Add a new Goal to a Workspace. + + Future> createGoal({required double teamID, required String goalName, required int dueDate, required String description, required bool multipleOwners, required List owners, required String color}) async { + try { + final response = await httpClient.post(Uri.parse("$endPoint/team/$teamID/goal"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": goalName, + "due_date": dueDate, + "description": description, + "multiple_owners": multipleOwners, + "owners": owners, + "color": color + })); + final goal = jsonDecode(response.body); + return goal; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// View the details of a Goal including its Targets. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + + Future> getGoal({required double goalID}) async { + try { + final response = await httpClient.get(Uri.parse("$endPoint/goal/$goalID"), headers: { + "Authorization": authToken, + }); + final goal = jsonDecode(response.body); + return goal; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Rename a Goal, set the due date, replace the description, add or remove owners, and set the Goal color. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + Future> updateGoal({required double goalID, required String goalName, required int dueDate, required String description, required List ownersToRemove, required List ownersToAdd, required String color}) async { + try { + final response = await httpClient.put(Uri.parse("$endPoint/goal/$goalID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": goalName, + "due_date": dueDate, + "description": description, + "rem_owners": ownersToRemove, + "add_owners": ownersToAdd, + "color": color + })); + final goal = jsonDecode(response.body); + return goal; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Remove a Goal from your Workspace. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + Future> deleteGoal({required double goalID}) async { + try { + final response = await httpClient.delete( + Uri.parse("$endPoint/goal/$goalID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + ); + final goal = jsonDecode(response.body); + return goal; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Add a Target to a Goal. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + /// + /// Type should be one of the ``["number","currency","boolean","percentage","automatic"]`` + + Future> createKeyResult({required double goalID, required String goalName, required List owners, required String type, required int stepsStart, required int stepsEnd, required String unit, required List taskIDs, required List listIDs}) async { + final allowedTypes = [ + "number", + "currency", + "boolean", + "percentage", + "automatic" + ]; + + if (!allowedTypes.contains(type)) { + throw ClickUpException(exceptionType: ClickUpExceptionType.invalidModel, exceptionMessage: "Please use one of the available types. Check function documentation for more information"); + } + + try { + final response = await httpClient.post(Uri.parse("$endPoint/goal/$goalID/key_result"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": goalName, + "owners": owners, + "type": type, + "steps_start": stepsStart, + "steps_end": stepsEnd, + "unit": unit, + "task_ids": taskIDs, + "list_ids": listIDs + })); + final keyResult = jsonDecode(response.body); + return keyResult; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Update a Target. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + /// + /// Type should be one of the ``["number","currency","boolean","percentage","automatic"]`` + + Future> editKeyResult({required double keyResultID, required String goalName, required List owners, required String type, required int stepsStart, required int stepsCurrent, required int stepsEnd, required String unit, required String note, required List taskIDs, required List listIDs}) async { + final allowedTypes = [ + "number", + "currency", + "boolean", + "percentage", + "automatic" + ]; + + if (!allowedTypes.contains(type)) { + throw ClickUpException(exceptionType: ClickUpExceptionType.invalidModel, exceptionMessage: "Please use one of the available types. Check function documentation for more information"); + } + + try { + final response = await httpClient.put(Uri.parse("$endPoint/key_result/$keyResultID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": goalName, + "owners": owners, + "type": type, + "steps_start": stepsStart, + "steps_end": stepsEnd, + "unit": unit, + "task_ids": taskIDs, + "list_ids": listIDs, + "steps_current": stepsCurrent, + "note": note + })); + final keyResult = jsonDecode(response.body); + return keyResult; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Update a Target. + /// + /// Goal ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + /// + /// Type should be one of the ``["number","currency","boolean","percentage","automatic"]`` + + Future> deleteKeyResult({required double keyResultID}) async { + try { + final response = await httpClient.delete( + Uri.parse("$endPoint/key_result/$keyResultID"), + headers: { + "Authorization": authToken, + }, + ); + final keyResult = jsonDecode(response.body); + return keyResult; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } } diff --git a/lib/src/core/endpoints/guests.dart b/lib/src/core/endpoints/guests.dart index f1e4742..29d8595 100644 --- a/lib/src/core/endpoints/guests.dart +++ b/lib/src/core/endpoints/guests.dart @@ -1,12 +1,256 @@ +import 'dart:convert'; + import 'package:http/http.dart'; +import '../clickup_exception.dart'; + class ClickUpGuests { late String endPoint; late String authToken; late Client httpClient; - ClickUpGuests( - {required this.endPoint, - required this.authToken, - required this.httpClient}); + ClickUpGuests({required this.endPoint, required this.authToken, required this.httpClient}); + + /// Invite a guest to join a Workspace. To invite a member to your Workspace, use the Invite User to Workspace endpoint. + /// + /// You'll also need to grant the guest access to specific items using the following endpoints: Add Guest to Folder, Add Guest to List, or Add Guest to Task. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> inviteGuestToWorkspace({required double teamID, required String email, required bool canEditTags, required bool canSeeTimeSpent, required bool canSeeTimeEstimated, required bool canCreateViews, required int customRoleID}) async { + try { + final response = await httpClient.post(Uri.parse("$endPoint/team/$teamID/guest"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "email": email, + "can_edit_tags": canEditTags, + "can_see_time_spent": canSeeTimeSpent, + "can_see_time_estimated": canSeeTimeEstimated, + "can_create_views": canCreateViews, + "custom_role_id": customRoleID + })); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// View information about a guest. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> getGuest({required double teamID, required double guestID}) async { + try { + final response = await httpClient.get(Uri.parse("$endPoint/team/$teamID/guest/$guestID"), headers: { + "Authorization": authToken, + }); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Rename and configure options for a guest. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> editGuestOnWorkspace({required double teamID, required double guestID, required String username, required bool canEditTags, required bool canSeeTimeSpent, required bool canSeeTimeEstimated, required bool canCreateViews, required int customRoleID}) async { + try { + final response = await httpClient.put(Uri.parse("$endPoint/team/$teamID/guest/$guestID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "username": username, + "can_edit_tags": canEditTags, + "can_see_time_spent": canSeeTimeSpent, + "can_see_time_estimated": canSeeTimeEstimated, + "can_create_views": canCreateViews, + "custom_role_id": customRoleID + })); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Revoke a guest's access to a Workspace. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> removeGuestFromWorkspace({required double teamID, required double guestID}) async { + try { + final response = await httpClient.delete(Uri.parse("$endPoint/team/$teamID/guest/$guestID"), headers: { + "Authorization": authToken, + }); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Share a task with a guest. + /// + /// Allowed permissions are ``["read", "comment", "edit", "create"]`` + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> addGuestToTask({required String taskID, required double guestID, required String permissionLevel, bool includeShared = true, bool useCustomTaskID = false, double teamID = 0}) async { + final allowedPermissions = [ + "read", + "comment", + "edit", + "create" + ]; + + if (!allowedPermissions.contains(permissionLevel)) { + throw ClickUpException(exceptionType: ClickUpExceptionType.invalidModel, exceptionMessage: "You need to select one of the allowed permissions for the guest user."); + } + try { + final response = await httpClient.post(Uri.parse(useCustomTaskID ? "$endPoint/task/$taskID/guest/$guestID?include_shared=$includeShared&custom_task_ids=true&team_id=$teamID" : "$endPoint/task/$taskID/guest/$guestID?include_shared=$includeShared"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "permission_level": permissionLevel + })); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Revoke a guest's access to a task. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> removeGuestFromTask({required String taskID, required double guestID, bool includeShared = true, bool useCustomTaskID = false, double teamID = 0}) async { + try { + final response = await httpClient.delete(Uri.parse(useCustomTaskID ? "$endPoint/task/$taskID/guest/$guestID?include_shared=$includeShared&custom_task_ids=true&team_id=$teamID" : "$endPoint/task/$taskID/guest/$guestID?include_shared=$includeShared"), headers: { + "Authorization": authToken + }); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Share a List with a guest. + /// + /// Allowed permissions are ``["read", "comment", "edit", "create"]`` + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> addGuestToList({required double listID, required double guestID, required String permissionLevel, bool includeShared = true}) async { + final allowedPermissions = [ + "read", + "comment", + "edit", + "create" + ]; + + if (!allowedPermissions.contains(permissionLevel)) { + throw ClickUpException(exceptionType: ClickUpExceptionType.invalidModel, exceptionMessage: "You need to select one of the allowed permissions for the guest user."); + } + try { + final response = await httpClient.post(Uri.parse("$endPoint/list/$listID/guest/$guestID?include_shared=$includeShared"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "permission_level": permissionLevel + })); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Revoke a guest's access to a List. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> removeGuestFromList({required double listID, required double guestID, bool includeShared = true}) async { + try { + final response = await httpClient.delete(Uri.parse("$endPoint/list/$listID/guest/$guestID?include_shared=$includeShared"), headers: { + "Authorization": authToken + }); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Share a Folder with a guest. + /// + /// Allowed permissions are ``["read", "comment", "edit", "create"]`` + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> addGuestToFolder({required double folderID, required double guestID, required String permissionLevel, bool includeShared = true}) async { + final allowedPermissions = [ + "read", + "comment", + "edit", + "create" + ]; + + if (!allowedPermissions.contains(permissionLevel)) { + throw ClickUpException(exceptionType: ClickUpExceptionType.invalidModel, exceptionMessage: "You need to select one of the allowed permissions for the guest user."); + } + try { + final response = await httpClient.post(Uri.parse("$endPoint/folder/$folderID/guest/$guestID?include_shared=$includeShared"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "permission_level": permissionLevel + })); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Revoke a guest's access to a Folder. + /// + /// Note: This endpoint is only available to Workspaces on our Enterprise Plan. + + Future> removeGuestFromFolder({required double folderID, required double guestID, bool includeShared = true}) async { + try { + final response = await httpClient.delete(Uri.parse("$endPoint/folder/$folderID/guest/$guestID?include_shared=$includeShared"), headers: { + "Authorization": authToken + }); + final guest = jsonDecode(response.body); + return guest; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } } diff --git a/lib/src/core/endpoints/lists.dart b/lib/src/core/endpoints/lists.dart index 486fa6a..7ab0ed9 100644 --- a/lib/src/core/endpoints/lists.dart +++ b/lib/src/core/endpoints/lists.dart @@ -1,12 +1,85 @@ +import 'dart:convert'; + import 'package:http/http.dart'; +import '../clickup_exception.dart'; + class ClickUpLists { late String endPoint; late String authToken; late Client httpClient; - ClickUpLists( - {required this.endPoint, - required this.authToken, - required this.httpClient}); + ClickUpLists({required this.endPoint, required this.authToken, required this.httpClient}); + + /// View the Lists within a Folder. + + Future> getLists({required double folderID, bool archived = false}) async { + try { + final response = await httpClient.get(Uri.parse("$endPoint/folder/$folderID/list?archived=$archived"), headers: { + "Authorization": authToken, + }); + final goals = jsonDecode(response.body); + return goals; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Add a new List to a Folder. + /// + /// Include a ``assignee`` to assign this List. + /// + /// Status refers to the List color rather than the task Statuses available in the List. + + Future> createList({required double folderID, required String listName, String? content, int? dueDate, bool? dueDateTime, int? priority, int? assignee, String? status}) async { + Map body = { + "name": listName + }; + + content != null + ? body.addAll({ + "content": content + }) + : () => {}; + dueDate != null + ? body.addAll({ + "due_date": dueDate + }) + : () => {}; + dueDateTime != null + ? body.addAll({ + "due_date_time": dueDateTime + }) + : () => {}; + priority != null + ? body.addAll({ + "priority": priority + }) + : () => {}; + assignee != null + ? body.addAll({ + "assignee": assignee + }) + : () => {}; + status != null + ? body.addAll({ + "status": status + }) + : () => {}; + + try { + final response = await httpClient.post(Uri.parse("$endPoint/folder/$folderID/list"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode(body)); + final goals = jsonDecode(response.body); + return goals; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } } diff --git a/lib/src/core/endpoints/task_checklists.dart b/lib/src/core/endpoints/task_checklists.dart index 051ccb9..1c68893 100644 --- a/lib/src/core/endpoints/task_checklists.dart +++ b/lib/src/core/endpoints/task_checklists.dart @@ -1,11 +1,141 @@ +import 'dart:convert'; + import 'package:http/http.dart'; +import '../clickup_exception.dart'; + class ClickUpTaskChecklists { late String endPoint; late String authToken; late Client httpClient; - ClickUpTaskChecklists( - {required this.endPoint, - required this.authToken, - required this.httpClient}); + ClickUpTaskChecklists({required this.endPoint, required this.authToken, required this.httpClient}); + + /// Adds a new checklist on a specific task in your space. + + Future> createChecklist({required String checklistName, required String taskID, bool useCustomTaskID = false, double teamID = 0}) async { + try { + final response = await httpClient.post(Uri.parse(useCustomTaskID ? "$endPoint/task/$taskID/checklist?custom_task_ids=true&team_id=$teamID" : "$endPoint/task/$taskID/checklist"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": checklistName + })); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Rename a task checklist, or reorder a checklist so it appears above or below other checklists on a task. + /// + /// Checklist ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + /// + /// Position refers to the order of appearance of checklists on a task. To set a checklist to appear at the top of the checklists section of a task, use ``"position": 0``. + + Future> editChecklist({required String checklistName, required int position, required String checklistID}) async { + try { + final response = await httpClient.put(Uri.parse("$endPoint/checklist/$checklistID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": checklistName, + "position": position + })); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Delete a checklist from a task. + /// + /// Checklist ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + + Future> deleteChecklist({required String checklistID}) async { + try { + final response = await httpClient.delete(Uri.parse("$endPoint/checklist/$checklistID"), headers: { + "Authorization": authToken, + }); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Add a line item to a task checklist. + /// + /// Checklist ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + + Future> createChecklistItem({required String checklistName, required int assignee, required String checklistID}) async { + try { + final response = await httpClient.post(Uri.parse("$endPoint/checklist/$checklistID/checklist_item"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": checklistName, + "assignee": assignee + })); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Update an individual line item in a task checklist. You can rename it, set the assignee, mark it as resolved, or nest it under another checklist item. + /// + /// Checklist ID and Checklist Item ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + /// + /// To nest a checklist item under another checklist item, include the other item's ``checklistItemId``. + + Future> editChecklistItem({required String checklistName, String assignee = "null", required bool resolved, String parent = "null", required String checklistID, required String checklistItemID}) async { + try { + final response = await httpClient.put(Uri.parse("$endPoint/checklist/$checklistID/checklist_item/$checklistItemID"), + headers: { + "Authorization": authToken, + "Content-Type": "application/json" + }, + body: jsonEncode({ + "name": checklistName, + "assignee": assignee, + "resolved": resolved, + "parent": parent + })); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } + + /// Delete a line item from a task checklist. + /// + /// Checklist ID and Checklist Item ID should be a valid UUID ``(b8a8-48d8-a0c6-b4200788a683)`` + + Future> deleteChecklistItem({required String checklistID, required String checklistItemID}) async { + try { + final response = await httpClient.delete(Uri.parse("$endPoint/checklist/$checklistID/checklist_item/$checklistItemID"), headers: { + "Authorization": authToken, + }); + final checklist = jsonDecode(response.body); + return checklist; + } catch (e) { + print(e.toString()); + throw ClickUpException(exceptionType: ClickUpExceptionType.requestError, exceptionMessage: "An error occured while making the request. Error is ${e.toString()}"); + } + } } diff --git a/pubspec.yaml b/pubspec.yaml index ab6d0c9..14eb33b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: clickup_dart_sdk description: A SDK for ClickUp for integrating or creating applications on top of the product. -version: 0.3.2 +version: 0.4.0 repository: https://github.com/ayazemre/clickup-dart-sdk environment: diff --git a/test/integration/goals_test.dart b/test/integration/goals_test.dart index 5df8964..49da5f9 100644 --- a/test/integration/goals_test.dart +++ b/test/integration/goals_test.dart @@ -1,5 +1,93 @@ +import 'package:clickup_dart_sdk/clickup_dart_sdk.dart'; import 'package:test/test.dart'; void main() { - test('goals ...', () async {}); + late String token; + late ClickUp clickUp; + group('Goals endpoint Tests', () { + setUp(() async { + // Additional setup goes here. + token = "pk_qwerty123456"; + clickUp = ClickUp(apiEndpoint: "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com")..initialize(authToken: token); + }); + + test('Goals - Get Goals', () async { + final goals = await clickUp.goals.getGoals(teamID: 0); + print(goals); + expect(goals.containsKey("goals"), true); + }); + test('Goals - Get Goals Include Completed', () async { + final goals = await clickUp.goals.getGoals(teamID: 0, includeCompleted: true); + print(goals); + expect(goals.containsKey("goals"), true); + }); + + test('Goals - Create Goal', () async { + final goal = await clickUp.goals.createGoal(teamID: 0, color: "red", description: "Hey", dueDate: 123456, goalName: "Test", multipleOwners: true, owners: [ + 123, + 456, + 789 + ]); + print(goal); + expect(goal.containsKey("goal"), true); + }); + test('Goals - Get Goal', () async { + final goal = await clickUp.goals.getGoal(goalID: 0); + print(goal); + expect(goal.containsKey("goal"), true); + }); + + test('Goals - Update Goal', () async { + final goal = await clickUp.goals.updateGoal(goalID: 0, color: "red", description: "Hey", dueDate: 123456, goalName: "Test", ownersToRemove: [ + 123, + 456 + ], ownersToAdd: [ + 789, + 146 + ]); + print(goal); + expect(goal.containsKey("goal"), true); + }); + + test('Goals - Delete Goal', () async { + final goal = await clickUp.goals.deleteGoal(goalID: 0); + print(goal); + expect(goal.isEmpty, true); + }); + test('Goals - Create Key Result', () async { + final keyResult = await clickUp.goals.createKeyResult(goalID: 0, goalName: "test", type: "number", unit: "km", stepsStart: 0, stepsEnd: 2, owners: [ + 0, + 1 + ], taskIDs: [ + "123", + "456" + ], listIDs: [ + "369", + "741" + ]); + print(keyResult); + expect(keyResult.containsKey("key_result"), true); + }); + + test('Goals - Edit Key Result', () async { + final keyResult = await clickUp.goals.editKeyResult(keyResultID: 0, goalName: "test", type: "number", unit: "km", note: "hey", stepsStart: 0, stepsCurrent: 1, stepsEnd: 2, owners: [ + 0, + 1 + ], taskIDs: [ + "123", + "456" + ], listIDs: [ + "369", + "741" + ]); + print(keyResult); + expect(keyResult.containsKey("key_result"), true); + }); + + test('Goals - Delete Key Result', () async { + final keyResult = await clickUp.goals.deleteKeyResult(keyResultID: 0); + print(keyResult); + expect(keyResult.isEmpty, true); + }); + }); } diff --git a/test/integration/guests_test.dart b/test/integration/guests_test.dart index c01774d..5aa900a 100644 --- a/test/integration/guests_test.dart +++ b/test/integration/guests_test.dart @@ -8,16 +8,68 @@ void main() { setUp(() async { // Additional setup goes here. token = "pk_qwerty123456"; - clickUp = ClickUp( - apiEndpoint: - "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com") - ..initialize(authToken: token); + clickUp = ClickUp(apiEndpoint: "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com")..initialize(authToken: token); }); - test('Authorization - Get Authorized User', () async { - final user = await clickUp.auth.getAuthorizedUser(); - print(user); - expect(user.containsKey("user"), true); + test('Guests - Invite Guest To Workspace', () async { + final guest = await clickUp.guests.inviteGuestToWorkspace(teamID: 1, email: "guest@example.com", canSeeTimeSpent: false, canCreateViews: false, canEditTags: false, canSeeTimeEstimated: true, customRoleID: 12345); + print(guest); + expect(guest.containsKey("team"), true); + }); + + test('Guests - Get Guest', () async { + final guest = await clickUp.guests.getGuest(teamID: 1, guestID: 2); + print(guest); + expect(guest.isEmpty, true); + }); + test('Guests - Edit Guest On Workspace', () async { + final guest = await clickUp.guests.editGuestOnWorkspace(teamID: 1, guestID: 2, username: "guest@example.com", canSeeTimeSpent: false, canCreateViews: false, canEditTags: false, canSeeTimeEstimated: true, customRoleID: 12345); + print(guest); + expect(guest.containsKey("guest"), true); + }); + + test('Guests - Remove Guest From Workspace', () async { + final guest = await clickUp.guests.removeGuestFromWorkspace(teamID: 1, guestID: 2); + print(guest); + expect(guest.containsKey("team"), true); + }); + test('Guests - Add Guest To Task', () async { + final guest = await clickUp.guests.addGuestToTask(taskID: "1234", guestID: 2456, permissionLevel: "read"); + print(guest); + expect(guest.containsKey("guest"), true); + }); + test('Guests - Add Guest To Task with Custom Task Id', () async { + final guest = await clickUp.guests.addGuestToTask(taskID: "1", guestID: 2, permissionLevel: "edit", includeShared: false, useCustomTaskID: true); + print(guest); + expect(guest.containsKey("guest"), true); + }); + test('Guests - Remove Guest From Task', () async { + final guest = await clickUp.guests.removeGuestFromTask(taskID: "1", guestID: 2, includeShared: false, useCustomTaskID: true); + print(guest); + expect(guest.containsKey("guest"), true); + }); + + test('Guests - Add Guest To List', () async { + final guest = await clickUp.guests.addGuestToList(listID: 1, guestID: 2, includeShared: false, permissionLevel: "read"); + print(guest); + expect(guest.containsKey("guest"), true); + }); + + test('Guests - Remove Guest From List', () async { + final guest = await clickUp.guests.removeGuestFromList(listID: 1, guestID: 2, includeShared: false); + print(guest); + expect(guest.containsKey("guest"), true); + }); + + test('Guests - Add Guest To Folder', () async { + final guest = await clickUp.guests.addGuestToFolder(folderID: 1, guestID: 2, includeShared: false, permissionLevel: "read"); + print(guest); + expect(guest.containsKey("guest"), true); + }); + test('Guests - Remove Guest From Folder', () async { + final guest = await clickUp.guests.removeGuestFromFolder(folderID: 1, guestID: 2, includeShared: false); + print(guest); + expect(guest.containsKey("guest"), true); }); }); } diff --git a/test/integration/lists_test.dart b/test/integration/lists_test.dart index c01774d..015484f 100644 --- a/test/integration/lists_test.dart +++ b/test/integration/lists_test.dart @@ -8,16 +8,24 @@ void main() { setUp(() async { // Additional setup goes here. token = "pk_qwerty123456"; - clickUp = ClickUp( - apiEndpoint: - "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com") - ..initialize(authToken: token); + clickUp = ClickUp(apiEndpoint: "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com")..initialize(authToken: token); }); - test('Authorization - Get Authorized User', () async { - final user = await clickUp.auth.getAuthorizedUser(); - print(user); - expect(user.containsKey("user"), true); + test('Lists - Get Lists', () async { + final lists = await clickUp.lists.getLists(folderID: 1, archived: false); + print(lists); + expect(lists.containsKey("lists"), true); + }); + + test('Lists - Create List', () async { + final lists = await clickUp.lists.createList(folderID: 1, listName: "test list"); + print(lists); + expect(lists.containsKey("id"), true); + }); + test('Lists - Create List with params', () async { + final lists = await clickUp.lists.createList(folderID: 1, listName: "test list", assignee: 1, content: "test"); + print(lists); + expect(lists.containsKey("id"), true); }); }); } diff --git a/test/integration/task_checklists_test.dart b/test/integration/task_checklists_test.dart index c01774d..7525fab 100644 --- a/test/integration/task_checklists_test.dart +++ b/test/integration/task_checklists_test.dart @@ -4,20 +4,45 @@ import 'package:test/test.dart'; void main() { late String token; late ClickUp clickUp; - group('API endpoint Tests', () { + group('Task Checklists endpoint Tests', () { setUp(() async { // Additional setup goes here. token = "pk_qwerty123456"; - clickUp = ClickUp( - apiEndpoint: - "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com") - ..initialize(authToken: token); + clickUp = ClickUp(apiEndpoint: "https://a00fb6e0-339c-4201-972f-503b9932d17a.remockly.com")..initialize(authToken: token); }); - test('Authorization - Get Authorized User', () async { - final user = await clickUp.auth.getAuthorizedUser(); - print(user); - expect(user.containsKey("user"), true); + test('Task Checklists - Create a checklist', () async { + final checklist = await clickUp.taskChecklists.createChecklist(checklistName: "Test", taskID: "asdf123asde"); + print(checklist); + expect(checklist.containsKey("checklist"), true); + }); + + test('Task Checklists - Edit a checklist', () async { + final checklist = await clickUp.taskChecklists.editChecklist(checklistID: "b8a8-48d8-a0c6-b4200788a683", checklistName: "New Name", position: 0); + print(checklist); + expect(checklist.isEmpty, true); + }); + + test('Task Checklists - Delete a checklist', () async { + final checklist = await clickUp.taskChecklists.deleteChecklist(checklistID: "b8a8-48d8-a0c6-b4200788a683"); + print(checklist); + expect(checklist.isEmpty, true); + }); + test('Task Checklists - Create a checklist item', () async { + final checklist = await clickUp.taskChecklists.createChecklistItem(checklistName: "New Name", assignee: 123, checklistID: "b8a8-48d8-a0c6-b4200788a683"); + print(checklist); + expect(checklist.containsKey("checklist"), true); + }); + + test('Task Checklists - Edit a checklist item', () async { + final checklist = await clickUp.taskChecklists.editChecklistItem(checklistName: "New Name", assignee: "123", resolved: false, parent: "test", checklistItemID: "b8a8-48d8-a0c6-b4200788a683", checklistID: "b8a8-48d8-a0c6-b4200788a683"); + print(checklist); + expect(checklist.containsKey("checklist"), true); + }); + test('Task Checklists - Edit a checklist item', () async { + final checklist = await clickUp.taskChecklists.deleteChecklistItem(checklistItemID: "b8a8-48d8-a0c6-b4200788a683", checklistID: "b8a8-48d8-a0c6-b4200788a683"); + print(checklist); + expect(checklist.isEmpty, true); }); }); }