Skip to content

Commit

Permalink
Merge pull request #1098 from crowdbotics/qa
Browse files Browse the repository at this point in the history
Release QA to Production
  • Loading branch information
Cody Redmond authored Feb 15, 2024
2 parents 9ee1682 + 5fa63d5 commit 6059531
Show file tree
Hide file tree
Showing 13 changed files with 198 additions and 65 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ Create new modules and test/validate your work locally before submitting a PR:
yarn run parse
```

Install modules globally to your system:
```sh
npm install -g cb
```

### macOS config
- make sure to have a compatible version of urllib3 with openssl. urllib3 v2.0 or higher is compatible with OpenSSL 1.1.1 or higher
Expand Down
64 changes: 39 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Crowdbotics Modules tool
*
* Run it anywhere with: npx crowdbotics/modules
* Run it anywhere with: cb
*
* Commands available:
* - parse
Expand Down Expand Up @@ -36,7 +36,7 @@ import { sendFeedback } from "./scripts/feedback.js";
import { logout } from "./scripts/logout.js";
import { modulesArchive, modulesGet, modulesList } from "./scripts/modules.js";
import { publish } from "./scripts/publish.js";
import { sendAmplitudeEvent } from "./scripts/amplitude/scripts.js";
import { Amplitude } from "./scripts/amplitude/wrapper.js";

const pkg = JSON.parse(
fs.readFileSync(new URL("package.json", import.meta.url), "utf8")
Expand Down Expand Up @@ -73,14 +73,6 @@ function dispatcher() {
invalid(`command doesn't exist: ${command}`);
}

// define the properties to track
const eventProperties = {
full_command: process.argv.slice(2).join(" "), // all of the commands in the user input
action: command // Just the first command
};

sendAmplitudeEvent(eventProperties);

return commands[command]();
}

Expand Down Expand Up @@ -142,6 +134,7 @@ const commands = {
"--type": String,
"--target": String
});

if (!args["--name"]) {
invalid("missing required argument: --name");
}
Expand All @@ -153,6 +146,12 @@ const commands = {
`invalid module name provided: '${args["--name"]}'. Use only alphanumeric characters, dashes and underscores.`
);
}

Amplitude.sendEvent({
name: "Create Module",
properties: { Name: args["--name"] }
});

