Skip to content

Commit

Permalink
Return Constraint Compilation Errors (#967)
Browse files Browse the repository at this point in the history
* Update GQL to match what the server will return for constraints and compilation errors.

* Update the UI to parse the ConstraintViolation response differently.

This will return an object containing all constraints processed by the server, including whether they have compilation errors or ran successfully.
  • Loading branch information
goetzrrGit authored and JosephVolosin committed Oct 21, 2024
1 parent 8d59337 commit ab65439
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 33 deletions.
37 changes: 20 additions & 17 deletions src/stores/constraints.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { keyBy } from 'lodash-es';
import { derived, get, writable, type Readable, type Writable } from 'svelte/store';
import type { Constraint, ConstraintResult } from '../types/constraint';
import type { Constraint, ConstraintResponse, ConstraintResult } from '../types/constraint';
import gql from '../utilities/gql';
import type { Status } from '../utilities/status';
import { modelId, planId, planStartTimeMs } from './plan';
Expand Down Expand Up @@ -34,30 +34,33 @@ export const constraintVisibilityMap: Readable<Record<Constraint['id'], boolean>

export const checkConstraintsStatus: Writable<Status | null> = writable(null);

export const constraintResultsResponse: Writable<ConstraintResult[]> = writable([]);
export const constraintResponse: Writable<ConstraintResponse[]> = writable([]);

export const constraintsColumns: Writable<string> = writable('2fr 3px 1fr');
export const constraintsFormColumns: Writable<string> = writable('1fr 3px 2fr');

/* Derived. */

export const constraintResults: Readable<ConstraintResult[]> = derived(
[constraintResultsResponse, planStartTimeMs],
([$constraintResultsResponse, $planStartTimeMs]) =>
$constraintResultsResponse.reduce((list: ConstraintResult[], constraintResult) => {
list.push({
...constraintResult,
violations: constraintResult.violations.map(violation => ({
...violation,
windows: violation.windows.map(({ end, start }) => ({
end: $planStartTimeMs + end / 1000,
start: $planStartTimeMs + start / 1000,
[constraintResponse, planStartTimeMs],
([$constraintResponse, $planStartTimeMs]) =>
$constraintResponse
.filter(response => response.success)
.map(successfulResponse => successfulResponse.results)
.reduce((list: ConstraintResult[], constraintResult) => {
list.push({
...constraintResult,
violations: constraintResult.violations.map(violation => ({
...violation,
windows: violation.windows.map(({ end, start }) => ({
end: $planStartTimeMs + end / 1000,
start: $planStartTimeMs + start / 1000,
})),
})),
})),
});
});

return list;
}, []),
return list;
}, []),
);

export const visibleConstraintResults: Readable<ConstraintResult[]> = derived(
Expand Down Expand Up @@ -88,5 +91,5 @@ export function setAllConstraintsVisible(visible: boolean) {

export function resetConstraintStores(): void {
checkConstraintsStatus.set(null);
constraintResultsResponse.set([]);
constraintResponse.set([]);
}
17 changes: 17 additions & 0 deletions src/types/constraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,20 @@ export type ConstraintResult = {
type: ConstraintType;
violations: ConstraintViolation[];
};

export type ConstraintResponse = {
errors: UserCodeError[];
results: ConstraintResult;
success: boolean;
};

export type UserCodeError = {
location: CodeLocation;
message: string;
stack: string;
};

export type CodeLocation = {
column: number;
line: number;
};
41 changes: 33 additions & 8 deletions src/utilities/effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { CommandDictionary as AmpcsCommandDictionary } from '@nasa-jpl/aeri
import { get } from 'svelte/store';
import { SearchParameters } from '../enums/searchParameters';
import { activityDirectives, activityDirectivesMap, selectedActivityDirectiveId } from '../stores/activities';
import { checkConstraintsStatus, constraintResultsResponse } from '../stores/constraints';
import { checkConstraintsStatus, constraintResponse } from '../stores/constraints';
import { catchError, catchSchedulingError } from '../stores/errors';
import {
createExpansionRuleError,
Expand Down Expand Up @@ -37,7 +37,7 @@ import type {
import type { ActivityMetadata } from '../types/activity-metadata';
import type { BaseUser, User, UserId } from '../types/app';
import type { ReqAuthResponse, ReqSessionResponse } from '../types/auth';
import type { Constraint, ConstraintInsertInput, ConstraintResult } from '../types/constraint';
import type { Constraint, ConstraintInsertInput, ConstraintResponse, ConstraintResult } from '../types/constraint';
import type {
ExpansionRule,
ExpansionRuleInsertInput,
Expand Down Expand Up @@ -285,19 +285,44 @@ const effects = {
checkConstraintsStatus.set(Status.Incomplete);
if (plan !== null) {
const { id: planId } = plan;
const data = await reqHasura<ConstraintResult[]>(
const data = await reqHasura<ConstraintResponse[]>(
gql.CHECK_CONSTRAINTS,
{
planId,
},
user,
);
const { constraintResponses } = data;
if (constraintResponses) {
constraintResponse.set(constraintResponses);

const { constraintResults } = data;
if (constraintResults != null) {
constraintResultsResponse.set(constraintResults);
checkConstraintsStatus.set(Status.Complete);
showSuccessToast('Check Constraints Complete');
// find only the constraints compiled.
const successfulConstraintResults: ConstraintResult[] = constraintResponses
.filter(constraintResponse => constraintResponse.success)
.map(constraintResponse => constraintResponse.results);

const failedConstraintResponses = constraintResponses.filter(
constraintResponse => !constraintResponse.success,
);

if (successfulConstraintResults.length === 0 && constraintResponses.length > 0) {
showFailureToast('All Constraints Failed');
checkConstraintsStatus.set(Status.Failed);
} else if (successfulConstraintResults.length !== constraintResponses.length) {
showFailureToast('Partial Constraints Checked');
checkConstraintsStatus.set(successfulConstraintResults.length !== 0 ? Status.Incomplete : Status.Failed);
} else {
showSuccessToast('All Constraints Checked');
checkConstraintsStatus.set(Status.Complete);
}

if (failedConstraintResponses.length > 0) {
failedConstraintResponses.forEach(failedConstraint => {
failedConstraint.errors.forEach(error => {
catchError(`${error.message}`, error.stack);
});
});
}
} else {
throw Error(`Unable to check constraints for plan with ID: "${plan.id}"`);
}
Expand Down
31 changes: 23 additions & 8 deletions src/utilities/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,32 @@ const gql = {

CHECK_CONSTRAINTS: `#graphql
query CheckConstraints($planId: Int!) {
constraintResults: constraintViolations(planId: $planId) {
constraintId
constraintName
resourceIds
type
violations {
activityInstanceIds
windows {
constraintResponses: constraintViolations(planId: $planId) {
success
results {
constraintId
constraintName
resourceIds
type
gaps {
end
start
}
violations {
activityInstanceIds
windows {
end
start
}
}
}
errors {
message
stack
location {
column
line
}
}
}
}
Expand Down

0 comments on commit ab65439

Please sign in to comment.