Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 16 |
CRAP | |
0.00% |
0 / 375 |
ResourceService | |
0.00% |
0 / 1 |
|
0.00% |
0 / 16 |
6162.00 | |
0.00% |
0 / 374 |
__construct | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 3 |
|||
__clone | |
0.00% |
0 / 1 |
2.00 | |
0.00% |
0 / 2 |
|||
getInstance | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 5 |
|||
addResources | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 23 |
|||
removeResource | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 13 |
|||
removeResourceFromBackendStorage | |
0.00% |
0 / 1 |
72.00 | |
0.00% |
0 / 24 |
|||
removeFilesFromS3 | |
0.00% |
0 / 1 |
6.00 | |
0.00% |
0 / 21 |
|||
getResourceById | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 12 |
|||
getPreSignedUrlByResourceId | |
0.00% |
0 / 1 |
90.00 | |
0.00% |
0 / 29 |
|||
getPreSignedS3URL | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 30 |
|||
validateS3Tags | |
0.00% |
0 / 1 |
42.00 | |
0.00% |
0 / 14 |
|||
uploadResource | |
0.00% |
0 / 1 |
30.00 | |
0.00% |
0 / 36 |
|||
migrationUploadResource | |
0.00% |
0 / 1 |
20.00 | |
0.00% |
0 / 34 |
|||
copyResource | |
0.00% |
0 / 1 |
110.00 | |
0.00% |
0 / 32 |
|||
cloneFilesInS3 | |
0.00% |
0 / 1 |
12.00 | |
0.00% |
0 / 40 |
|||
cloneFilesInLocalStorage | |
0.00% |
0 / 1 |
182.00 | |
0.00% |
0 / 56 |
<?php | |
namespace com\linways\core\ams\professional\service; | |
use Aws\S3\S3Client; | |
use Aws\Credentials\Credentials; | |
use com\linways\base\util\SecurityUtils; | |
use com\linways\core\ams\professional\dto\Resource; | |
use com\linways\core\ams\professional\request\CopyResourceRequest; | |
use com\linways\core\ams\professional\util\S3Utils; | |
use com\linways\core\ams\professional\constant\BackendTypes; | |
use com\linways\core\ams\professional\constant\resource\S3TagConstant; | |
use com\linways\core\ams\professional\mapper\ResourceMapper; | |
use com\linways\core\ams\professional\response\GetResourceUrl; | |
use com\linways\core\ams\professional\request\AddResourceRequest; | |
use com\linways\core\ams\professional\request\RemoveResourceRequest; | |
use com\linways\core\ams\professional\request\UploadResourceRequest; | |
use com\linways\core\ams\professional\request\GetPreSignedUrlRequest; | |
use com\linways\core\ams\professional\exception\ProfessionalException; | |
use com\linways\core\ams\professional\util\UUID; | |
use function strings\lowerCase; | |
use com\linways\core\ams\professional\util\CommonUtil; | |
class ResourceService extends BaseService | |
{ | |
/** | |
* Presence of a static member variable | |
* | |
* @var null | |
*/ | |
private static $_instance = null; | |
/** | |
* @var array | |
*/ | |
private $mapper = []; | |
/** | |
* Locked down the constructor or Prevent any outside instantiation of this class | |
* | |
* ResourceService constructor. | |
*/ | |
private function __construct() | |
{ | |
$this->mapper = ResourceMapper::getInstance()->getMapper(); | |
} | |
/** | |
* Prevent any object or instance of that class to be cloned or Prevent any copy of this object | |
*/ | |
private function __clone() | |
{ | |
} | |
/** | |
* Have a single globally accessible static method | |
* | |
* @return ResourceService|null | |
*/ | |
public static function getInstance() | |
{ | |
if (!is_object(self::$_instance)) | |
self::$_instance = new self(); | |
return self::$_instance; | |
} | |
/** | |
* @param AddResourceRequest $request | |
* @return Object|integer | |
* @throws ProfessionalException | |
*/ | |
public function addResources(AddResourceRequest $request) | |
{ | |
$request->backendType = $this->realEscapeString($request->backendType); | |
$request->path = $this->realEscapeString($request->path); | |
$request->context = $this->realEscapeString($request->context); | |
$request->isConfirmed = $this->realEscapeString($request->isConfirmed); | |
if (empty($request->path) && $request->backendType != 'VIEWWAY') { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_PATH, "Invalid resource path given"); | |
} | |
if (empty($request->context)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_CONTEXT, "Invalid resource context given"); | |
} | |
$request->storageObject = addslashes($request->storageObject); | |
$request->id = SecurityUtils::getRandomString(); | |
$sql = "INSERT INTO lin_resource (id,path, context, backend_type,storage_object, created_by, updated_by, created_date, updated_date) | |
VALUES ('$request->id','$request->path','$request->context','$request->backendType','$request->storageObject', | |
$request->createdBy,$request->updatedBy,UTC_TIMESTAMP(),UTC_TIMESTAMP())"; | |
try { | |
$this->executeQuery($sql); | |
return $request->id; | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* Caution: This will remove the file history completely from the lin_resource table and backend storage. | |
* If backend type is local storage, then this will remove file from the database only. | |
* @param RemoveResourceRequest $request | |
* @throws ProfessionalException | |
*/ | |
public function removeResource(RemoveResourceRequest $request) | |
{ | |
$request = $this->realEscapeObject($request); | |
if (empty($request->resourceId)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_ID, "Invalid resource details given"); | |
} | |
try { | |
$this->removeResourceFromBackendStorage($request); | |
$sql = "DELETE FROM lin_resource WHERE id = '$request->resourceId'"; | |
$this->executeQueryForObject($sql); | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/**my | |
* @param RemoveResourceRequest $request | |
* @throws ProfessionalException | |
*/ | |
private function removeResourceFromBackendStorage(RemoveResourceRequest $request) | |
{ | |
$resource = $this->getResourceById($request->resourceId); | |
if (empty($resource)) { | |
throw new ProfessionalException(ProfessionalException::RESOURCE_NOT_FOUND, "Resource not found in the storage"); | |
} | |
try { | |
switch ($resource->backendType) { | |
case BackendTypes::S3: | |
$this->removeFilesFromS3($resource->storageObject, $request); | |
break; | |
case BackendTypes::LOCAL_STORAGE: | |
//in future, if the service part and web part in different servers this code won't work | |
unlink(DOCUMENT_ROOT . $resource->path); | |
break; | |
case BackendTypes::GOOGLE_DRIVE: | |
case BackendTypes::YOUTUBE: | |
case BackendTypes::EXTERNAL_URL: | |
//No need to remove file from user google account | |
break; | |
default: | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_STORAGE_TYPE, "Invalid resource storage type"); | |
} | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* @param $storageObject | |
* @param RemoveResourceRequest $request | |
* @throws ProfessionalException | |
*/ | |
private function removeFilesFromS3($storageObject, RemoveResourceRequest $request) | |
{ | |
try { | |
$tempStorageObject = CommonUtil::convertObjectToUTF8Format($storageObject); //remove unwanted Char from json content | |
$storageObject = json_decode($tempStorageObject); | |
$bucket = $storageObject->bucket; | |
$key = $storageObject->key; | |
$credentials = new Credentials($request->accessKey, $request->secretKey); | |
$s3 = new S3Client([ | |
'version' => '2006-03-01', | |
'region' => 'ap-south-1', | |
'credentials' => $credentials, | |
]); | |
$s3->deleteObject([ | |
'Bucket' => $bucket, | |
'Key' => $key | |
]); | |
} catch (\Exception $e) { | |
error_log("CODE:" . $e->getCode() . " Message:" . $e->getMessage()); | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* @param $resourceId | |
* @return Object|Resource | |
* @throws ProfessionalException | |
*/ | |
public function getResourceById($resourceId) | |
{ | |
$resourceId = $this->realEscapeString($resourceId); | |
if (empty($resourceId)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_ID, "Invalid resource id"); | |
} | |
try { | |
$sql = "SELECT id,path,context,storage_object,backend_type FROM lin_resource WHERE id = '$resourceId'"; | |
return $this->executeQueryForObject($sql, false, $this->mapper[ResourceMapper::GET_RESOURCE]); | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* @param GetPreSignedUrlRequest $request | |
* @return GetResourceUrl | |
* @throws ProfessionalException | |
*/ | |
public function getPreSignedUrlByResourceId(GetPreSignedUrlRequest $request) | |
{ | |
if (empty($request->resourceId)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_ID, "Invalid resource id"); | |
} | |
try { | |
$resource = $this->getResourceById($request->resourceId); | |
switch ($resource->backendType) { | |
case BackendTypes::S3: | |
return $this->getPreSignedS3URL($resource, $request); | |
case BackendTypes::LOCAL_STORAGE: | |
case BackendTypes::EXTERNAL_URL: | |
$response = new GetResourceUrl(); | |
$response->url = $resource->path; | |
case BackendTypes::GOOGLE_DRIVE: | |
$response = new GetResourceUrl(); | |
$response->url = $resource->path; | |
$storageObject = json_decode($resource->storageObject); | |
$response->name = ""; | |
if (!empty($storageObject) && !empty($storageObject->name)) { | |
$response->name = $storageObject->name; | |
} | |
return $response; | |
default: | |
throw new ProfessionalException(ProfessionalException::RESOURCE_BACKEND_TYPE_NOT_FOUND, "Resource type not found"); | |
} | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* @param Resource $resource | |
* @param GetPreSignedUrlRequest $request | |
* @return GetResourceUrl | |
* @throws ProfessionalException | |
*/ | |
private function getPreSignedS3URL(Resource $resource, GetPreSignedUrlRequest $request) | |
{ | |
$response = new GetResourceUrl(); | |
$tempStorageObject = CommonUtil::convertObjectToUTF8Format($resource->storageObject); | |
$storageObject = json_decode($tempStorageObject); | |
$storageObject->key = htmlspecialchars_decode($storageObject->key); | |
$storageObject->name = htmlspecialchars_decode($storageObject->name); | |
if($request->utfConversion){ | |
$storageObject->key = utf8_decode($storageObject->key); | |
$storageObject->name = utf8_decode($storageObject->name); | |
} | |
if (empty($storageObject)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_ID, "Resource not found. Contact your system administrator"); | |
} | |
$key = $storageObject->key; | |
$bucket = $storageObject->bucket; | |
$credentials = new Credentials($request->accessKey, $request->secretKey); | |
$s3Client = new S3Client([ | |
'version' => '2006-03-01', | |
'region' => 'ap-south-1', | |
'credentials' => $credentials, | |
]); | |
$cmd = $s3Client->getCommand('GetObject', [ | |
'Bucket' => $bucket, | |
'Key' => $key | |
]); | |
// $test = $s3Client->getObject('GetObject', [ | |
// 'Bucket' => $bucket, | |
// 'Key' => $key | |
// ]); | |
$request = $s3Client->createPresignedRequest($cmd, '+30 minutes'); | |
$response->url = (string) $request->getUri(); | |
$response->name = $storageObject->name; | |
return $response; | |
} | |
/** | |
* Validates the tags added the object | |
* returns string if valid else throws errors | |
* @param array $tags | |
* @throws ProfessionalException | |
* @return string | |
*/ | |
public function validateS3Tags($tags){ | |
if(empty($tags)){ | |
return ""; | |
} | |
$responseTag = []; | |
foreach($tags as $key => $value){ | |
$key = strtolower(trim($key)); | |
if(!array_key_exists($key, S3TagConstant::S3TAGS)) | |
throw new ProfessionalException( ProfessionalException::INVALID_S3_TAGS, "The tag key added to this object is invalid" ); | |
if(!in_array($value, S3TagConstant::S3TAGS[$key]) && count(S3TagConstant::S3TAGS[$key]) > 0) | |
throw new ProfessionalException( ProfessionalException::INVALID_S3_TAGS, "The tag value added to this object is invalid" ); | |
$responseTag [] = "$key=$value"; | |
} | |
return implode('&',$responseTag); | |
} | |
/** | |
* @param UploadResourceRequest $request | |
* @return int|Object | |
* @return AddResourceRequest object if returnResourceObjectBeforeAddingToLinResource is true. In case if you only need to upload to s3 and use the object. Adding to linresource table can be skipped. | |
* @throws ProfessionalException | |
*/ | |
public function uploadResource(UploadResourceRequest $request) | |
{ | |
$request = $this->realEscapeObject($request); | |
$addResourceRequest = new AddResourceRequest(); | |
$addResourceRequest->context = $request->context; | |
$addResourceRequest->backendType = $request->backendType; | |
try { | |
if ($request->backendType === BackendTypes::S3) { | |
$credentials = new Credentials($request->accessKey, $request->secretKey); | |
$s3Client = new S3Client([ | |
'version' => '2006-03-01', | |
'region' => 'ap-south-1', | |
'credentials' => $credentials, | |
]); | |
$tags = $this->validateS3Tags($request->putObjectTags); | |
$key = $request->s3FolderPath . ($request->key ? $request->key : md5(uniqid(rand())) . "-" . time() . "-" . $request->fileName); | |
$result = $s3Client->putObject([ | |
'Bucket' => $request->bucket, | |
'Key' => $key, | |
'SourceFile' => $request->filePath, | |
'Tagging' => $tags | |
]); | |
$s3Object = (object) ["bucket" => $request->bucket, "key" => $key, "name" => $request->fileName]; | |
unlink($request->filePath); | |
$addResourceRequest->storageObject = json_encode($s3Object); | |
$addResourceRequest->path = $s3Object->key; | |
} | |
$addResourceRequest->updatedBy = $request->createdBy; | |
$addResourceRequest->createdBy = $request->createdBy; | |
if ($request->returnResourceObjectBeforeAddingToLinResource == true) { | |
return $addResourceRequest; | |
} | |
return $this->addResources($addResourceRequest); | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
public function migrationUploadResource(UploadResourceRequest $request) | |
{ | |
$request = $this->realEscapeObject($request); | |
$addResourceRequest = new AddResourceRequest(); | |
$addResourceRequest->context = $request->context; | |
$addResourceRequest->backendType = $request->backendType; | |
try { | |
if ($request->backendType === BackendTypes::S3) { | |
$credentials = new Credentials($request->accessKey, $request->secretKey); | |
$s3Client = new S3Client([ | |
'version' => '2006-03-01', | |
'region' => 'ap-south-1', | |
'credentials' => $credentials, | |
]); | |
$key = $request->s3FolderPath . ($request->key ? $request->key : md5(uniqid(rand())) . "-" . time() . "-" . $request->fileName); | |
$result = $s3Client->putObject([ | |
'Bucket' => $request->bucket, | |
'Key' => $key, | |
'SourceFile' => $request->filePath | |
]); | |
$s3Object = (object) ["bucket" => $request->bucket, "key" => $key, "name" => $request->fileName]; | |
//unlink($request->filePath); | |
$addResourceRequest->storageObject = json_encode($s3Object); | |
$addResourceRequest->path = $s3Object->key; | |
} | |
$addResourceRequest->updatedBy = $request->createdBy; | |
$addResourceRequest->createdBy = $request->createdBy; | |
$addResourceRequest->backendType = $this->realEscapeString($addResourceRequest->backendType); | |
$addResourceRequest->path = $this->realEscapeString($addResourceRequest->path); | |
$addResourceRequest->context = $this->realEscapeString($addResourceRequest->context); | |
$addResourceRequest->isConfirmed = $this->realEscapeString($addResourceRequest->isConfirmed); | |
return $addResourceRequest; | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* @param CopyResourceRequest $request | |
* @return int|Object | |
* @throws ProfessionalException | |
*/ | |
public function copyResource(CopyResourceRequest $request) | |
{ | |
if (empty($request->resourceId)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_ID, "Invalid resource details given"); | |
} | |
if (!$request->isCopyToSameDirectory) { | |
if (empty($request->directoryName)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_DIRECTORY_NAME, "Invalid directory name"); | |
} | |
if (empty($request->userType)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_USER_TYPE, "Invalid user type"); | |
} | |
if (empty($request->userName)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_USER_NAME, "Invalid user name"); | |
} | |
} | |
try { | |
$resource = $this->getResourceById($request->resourceId); | |
if (empty($resource)) { | |
throw new ProfessionalException(ProfessionalException::RESOURCE_NOT_FOUND, "The given resource doesn't exist"); | |
} | |
switch ($resource->backendType) { | |
case BackendTypes::S3: | |
return $this->cloneFilesInS3($request, $resource); | |
case BackendTypes::LOCAL_STORAGE: | |
return $this->cloneFilesInLocalStorage($request, $resource); | |
default: | |
throw new ProfessionalException(ProfessionalException::INVALID_RESOURCE_STORAGE_TYPE, "Invalid resource storage type given"); | |
} | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* Cloning S3 Object | |
* | |
* @param CopyResourceRequest $request | |
* @param $resource | |
* @return int|Object | |
* @throws ProfessionalException | |
*/ | |
private function cloneFilesInS3(CopyResourceRequest $request, $resource) | |
{ | |
try { | |
$credentials = new Credentials($request->accessKey, $request->secretKey); | |
$s3Client = new S3Client([ | |
'version' => '2006-03-01', | |
'region' => 'ap-south-1', | |
'credentials' => $credentials, | |
]); | |
$storageObject = json_decode($resource->storageObject); | |
$uuid = UUID::v4(); | |
$fileName = $storageObject->fileName; | |
$extension = ltrim(strstr($fileName, '.'), '.'); | |
/** | |
* Copying to different directory | |
*/ | |
if (!$request->isCopyToSameDirectory) { | |
$key = $request->collegeCode . "/" . $request->directoryName . "/" . date('Y') . "/" . $uuid . "-" . $request->userId . "." . $extension; | |
} else { | |
$path = $storageObject->key; | |
$directoryName = dirname($path); | |
$key = $directoryName . "/" . $uuid . "." . $extension; | |
} | |
$s3Client->copyObject([ | |
'Bucket' => $storageObject->bucket, | |
'CopySource' => $storageObject->key, | |
'Key' => $key | |
]); | |
$newStorageObject = new \stdClass(); | |
$newStorageObject->key = $key; | |
$newStorageObject->bucket = $storageObject->bucket; | |
$newStorageObject->name = $fileName; | |
$addResourceRequest = new AddResourceRequest(); | |
$addResourceRequest->context = $request->context; | |
$addResourceRequest->backendType = $resource->backendType; | |
$addResourceRequest->path = $key; | |
$addResourceRequest->storageObject = json_encode($newStorageObject); | |
$addResourceRequest->updatedBy = $request->createdBy; | |
$addResourceRequest->createdBy = $request->createdBy; | |
return $this->addResources($addResourceRequest); | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
/** | |
* Cloning file in the local storage | |
* | |
* @param CopyResourceRequest $request | |
* @param Resource $resource | |
* @return int|Object | |
* @throws ProfessionalException | |
*/ | |
private function cloneFilesInLocalStorage(CopyResourceRequest $request, $resource) | |
{ | |
$path = $resource->path; | |
$pathParts = pathinfo($path); | |
$fileName = $pathParts['filename']; | |
$fileExtension = $pathParts['extension']; | |
$newFileName = $fileName . "-copy-" . time() . "." . $fileExtension; | |
$newPath = ""; | |
try { | |
/** | |
* Checking developers assigned exact path to copy. | |
* Here file with same name will be overwrite | |
*/ | |
if (!empty($request->destinationPath)) { | |
if (mkdir(dirname($request->destinationPath), 0777, true)) { | |
if (!copy($path, $request->destinationPath)) { | |
throw new ProfessionalException(ProfessionalException::FAILED_TO_COPY_THE_FILE, "Failed to copy the file"); | |
} | |
$newPath = $request->destinationPath; | |
} else { | |
throw new ProfessionalException(ProfessionalException::FAILED_TO_CREATE_THE_DIRECTORY, "Failed to copy the file"); | |
} | |
} elseif (!$request->isCopyToSameDirectory) { | |
/** | |
* Copying to the same directory flag is set, then only file name will change | |
*/ | |
$directoryName = dirname($path); | |
$newPath = $directoryName . "/" . $newFileName; | |
if (!copy($path, $newPath)) { | |
throw new ProfessionalException(ProfessionalException::FAILED_TO_CREATE_THE_DIRECTORY, "Failed to copy the file"); | |
} | |
} else { | |
/** | |
* Dynamic file name and path directory creation is set here | |
*/ | |
if (empty($request->userType) || empty($request->userCode)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_USER_TYPE, "Invalid user details given"); | |
} | |
if (empty($request->directoryName) || empty($request->parentDirectory)) { | |
throw new ProfessionalException(ProfessionalException::INVALID_DIRECTORY_NAME, "Invalid target directory details given"); | |
} | |
$userType = lowerCase($request->userType); | |
$newPath = DOCUMENT_ROOT . $userType . "/" . $request->parentDirectory . "/" . $request->collegeCode . "/" . $request->userCode . "/" . $request->directoryName . "/" . $newFileName; | |
if (mkdir(dirname($newPath), 0777, true)) { | |
if (!copy($path, $newPath)) { | |
throw new ProfessionalException(ProfessionalException::FAILED_TO_COPY_THE_FILE, "Failed to copy the file"); | |
} | |
} else { | |
throw new ProfessionalException(ProfessionalException::FAILED_TO_CREATE_THE_DIRECTORY, "Failed to copy the file"); | |
} | |
} | |
$newStorageObject = new \stdClass(); | |
$newStorageObject->key = $newPath; | |
$newStorageObject->bucket = ""; | |
$newStorageObject->name = $newFileName; | |
$addResourceRequest = new AddResourceRequest(); | |
$addResourceRequest->context = $request->context; | |
$addResourceRequest->backendType = $resource->backendType; | |
$addResourceRequest->path = $newPath; | |
$addResourceRequest->storageObject = json_encode($newStorageObject); | |
$addResourceRequest->updatedBy = $request->createdBy; | |
$addResourceRequest->createdBy = $request->createdBy; | |
return $this->addResources($addResourceRequest); | |
} catch (\Exception $e) { | |
throw new ProfessionalException($e->getCode(), $e->getMessage()); | |
} | |
} | |
} |