keboola/gooddata-php-client

View on GitHub
src/Projects.php

Summary

Maintainability
C
1 day
Test Coverage
<?php
/**
 * @package gooddata-php-client
 * @copyright Keboola
 * @author Jakub Matejka <jakub@keboola.com>
 */
namespace Keboola\GoodData;

class Projects
{
    const ROLE_ADMIN = 'adminRole';
    const ROLE_EDITOR = 'editorRole';
    const ROLE_READ_ONLY = 'readOnlyUserRole';
    const ROLE_DASHBOARD_ONLY = 'dashboardOnlyRole';
    const ROLE_KEBOOLA_EDITOR = 'keboolaEditorPlusRole';

    public static $roles = [
        'admin' => self::ROLE_ADMIN,
        'editor' => self::ROLE_EDITOR,
        'readOnly' => self::ROLE_READ_ONLY,
        'dashboardOnly' => self::ROLE_DASHBOARD_ONLY,
        'keboolaEditor' => self::ROLE_KEBOOLA_EDITOR
    ];

    /** @var  Client */
    protected $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    public static function getPidFromUri($uri)
    {
        return substr($uri, strrpos($uri, '/')+1);
    }

    public static function getUriFromPid($pid)
    {
        return "/gdc/projects/$pid";
    }

    public function createProject($title, $authToken, $description = null, $testing = false, $driver = 'Pg')
    {
        $result = $this->client->post('/gdc/projects', [
            'project' => [
                'content' => [
                    'guidedNavigation' => 1,
                    'driver' => $driver,
                    'authorizationToken' => $authToken,
                    'environment' => $testing ? 'TESTING' : 'PRODUCTION'
                ],
                'meta' => [
                    'title' => $title,
                    'summary' => $description
                ]
            ]
        ]);

        if (empty($result['uri']) || strpos($result['uri'], '/gdc/projects/') === false) {
            throw Exception::unexpectedResponseError('Create project failed', 'POST', '/gdc/projects', $result);
        }

        // Wait until project is ready
        $projectUri = $result['uri'];
        $repeat = true;
        $try = 1;
        do {
            sleep(Client::WAIT_INTERVAL * ($try + 1));

            $result = $this->client->get($projectUri);
            if (isset($result['project']['content']['state']) && $result['project']['content']['state'] != 'DELETED') {
                if ($result['project']['content']['state'] == 'ENABLED') {
                    $repeat = false;
                }
            } else {
                throw Exception::unexpectedResponseError('Create project polling failed', 'GET', $projectUri, $result);
            }

            $try++;
        } while ($repeat);

        return self::getPidFromUri($projectUri);
    }

    public function getProject($pid)
    {
        return $this->client->get("/gdc/projects/$pid");
    }

    public function deleteProject($pid)
    {
        $this->client->delete("/gdc/projects/$pid");
    }

    public function getUsersForProjectYield($pid, $limit = 1000, $offset = 0)
    {
        $continue = true;
        do {
            $result = $this->client->get("/gdc/projects/$pid/users?offset=$offset&limit=$limit");
            if (count($result['users'])) {
                yield $result['users'];
            } else {
                $continue = false;
            }
            $offset += $limit;
        } while ($continue);
    }

    public function cloneProject($pidSource, $pidTarget, $includeData = 0, $includeUsers = 0)
    {
        $uri = "/gdc/md/$pidSource/maintenance/export";
        $params = [
            'exportProject' => [
                'exportUsers' => $includeUsers,
                'exportData' => $includeData
            ]
        ];
        $result = $this->client->post($uri, $params);
        if (empty($result['exportArtifact']['token']) || empty($result['exportArtifact']['status']['uri'])) {
            throw Exception::unexpectedResponseError('Clone project export failed', 'POST', $uri, $result);
        }

        $this->client->pollTask($result['exportArtifact']['status']['uri']);

        $uri = "/gdc/md/$pidTarget/maintenance/import";
        $result = $this->client->post($uri, [
            'importProject' => [
                'token' => $result['exportArtifact']['token']
            ]
        ]);
        if (empty($result['uri'])) {
            throw Exception::unexpectedResponseError('Clone project import failed', 'POST', $uri, $result);
        }

        $this->client->pollTask($result['uri']);
    }

    /**
     * @return bool | array
     */
    public function validate($pid, array $validate = ['pdm'])
    {
        $uri = "/gdc/md/$pid/validate";
        $result = $this->client->post($uri, ['validateProject' => $validate]);
        if (!isset($result['asyncTask']['link']['poll'])) {
            throw Exception::unexpectedResponseError('Project validation failed', 'POST', $uri, $result);
        }

        // Wait until validation is ready
        $repeat = true;
        $try = 1;
        do {
            sleep(Client::WAIT_INTERVAL * ($try + 1));

            $result = $this->client->get($result['asyncTask']['link']['poll']);
            if (isset($result['projectValidateResult'])) {
                return $result['projectValidateResult'];
            }

            $try++;
        } while ($repeat);

        return false;
    }

