Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OP#49859] add create work package form #500

Merged
merged 6 commits into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
['name' => 'openProjectAPI#isValidOpenProjectInstance', 'url' => '/is-valid-op-instance', 'verb' => 'POST'],
['name' => 'openProjectAPI#getOpenProjectOauthURLWithStateAndPKCE', 'url' => '/op-oauth-url', 'verb' => 'GET'],
['name' => 'openProjectAPI#getProjectFolderSetupStatus', 'url' => '/project-folder-status', 'verb' => 'GET'],
['name' => 'openProjectAPI#getAvailableOpenProjectProjects', 'url' => '/projects','verb' => 'GET'],
['name' => 'openProjectAPI#getOpenProjectWorkPackageForm', 'url' => '/projects/{projectId}/work-packages/form','verb' => 'POST'],
['name' => 'openProjectAPI#getAvailableAssigneesOfAProject', 'url' => '/projects/{projectId}/available-assignees','verb' => 'GET'],
['name' => 'openProjectAPI#createWorkPackage', 'url' => '/create/work-packages','verb' => 'POST'],
],
'ocs' => [
['name' => 'files#getFileInfo', 'url' => '/fileinfo/{fileId}', 'verb' => 'GET'],
Expand Down
164 changes: 159 additions & 5 deletions lib/Controller/OpenProjectAPIController.php
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,14 @@ public function getNotifications(): DataResponse {
*
* @param ?string $searchQuery
* @param ?int $fileId
*
* @param bool $isSmartPicker
* @return DataResponse
*/
public function getSearchedWorkPackages(?string $searchQuery = null, ?int $fileId = null, bool $isSmartPicker = false): DataResponse {
public function getSearchedWorkPackages(
?string $searchQuery = null,
?int $fileId = null,
bool $isSmartPicker = false
): DataResponse {
if ($this->accessToken === '') {
return new DataResponse('', Http::STATUS_UNAUTHORIZED);
} elseif (!OpenProjectAPIService::validateURL($this->openprojectUrl)) {
Expand Down Expand Up @@ -205,8 +209,10 @@ public function linkWorkPackageToFile(array $values): DataResponse {
$values,
$this->userId,
);
} catch (OpenprojectErrorException | InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
} catch (OpenprojectErrorException $e) {
return new DataResponse($e->getMessage(), $e->getcode());
} catch (NotPermittedException | NotFoundException $e) {
return new DataResponse('file not found', Http::STATUS_NOT_FOUND);
} catch (\Exception $e) {
Expand Down Expand Up @@ -266,7 +272,7 @@ public function getWorkPackageFileLinks(int $id): DataResponse {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
} catch (NotFoundException $e) {
return new DataResponse($e->getMessage(), Http::STATUS_NOT_FOUND);
} catch (Exception $e) {
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result);
Expand All @@ -293,7 +299,7 @@ public function deleteFileLink(int $id): DataResponse {
return new DataResponse($e->getMessage(), Http::STATUS_BAD_REQUEST);
} catch (NotFoundException $e) {
return new DataResponse($e->getMessage(), Http::STATUS_NOT_FOUND);
} catch (Exception $e) {
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result);
Expand Down Expand Up @@ -353,6 +359,154 @@ public function getOpenProjectWorkPackageType(string $id): DataResponse {
return $response;
}

/**
* @NoAdminRequired
*/
public function getAvailableOpenProjectProjects(): DataResponse {
if ($this->accessToken === '') {
return new DataResponse('', Http::STATUS_UNAUTHORIZED);
} elseif (!OpenProjectAPIService::validateURL($this->openprojectUrl)) {
return new DataResponse('', Http::STATUS_BAD_REQUEST);
}
SagarGi marked this conversation as resolved.
Show resolved Hide resolved
try {
$result = $this->openprojectAPIService->getAvailableOpenProjectProjects($this->userId);
} catch (OpenprojectErrorException $e) {
return new DataResponse($e->getMessage(), $e->getCode());
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result);
}


/**
* @NoAdminRequired
*
* @param string $projectId
* @param array<mixed> $body body is same in the format that OpenProject api expects the body to be i.e
* {
* _links: {
* type: {
* href: '/api/v3/types/1'
* title: 'Task'
* },
* status: {
* href: '/api/v3/statuses/1'
* title: 'New'
* },
* assignee: {
* href: ''
* title: ''
* },
* project: {
* href: '...'
* title: '...'
* },
* },
* subject: "something",
* description: {
* format: 'markdown',
* raw: '',
* html: ''
* }
* }
* See POST request for create work package https://www.openproject.org/docs/api/endpoints/work-packages/
* Note that this api will send `200` even with empty body and the body content is similar to that of create workpackages
* @return DataResponse
*/
public function getOpenProjectWorkPackageForm(string $projectId, array $body): DataResponse {
if ($this->accessToken === '') {
return new DataResponse('', Http::STATUS_UNAUTHORIZED);
} elseif (!OpenProjectAPIService::validateURL($this->openprojectUrl)) {
return new DataResponse('', Http::STATUS_BAD_REQUEST);
}
try {
$result = $this->openprojectAPIService->getOpenProjectWorkPackageForm($this->userId, $projectId, $body);
} catch (OpenprojectErrorException $e) {
return new DataResponse($e->getMessage(), $e->getcode());
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result);
}

/**
* @NoAdminRequired
*
* @param string $projectId
* @return DataResponse
*/
public function getAvailableAssigneesOfAProject(string $projectId): DataResponse {
if ($this->accessToken === '') {
return new DataResponse('', Http::STATUS_UNAUTHORIZED);
} elseif (!OpenProjectAPIService::validateURL($this->openprojectUrl)) {
return new DataResponse('', Http::STATUS_BAD_REQUEST);
}
try {
$result = $this->openprojectAPIService->getAvailableAssigneesOfAProject($this->userId, $projectId);
} catch (OpenprojectErrorException $e) {
return new DataResponse($e->getMessage(), $e->getcode());
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result);
}

/**
* @NoAdminRequired
*
* @param array<mixed> $body body is same in the format that OpenProject api expects the body to be i.e
* {
* _links: {
* type: {
* href: '/api/v3/types/1'
* title: 'Task'
* },
* status: {
* href: '/api/v3/statuses/1'
* title: 'New'
* },
* assignee: {
* href: ''
* title: ''
* },
* project: {
* href: '...'
* title: '...'
* },
* },
* subject: "something",
* description: {
* format: 'markdown',
* raw: '',
* html: ''
* }
* }
* See POST request for create work package https://www.openproject.org/docs/api/endpoints/work-packages/
* @return DataResponse
*/
public function createWorkPackage(array $body): DataResponse {
if ($this->accessToken === '') {
return new DataResponse('', Http::STATUS_UNAUTHORIZED);
} elseif (!OpenProjectAPIService::validateURL($this->openprojectUrl)) {
return new DataResponse('', Http::STATUS_BAD_REQUEST);
}
// we don't want to check if all the data in the body is set or not because
// that calculation will be done by the openproject api itself
// we don't want to duplicate the logic
if (empty($body)) {
return new DataResponse('Body cannot be empty', Http::STATUS_BAD_REQUEST);
}
try {
$result = $this->openprojectAPIService->createWorkPackage($this->userId, $body);
} catch (OpenprojectErrorException $e) {
return new DataResponse($e->getMessage(), $e->getcode());
} catch (\Exception $e) {
return new DataResponse($e->getMessage(), Http::STATUS_INTERNAL_SERVER_ERROR);
}
return new DataResponse($result, Http::STATUS_CREATED);
}

/**
* check if there is a OpenProject behind a certain URL
*
Expand Down
113 changes: 111 additions & 2 deletions lib/Service/OpenProjectAPIService.php
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ public function request(string $userId,
$this->logger->warning('OpenProject API error : '. $message, ['app' => $this->appName]);
}
return [
'error' => $e->getMessage(),
'error' => $response->getBody(),
'message' => $e->getMessage(),
'statusCode' => $response->getStatusCode(),
];
} catch (ConnectException $e) {
Expand Down Expand Up @@ -769,7 +770,7 @@ public function linkWorkPackageToFile(
);

if (isset($result['error'])) {
throw new OpenprojectErrorException($result['error']);
throw new OpenprojectErrorException($result['error'], $result['statusCode']);
}
if (
!isset($result['_type']) ||
Expand Down Expand Up @@ -1252,4 +1253,112 @@ public function getLinkToOpenProject(array $entry, string $url): string {
? $url . '/projects/' . $projectId . '/work_packages/' . $entry['id'] . '/activity'
: '';
}

/**
* @param string $userId
*
* @return array<mixed>
*
* @throws OpenprojectErrorException
* @throws OpenprojectResponseException|PreConditionNotMetException
*/
public function getAvailableOpenProjectProjects(string $userId): array {
$resultsById = [];
$filters[] = [
'storageUrl' =>
['operator' => '=', 'values' => [$this->urlGenerator->getBaseUrl()]],
'userAction' =>
['operator' => '&=', 'values' => ["file_links/manage", "work_packages/create"]]
];
$params = [
'filters' => \Safe\json_encode($filters)
];
$result = $this->request($userId, 'work_packages/available_projects', $params);
if (isset($result['error'])) {
throw new OpenprojectErrorException($result['error'], $result['statusCode']);
}
if (
!isset($result['_embedded']) ||
!isset($result['_embedded']['elements'])
) {
throw new OpenprojectResponseException('Malformed response');
}
foreach ($result['_embedded']['elements'] as $project) {
$resultsById[$project['id']] = $project;
}
return $resultsById;
}

/**
* @param string $userId
* @param string $projectId
* @param array<mixed> $body
*
* @return array<mixed>
* @throws OpenprojectResponseException|PreConditionNotMetException|JsonException|OpenprojectErrorException
*/
public function getOpenProjectWorkPackageForm(string $userId, string $projectId, array $body): array {
$params['body'] = \Safe\json_encode($body);
$result = $this->request($userId, 'projects/'.$projectId.'/work_packages/form', $params, 'POST');
if (isset($result['error'])) {
throw new OpenprojectErrorException($result['error'], $result['statusCode']);
}
if (
!isset($result['_type']) ||
$result['_type'] !== 'Form' ||
!isset($result['_embedded']) ||
!isset($result['_embedded']['payload']) ||
!isset($result['_embedded']['schema'])
) {
throw new OpenprojectResponseException('Malformed response');
}
return $result['_embedded'];
}

/**
* @param string $userId
* @param string $projectId
*
* @return array<mixed>
* @throws OpenprojectResponseException|PreConditionNotMetException|OpenprojectErrorException
*/
public function getAvailableAssigneesOfAProject(string $userId, string $projectId): array {
$result = $this->request($userId, 'projects/'.$projectId.'/available_assignees');
if (isset($result['error'])) {
throw new OpenprojectErrorException($result['error'], $result['statusCode']);
}
if (
!isset($result['_type']) ||
$result['_type'] !== 'Collection' ||
!isset($result['_embedded']) ||
!isset($result['_embedded']['elements'])
) {
throw new OpenprojectResponseException('Malformed response');
}
return $result['_embedded']['elements'];
}

/**
* @param string $userId
* @param array<mixed> $body
*
* @return array<mixed>
* @throws OpenprojectResponseException|PreConditionNotMetException|JsonException|OpenprojectErrorException
*/
public function createWorkPackage(string $userId, array $body): array {
$params['body'] = \Safe\json_encode($body);
$result = $this->request($userId, 'work_packages', $params, 'POST');
if (isset($result['error'])) {
throw new OpenprojectErrorException($result['error'], $result['statusCode']);
}
if (
!isset($result['_type']) ||
$result['_type'] !== 'WorkPackage' ||
!isset($result['_embedded']) ||
!isset($result['id'])
) {
throw new OpenprojectResponseException('Malformed response');
}
return $result;
}
}
Loading
Loading