Skip to content
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
46 changes: 46 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Publish

on:
push:
branches:
- main
paths:
- deno.json

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # The OIDC ID token is used for authentication with JSR.
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 2 # Need previous commit to compare versions

- name: Check if version changed
id: check_version
run: |
# Get current version
CURRENT_VERSION=$(jq -r '.version' deno.json)

# Get previous version
PREVIOUS_VERSION=$(git show HEAD~1:deno.json 2>/dev/null | jq -r '.version // empty' || echo "")

echo "Current version: $CURRENT_VERSION"
echo "Previous version: $PREVIOUS_VERSION"

if [ -z "$PREVIOUS_VERSION" ]; then
echo "No previous version found, publishing..."
echo "should_publish=true" >> $GITHUB_OUTPUT
elif [ "$CURRENT_VERSION" != "$PREVIOUS_VERSION" ]; then
echo "Version changed from $PREVIOUS_VERSION to $CURRENT_VERSION, publishing..."
echo "should_publish=true" >> $GITHUB_OUTPUT
else
echo "Version unchanged ($CURRENT_VERSION), skipping publish"
echo "should_publish=false" >> $GITHUB_OUTPUT
fi

- name: Publish to JSR
if: steps.check_version.outputs.should_publish == 'true'
run: npx jsr publish
7 changes: 5 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@goog/flow-lens",
"version": "0.1.14",
"version": "0.1.15",
"license": "Apache",
"exports": "./src/main/main.ts",
"imports": {
Expand All @@ -15,6 +15,9 @@
"xml2js": "npm:xml2js@^0.6.2"
},
"tasks": {
"test": "deno test --allow-read --allow-env --allow-write --allow-run"
"test": "deno test --allow-read --allow-env --allow-write --allow-run",
"format": "deno fmt",
"format:check": "deno fmt --check",
"lint": "deno lint"
}
}
36 changes: 26 additions & 10 deletions src/main/flow_comparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,28 +60,44 @@ export function compareFlows(oldFlow: ParsedFlow, newFlow: ParsedFlow) {

/**
* Compares two objects recursively.
*
* Implementation of deep equality check requires a lot of boilerplate code.
* This is a simple implementation that works for our use case.
*/
// Implementation of deep equality check requires a lot of boilerplate code.
// This is a simple implementation that works for our use case, but it requires
// us to cast the types to any.
// tslint:disable-next-line:no-any
function areEqual(node1: any, node2: any): boolean {
function areEqual(node1: unknown, node2: unknown): boolean {
if (node1 === node2) {
return true;
}

const keys1 = Object.keys(node1);
const keys2 = Object.keys(node2);
if (
typeof node1 !== OBJECT ||
typeof node2 !== OBJECT ||
node1 === null ||
node2 === null
) {
return false;
}

// At this point, TypeScript knows both are non-null objects
const obj1 = node1 as Record<string, unknown>;
const obj2 = node2 as Record<string, unknown>;
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);

if (keys1.length !== keys2.length) {
return false;
}

for (const key of keys1) {
const val1 = node1[key];
const val2 = node2[key];
const val1 = obj1[key];
const val2 = obj2[key];

if (typeof val1 === OBJECT && typeof val2 === OBJECT) {
if (
typeof val1 === OBJECT &&
typeof val2 === OBJECT &&
val1 !== null &&
val2 !== null
) {
if (!areEqual(val1, val2)) {
return false;
}
Expand Down
16 changes: 7 additions & 9 deletions src/main/flow_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class FlowParser {
return this.generateParsedFlow(flowFromXml.Flow);
}

private async parseXmlFile(): Promise<flowTypes.FlowDefinition> {
private parseXmlFile(): Promise<flowTypes.FlowDefinition> {
return new Promise<flowTypes.FlowDefinition>((resolve, reject) => {
new Parser({ explicitArray: false }).parseString(
this.flowXml,
Expand Down Expand Up @@ -621,9 +621,7 @@ function setFlowStart(start: flowTypes.FlowStart | undefined) {
return;
}
if (start.filters) {
start.filters = ensureArray(
start.filters,
) as flowTypes.FlowRecordFilter[];
start.filters = ensureArray(start.filters) as flowTypes.FlowRecordFilter[];
}
if (start.scheduledPaths) {
start.scheduledPaths = ensureArray(
Expand Down Expand Up @@ -734,9 +732,9 @@ function isCustomError(
return (node as flowTypes.FlowCustomError).customErrorMessages !== undefined;
}

function isFlowStart(
node: flowTypes.FlowNode,
): node is flowTypes.FlowStart {
return (node as flowTypes.FlowStart).name === START &&
(node as flowTypes.FlowStart).label === undefined;
function isFlowStart(node: flowTypes.FlowNode): node is flowTypes.FlowStart {
return (
(node as flowTypes.FlowStart).name === START &&
(node as flowTypes.FlowStart).label === undefined
);
}
2 changes: 1 addition & 1 deletion src/main/flow_to_uml_transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ class Reader implements Filter<FlowDifference> {
let old: string | undefined = undefined;
try {
old = this.changeDetector.getFileContent(input, "old");
} catch (error: unknown) {
} catch (_error: unknown) {
console.log(ERROR_MESSAGES.previousFlowNotFound(input));
}
return {
Expand Down
15 changes: 8 additions & 7 deletions src/main/uml_generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,13 +473,14 @@ export abstract class UmlGenerator {
innerNodeContent.push(limit);
}

let innerNode = {
id: `${node.name}__LookupDetails`,
type: `sObject: ${node.object}`,
label: "",
content: innerNodeContent,
};
return [innerNode];
return [
{
id: `${node.name}__LookupDetails`,
type: `sObject: ${node.object}`,
label: "",
content: innerNodeContent,
},
];
}

private getFieldsQueried(node: flowTypes.FlowRecordLookup): string[] {
Expand Down
4 changes: 2 additions & 2 deletions src/main/uml_writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class UmlWriter {
if (config.mode === Mode.JSON) {
this.writeJsonFile(config);
} else if (config.mode === Mode.GITHUB_ACTION) {
await this.writeGithubComment(config);
await this.writeGithubComment();
} else if (config.mode === Mode.MARKDOWN) {
this.writeMarkdownFiles(config);
}
Expand All @@ -77,7 +77,7 @@ export class UmlWriter {
);
}

private async writeGithubComment(config: RuntimeConfig) {
private async writeGithubComment() {
try {
const existingComments = await this.githubClient
.getAllCommentsForPullRequest();
Expand Down
18 changes: 13 additions & 5 deletions src/test/argument_processor_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const INVALID_MODE = "unsupported";
function setupTest(
configModifications: (config: RuntimeConfig) => void = () => {},
) {
let testConfiguration = getTestConfig();
const testConfiguration = getTestConfig();
configModifications(testConfiguration);
return {
argumentProcessor: new ArgumentProcessor(testConfiguration),
Expand Down Expand Up @@ -369,7 +369,9 @@ Deno.test("ArgumentProcessor", async (t) => {
});
try {
invalidDiagramTool.argumentProcessor.getConfig();
} catch {}
} catch {
// Error is expected; we check errors via getErrors() instead
}
assertEquals(invalidDiagramTool.argumentProcessor.getErrors(), [
ERROR_MESSAGES.githubActionRequiresMermaid,
]);
Expand All @@ -387,7 +389,9 @@ Deno.test("ArgumentProcessor", async (t) => {
});
try {
invalidToHash.argumentProcessor.getConfig();
} catch {}
} catch {
// Error is expected; we check errors via getErrors() instead
}
assertEquals(invalidToHash.argumentProcessor.getErrors(), [
ERROR_MESSAGES.githubActionRequiresHeadHash,
]);
Expand All @@ -405,7 +409,9 @@ Deno.test("ArgumentProcessor", async (t) => {
});
try {
invalidFromHash.argumentProcessor.getConfig();
} catch {}
} catch {
// Error is expected; we check errors via getErrors() instead
}
assertEquals(invalidFromHash.argumentProcessor.getErrors(), [
ERROR_MESSAGES.githubActionRequiresHeadMinusOne,
]);
Expand All @@ -423,7 +429,9 @@ Deno.test("ArgumentProcessor", async (t) => {
});
try {
multipleInvalid.argumentProcessor.getConfig();
} catch {}
} catch {
// Error is expected; we check errors via getErrors() instead
}
assertEquals(multipleInvalid.argumentProcessor.getErrors(), [
ERROR_MESSAGES.githubActionRequiresMermaid,
ERROR_MESSAGES.githubActionRequiresHeadHash,
Expand Down
49 changes: 32 additions & 17 deletions src/test/flow_to_uml_transformer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,34 +31,49 @@ import {
import { UmlGeneratorContext } from "../main/uml_generator_context.ts";
import { ERROR_MESSAGES as XML_READER_ERROR_MESSAGES } from "../main/xml_reader.ts";

type TestableFlowFileChangeDetector =
& Omit<
FlowFileChangeDetector,
"executeGitCommand"
>
& {
executeGitCommand: (args: string[]) => Uint8Array;
};

function asTestable(
detector: FlowFileChangeDetector,
): TestableFlowFileChangeDetector {
return detector as unknown as TestableFlowFileChangeDetector;
}

const SAMPLE_FLOW_FILE_PATH = `./src/test/goldens/sample.flow-meta.xml`;
const PLANT_UML_SIGNATURE = "skinparam State";
const GENERATOR_CONTEXT = new UmlGeneratorContext(DiagramTool.PLANTUML);
const CHANGE_DETECTOR = new FlowFileChangeDetector();

Deno.test("FlowToUmlTransformer", async (t) => {
let transformer: FlowToUmlTransformer;
let result: Map<string, FlowDifference>;

await t.step("setup", () => {
// Mock the private method which executes git commands using spyOn
const executeGitCommand = () => {
return new TextEncoder().encode(
Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH),
);
};
// Replace the private method with our mock implementation, using type assertion to access it.
(CHANGE_DETECTOR as any).executeGitCommand = executeGitCommand;
});
function createMockedDetector(): FlowFileChangeDetector {
const detector = new FlowFileChangeDetector();
const testableDetector = asTestable(detector);
testableDetector.executeGitCommand = () => {
return new TextEncoder().encode(
Deno.readTextFileSync(SAMPLE_FLOW_FILE_PATH),
);
};
return detector;
}

let transformer: FlowToUmlTransformer | undefined;
let result: Map<string, FlowDifference> | undefined;
const changeDetector = createMockedDetector();

Deno.test("FlowToUmlTransformer", async (t) => {
await t.step("should transform a flow file to a UML diagram", async () => {
const mockConfig = getTestConfig();
const originalGetInstance = Configuration.getInstance;
Configuration.getInstance = () => mockConfig;
transformer = new FlowToUmlTransformer(
[SAMPLE_FLOW_FILE_PATH],
GENERATOR_CONTEXT,
CHANGE_DETECTOR,
changeDetector,
);

result = await transformer.transformToUmlDiagrams();
Expand Down Expand Up @@ -100,7 +115,7 @@ Deno.test("FlowToUmlTransformer", async (t) => {
transformer = new FlowToUmlTransformer(
[fakeFilePath],
GENERATOR_CONTEXT,
CHANGE_DETECTOR,
changeDetector,
);

result = await transformer.transformToUmlDiagrams();
Expand Down
Loading
Loading