    public function getRoleUri($pid, $role)
    {
        $uri = "/gdc/projects/$pid/roles";
        $result = $this->client->get($uri);
        if (!isset($result['projectRoles']['roles'])) {
            throw Exception::unexpectedResponseError('Fetching roles in projects failed', 'GET', $uri, $result);
        }

        foreach ($result['projectRoles']['roles'] as $roleUri) {
            $roleResult = $this->client->get($roleUri);
            if (isset($roleResult['projectRole']['meta']['identifier'])
                && $roleResult['projectRole']['meta']['identifier'] == $role) {
                return $roleUri;
            }
        }

        throw Exception::error($uri, "Role '$role' not found in project");
    }

    public function isAccessible($pid)
    {
        $projectUri = self::getUriFromPid($pid);
        $uid = $this->client->getUsers()->getCurrentUid();
        foreach ($this->client->getUsers()->getProjectsYield($uid) as $projects) {
            foreach ($projects as $project) {
                if ($project['project']['links']['self'] == $projectUri) {
                    return true;
                }
            }
        }
        return false;
    }


    public function isAccessibleByUser($pid, $uid)
    {
        try {
            $this->client->get("/gdc/projects/$pid/users/$uid");
            return true;
        } catch (Exception $e) {
            if ($e->getCode() == 404 || $e->getCode() == 403) {
                return false;
            } else {
                throw $e;
            }
        }
    }

    public function addUser($pid, $uid, $role = self::ROLE_ADMIN)
    {
        $projectRoleUri = $this->getRoleUri($pid, $role);

        $uri = "/gdc/projects/$pid/users";
        $params = [
            'user' => [
                'content' => [
                    'status' => 'ENABLED',
                    'userRoles' => [$projectRoleUri]
                ],
                'links' => [
                    'self' => Users::getUriFromUid($uid)
                ]
            ]
        ];
        $result = $this->client->post($uri, $params);

        if ((isset($result['projectUsersUpdateResult']['successful'])
                && count($result['projectUsersUpdateResult']['successful']))
            || (isset($result['projectUsersUpdateResult']['failed'])
                && !count($result['projectUsersUpdateResult']['failed']))) {
            // SUCCESS
            // Sometimes API does not return
        } else {
            $errors = [];
            if (isset($result['projectUsersUpdateResult']['failed'])) {
                foreach ($result['projectUsersUpdateResult']['failed'] as $f) {
                    $errors[] = $f['message'];
                }
            }
            throw Exception::error($uri, 'Error in addition to project: ' . implode('; ', $errors));
        }
    }

    public function removeUser($pid, $uid)
    {
        return $this->client->delete("/gdc/projects/$pid/users/$uid");
    }

    public function leaveProject($pid, $uid)
    {
        return $this->removeUser($pid, $uid);
    }

    public function inviteUser($pid, $email, $role = Projects::ROLE_ADMIN, $filters = [])
    {
        $projectRoleUri = $this->getRoleUri($pid, $role);

        try {
            $result = $this->client->post("/gdc/projects/$pid/invitations", [
                'invitations' => [
                    [
                        'invitation' => [
                            'content' => [
                                'email' => $email,
                                'role' => $projectRoleUri,
                                'userFilters' => $filters
                            ]
                        ]
                    ]
                ]
            ]);
            if (isset($result['createdInvitations']['uri']) && count($result['createdInvitations']['uri'])) {
                return current($result['createdInvitations']['uri']);
            } else {
                if (isset($result['createdInvitations']['loginsAlreadyInProject'])
                    && count($result['createdInvitations']['loginsAlreadyInProject'])) {
                    return true;
                }
                throw Exception::unexpectedResponseError(
                    'Invitation to project failed',
                    'POST',
                    "/gdc/projects/pid/invitations",
                    $result
                );
            }
        } catch (Exception $e) {
            if (isset($e->getData()['error']['message'])
                && strpos($e->getData()['error']['message'], 'is already member') !== false) {
                return true;
            } else {
                throw $e;
            }
        }
    }

    public function disableUser($uid, $pid)
    {
        try {
            $result = $this->client->get("/gdc/projects/$pid/users/$uid");
            $this->client->post("/gdc/projects/$pid/users", [
                'user' => [
                    'content' => [
                        'status' => 'DISABLED',
                        'userRoles' => $result['user']['content']['userRoles']
                    ],
                    'links' => [
                        'self' => $result['user']['links']['self']
                    ]
                ]
            ]);
        } catch (Exception $e) {
            if ($e->getCode() != 404) {
                throw $e;
            }
        }
    }

    public function cancelInvitation($pid, $email)
    {
        $result = $this->client->get("/gdc/projects/$pid/invitations");
        foreach ($result['invitations'] as $r) {
            if (strtolower($r['invitation']['content']['email']) != strtolower($email)) {
                continue;
            }
            try {
                $this->client->delete($r['invitation']['links']['self']);
            } catch (Exception $e) {
                // Ignore
            }
        }
    }
}