Skip to content

Commit

Permalink
Merge pull request #4 from prismatic-io/tr/improvements-to-components…
Browse files Browse the repository at this point in the history
…-dev-test

Improvements to components:dev:test
  • Loading branch information
taylorreece committed Oct 25, 2022
2 parents 7055e19 + 0d9a06e commit 257e6c7
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 58 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prismatic-io/prism",
"version": "4.3.4",
"version": "4.3.5",
"description": "Build, deploy, and support integrations in Prismatic from the comfort of your command line",
"keywords": [
"prismatic",
Expand Down
18 changes: 1 addition & 17 deletions src/commands/components/dev/run.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,7 @@
import { Command, Flags, CliUx } from "@oclif/core";
import { isEmpty } from "lodash";
import { gqlRequest, gql } from "../../../graphql";
import { spawn } from "child_process";

const spawnProcess = (
[command, ...args]: string[],
env: Record<string, string>
): Promise<void> => {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
env: { ...process.env, ...env },
});

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on("close", (code) => (code === 0 ? resolve() : reject()));
});
};
import { spawnProcess } from "../../../utils/process";

export default class RunCommand extends Command {
static description = `Fetch an integration's active connection and execute a CLI command with that connection's fields as an environment variable.`;
Expand Down
32 changes: 30 additions & 2 deletions src/commands/components/dev/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { pollForActiveConfigVarState } from "../../../utils/integration/query";
import { Expression } from "../../../utils/integration/export";
import { exists } from "../../../fs";
import { whoAmI } from "../../../utils/user/query";
import { spawnProcess } from "../../../utils/process";
import { writeFinalStepResults } from "../../../utils/execution/stepResults";

const setTimeoutPromise = promisify(setTimeout);

Expand Down Expand Up @@ -138,21 +140,41 @@ const valuesFromAnswers = ({

export default class TestCommand extends Command {
static description =
"Run a Component in Prismatic by publishing it into a test Integration";
"Run an action of a component within a test integration in the integration runner";
static flags = {
envPath: Flags.string({
required: false,
default: ".env",
char: "e",
description: "Path to dotenv file to load for supplying testing values",
}),
build: Flags.boolean({
required: false,
default: true,
allowNo: true,
char: "b",
description: "Build the component prior to testing",
}),
"output-file": Flags.string({
required: false,
char: "o",
description: "Output the results of the action to a specified file",
}),
};

async run() {
const {
flags: { envPath },
flags: { envPath, build, "output-file": outputFile },
} = await this.parse(TestCommand);

// Save the current working directory, so we can return later after moving to dist/
const cwd = process.cwd();

if (build) {
console.log("Building component...");
await spawnProcess(["npm", "run", "build"], {});
}

if (await exists(envPath)) {
const { error } = dotenv.config({ path: envPath, override: true });
if (error) {
Expand Down Expand Up @@ -334,6 +356,12 @@ export default class TestCommand extends Command {

await displayLogs(executionId);

if (outputFile) {
process.chdir(cwd);
console.log(`Writing step results to ${outputFile}`);
await writeFinalStepResults(executionId, outputFile);
}

CliUx.ux.action.stop();
}
}
41 changes: 5 additions & 36 deletions src/commands/executions/step-result/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,11 @@ import { fs } from "../../../fs";
import { Command, Flags } from "@oclif/core";
import { gql, gqlRequest } from "../../../graphql";
import axios from "axios";
import { decode } from "@msgpack/msgpack";
import { extension } from "mime-types";

interface DeserializeResult {
data: unknown;
contentType: string;
}

const deserialize = (data: Buffer): DeserializeResult | unknown => decode(data);

export const parseData = (data: string, contentType = ""): string => {
if (data === null || data === undefined) {
return "";
}
// Some content-types require additional processing.
const dataType = extension(contentType);
if (
contentType.startsWith("text/") ||
(dataType && ["json", "xml", "csv", "xhtml"].includes(dataType))
) {
// If the content-types specifies that the data is text/, or if the extension
// is a well-known text-like type, convert the data to a string.
if (dataType === "json") {
try {
// If the payload is supposed to be JSON, ensure that it can be parsed.
return JSON.parse(data.toString());
} catch {
throw new Error("Received malformed JSON payload.");
}
}
return data;
} else if (contentType.startsWith("binary/")) {
return data;
}
return JSON.stringify(data);
};
import {
deserialize,
DeserializeResult,
parseData,
} from "../../../utils/execution/stepResults";

export default class GetCommand extends Command {
static description =
Expand Down
84 changes: 84 additions & 0 deletions src/utils/execution/stepResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { decode } from "@msgpack/msgpack";
import axios from "axios";
import { fs } from "../../fs";
import { gql, gqlRequest } from "../../graphql";
import { extension } from "mime-types";

export interface DeserializeResult {
data: unknown;
contentType: string;
}

export const deserialize = (data: Buffer): DeserializeResult | unknown =>
decode(data);

export const parseData = (data: any, contentType = ""): string | Buffer => {
if (data === null || data === undefined) {
return "";
}
// Some content-types require additional processing.
const dataType = extension(contentType);
if (
contentType.startsWith("text/") ||
(dataType && ["json", "xml", "csv", "xhtml"].includes(dataType))
) {
// If the content-types specifies that the data is text/, or if the extension
// is a well-known text-like type, convert the data to a string.
if (dataType === "json") {
try {
// If the payload is supposed to be JSON, ensure that it can be parsed.
return JSON.parse(data.toString());
} catch {
throw new Error("Received malformed JSON payload.");
}
}
return data;
} else if (contentType.startsWith("binary/")) {
return data;
}

return typeof data === "string" ||
(typeof data === "object" && Buffer.isBuffer(data))
? data
: JSON.stringify(data);
};

const getFinalStepResult = async (executionId: string) => {
const result = await gqlRequest({
document: gql`
query executionResults($executionId: ID!) {
executionResult(id: $executionId) {
stepResults(last: 1) {
nodes {
id
stepName
resultsUrl
}
}
}
}
`,
variables: { executionId },
});
const { resultsUrl } = result.executionResult.stepResults.nodes[0];
const response = await axios.get(resultsUrl, {
responseType: "arraybuffer",
});
const resultsBuffer = Buffer.from(await response.data);
const { data: deserializedResult, contentType } = decode(
resultsBuffer
) as DeserializeResult;

return {
data: parseData(deserializedResult as string, contentType),
contentType,
};
};

export const writeFinalStepResults = async (
executionId: string,
fileName: string
): Promise<void> => {
const result = await getFinalStepResult(executionId);
await fs.writeFile(fileName, result.data);
};
17 changes: 17 additions & 0 deletions src/utils/process.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { spawn } from "child_process";

export const spawnProcess = (
[command, ...args]: string[],
env: Record<string, string>
): Promise<void> => {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
env: { ...process.env, ...env },
});

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on("close", (code) => (code === 0 ? resolve() : reject()));
});
};

0 comments on commit 257e6c7

Please sign in to comment.