From 8cb22e6739a0de7acb790dad17fc11b43766865e Mon Sep 17 00:00:00 2001 From: Igor Turko Date: Wed, 14 Sep 2022 22:25:27 +0300 Subject: [PATCH] Igors assignment --- src/auth.ts | 9 +++-- src/getConfig.ts | 13 +++--- src/getData.ts | 103 ++++++++++++++++++++++++++++++++++++++++++++++- src/getSchema.ts | 64 +++++++++++++++-------------- 4 files changed, 146 insertions(+), 43 deletions(-) diff --git a/src/auth.ts b/src/auth.ts index 92e1560..1b1dec8 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,4 +1,9 @@ const clientIdProperty = 'clientId'; +const cc = DataStudioApp.createCommunityConnector(); + +const isAdminUser = () => { + return true; +}; const resetAuth = () => PropertiesService.getUserProperties().deleteProperty(clientIdProperty); @@ -7,9 +12,7 @@ const isAuthValid = () => !!PropertiesService.getUserProperties().getProperty(clientIdProperty); const getAuthType = () => { - const cc = DataStudioApp.createCommunityConnector(); - - return cc.newAuthTypeResponse().setAuthType(cc.AuthType.USER_TOKEN).build(); + return cc.newAuthTypeResponse().setAuthType(cc.AuthType.NONE).build(); }; interface SetCredentialsInput { diff --git a/src/getConfig.ts b/src/getConfig.ts index 099543f..9b6548b 100644 --- a/src/getConfig.ts +++ b/src/getConfig.ts @@ -1,6 +1,5 @@ const getConfig = () => { const cc = DataStudioApp.createCommunityConnector(); - const config = cc.getConfig(); // Add name input @@ -9,7 +8,7 @@ const getConfig = () => { .setId('name') .setName('Name') .setHelpText('Your name for registering with the API') - .setAllowOverride(true); + .setAllowOverride(false); // Add email input config @@ -17,22 +16,20 @@ const getConfig = () => { .setId('email') .setName('Email') .setHelpText('Your email for registering with the API') - .setAllowOverride(true); + .setAllowOverride(false); // Add an input for specifying a maximum number of posts to retrieve config - .newSelectMultiple() + .newSelectSingle() .setId('postLimit') .setName('Post limit') .setHelpText( 'Maximum number of posts that will be fetched from the API' ) + .addOption(config.newOptionBuilder().setValue('10').setLabel('10')) .addOption(config.newOptionBuilder().setValue('100').setLabel('100')) .addOption(config.newOptionBuilder().setValue('1000').setLabel('1000')) - .addOption( - config.newOptionBuilder().setValue('10000').setLabel('10000') - ) - .setAllowOverride(true); + .setAllowOverride(false); return config.build(); }; diff --git a/src/getData.ts b/src/getData.ts index 4a0f907..3a4fc92 100644 --- a/src/getData.ts +++ b/src/getData.ts @@ -1,3 +1,104 @@ +const cc = DataStudioApp.createCommunityConnector(); + const getData = (request: unknown) => { - // TODO: Implement data fetching + let fields = cc.getFields(); + const fieldIds = request.fields.map((field) => field.name); + const { postLimit = null } = request.configParams; + + fieldIds.forEach((fieldId) => { + fields = _getField(fields, fieldId); + }); + + const requestOptions = { + muteHttpExceptions: true, + method: 'get', + }; + + const slToken = _getSlToken(request); + const httpResponse = UrlFetchApp.fetch( + `https://api.supermetrics.com/assignment/posts?sl_token=${slToken}`, + requestOptions + ); + const statusCode = httpResponse.getResponseCode(); + + if (statusCode !== 200) { + Logger.log('An exception occurred accessing the posts API:'); + Logger.log(statusCode); + Logger.log(httpResponse.getAllHeaders()); + Logger.log(httpResponse.getContentText()); + _sendUserError( + `The API replied with an unsuccessful status code of ${statusCode}` + ); + + return; + } + + const { data = {} } = JSON.parse(httpResponse.getContentText()) || {}; + + const posts = data.posts || []; + const filteredPosts = postLimit ? posts.slice(0, postLimit) : posts; + const normalizedPosts = filteredPosts.map((post) => ({ + values: fieldIds.map((fieldId) => _getDataField(post, fieldId)), + })); + + const result = { + schema: fields.build(), + rows: normalizedPosts, + filtersApplied: false, + }; + + Logger.log('getData finished with: '); + Logger.log(result); + + return result; +}; + +const _getSlToken = (request) => { + const { name, email } = request.configParams; + const requestOptions = { + muteHttpExceptions: true, + method: 'post', + payload: { + client_id: 'ju16a6m81mhid5ue1z3v2g0uh', + email: email, + name: name, + }, + }; + + const httpResponse = UrlFetchApp.fetch( + 'https://api.supermetrics.com/assignment/register', + requestOptions + ); + const statusCode = httpResponse.getResponseCode(); + + if (statusCode !== 200) { + Logger.log('An exception occurred accessing the register API:'); + Logger.log(statusCode); + Logger.log(httpResponse.getAllHeaders()); + Logger.log(httpResponse.getContentText()); + _sendUserError( + `The API replied with an unsuccessful status code of ${statusCode}` + ); + + return; + } + + const { data = {} } = JSON.parse(httpResponse.getContentText()) || {}; + + return data.sl_token; +}; + +const _sendUserError = (message) => { + cc.newUserError().setText(message).throwException(); +}; + +const _getDataField = (post, fieldId) => { + switch (fieldId) { + case 'userName': + return post.from_name; + case 'postLength': + return (post.message || '').length; + default: + throw new Error(`Invalid fieldId: ${fieldId}`); + } }; diff --git a/src/getSchema.ts b/src/getSchema.ts index 315dbae..85feb2a 100644 --- a/src/getSchema.ts +++ b/src/getSchema.ts @@ -1,40 +1,42 @@ -const getSchema = () => { - const cc = DataStudioApp.createCommunityConnector(); +const cc = DataStudioApp.createCommunityConnector(); +const types = cc.FieldType; +const aggregationType = cc.AggregationType; - const fields = cc.getFields(); +const _getField = (fields, fieldId) => { + switch (fieldId) { + case 'userName': + fields + .newDimension() + .setId('userName') + .setName('User name') + .setDescription('Name of user who made the post') + .setType(types.TEXT); + break; + case 'postLength': + fields + .newMetric() + .setId('postLength') + .setName('Post length') + .setDescription('Number of characters in the post') + .setType(types.NUMBER) + .setAggregation(aggregationType.AUTO); + break; + default: + throw new Error(`Invalid fieldId: ${fieldId}`); + } - [ - { - id: 'userName', - name: 'User name', - description: 'Name of user who made the post', - type: cc.FieldType.TEXT, - metOrDim: 'dim', - }, - { - id: 'postLength', - name: 'Post length', - description: 'Number of characters in the post', - type: cc.FieldType.NUMBER, - metOrDim: 'met', - }, - ].forEach((field) => { - const newField = - field.metOrDim === 'dim' - ? fields.newDimension() - : fields.newMetric(); + return fields; +}; + +const getSchema = (request) => { + let fields = cc.getFields(); - newField - .setId(field.id) - .setName(field.name) - .setDescription(field.description) - .setType(field.type); + ['userName', 'postLength'].forEach((fieldId) => { + fields = _getField(fields, fieldId); }); fields.setDefaultDimension('userName'); fields.setDefaultMetric('postLength'); - return { - schema: fields.build(), - }; + return { schema: fields.build() }; };