Skip to content

[WIP] #148 Create instruction of how to create dev/draft copy of our Dialog Flow App and add helpful scripts #278

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

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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: 3 additions & 1 deletion functions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
"object.entries": "^1.0.4",
"raven": "^2.6.0",
"replaceall": "^0.1.6",
"supports-color": "^5.4.0"
"supports-color": "^5.4.0",
"unzip": "^0.1.11",
"zipfolder": "^1.0.4"
},
"devDependencies": {
"axios-mock-adapter": "^1.15.0",
Expand Down
12 changes: 12 additions & 0 deletions functions/tests/uploader/agent/agent-downloader.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const {expect} = require('chai');
const agentDownloader = require('../../../uploader/agent/agent-downloader');

describe('uploader', () => {
describe('agent-downloader', () => {
describe('downloadAgent', () => {
it('should be defined', () => {
expect(agentDownloader.downloadAgent).to.be.ok;
});
});
});
});
12 changes: 12 additions & 0 deletions functions/tests/uploader/agent/agent-uploader.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const {expect} = require('chai');
const agentUploader = require('../../../uploader/agent/agent-uploader');

describe('uploader', () => {
describe('agent-uploader', () => {
describe('uploadAgent', () => {
it('should be defined', () => {
expect(agentUploader.uploadAgent).to.be.ok;
});
});
});
});
36 changes: 36 additions & 0 deletions functions/tests/uploader/utils/base64.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const {expect} = require('chai');
const base64 = require('../../../uploader/utils/base64');
const fs = require('fs');
const base64Str = 'UEsDBBQAAAAAAJBqvEwAAAAAAAAAAAAAAAANACAAc2FtcGxlX2FnZW50L1VUDQAHyLQLW+K0C1vctAtbdXgLAAEE6AMAAAToAwAAUEsBAhQDFAAAAAAAkGq8TAAAAAAAAAAAAAAAAA0AIAAAAAAAAAAAAO1BAAAAAHNhbXBsZV9hZ2VudC9VVA0AB8i0C1vitAtb3LQLW3V4CwABBOgDAAAE6AMAAFBLBQYAAAAAAQABAFsAAABLAAAAAAA=';

describe('uploader', () => {
describe('utils', () => {
describe('base64', () => {
describe('base64Encode', () => {
it('should be defined', () => {
expect(base64.base64Encode).to.be.ok;
});

it('should return encoded string', () => {
base64.base64Encode('./tests/uploader/utils/fixtures/sample_agent.zip')
.then(value => {
expect(value).to.be.a('string');
});
});
});

describe('base64Decode', () => {
it('should be defined', () => {
expect(base64.base64Decode).to.be.ok;
});

it('should return success', () => {
base64.base64Decode(base64Str, './tests/uploader/utils/fixtures/sample_agent.zip')
.then(value => {
expect(value).to.be.eql('successe');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it looks like typo

});
});
});
});
});
});
Binary file not shown.
20 changes: 20 additions & 0 deletions functions/tests/uploader/utils/get-access-token.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const {expect} = require('chai');
const accessToken = require('../../../uploader/utils/get-access-token');

describe('uploader', () => {
describe('utils', () => {
describe('get-access-token', () => {
describe('getBasicHeaderRequest', () => {
it('should be defined', () => {
expect(accessToken.getBasicHeaderRequest).to.be.ok;
});
});
describe('getAccessToken', () => {
it('should be defined', () => {
expect(accessToken.getAccessToken).to.be.ok;
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like this new line should be between describe calls.

});
});
});
});
19 changes: 19 additions & 0 deletions functions/tests/uploader/utils/prepare-agent.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const {expect} = require('chai');
const prepareAgent = require('../../../uploader/utils/prepare-agent');

describe('uploader', () => {
describe('utils', () => {
describe('prepare-agent', () => {
describe('prepareAgentToPostToDF', () => {
it('should be defined', () => {
expect(prepareAgent.prepareAgentToPostToDF).to.be.ok;
});
});
describe('prepareAgentToFetchFromDF', () => {
it('should be defined', () => {
expect(prepareAgent.prepareAgentToFetchFromDF).to.be.ok;
});
});
});
});
});
35 changes: 35 additions & 0 deletions functions/uploader/agent/agent-downloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const axios = require('axios');
const debug = require(`debug`)(`ia:uploader:agent:agent-downloader:debug`);
const error = require(`debug`)(`ia:uploader:agent:agent-downloader:error`);
const {getAccessToken} = require('../utils/get-access-token');
const {prepareAgentToFetchFromDF} = require('../utils/prepare-agent');

const config = require('../config');
const mustache = require('mustache');

function downloadAgent () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add jsdoc here, and every other function. Short description and @param and @returns.

return getAccessToken()
.then(accessToken => {
var agentId = config.agent.ID;
return axios(mustache.render(
config.dfendpoints.AGENT_EXPORT_URL,
{
agentId,
accessToken,
}
), {method: `POST`});
})
.then(res => res.data)
.then(data => {
debug(data);
return prepareAgentToFetchFromDF(data.response.agentContent, '../../agent/', 'agent.zip');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'../../agent/', 'agent.zip'
should be in config

})
.catch(e => {
error(`Get error in downloading agent from DF, error: ${e}`);
return Promise.reject(e);
});
}

module.exports = {
downloadAgent,
};
36 changes: 36 additions & 0 deletions functions/uploader/agent/agent-uploader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const axios = require('axios');
const debug = require(`debug`)(`ia:uploader:agent:agent-uploader:debug`);
const error = require(`debug`)(`ia:uploader:agent:agent-uploader:error`);
const util = require(`util`);
const {getAccessToken} = require('../utils/get-access-token');
const {prepareAgentToPostToDF} = require('../utils/prepare-agent');

const config = require('../config');
const mustache = require('mustache');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not written rule - but it is more like a recommendation to put all imports in alpha betting order and split them into sections - of local modules (../config) and third party libs (ex axios). I'm trying to follow this rule in every module - it helps to reduce imports duplications and make it more consistent.


function uploadAgent () {
return Promise.all([getAccessToken(), prepareAgentToPostToDF('../../agent', './')])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'../../agent', './'
should be in config

.then(values => {
const [accessToken, base64] = values;
var agentId = config.agent.ID;
return axios(mustache.render(
config.dfendpoints.AGENT_IMPORT_URL,
{
agentId,
accessToken,
}
), {headers: {'content-type': `application/json; charset=UTF-8`}, method: `POST`, data: JSON.stringify({'agentContent': base64})});
})
.then(res => res.data)
.then(data => {
debug(util.inspect(data, false, null));
})
.catch(e => {
error(`Get error in uploading agent from DF, error: ${e}`);
return Promise.reject(e);
});
}

module.exports = {
uploadAgent,
};
Binary file added functions/uploader/agent/sample_agent.zip
Binary file not shown.
31 changes: 18 additions & 13 deletions functions/uploader/config.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
module.exports = {
dfendpoints: {
DF_ENTITY_POST_URL: 'https://dialogflow.googleapis.com/v2/projects/internet-archive/agent/entityTypes/{{entityid}}/entities:batchUpdate?access_token={{accesstoken}}',
uploader: {
collection: {
ID: 'etree',
FIELD: 'creator',
LIMIT: 30000,
ENTITY_ID: 'ENTITY_ID will goes here',
},
genres: {
ID: 'georgeblood',
FIELD: 'subject',
LIMIT: 30000,
ENTITY_ID: 'ENTITY_ID will goes here',
},
AGENT_EXPORT_URL: 'https://dialogflow.googleapis.com/v2/projects/{{agentId}}/agent:export?access_token={{accessToken}}',
AGENT_IMPORT_URL: 'https://dialogflow.googleapis.com/v2/projects/{{agentId}}/agent:import?access_token={{accessToken}}',
},
agent: {
ID: 'agent_id_will_goes_here'
},
uploader: {
collection: {
ID: 'etree',
FIELD: 'creator',
LIMIT: 30000,
ENTITY_ID: 'ENTITY_ID will goes here',
},
genres: {
ID: 'georgeblood',
FIELD: 'subject',
LIMIT: 30000,
ENTITY_ID: 'ENTITY_ID will goes here',
},
},
};
9 changes: 9 additions & 0 deletions functions/uploader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,23 @@ const error = require(`debug`)(`ia:uploader:index:error`);

const {uploadCollection} = require('./entities/collection');
const {uploadGenres} = require('./entities/genres');
const {downloadAgent} = require('./agent/agent-downloader');
const {uploadAgent} = require('./agent/agent-uploader');

const ALL = `all`;

const entities = {
collection: uploadCollection,
genres: uploadGenres,
};

const agent = {
upload: uploadAgent,
download: downloadAgent,
};

const all = {
agent: agent,
entities: entities,
};

Expand Down
41 changes: 41 additions & 0 deletions functions/uploader/utils/base64.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const error = require(`debug`)(`ia:uploader:utils:base64:error`);
const fs = require('fs');

// function to encode file data to base64 encoded string
function base64Encode (file) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name of function could confuse. Because data flow direction is not clear here -- "from file" or "to file", and why do we need file argument here

// read binary data
return new Promise(function (resolve, reject) {
fs.readFile(file, function (err, bitmap) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think bytes name could be better here?

console.log(Buffer.from(bitmap).toString('base64'));
if (err) return reject(err);
else return resolve(Buffer.from(bitmap).toString('base64'));
});
})
.catch(function (err) {
console.log(err);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you use debug module here?

});
}

// function to create file from base64 encoded string
function base64Decode (base64str, file) {
// create buffer object from base64 encoded string, it is important to tell the constructor that the string is base64 encoded
var bitmap = Buffer.from(base64str, 'base64');
// write buffer to file
return new Promise(function (resolve, reject) {
fs.writeFile(file, bitmap, 'utf8', function (err) {
console.log(file);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be better to use debug module here

console.log(bitmap);
console.log(err);
if (err) return reject(err);
else return resolve('success');
});
})
.catch(function (err) {
console.log(err);
});
}

module.exports = {
base64Encode,
base64Decode,
};
44 changes: 44 additions & 0 deletions functions/uploader/utils/get-access-token.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
const debug = require(`debug`)(`ia:uploader:utils:get-access-token:debug`);
const error = require(`debug`)(`ia:uploader:utils:get-access-token:error`);
const util = require(`util`);
const { exec } = require('child-process-promise');

function getBasicHeaderRequest () {
return getAccessToken()
.then(accessToken => {
const basicHeaderRequest = {
'content-type': `application/json; charset=UTF-8`,
'authorization': `BEARER ${accessToken}`
};
return Promise.resolve(basicHeaderRequest);
})
.catch(e => {
error(`Get error in fetching basicHeaderRequest, error: ${util.inspect(e, false, null)}`);
return Promise.reject(e);
});
}

function getAccessToken () {
debug('getting access token');
return exec('gcloud auth print-access-token')
.then(values => {
var stdout = values.stdout;
var stderr = values.stderr;
if (stdout) {
var filteredstdout = stdout.replace(/\n$/, '');
debug(util.inspect(filteredstdout, false, null));
return Promise.resolve(filteredstdout);
} else if (stderr) {
error(stderr);
return Promise.reject(new Error('ERROR: ' + stderr));
} else {
error('Having trouble with GCloud execution');
return Promise.reject(new Error('ERROR: Having trouble with GCloud execution'));
}
});
}

module.exports = {
getBasicHeaderRequest,
getAccessToken,
};
43 changes: 43 additions & 0 deletions functions/uploader/utils/prepare-agent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const error = require(`debug`)(`ia:uploader:agent:utils:prepare_agent:error`);
const debug = require(`debug`)(`ia:uploader:agent:utils:prepare_agent:debug`);
const fs = require('fs');
const unzip = require(`unzip`);
const zip = require('zipfolder');

const {base64Encode, base64Decode} = require('./base64');

// function to encode file data to base64 encoded string
function prepareAgentToPostToDF (folderPath, zipPath) {
return zip.zipFolder({folderPath: folderPath, targetFolderPath: zipPath})
.then(function (path) {
var encodedBase64 = base64Encode(path);
debug(encodedBase64);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need to log Promise here? You would get the same Promise log entry here in any case.

return Promise.resolve(encodedBase64);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really need Promise.resolve here? base64Encode returns Promise, so it should work without safety wrapper Promise.resolve.

});
}

// function to create file from base64 encoded string
function prepareAgentToFetchFromDF (base64str, folderPath, zipPath) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add more details in jsdoc

return base64Decode(base64str, zipPath)
.then(isDone => {
return fs.createReadStream(zipPath).pipe(unzip.Extract({ path: folderPath }));
})
.then(close => {
debug(close);
return new Promise(function (resolve, reject) {
fs.unlink(zipPath, function (err) {
if (err) return reject(err);
else return resolve(zipPath);
});
});
})
.catch(e => {
error(`Get error in generating agent.zip, error: ${e}`);
return Promise.reject(e);
});
}

module.exports = {
prepareAgentToPostToDF,
prepareAgentToFetchFromDF,
};