createModule(args["--name"], args["--type"], args["--target"], gitRoot());
},
commit: () => {
Expand Down Expand Up @@ -206,6 +205,7 @@ demo`;
const args = arg({
"--version": String
});
Amplitude.sendEvent({ name: "Upgrade Scaffold" });
upgradeScaffold(args["--version"]);
},
login: () => {
Expand Down Expand Up @@ -275,6 +275,7 @@ demo`;

switch (action) {
case "list":
Amplitude.sendEvent({ name: "List Modules" });
modulesList({
search: args["--search"],
status: args["--status"],
Expand All @@ -291,6 +292,11 @@ demo`;
);
}

Amplitude.sendEvent({
name: "View Module Details",
properties: { "Module Id": id }
});

modulesGet(id);
break;

Expand All @@ -302,6 +308,11 @@ demo`;
);
}

Amplitude.sendEvent({
name: args["--unarchive"] ? "Unarchive Module" : "Archive Module",
properties: { "Module Id": id }
});

modulesArchive(id, !!args["--unarchive"]);
break;

Expand All @@ -324,6 +335,9 @@ demo`;
}
},
publish: () => {
Amplitude.sendEvent({
name: "Publish Modules"
});
publish();
},

Expand Down Expand Up @@ -355,7 +369,7 @@ demo`;
},

help: () => {
console.log(`usage: npx crowdbotics/modules <command>
console.log(`usage: cb <command>
Commands available:
parse Parse and validate your modules
Expand All @@ -374,46 +388,46 @@ Commands available:
modules Manage modules for your organization
Parse and validate your modules:
npx crowdbotics/modules parse --source <path>
cb parse --source <path>
Parse modules and write the data to a json file:
npx crowdbotics/modules parse --source <path> --write <path>
cb parse --source <path> --write <path>
Create a demo app:
npx crowdbotics/modules demo
cb demo
Create a module of a given type:
npx crowdbotics/modules create --name <module-name> --type <all/react-native/django>
cb create --name <module-name> --type <all/react-native/django>
Initialize a modules repository:
npx crowdbotics/modules init --name <my-modules-repository-name>
cb init --name <my-modules-repository-name>
Upgrade your scaffold to the latest master:
npx crowdbotics/modules upgrade
cb upgrade
Upgrade your scaffold to a specific version (git tag, git commit or branch name):
npx crowdbotics/modules upgrade --version 2.3.0
cb upgrade --version 2.3.0
Install one or modules to your demo app:
npx crowdbotics/modules add <module-name> <module-name-2>
cb add <module-name> <module-name-2>
Remove one or modules from your demo app:
npx crowdbotics/modules remove <module-name> <module-name-2>
cb remove <module-name> <module-name-2>
Install modules from other directory:
npx crowdbotics/modules add --source ../other-repository <module-name>
cb add --source ../other-repository <module-name>
Install modules to other app that is not "demo":
npx crowdbotics/modules add --project ../other-project <module-name>
cb add --project ../other-project <module-name>
Remove modules from app that is not "demo":
npx crowdbotics/modules remove --project ../other-project <module-name>
cb remove --project ../other-project <module-name>
Update a module definition from the demo app:
npx crowdbotics/modules commit <module-name>
cb commit <module-name>
Update a module definition from other app:
npx crowdbotics/modules commit <module-name> --source <path>
cb commit <module-name> --source <path>
Glossary:
<module-name> stands for the name of the directory where the module is defined.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Library of modules for React Native and Django apps",
"main": "index.js",
"bin": {
"crowdbotics-modules": "./index.js"
"cb": "./index.js"
},
"type": "module",
"engines": {
Expand Down
5 changes: 5 additions & 0 deletions scripts/amplitude/constants.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
export const PRODUCTION_AMPLITUDE_KEY = "b48a6aaef8d0ac8df1f2a78196473f06";
export const DEVELOPMENT_AMPLITUDE_KEY = "8fa9ae919aaf5bbfb85f8275b734b11c";

export const CUSTOMER_TYPE = {
SMB: "SMB",
ENT: "ENT"
};
28 changes: 5 additions & 23 deletions scripts/amplitude/scripts.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import inquirer from "inquirer";
import { section } from "../../utils.js";
import { configFile } from "../utils/configFile.js";
import { AMPLITUDE_API_KEY, HAS_ASKED_OPT_IN_NAME, OPT_IN_NAME } from "./config.js";
import { init, track } from "@amplitude/analytics-node";
import { HAS_ASKED_OPT_IN_NAME, OPT_IN_NAME } from "./config.js";

export const askOptIn = async () => {
const { optInStatus } = await inquirer.prompt({
Expand All @@ -11,7 +10,9 @@ export const askOptIn = async () => {
type: "input"
});

if (optInStatus.trim().toLowerCase() !== "y") {
const optedIn = optInStatus.trim().toLowerCase() === "y";

if (!optedIn) {
section("Thanks for your response. We will not send diagnostics & usage.");
configFile.set(OPT_IN_NAME, false);
} else {
Expand All @@ -20,24 +21,5 @@ export const askOptIn = async () => {
}
configFile.set(HAS_ASKED_OPT_IN_NAME, true);
configFile.save();
};

export const sendAmplitudeEvent = (eventProperties) => {
const username = configFile.get("email");
const isOptedIn = configFile.get(OPT_IN_NAME) || false;
try {
// track only if email is available and user is opted In
if (username && isOptedIn) {
init(AMPLITUDE_API_KEY);

// track the event
track("Crowdbotics CLI", eventProperties, {
user_id: username
});
}
} catch (error) {
track("Crowdbotics CLI", { ...eventProperties, amplitudeError: error }, {
user_id: username
});
}
return optedIn;
};
71 changes: 71 additions & 0 deletions scripts/amplitude/wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { configFile } from "../utils/configFile.js";
import { AMPLITUDE_API_KEY, OPT_IN_NAME } from "./config.js";
import { init, track, Identify, identify } from "@amplitude/analytics-node";
import { currentUser } from "../utils/user.js";

class AmplitudeWrapper {
get userType() {
// TODO: Implement once we have the data available in the UserSerializer
return undefined;
}

get optedIn() {
return configFile.get(OPT_IN_NAME);
}

get appProps() {
return {};
}

get userProps() {
const org = currentUser.get("organization");
return {
"User Type": this.userType,
"Org ID": org?.id,
Source: "CLI"
};
}

async init({ token = AMPLITUDE_API_KEY, options = {} } = {}) {
if (!this.optedIn || !token) return;

try {
await init(token, { ...options, includeUtm: true }).promise;
} catch {
// Ignore errors during initialization
}
}

async loadAndIdentify(user) {
await currentUser.setUser(user);
if (!currentUser.get("email")) return;

const identifyEvent = new Identify();
identifyEvent.set("Django Id", currentUser.get("id"));
identify(identifyEvent, { user_id: currentUser.get("email") });
}

async sendEvent({ name, properties = {}, user }) {
if (!this.optedIn) return;

try {
await this.init();
await this.loadAndIdentify(user);

const userEmail = currentUser.get("email");
if (userEmail) {
const updatedProps = {
...properties,
...this.appProps,
...this.userProps
};

await track(name, updatedProps, { user_id: userEmail }).promise;
}
} catch (error) {
console.warn("Error handling analytics - skipping");
}
}
}

export const Amplitude = new AmplitudeWrapper();
12 changes: 9 additions & 3 deletions scripts/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,21 @@ function generateDjangoFiles(base, name, relative = "/") {
const sanitizedName = name.replaceAll("-", "_");
const djangoName = `django_${sanitizedName}`;
const basePath = path.join(base, relative, djangoName);
const innerAppPath = path.join(basePath, sanitizedName);

fs.mkdirSync(basePath, { recursive: true });
execSync(`cd ${basePath}`, execOptions);
fs.mkdirSync(innerAppPath, { recursive: true });
execSync(`cd ${innerAppPath}`, execOptions);
configurePython();
execSync("pipenv install django==3.2.23", execOptions);
execSync(
`pipenv run django-admin startapp ${sanitizedName} ${basePath}`,
`pipenv run django-admin startapp ${sanitizedName} ${innerAppPath}`,
execOptions
);

const appsFileData = fs.readFileSync(`${innerAppPath}/apps.py`, "utf8");
const result = appsFileData.replace(/name = '.*'/, `name = 'modules.django_${sanitizedName}.${sanitizedName}'`);
fs.writeFileSync(`${innerAppPath}/apps.py`, result, "utf8");

fs.writeFileSync(
path.join(base, relative, djangoName, "setup.py"),
setupPy(sanitizedName),
Expand Down
8 changes: 3 additions & 5 deletions scripts/feedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ export const sendFeedback = async (message) => {
body: { message }
});

if (response.status === 401) {
return invalid("There was an error sending your feedback. Please ensure you have logged in with the 'login' command, then try again.");
}

if (!response.ok) {
return invalid("Unable to send feedback at this time, please try again.");
return invalid(
"Unable to send feedback at this time, please try again later."
);
}
} catch (error) {
return invalid(error);
Expand Down
4 changes: 3 additions & 1 deletion scripts/logout.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const logout = async () => {
});
configFile.save();
} catch (e) {
return invalid("An error occurred while logging out. Please try again");
return invalid(
"An error occurred while logging out. Please try again later."
);
}
valid("Logout successful");
};
Loading

0 comments on commit 6059531

Please sign in to comment.