-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cover provider with unit tests (#32)
* add mocha as test framework Signed-off-by: Michael Granados <[email protected]> * separate provider in module to provide better access to separated areas Signed-off-by: Michael Granados <[email protected]> * cover checkServiceAccount util Signed-off-by: Michael Granados <[email protected]> * cover checkBucket to verify if bucket exists on GCS Signed-off-by: Michael Granados <[email protected]> * cover mergeConfig function Signed-off-by: Michael Granados <[email protected]> * cover upload action Signed-off-by: Michael Granados <[email protected]> * add istanbul coverage Signed-off-by: Michael Granados <[email protected]> * change workflow Signed-off-by: Michael Granados <[email protected]> * remove not used line Signed-off-by: Michael Granados <[email protected]> * cover #delete action Signed-off-by: Michael Granados <[email protected]> * add tests for filenames using real cases for upload and upload with reference Signed-off-by: Michael Granados <[email protected]> * rescue separate general parse JSON error from specific attributes errors Signed-off-by: Michael Granados <[email protected]> * add EOL Signed-off-by: Michael Granados <[email protected]> * cover malformed JSON Signed-off-by: Michael Granados <[email protected]>
- Loading branch information
Showing
11 changed files
with
1,050 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[{package.json,*.yml}] | ||
indent_style = space | ||
indent_size = 2 | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
{ | ||
"env": { | ||
"node": true, | ||
"es6": true | ||
"es6": true, | ||
"mocha": true | ||
}, | ||
"plugins": ["prettier"], | ||
"extends": [ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
name: Test project | ||
on: push | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v1 | ||
- uses: actions/setup-node@v1 | ||
with: | ||
node-version: '10.x' | ||
registry-url: https://registry.npmjs.org/ | ||
- run: npm install | ||
- run: npm run coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"recursive": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"all": true, | ||
"include": ["lib"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,202 +1,7 @@ | ||
'use strict'; | ||
|
||
const path = require('path'); | ||
const slugify = require('slugify'); | ||
const { Storage } = require('@google-cloud/storage'); | ||
const { init } = require('./provider'); | ||
|
||
/** | ||
* Check validity of Service Account configuration | ||
* @param config | ||
* @returns {{private_key}|{client_email}|{project_id}|any} | ||
*/ | ||
const checkServiceAccount = (config) => { | ||
if (!config.serviceAccount) { | ||
throw new Error('"Service Account JSON" is required!'); | ||
} | ||
if (!config.bucketName) { | ||
throw new Error('"Bucket name" is required!'); | ||
} | ||
if (!config.baseUrl) { | ||
/** Set to default **/ | ||
config.baseUrl = 'https://storage.googleapis.com/{bucket-name}'; | ||
} | ||
|
||
let serviceAccount; | ||
|
||
try { | ||
serviceAccount = | ||
typeof config.serviceAccount === 'string' | ||
? JSON.parse(config.serviceAccount) | ||
: config.serviceAccount; | ||
} catch (e) { | ||
throw new Error( | ||
'Error parsing data "Service Account JSON", please be sure to copy/paste the full JSON file.' | ||
); | ||
} | ||
|
||
/** | ||
* Check exist | ||
*/ | ||
if (!serviceAccount.project_id) { | ||
throw new Error( | ||
'Error parsing data "Service Account JSON". Missing "project_id" field in JSON file.' | ||
); | ||
} | ||
if (!serviceAccount.client_email) { | ||
throw new Error( | ||
'Error parsing data "Service Account JSON". Missing "client_email" field in JSON file.' | ||
); | ||
} | ||
if (!serviceAccount.private_key) { | ||
throw new Error( | ||
'Error parsing data "Service Account JSON". Missing "private_key" field in JSON file.' | ||
); | ||
} | ||
return serviceAccount; | ||
}; | ||
|
||
/** | ||
* Check bucket exist, or create it | ||
* @param GCS | ||
* @param bucketName | ||
* @returns {Promise<void>} | ||
*/ | ||
const checkBucket = async (GCS, bucketName) => { | ||
let bucket = GCS.bucket(bucketName); | ||
await bucket.exists().then((data) => { | ||
if (!data[0]) { | ||
throw new Error( | ||
'An error occurs when we try to retrieve the Bucket "' + | ||
bucketName + | ||
'". Check if bucket exist on Google Cloud Platform.' | ||
); | ||
} | ||
}); | ||
}; | ||
|
||
/** | ||
* Merge uploadProvider config with gcs key in custom Strapi config | ||
* @param uploadProviderConfig | ||
* @returns {{private_key}|{client_email}|{project_id}|any} | ||
*/ | ||
const mergeConfigs = (providerConfig) => { | ||
let customGcsConfig = strapi.config.gcs ? strapi.config.gcs : {}; | ||
let customEnvGcsConfig = strapi.config.currentEnvironment.gcs | ||
? strapi.config.currentEnvironment.gcs | ||
: {}; | ||
return { ...providerConfig, ...customGcsConfig, ...customEnvGcsConfig }; | ||
}; | ||
|
||
/** | ||
* | ||
* @type {{init(*=): {upload(*=): Promise<unknown>, delete(*): Promise<unknown>}}} | ||
*/ | ||
module.exports = { | ||
init(providerConfig) { | ||
const config = mergeConfigs(providerConfig); | ||
const serviceAccount = checkServiceAccount(config); | ||
const GCS = new Storage({ | ||
projectId: serviceAccount.project_id, | ||
credentials: { | ||
client_email: serviceAccount.client_email, | ||
private_key: serviceAccount.private_key, | ||
}, | ||
}); | ||
|
||
return { | ||
upload(file) { | ||
return new Promise((resolve, reject) => { | ||
const backupPath = | ||
file.related && file.related.length > 0 && file.related[0].ref | ||
? `${file.related[0].ref}` | ||
: `${file.hash}`; | ||
const filePath = file.path ? `${file.path}/` : `${backupPath}/`; | ||
const fileName = | ||
slugify(path.basename(file.name + '_' + file.hash, file.ext)) + file.ext.toLowerCase(); | ||
|
||
checkBucket(GCS, config.bucketName) | ||
.then(() => { | ||
/** | ||
* Check if the file already exist and force to remove it on Bucket | ||
*/ | ||
GCS.bucket(config.bucketName) | ||
.file(`${filePath}${fileName}`) | ||
.exists() | ||
.then((exist) => { | ||
if (exist[0]) { | ||
strapi.log.info('File already exist, try to remove it.'); | ||
const fileName = `${file.url.replace( | ||
config.baseUrl.replace('{bucket-name}', config.bucketName) + '/', | ||
'' | ||
)}`; | ||
|
||
GCS.bucket(config.bucketName) | ||
.file(`${fileName}`) | ||
.delete() | ||
.then(() => { | ||
strapi.log.debug(`File ${fileName} successfully deleted`); | ||
}) | ||
.catch((error) => { | ||
if (error.code === 404) { | ||
return strapi.log.warn( | ||
'Remote file was not found, you may have to delete manually.' | ||
); | ||
} | ||
}); | ||
} | ||
}); | ||
}) | ||
.then(() => { | ||
/** | ||
* Then save file | ||
*/ | ||
GCS.bucket(config.bucketName) | ||
.file(`${filePath}${fileName}`) | ||
.save(file.buffer, { | ||
contentType: file.mime, | ||
public: true, | ||
metadata: { | ||
contentDisposition: `inline; filename="${file.name}"`, | ||
}, | ||
}) | ||
.then(() => { | ||
file.url = `${config.baseUrl.replace( | ||
/{bucket-name}/, | ||
config.bucketName | ||
)}/${filePath}${fileName}`; | ||
strapi.log.debug(`File successfully uploaded to ${file.url}`); | ||
resolve(); | ||
}) | ||
.catch((error) => { | ||
return reject(error); | ||
}); | ||
}); | ||
}); | ||
}, | ||
delete(file) { | ||
return new Promise((resolve, reject) => { | ||
const fileName = `${file.url.replace( | ||
config.baseUrl.replace('{bucket-name}', config.bucketName) + '/', | ||
'' | ||
)}`; | ||
|
||
GCS.bucket(config.bucketName) | ||
.file(fileName) | ||
.delete() | ||
.then(() => { | ||
strapi.log.debug(`File ${fileName} successfully deleted`); | ||
}) | ||
.catch((error) => { | ||
if (error.code === 404) { | ||
return strapi.log.warn( | ||
'Remote file was not found, you may have to delete manually.' | ||
); | ||
} | ||
reject(error); | ||
}); | ||
resolve(); | ||
}); | ||
}, | ||
}; | ||
}, | ||
init, | ||
}; |
Oops, something went wrong.