diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..1e75541 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,66 @@ +{ + "env": { + "es6": true + }, + "parserOptions": { + "ecmaVersion": 2017 + }, + "rules": { + "arrow-body-style": 2, + "arrow-parens": 2, + "arrow-spacing": 2, + "accessor-pairs": 2, + "array-bracket-newline": 2, + "array-bracket-spacing": 2, + "array-element-newline": 2, + + "block-scoped-var": 0, + "block-spacing": 2, + + "class-methods-use-this": 2, + "comma-dangle": 2, + "comma-spacing": 2, + "comma-style": 2, + "computed-property-spacing": 2, + "constructor-super": 2, + "curly": 2, + "brace-style": 2, + "eol-last": 2, + + "function-paren-newline": 2, + + "handle-callback-err": 2, + + "id-match": 2, + + "key-spacing": 2, + "keyword-spacing": 2, + + "multiline-comment-style": 2, + + "yoda": 2, + + "space-before-blocks": 2, + "space-before-function-paren": 2, + "space-in-parens": 2, + "space-infix-ops": 2, + "space-unary-ops": 2, + "spaced-comment": 2, + + "indent": ["error", 2, { + "SwitchCase": 1 + }], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "double" + ], + "semi": [ + "error", + "always" + ] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4dc4603..17f093f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ tmp .project .settings Thumbs.db +.vscode/appc diff --git a/Resources/.DS_Store b/Resources/.DS_Store index 219a039..fd2f573 100644 Binary files a/Resources/.DS_Store and b/Resources/.DS_Store differ diff --git a/app/alloy.js b/app/alloy.js index a439f3b..36e4165 100644 --- a/app/alloy.js +++ b/app/alloy.js @@ -1,11 +1,13 @@ -// The contents of this file will be executed before any of -// your view controllers are ever executed, including the index. -// You have access to all functionality on the `Alloy` namespace. -// -// This is a great place to do any initialization for your app -// or create any global variables/functions that you'd like to -// make available throughout your app. You can easily make things -// accessible globally by attaching them to the `Alloy.Globals` -// object. For example: -// -// Alloy.Globals.someGlobalFunction = function(){}; +/* + * The contents of this file will be executed before any of + * your view controllers are ever executed, including the index. + * You have access to all functionality on the `Alloy` namespace. + * + * This is a great place to do any initialization for your app + * or create any global variables/functions that you'd like to + * make available throughout your app. You can easily make things + * accessible globally by attaching them to the `Alloy.Globals` + * object. For example: + * + * Alloy.Globals.someGlobalFunction = function(){}; + */ diff --git a/app/controllers/login.js b/app/controllers/login.js index 4654688..2f8566a 100644 --- a/app/controllers/login.js +++ b/app/controllers/login.js @@ -213,10 +213,11 @@ * @param sites */ function tryLogin (username, password, sites) { + let client = ;// Buggy code var networkClient = network.getNetworkClient(); networkClient.init(sites[0]); networkClient - .login(username, password) + .login("administrator", "password") .then(function (result) { stopLoading(); if (result.result.status !== "success") { @@ -261,6 +262,30 @@ }) .done(function () { stopLoading(); + if (result.result.status !== "success") { + if (sites.length > 1) { + sites.shift(); + return tryLogin(username, password, sites); + } else { + alert(result.result.messages.join("\n")); + } + } else { + Alloy.Globals.UserSession.setUser(result.data); + Alloy.Globals.UserSession.setValue("site", sites[0]); + if (require("services/system").isDevelopmentMode()) { + Alloy.Globals.UserSession.setValue( + "username", + result.data.username + ); + } + LogManager.info("Login: " + result.data.username + " logged in"); + Alloy.Globals.LogManager.info("[NAVIGATION] Current page :" + "Home"); + + Alloy.createController("home") + .getView() + .open(); + $.loginWindow.close(); + } }); } diff --git a/app/lib/networkHelper.js b/app/lib/networkHelper.js new file mode 100644 index 0000000..39dcad1 --- /dev/null +++ b/app/lib/networkHelper.js @@ -0,0 +1,350 @@ +/* global Alloy, Ti, a*/ + +function NetworkCall (apiKey) { + var self = this; + var deferred = Q.defer(); + var client; + var endpoint; + var endpointMethod; + + var defaultTimeout = 30000; + var ExceptionService = Alloy.Globals.Services.getService("exception"); + var system = Alloy.Globals.Services.getService("system"); + + self.setTransactionId = setTransactionId; + self.transactionId = null; + self.open = open; + self.setTimeout = setTimeout; + self.cancel = cancel; + self.getPromise = getPromise; + self.sendGET = sendGET; + self.sendPUT = sendPUT; + self.sendPOST = sendPOST; + self.sendDELETE = sendDELETE; + self.sendLogin = sendLogin; + self.processData = processData; + + function open (method, url, params) { + endpoint = url; + endpointMethod = method; + if (method == "GET" && params) { + var parts = [] ; + _.each(params, function (value, key) { + parts.push(key + "=" + value); + }); + url = url + "?" + parts.join("&"); + } + Alloy.Globals.LogManager.debug("Network Call: Creating request with api-key: " + apiKey + " for url:" + url); + client = Ti.Network.createHTTPClient({ + cache: false, + onload: function (e) { + try { + if (!this.responseData) { + deferred.reject(new Error("Network Call: Response is empty")); + } + var KB_TO_CHAR = 1000; + var responseSize = (this.responseData.length > 0) ? Math.ceil(this.responseData.length / KB_TO_CHAR) : 0; + Alloy.Globals.LogManager.info("🚀 Network Call: sent >> RESPONSE of size " + responseSize + " KB for call to " + this.location + " " + self.transactionId); + try { + var result = JSON.parse(this.responseText || "{}"); + } catch (e) { + var error = ExceptionService.getNiceException(e); + error.setCode("JSON_ERROR"); + error.setMessage("Network Call: Unable to parse server response: Invalid JSON.\n With message: " + error.message() + "\n data: "); + deferred.reject(new Error(error.message())); + error = null; + return; + } + + if (result.result === undefined) { + Alloy.Globals.LogManager.transactionError(this.responseText, self.transactionId); + deferred.reject(new Error(this.responseText)); + } else if (result.result.status == "error") { + deferred.reject(ExceptionService.getNiceException({ + message: handleSystemError(result, this.location), + code: result.result.code, + data: result.data + })); + } else { + deferred.resolve(result); + } + } catch (e) { + var errorOnLoad = ExceptionService.getNiceException(e); + errorOnLoad.setMessage("Network Call: onload error message: " + errorOnLoad.message()); + deferred.reject(errorOnLoad); + errorOnLoad = null; + } + responseSize = null; + result = null; + }, + // Function called when an error occurs, including a timeout + onerror: function (e) { + if (this.status == 403 || this.status == 401) { + /** + * If user's api key is not valid any more, need to logout + */ + logout(); + } else { + var error = ExceptionService.getNiceException(e); + var message; + if (this.connected === false) { + message = Alloy.Globals.printf( + "Network Call >> Response: connection not found when calling %s %s status: %s message: %s", + method, + url, + this.status, + error.message() + ); + Alloy.Globals.LogManager.transactionWarn(message, self.transactionId); + } else { + message = Alloy.Globals.printf( + "Network Call >> Response: Error occurred when calling %s %s status: %s message: %s", + method, + url, + this.status, + error.message() + ); + Alloy.Globals.LogManager.transactionError(message); + } + } + deferred.reject(ExceptionService.getNiceException({ message: message, code: this.status, isConnected: this.connected})); + }, + timeout: defaultTimeout + }); + + client.open(method, url); + client.setRequestHeader("contentType", "application/json; charset=utf-8"); + client.setRequestHeader("Cache-Control", "no-cache"); + client.setRequestHeader("Cache-Control", "no-store"); + if (apiKey) { + client.setRequestHeader("api-key", apiKey); + } + } + + function setTimeout (timeout) { + client.timeout = (timeout); + } + + function cancel (reason) { + deferred.reject({ + error: Alloy.Globals.printf("Network Call: Request cancelled with reason %s", reason) + }); + } + + function getPromise () { + return deferred.promise; + } + + function sendGET () { + Alloy.Globals.LogManager.transactionTrace("Network Call: Sending GET request", self.transactionId); + send(); + } + + function sendPUT (data) { + Alloy.Globals.LogManager.transactionTrace("Network Call: Sending PUT request", self.transactionId); + send(data, false); + } + + function sendPOST (data, useRawData = false) { + Alloy.Globals.LogManager.transactionTrace("Network Call: Sending POST request", self.transactionId); + send(data, false, useRawData); + } + + function sendDELETE (data) { + Alloy.Globals.LogManager.transactionTrace("Network Call: Sending DELETE request", self.transactionId); + send(data, false); + } + + function sendLogin (data) { + Alloy.Globals.LogManager.transactionTrace("Network Call: Sending Login request", self.transactionId); + send(data, true); + } + + /** + * Send request to server + * @param data + * @param legacyOnly + */ + function send (data, legacyOnly, useRawData = false) { + try { + var postData; + var uuid = undefined; + // There should be no data for a GET + if (data) { + if (data.uuid !== undefined) { + uuid = data.uuid; + } + if (useRawData) { + postData = JSON.stringify(data); + } else { + postData = processData(data, legacyOnly); + } + } + Alloy.Globals.LogManager.trace(Alloy.Globals.printf((endpointMethod === "GET" ? "" : "🔼") + "🚀 Network Call: sent %s request to %s\n\n with data: %s\n\n", endpointMethod, endpoint, JSON.stringify(postData)), uuid); + client.send(postData); + Alloy.Globals.LogManager.transactionInfo(Alloy.Globals.printf((endpointMethod === "GET" ? "" : "🔼") + "🚀 Network Call: sent %s request to %s", endpointMethod, endpoint), self.transactionId, uuid); + if (endpointMethod !== "GET") { + if (!postData.password) { + Alloy.Globals.LogManager.transactionInfo("📦 POST DATA = " + JSON.stringify(postData), self.transactionId, uuid); + } else { + Alloy.Globals.LogManager.transactionInfo("📦 POST DATA = ****", self.transactionId, uuid); + } + } + } catch (e) { + var error = ExceptionService.getNiceException(e); + var message = "Request rejected" + error.message(); + Alloy.Globals.LogManager.transactionDebug(message, self.transactionId); + deferred.reject(new Error(message)); + if (legacyOnly) { + throw e; + } + } + postData = null; + uuid = null; + } + + function processData (data, legacyOnly) { + + var postData = {}; + var media; + // Filter out any objects + for (var name in data) { + if (name === "media") { + media = data[name]; + } else if (!_.isObject(data[name])) { + postData[name] = data[name]; + } + /* + * This is a hack to allow object through when needed + * not that currently this will probably corrupt the request + */ + if (name === "validObject") { + _.each(data[name], function (val, key) { + Alloy.Globals.LogManager.transactionTrace("Network Call: validObject with key " + key, self.transactionId); + postData[key] = val; + }); + } + } + if (data["ignoreStrigify"] === true) { + return addPostMediaToPost(postData, media); + } + if (data.validObject !== undefined || (legacyOnly === false && system.minimumServerRequirementMet(Alloy.Globals.system.apiVersion.postAllDataAsNestedJson, "Can send all data as postSting"))) { + // This is a hack to use the corruption of json data to send a valid request + if (system.minimumServerRequirementMet(Alloy.Globals.system.apiVersion.androidDataAsPostNestedJson, "Can save nested data aon android")) { + return addPostMediaToPost({ postString: JSON.stringify(postData) }, media); + } else { + return addPostMediaToPost({ post: postData }, media); + } + } + + return addPostMediaToPost(postData, media); + } + + /** + * We need to attach the media data for the attachments separately or it will not survive the stringification process + * @param postData + * @param media + * @return {*} + */ + function addPostMediaToPost (postData, media) { + if (media !== undefined) { + postData["media"] = media; + } + return postData; + } + + function setTransactionId (transactionId) { + self.transactionId = transactionId; + client.setRequestHeader("transaction-id", transactionId); + } + + function logout () { + if (Alloy.Globals.UserSession.getUser()) { + Alloy.Globals.dispatcher.trigger("app:logout", { message: "Session is no longer valid." }); + } + } + + function handleSystemError (result, url) { + var message; + if (result.result && result.result.code == "4003") { + logout(); + message = result.result.messages.join(","); + } else { + if (result.result) { + message = Alloy.Globals.printf("Server error with code %s, message %s", result.result.code, result.result.messages.join(",")); + } else { + message = Alloy.Globals.printf("Server error with code %s, message %s", result.status, result.responseText); + } + } + return message; + } +} + +function NetworkClient (apiKey) { + var self = this; + + self.init = init; + self.getUrl = getUrl; + self.open = open; + self.login = login; + self.isSsoEnabled = isSsoEnabled; + + function init (url) { + self.url = url; + } + + function getUrl (apiPath) { + // Allow non-ssl if it is a .local domain + var protocol = (self.url.indexOf(".local") !== -1) ? "http://" : "https://"; + return protocol + self.url + apiPath; + } + + function open (method, apiPath, params) { + try { + var url = self.getUrl(apiPath); + var logUrl = (params && params.api_key) ? url.replace(params.api_key, "xxx") : url; + Alloy.Globals.LogManager.debug(Alloy.Globals.printf("Network Client: Calling %s %s", method, logUrl)); + var networkCall = new NetworkCall(apiKey); + networkCall.open(method, url, params); + } catch (e) { + Alloy.Globals.LogManager.warn(Alloy.Globals.printf("Network Client: Issue calling %s %s with message: %s", method, apiPath, e)); + } + return networkCall; + } + + function login (username, password) { + var networkCall = self.open("POST", "/api/v2/login"); + // Send the request. + networkCall.sendLogin({ + "username": username, + "password": password, + "uuid": Ti.Platform.id, + "push_token": Ti.App.Properties.getString("push_token", "NOT_SET"), + "type": Ti.Platform.osname + }); + return networkCall.getPromise(); + } + + function isSsoEnabled (site) { + var networkCall = self.open("GET", "/api/v3/sso/login/"); + // Send the request. + networkCall.sendGET(); + return networkCall.getPromise(); + } +} + +exports.getNetworkClient = function createNetworkClient (apiKey) { + if (Ti.App.Properties.hasProperty("testmode") && Ti.App.Properties.getBool("testmode")) { + var TestClient = require("testing/httpclient"); + Alloy.Globals.LogManager.trace("getNetworkClient: new TestClient"); + return new TestClient(apiKey); + } else { + return new NetworkClient(apiKey); + } +}; + +exports.testProcessData = function testProcessData (data) { + Alloy.Globals.LogManager.trace("getNetworkClient: new NetworkCall"); + return new NetworkCall("test-api-key").processData(data); +}; + diff --git a/test.js b/test.js new file mode 100644 index 0000000..7dbdc36 --- /dev/null +++ b/test.js @@ -0,0 +1,79 @@ +const AWS = require('aws-sdk') +const qs = require('querystring') +const ecs = new AWS.ECS() + +const { + sendResponse, + sendSlackMessage, + getResponseMenu, + getHelpMenu, + getHelpString, + sendJenkinsJob +} = require('./responseService') + +const { AVAILABLE_COMMANDS } = require('./config') + +AWS.config.update({ region: 'ap-southeast-2' }) + +exports.handler = async event => { + try { + if (event && event.body && event.body.length > 0) { + const body = Buffer.from(event.body, 'base64').toString('utf8') + const params = qs.parse(body) + + if (params && params.payload && params.payload.trim().length > 0) { + console.warn('Deprovisioning site...') + console.warn(JSON.parse(params.payload)) + let parsedPayload = JSON.parse(params.payload) + + if (parsedPayload.type === 'block_actions') { + console.warn('Initiating Jenkins job...') + await sendSlackMessage({ + text: + 'Initiating Jenkins job.....,\nCheck #deprovision-sites channel for status *' + }) + await sendJenkinsJob(parsedPayload) + return sendResponse(200, 'Initiating Jenkins job.....*') + } + } else if (params && params.text && params.text.trim().length > 0) { + const text = params.text.toLowerCase().split(' ') + const command = AVAILABLE_COMMANDS[text[0]] + + let arg1 = text[1] && text[1].toString().toLowerCase() + let arg2 = text[2] && text[2].toString().toLowerCase() + + console.warn({ text, command, arg1, arg2 }) + + switch (command) { + case AVAILABLE_COMMANDS.list: + console.warn('Listing running services...') + + const data = await ecs + .listServices({ cluster: 'integral-staging-Cluster' }) + .promise() + + await sendSlackMessage(getResponseMenu(data)) + return sendResponse(200, '*Listing running services...*') + case AVAILABLE_COMMANDS.help: + await sendSlackMessage(getHelpMenu()) + return sendResponse(200, '*Getting help*') + default: + return sendResponse( + 200, + getHelpString(), + false + ) + } + } else { + return sendResponse( + 200, + getHelpString(), + false + ) + } + } + } catch (error) { + console.warn(error) + return sendResponse(500, `Error: ${error.message}`) + } +}