From a98498196e94e34cfd140898dfa27aa22a6fbb82 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 18:43:11 +0500 Subject: [PATCH 1/6] CUR-4163-add background job for publishing --- .../Api/V1/MicroSoftTeamController.php | 47 ++++++---- .../V1/MSTeamCreateAssignmentRequest.php | 3 +- app/Jobs/PublishProject.php | 77 ++++++++++++++++ .../ProjectPublishNotification.php | 92 +++++++++++++++++++ .../MicrosoftTeam/MicrosoftTeamRepository.php | 65 ++++++++++++- .../MicrosoftTeamRepositoryInterface.php | 7 +- 6 files changed, 263 insertions(+), 28 deletions(-) create mode 100644 app/Jobs/PublishProject.php create mode 100644 app/Notifications/ProjectPublishNotification.php diff --git a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php index 071dadfdc..954026526 100644 --- a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php +++ b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php @@ -19,6 +19,7 @@ use App\Models\Playlist; use App\Models\Project; use App\Models\Activity; +use App\Jobs\PublishProject; use App\User; use Redirect; @@ -201,11 +202,12 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) $authUser = auth()->user(); $token = $authUser->msteam_access_token; $response = json_decode($this->microsoftTeamRepository->createMsTeamClass($token, $data),true); - + if($response['code'] === 202) { return response([ 'message' => 'Class have been created successfully', - 'classId' => $response['classId'] + 'classId' => $response['classId'], + 'aSyncUrl'=> $response['aSyncURL'], ], 200); } @@ -225,7 +227,7 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) * * @response 200 { * "message": [ - * "Project has been published successfully." + * "Your request to publish project [project->name] into MS Team has been received and is being processed." * ] * } * @@ -236,8 +238,21 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) * } * * @response 500 { - * "errors": "MS Team error message", - * "statusCode" : MS team status code + * "message": "The given data was invalid.", + * "errors": { + * "aSyncUrl": [ + * "The a sync url field is required." + * ] + * } + * } + * + * @response 500 { + * "message": "The given data was invalid.", + * "errors": { + * "aSyncUrl": [ + * "The classId field is required." + * ] + * } * } * * @param MSTeamCreateAssignmentRequest $createAssignmentRequest @@ -246,28 +261,22 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) */ public function publishProject(MSTeamCreateAssignmentRequest $createAssignmentRequest, Project $project) { - $createAssignmentRequest->validated(); + //$createAssignmentRequest->validated(); if(!$project->shared) { // temporary check will remove it in future return response([ 'errors' => 'Project must be shared as we are temporarily publishing the shared link.', ], 500); } - $authUser = auth()->user(); - $token = $authUser->msteam_access_token; $classId = $createAssignmentRequest->get('classId'); - - $response = json_decode($this->microsoftTeamRepository->createMSTeamAssignment($token, $classId, $project), true); - - if($response['code'] === 201) { - return response([ - 'message' => 'Project has been published successfully.', - ], 200); - } + // pushed cloning of project in background + PublishProject::dispatch(auth()->user(), $project, $classId)->delay(now()->addSecond()); + return response([ - 'errors' => $response['message'], - 'statusCode' => $response['code'] - ], 500); + 'message' => "Your request to publish project [$project->name] into MS Team has been received and is being processed.
+ You will be alerted in the notification section in the title bar when complete.", + ], 200); + } } diff --git a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php index 40774e7d2..6e3c4fc83 100644 --- a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php +++ b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php @@ -24,7 +24,8 @@ public function authorize() public function rules() { return [ - 'classId' => 'required|string|max:255' + //'classId' => 'required|string|max:255', + //'aSyncUrl'=> 'required|string|max:255', ]; } } diff --git a/app/Jobs/PublishProject.php b/app/Jobs/PublishProject.php new file mode 100644 index 000000000..bf863a72a --- /dev/null +++ b/app/Jobs/PublishProject.php @@ -0,0 +1,77 @@ +user = $user; + $this->project = $project; + $this->classId = $classId; + $this->aSyncUrl = ''; + } + + /** + * Execute the job. + * + * @return void + */ + public function handle(MicrosoftTeamRepositoryInterface $microsoftTeamRepository) + { + try { + if(empty($this->classId)) { + $response = $microsoftTeamRepository->createMsTeamClass($this->user->msteam_access_token, ['displayName'=>$this->project->name]); + \Log::info($response); + $class = json_decode($response, true); + $this->classId = $class['classId']; + $this->aSyncUrl = $class['aSyncURL']; + } + + $microsoftTeamRepository->createMSTeamAssignment($this->user->msteam_access_token, $this->classId, $this->project, $this->aSyncUrl); + $userName = rtrim($this->user->first_name . ' ' . $this->user->last_name, ' '); + + $this->user->notify(new ProjectPublishNotification($userName, $this->project->name)); + } catch (\Exception $e) { + \Log::error($e->getMessage()); + } + } +} diff --git a/app/Notifications/ProjectPublishNotification.php b/app/Notifications/ProjectPublishNotification.php new file mode 100644 index 000000000..019c757e0 --- /dev/null +++ b/app/Notifications/ProjectPublishNotification.php @@ -0,0 +1,92 @@ +userName = $userName; + $this->projectName = $projectName; + + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return [ 'database']; + } + + /* + * G*et the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toDatabase($notifiable) + { + $message = "Project [$this->projectName] has been published into Microsoft Team successfully."; + + + return [ + 'message' => $message, + 'project' => $this->projectName, + + ]; + } + + + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } + + /** + * Return broadcast message type + * @return string + */ + public function broadcastType() + { + return 'Publish Notification'; + } +} diff --git a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php index 63335f8a3..d3c7a0c03 100644 --- a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php +++ b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php @@ -102,8 +102,9 @@ public function getLoginUrl($gid) */ public function getClassesList($token) { - - $apiURL = $this->landingUrl . 'education/classes'; + $accountId = $this->getUserDetails($token); + + $apiURL = $this->landingUrl . 'users/' . $accountId . '/joinedTeams'; $headers = [ 'Content-length' => 0, 'Content-type' => 'application/json', @@ -114,6 +115,7 @@ public function getClassesList($token) $statusCode = $response->status(); $responseBody = json_decode($response->getBody(), true); + return $responseBody['value']; } @@ -135,10 +137,11 @@ public function createMsTeamClass($token, $data) $response = Http::withHeaders($headers)->withOptions(["verify"=>false])->post($apiURL, $data); $statusCode = $response->status(); - + \Log::info($statusCode); $returnArr = [ "code" => $statusCode, "classId" => ($statusCode === 202) ? $this->fetchClassIdFromHeader($response->getHeaders()) : Null, + "aSyncURL" => ($statusCode === 202) ? $response->getHeaders()['Location'][0] : Null, ]; return json_encode($returnArr); } @@ -147,11 +150,17 @@ public function createMsTeamClass($token, $data) * @param $token string * @param $classId string * @param $project Project + * @param $aSyncUrl string */ - public function createMSTeamAssignment($token, $classId, $project) + public function createMSTeamAssignment($token, $classId, $project, $aSyncUrl) { + \Log::info('in createMSTeamAssignment'); + if(!empty($aSyncUrl)) { + $this->checkClassAsyncStatus($aSyncUrl, $token); // Check if class fully functional + } + $apiURL = $this->landingUrl . 'education/classes/' . $classId . '/assignments'; - + $playlists = $project->playlists; foreach ($playlists as $playlist) { @@ -211,6 +220,34 @@ public function createMSTeamAssignment($token, $classId, $project) return json_encode($returnArr); } + /** + * @param string $aSyncUrl + * @param string $token + */ + private function checkClassAsyncStatus($aSyncUrl, $token) + { + \Log::info('in checkClassAsyncStatus'); + if($aSyncUrl !== Null) { + $apiURL = $this->landingUrl . $aSyncUrl; + $headers = [ + 'Content-length' => 0, + 'Content-type' => 'application/json', + 'Authorization' => 'Bearer ' . $token + ]; + + $response = Http::withHeaders($headers)->get($apiURL); + if ($response->status() === 200) { + $responseBody = json_decode($response->getBody(), true); + if($responseBody['status'] === "succeeded") { + return $responseBody['status']; + }else { + sleep(30); + $this->checkClassAsyncStatus($aSyncUrl, $token); + } + } + } + } + /** * @param string $header * @return string @@ -220,4 +257,22 @@ private function fetchClassIdFromHeader($header) // Can implement teamsAsyncOperation if needed return substr($header['Content-Location'][0], 8, 36); } + + /** + * @param string $token + */ + private function getUserDetails($token) + { + $apiURL = $this->landingUrl . '/me'; + $headers = [ + 'Content-length' => 0, + 'Content-type' => 'application/json', + 'Authorization' => 'Bearer ' . $token + ]; + + $response = Http::withHeaders($headers)->get($apiURL); + $responseBody = json_decode($response->getBody(), true); + return $responseBody['id']; + } + } diff --git a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php index 1136c5061..dc9f4d3be 100644 --- a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php +++ b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php @@ -36,10 +36,11 @@ public function getClassesList($token); */ public function createMsTeamClass($token, $data); - /** + /** * @param $token string * @param $classId string - * @param $activity Activity + * @param $project Project + * @param $aSyncUrl string */ - public function createMSTeamAssignment($token, $classId, $activity); + public function createMSTeamAssignment($token, $classId, $project, $aSyncUrl); } From c08f049269d80dd6a53e53bb76d5215668c49f70 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 18:52:20 +0500 Subject: [PATCH 2/6] CUR-4163 PR review changes --- app/Http/Controllers/Api/V1/MicroSoftTeamController.php | 4 ++-- app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php index 954026526..1eed9b420 100644 --- a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php +++ b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php @@ -223,7 +223,7 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) * Publish the project activities as an assignment * * @urlParam Project $project required The Id of a project. Example: 9 - * @bodyParam classId required string Id of the class. Example: Test Class + * @bodyParam classId optional string Id of the class. Example: bebe45d4-d0e6-4085-b418-e98a51db70c3 * * @response 200 { * "message": [ @@ -261,7 +261,7 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) */ public function publishProject(MSTeamCreateAssignmentRequest $createAssignmentRequest, Project $project) { - //$createAssignmentRequest->validated(); + $createAssignmentRequest->validated(); if(!$project->shared) { // temporary check will remove it in future return response([ diff --git a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php index 6e3c4fc83..49541ce4f 100644 --- a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php +++ b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php @@ -24,8 +24,8 @@ public function authorize() public function rules() { return [ - //'classId' => 'required|string|max:255', - //'aSyncUrl'=> 'required|string|max:255', + 'classId' => 'string|max:255', + ]; } } From fdd311deac6717eb091612cb6792f68cd4ce8aaa Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 18:53:51 +0500 Subject: [PATCH 3/6] CUR-4163 PR review changes --- app/Http/Controllers/Api/V1/MicroSoftTeamController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php index 1eed9b420..64b810dbd 100644 --- a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php +++ b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php @@ -227,7 +227,7 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) * * @response 200 { * "message": [ - * "Your request to publish project [project->name] into MS Team has been received and is being processed." + * "Your request to publish project [project->name] into MS Team has been received and is being processed.
You will be alerted in the notification section in the title bar when complete." * ] * } * From 1474c837ae9ccc8ef6038fd5fd0d676c266e2d86 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 18:55:35 +0500 Subject: [PATCH 4/6] CUR-4163 PR review changes --- .../Api/V1/MicroSoftTeamController.php | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php index 64b810dbd..78a78b6d6 100644 --- a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php +++ b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php @@ -236,25 +236,6 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) * "Project must be shared as we are temporarily publishing the shared link." * ] * } - * - * @response 500 { - * "message": "The given data was invalid.", - * "errors": { - * "aSyncUrl": [ - * "The a sync url field is required." - * ] - * } - * } - * - * @response 500 { - * "message": "The given data was invalid.", - * "errors": { - * "aSyncUrl": [ - * "The classId field is required." - * ] - * } - * } - * * @param MSTeamCreateAssignmentRequest $createAssignmentRequest * @param Project $project * @return Response From 00172561f5454c8c211670ee8c98af217a0dbc60 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 19:19:23 +0500 Subject: [PATCH 5/6] CUR-4163 pr review changes --- app/Http/Controllers/Api/V1/MicroSoftTeamController.php | 6 +++--- app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php | 2 +- app/Notifications/ProjectPublishNotification.php | 3 +-- app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php index 78a78b6d6..d179af449 100644 --- a/app/Http/Controllers/Api/V1/MicroSoftTeamController.php +++ b/app/Http/Controllers/Api/V1/MicroSoftTeamController.php @@ -242,16 +242,16 @@ public function createMsTeamClass(MSTeamCreateClassRequest $createClassRequest) */ public function publishProject(MSTeamCreateAssignmentRequest $createAssignmentRequest, Project $project) { - $createAssignmentRequest->validated(); + $data = $createAssignmentRequest->validated(); if(!$project->shared) { // temporary check will remove it in future return response([ 'errors' => 'Project must be shared as we are temporarily publishing the shared link.', ], 500); } - $classId = $createAssignmentRequest->get('classId'); + $classId = isset($data['classId']) ? $data['classId'] : ''; - // pushed cloning of project in background + // pushed publishing of project in background PublishProject::dispatch(auth()->user(), $project, $classId)->delay(now()->addSecond()); return response([ diff --git a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php index 49541ce4f..525a4231d 100644 --- a/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php +++ b/app/Http/Requests/V1/MSTeamCreateAssignmentRequest.php @@ -24,7 +24,7 @@ public function authorize() public function rules() { return [ - 'classId' => 'string|max:255', + 'classId' => 'UUID|max:255', ]; } diff --git a/app/Notifications/ProjectPublishNotification.php b/app/Notifications/ProjectPublishNotification.php index 019c757e0..0bada50a1 100644 --- a/app/Notifications/ProjectPublishNotification.php +++ b/app/Notifications/ProjectPublishNotification.php @@ -23,7 +23,7 @@ class ProjectPublishNotification extends Notification * @var string */ public $projectName; - + /** * Create a new notification instance. * @@ -57,7 +57,6 @@ public function via($notifiable) public function toDatabase($notifiable) { $message = "Project [$this->projectName] has been published into Microsoft Team successfully."; - return [ 'message' => $message, diff --git a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php index d3c7a0c03..1a6a6c4cf 100644 --- a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php +++ b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php @@ -227,7 +227,7 @@ public function createMSTeamAssignment($token, $classId, $project, $aSyncUrl) private function checkClassAsyncStatus($aSyncUrl, $token) { \Log::info('in checkClassAsyncStatus'); - if($aSyncUrl !== Null) { + if ($aSyncUrl !== Null) { $apiURL = $this->landingUrl . $aSyncUrl; $headers = [ 'Content-length' => 0, @@ -238,9 +238,9 @@ private function checkClassAsyncStatus($aSyncUrl, $token) $response = Http::withHeaders($headers)->get($apiURL); if ($response->status() === 200) { $responseBody = json_decode($response->getBody(), true); - if($responseBody['status'] === "succeeded") { + if ($responseBody['status'] === "succeeded") { return $responseBody['status']; - }else { + } else { sleep(30); $this->checkClassAsyncStatus($aSyncUrl, $token); } From d72261c8f57b56102d30cc2c9801ce377ab4d813 Mon Sep 17 00:00:00 2001 From: Mahmood Ali Waseem Date: Mon, 26 Sep 2022 19:28:21 +0500 Subject: [PATCH 6/6] CUR-4163 pr review changes --- .../MicrosoftTeam/MicrosoftTeamRepository.php | 40 +++++++++---------- .../MicrosoftTeamRepositoryInterface.php | 2 +- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php index 1a6a6c4cf..14c93c81b 100644 --- a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php +++ b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepository.php @@ -160,6 +160,15 @@ public function createMSTeamAssignment($token, $classId, $project, $aSyncUrl) } $apiURL = $this->landingUrl . 'education/classes/' . $classId . '/assignments'; + $assignmentDueDays = config('ms-team-configs.assignment_due_days'); + + $headers = [ + 'Content-length' => 0, + 'Content-type' => 'application/json', + 'Authorization' => 'Bearer ' . $token + ]; + + $postInput['dueDateTime'] =date('c', strtotime(date('Y-m-d'). ' + ' . $assignmentDueDays . ' days')); $playlists = $project->playlists; @@ -167,19 +176,8 @@ public function createMSTeamAssignment($token, $classId, $project, $aSyncUrl) $activities = $playlist->activities; foreach($activities as $activity) { - $assignmentDueDays = config('ms-team-configs.assignment_due_days'); - // Logic is in progress - $postInput = [ - 'displayName' => $activity->title, - 'dueDateTime' => date('c', strtotime(date('Y-m-d'). ' + ' . $assignmentDueDays . ' days')), // Need to discuss the due date logic currently hardcoded - ]; + $postInput['displayName'] = $activity->title; - $headers = [ - 'Content-length' => 0, - 'Content-type' => 'application/json', - 'Authorization' => 'Bearer ' . $token - ]; - $response = Http::withHeaders($headers)->withOptions(["verify"=>false]) ->retry(3, 6000)->post($apiURL, $postInput); $responseBody = json_decode($response->getBody(), true); @@ -264,15 +262,15 @@ private function fetchClassIdFromHeader($header) private function getUserDetails($token) { $apiURL = $this->landingUrl . '/me'; - $headers = [ - 'Content-length' => 0, - 'Content-type' => 'application/json', - 'Authorization' => 'Bearer ' . $token - ]; - - $response = Http::withHeaders($headers)->get($apiURL); - $responseBody = json_decode($response->getBody(), true); - return $responseBody['id']; + $headers = [ + 'Content-length' => 0, + 'Content-type' => 'application/json', + 'Authorization' => 'Bearer ' . $token + ]; + + $response = Http::withHeaders($headers)->get($apiURL); + $responseBody = json_decode($response->getBody(), true); + return $responseBody['id']; } } diff --git a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php index dc9f4d3be..ef0daa6d6 100644 --- a/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php +++ b/app/Repositories/MicrosoftTeam/MicrosoftTeamRepositoryInterface.php @@ -36,7 +36,7 @@ public function getClassesList($token); */ public function createMsTeamClass($token, $data); - /** + /** * @param $token string * @param $classId string * @param $project Project