Skip to content
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

Alpha 2 #72

Merged
merged 6 commits into from
Jan 7, 2024
Merged
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
49 changes: 36 additions & 13 deletions cli/mrcli-company.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import CLIOutput from '../src/cli/output.js'
import FilesystemOperators from '../src/cli/filesystem.js'
import ArchivePackage from '../src/cli/archive.js'
import ora from 'ora'
import WizardUtils from '../src/cli/commonWizard.js'

// Related object type
const objectType = 'Companies'
Expand All @@ -33,8 +34,16 @@ const environment = new Environmentals(
// Filesystem object
const fileSystem = new FilesystemOperators()

// Create the environmental settings
const myArgs = environment.parseCLIArgs()
// Process the command line options
let myProgram = environment.parseCLIArgs(true)
myProgram
.option('-o, --allow_orphans', 'Allow orphaned interactions to remain in the system', false)

// Parse the command line arguments into myArgs and obtain the options
let myArgs = myProgram.parse(process.argv)
myArgs = myArgs.opts()

// Read the environmental settings
const myConfig = environment.readConfig(myArgs.conf_file)
let myEnv = environment.getEnv(myArgs, myConfig)
const accessToken = await environment.verifyAccessToken()
Expand All @@ -43,6 +52,9 @@ const processName = 'mrcli-company'
// Output object
const output = new CLIOutput(myEnv, objectType)

// CLI Wizard object
const wutils = new WizardUtils()

// Construct the controller objects
const companyCtl = new Companies(accessToken, myEnv.gitHubOrg, processName)
// const studyCtl = new Studies(accessToken, myEnv.gitHubOrg, processName)
Expand Down Expand Up @@ -213,17 +225,28 @@ if (myArgs.report) {
}
// TODO: Need to reimplement the below to account for GitHub
} else if (myArgs.delete) {
console.error('ERROR (%d): Delete not implemented.', -1)
process.exit(-1)
// Delete an object
const [success, stat, resp] = await companyCtl.deleteObj(myArgs.delete)
if(success) {
console.log(`SUCCESS: deleted company object.`)
process.exit(0)
} else {
console.error('ERROR (%d): Unable to delete company object.', -1)
process.exit(-1)
}
// Use operationOrNot to confirm the delete
const deleteOrNot = await wutils.operationOrNot(`Preparing to delete the company [${myArgs.delete}], are you sure?`)
if(!deleteOrNot) {
console.log(`INFO: Delete of [${myArgs.delete}] cancelled.`)
process.exit(0)
}
// If allow_orphans is set log a warning to the user that they are allowing orphaned interactions
if(myArgs.allow_orphans) {
console.log(chalk.bold.yellow(`WARNING: Allowing orphaned interactions to remain in the system.`))
}
// Delete the object
const mySpinner = new ora(`Deleting company [${myArgs.delete}] ...`)
mySpinner.start()
const [success, stat, resp] = await companyCtl.deleteObj(myArgs.delete, myArgs.allow_orphans)
mySpinner.stop()
if(success) {
console.log(`SUCCESS: ${stat.status_msg}`)
process.exit(0)
} else {
console.log(`ERROR: ${stat.status_msg}`)
process.exit(-1)
}
} else if (myArgs.add_wizard) {
myEnv.DEFAULT = {company: 'Unknown'}
const newCompany = new AddCompany(myEnv, companyCtl)
Expand Down
24 changes: 16 additions & 8 deletions cli/mrcli-interaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@ import CLIOutput from '../src/cli/output.js'
import FilesystemOperators from '../src/cli/filesystem.js'
import ArchivePackage from '../src/cli/archive.js'
import ora from 'ora'

// External modules
import chalk from 'chalk'
import WizardUtils from "../src/cli/commonWizard.js"

// Reset the status of objects for caffiene reprocessing
async function resetStatuses(interactionType, interactionCtl, objStatus=0) {
Expand Down Expand Up @@ -78,6 +76,9 @@ const processName = 'mrcli-interaction'
// Output object
const output = new CLIOutput(myEnv, objectType)

// Common wizard utilities
const wutils = new WizardUtils(objectType)

// Construct the controller objects
const companyCtl = new Companies(accessToken, myEnv.gitHubOrg, processName)
const interactionCtl = new Interactions(accessToken, myEnv.gitHubOrg, processName)
Expand Down Expand Up @@ -196,15 +197,22 @@ if (myArgs.report) {
process.exit(-1)
}
} else if (myArgs.delete) {
console.error('ERROR (%d): Delete not implemented.', -1)
process.exit(-1)
// Delete an object
// Use operationOrNot to confirm the delete
const deleteOrNot = await wutils.operationOrNot(`Preparing to delete the interaction [${myArgs.delete}], are you sure?`)
if(!deleteOrNot) {
console.log(`INFO: Delete of [${myArgs.delete}] cancelled.`)
process.exit(0)
}
// Delete the object
const mySpinner = new ora(`Deleting interaction [${myArgs.delete}] ...`)
mySpinner.start()
const [success, stat, resp] = await interactionCtl.deleteObj(myArgs.delete)
mySpinner.stop()
if(success) {
console.log(`SUCCESS: deleted interaction object.`)
console.log(`SUCCESS: ${stat.status_msg}`)
process.exit(0)
} else {
console.error('ERROR (%d): Unable to delete interaction object.', -1)
console.log(`ERROR: ${stat.status_msg}`)
process.exit(-1)
}
} else if (myArgs.add_wizard) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mediumroast_js",
"version": "0.4.19",
"version": "0.4.25",
"description": "A Command Line Interface (CLI) and Javascript SDK to interact with mediumroast.io.",
"main": "cli/mrcli.js",
"scripts": {
Expand Down
42 changes: 10 additions & 32 deletions src/api/authorize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import crypto from "node:crypto"
import open from "open"
import * as octoDevAuth from '@octokit/auth-oauth-device'
import chalk from "chalk"
import Table from 'cli-table'


class Auth0Auth {
Expand Down Expand Up @@ -226,17 +227,17 @@ class GitHubAuth {
clientId: env.clientId,
onVerification(verifier) {
deviceCode = verifier.device_code
// Print the verification artifact to the console
console.log(
chalk.blue.bold(
`
If your platform supports this, opening your browser. Otherwise, navigate to the URL below in your browser.
\n\tAuthorization URL: ${verifier.verification_uri}
Type or paste the code below into your browser and follow the prompts to authorize this client.
\n\tDevice authorization code: ${verifier.user_code}
`

)
chalk.blue.bold(`If your OS supports it, opening your browser, otherwise, navigate to the website below.\n`)
)
const table = new Table({
rows: [
[chalk.blue.bold(`Authorization website:`), chalk.bold.red(verifier.verification_uri)],
[chalk.blue.bold(`Authorization code:`), chalk.bold.red(verifier.user_code)]
]
})
console.log(table.toString())
open(verifier.verification_uri)
}
})
Expand All @@ -253,29 +254,6 @@ class GitHubAuth {
return accessToken
}


/**
* @function refreshAccessToken
* @description Assuming device flow authorization, generate a new access token from a valid refresh token
* @param {String} clientId - The clientId for the device
* @param {String} refreshToken - A valid unexpired refresh token
* @param {String} accessTokenUrl - Url needed to obtain the access token using a refresh token
* @param {String} contentType - Accepted content type defaults to 'application/json'
* @param {String} grantType - Targeted grant type defaults to 'refresh_token'
* @returns {Array} An array with position 0 being boolean to signify sucess/failure and position 1 being token data/err string
*/
async refreshAccessToken(clientId, refreshToken, accessTokenUrl, contentType='application/json', grantType='refresh_token'){

try {
const resp = await axios.post(accessTokenUrl, null, {
params: {grant_type: grantType, refresh_token: refreshToken,client_id: clientId},
headers: {Accept: contentType},
})
return [true, resp.data]
} catch (err) {
return [false, err.message]
}
}
}

export {Auth0Auth, GitHubAuth}
101 changes: 94 additions & 7 deletions src/api/gitHubServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// Import required modules
import GitHubFunctions from './github.js'
import { createHash } from 'crypto'
import ora from 'ora'


class baseObjects {
Expand Down Expand Up @@ -73,12 +74,20 @@ class baseObjects {
* @param {String} value - the value for the defined attribute
* @returns {Array} the results from the called function mrRest class
*/
async findByX(attribute, value) {
async findByX(attribute, value, allObjects=null) {
if(attribute === 'name') {
value = value.toLowerCase()
}
// console.log(`Searching for ${this.objType} where ${attribute} = ${value}`)
let myObjects = []
const allObjectsResp = await this.serverCtl.readObjects(this.objType)
const allObjects = allObjectsResp[2].mrJson
if(allObjects === null) {
const allObjectsResp = await this.serverCtl.readObjects(this.objType)
allObjects = allObjectsResp[2].mrJson
}
for(const obj in allObjects) {
if(allObjects[obj][attribute] === value) {
let currentObject
attribute == 'name' ? currentObject = allObjects[obj][attribute].toLowerCase() : null
if(currentObject === value) {
myObjects.push(allObjects[obj])
}
}
Expand Down Expand Up @@ -117,9 +126,8 @@ class baseObjects {
* @returns {Array} the results from the called function mrRest class
* @todo implment when available in the backend
*/
async deleteObj(id, endpoint='delete') {
const fullEndpoint = '/' + this.apiVersion + '/' + this.objType + '/' + endpoint
return this.rest.deleteObj(fullEndpoint, {"id": id})
async deleteObj(objName, source, repoMetadata=null, catchIt=true) {
return await this.serverCtl.deleteObject(objName, source, repoMetadata, catchIt)
}

/**
Expand Down Expand Up @@ -223,6 +231,77 @@ class Companies extends baseObjects {
]
return await super.updateObj(name, key, value, dontWrite, system, whiteList)
}

async deleteObj(objName, allowOrphans=false) {
let source = {
from: 'Companies',
to: ['Interactions']
}

// If allowOrphans is true then use the baseObjects deleteObj
if(allowOrphans){
return await super.deleteObj(objName, source)
}

// Catch the Companies and Interaction containers
// Assign repoMetadata to capture Companies nad Studies
let repoMetadata = {
containers: {
Companies: {},
Interactions: {}
},
branch: {}
}
const caught = await this.serverCtl.catchContainer(repoMetadata)

// Use findByX to get all linkedInteractions
// NOTE: This has to be done here because the company has been deleted in the next step
const getCompanyObject = await this.findByX('name', objName, caught[2].containers.Companies.objects)
if(!getCompanyObject[0]) {
return getCompanyObject
}
const linkedInteractions = getCompanyObject[2][0].linked_interactions

// Delete the company
// Use deleteObj to delete the company
const deleteCompanyObjResp = await this.serverCtl.deleteObject(
objName,
source,
caught[2],
false
)
if(!deleteCompanyObjResp[0]) {
return deleteCompanyObjResp
}

// Delete all linkedInteractions
// Update source to be from the perspective of the Interactions
source = {
from: 'Interactions',
to: ['Companies']
}
// Use deleteObect to delete all linkedInteractions
for(const interaction in linkedInteractions) {
const deleteInteractionObjResp = await this.serverCtl.deleteObject(
interaction,
source,
caught[2],
false
)
if(!deleteInteractionObjResp[0]) {
return deleteInteractionObjResp
}
}

// Release the container
const relased = await this.serverCtl.releaseContainer(caught[2])
if(!relased[0]) {
return relased
}

// Return the response
return [true, {status_code: 200, status_msg: `deleted company [${objName}] and all linked interactions`}, null]
}
}


Expand Down Expand Up @@ -251,6 +330,14 @@ class Interactions extends baseObjects {
]
return await super.updateObj(name, key, value, dontWrite, system, whiteList)
}

async deleteObj(objName) {
const source = {
from: 'Interactions',
to: ['Companies']
}
return await super.deleteObj(objName, source)
}
}

// Export classes for consumers
Expand Down
Loading