Skip to content

Commit

Permalink
Merge pull request #7139 from TouK/1.18-ports-2
Browse files Browse the repository at this point in the history
1.18 ports 2
  • Loading branch information
arkadius authored Nov 12, 2024
2 parents 03566ab + 478bd88 commit 15a4f9d
Show file tree
Hide file tree
Showing 18 changed files with 207 additions and 54 deletions.
15 changes: 6 additions & 9 deletions designer/client/cypress/e2e/activities.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,12 @@ describe("Activities", () => {
makeScreenshot();

// modify comment
cy.intercept("/api/processes/*/activity/comment/*").as("modifyComment");
cy.get("[data-testid=activity-row-3]").as("modifyCommentRow").trigger("mouseover");
cy.get("@modifyCommentRow").find("[data-testid=edit-comment-icon]").click();
cy.intercept("/api/processes/*/activity/comment/*").as("editComment");
cy.get("[data-testid=activity-row-3]").as("editCommentRow").trigger("mouseover");
cy.get("@editCommentRow").find("[data-testid=edit-comment-icon]").click();
cy.get("[data-testid=window]").find("textarea").eq(0).type(" new comment");
cy.get("[data-testid=window]")
.find("button")
.contains(/^Modify/i)
.click();
cy.wait("@modifyComment");
cy.get("@modifyCommentRow").contains("test comment new comment").should("be.visible");
cy.get("[data-testid=window]").find("button").contains(/^Edit/i).click();
cy.wait("@editComment");
cy.get("@editCommentRow").contains("test comment new comment").should("be.visible");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { getProcessName } from "../../reducers/selectors/graph";
import { getScenarioActivities } from "../../actions/nk/scenarioActivities";
import { ModifyActivityCommentMeta } from "../toolbars/activities/types";

const ModifyExistingCommentDialog = (props: WindowContentProps<number, ModifyActivityCommentMeta>) => {
const ModifyActivityCommentDialog = (props: WindowContentProps<number, ModifyActivityCommentMeta>) => {
const meta = props.data.meta;
const [comment, setState] = useState(meta.existingComment);
const { t } = useTranslation();
Expand All @@ -35,9 +35,9 @@ const ModifyExistingCommentDialog = (props: WindowContentProps<number, ModifyAct
const buttons: WindowButtonProps[] = useMemo(
() => [
{ title: t("dialog.button.cancel", "Cancel"), action: () => props.close(), classname: LoadingButtonTypes.secondaryButton },
{ title: t("dialog.button.modify", "Modify"), action: () => confirmAction() },
{ title: meta.confirmButtonText, action: () => confirmAction() },
],
[confirmAction, props, t],
[confirmAction, meta.confirmButtonText, props, t],
);

return (
Expand Down Expand Up @@ -65,4 +65,4 @@ const ModifyExistingCommentDialog = (props: WindowContentProps<number, ModifyAct
);
};

export default ModifyExistingCommentDialog;
export default ModifyActivityCommentDialog;
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,10 @@ export const ActivityItem = forwardRef(
/>
)}

{activity?.attachment?.file.status === "DELETED" && (
<Typography component={SearchHighlighter} highlights={[searchQuery]} variant={"overline"}>
{t("activityItem.attachmentRemoved", "File ‘{{filename}}’ removed", {
filename: activity.attachment.filename,
})}
</Typography>
)}

{activity.additionalFields.map((additionalField, index) => {
const additionalFieldText = `${additionalField.name}: ${additionalField.value}`;
const additionalFieldText = additionalField.name
? `${additionalField.name}: ${additionalField.value}`
: additionalField.value;

return (
<Typography component={SearchHighlighter} highlights={[searchQuery]} key={index} variant={"overline"}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ const CommentActivity = ({
commentContent={activityComment.content}
data-testid={`edit-comment-icon`}
key={activityAction.id}
title={t("panels.actions.editComment.title", "Edit comment")}
confirmButtonText={t("panels.actions.editComment.confirmButton", "Edit")}
{...getEventTrackingProps({ selector: EventTrackingSelector.ScenarioActivitiesEditComment })}
/>
);
Expand Down Expand Up @@ -108,7 +110,13 @@ export const ActivityItemComment = ({ comment, searchQuery, activityActions, sce
};

return (
<Box ref={multilineDetection} display="grid" gridTemplateColumns={isMultiline ? "1fr 10%" : "1fr 15%"} alignItems="flex-start">
<Box
ref={multilineDetection}
display="grid"
gridTemplateColumns={isMultiline ? "1fr 10%" : "1fr 15%"}
alignItems="flex-start"
pr={0.5}
>
<CommentContent
content={comment.content.value}
commentSettings={commentSettings}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,47 @@ interface Props {
scenarioActivityId: string;
activityType: ActivityType;
activityAction: ActionMetadata;
title: string;
confirmButtonText: string;
}
export const ActivityItemCommentModify = ({ commentContent, scenarioActivityId, activityType, activityAction, ...props }: Props) => {
export const ActivityItemCommentModify = ({
commentContent,
scenarioActivityId,
activityType,
activityAction,
title,
confirmButtonText,
...props
}: Props) => {
const featuresSettings = useSelector(getFeatureSettings);
const { open } = useWindows();

const handleOpenModifyComment = useCallback(() => {
const permittedModifyCommentTypes: ActivityType[] = ["SCENARIO_DEPLOYED", "SCENARIO_CANCELED", "SCENARIO_PAUSED"];

open<ModifyActivityCommentMeta>({
title: "Modify comment",
title,
isModal: true,
shouldCloseOnEsc: true,
kind: WindowKind.modifyComment,
kind: WindowKind.modifyActivityComment,
meta: {
existingComment: commentContent.value,
scenarioActivityId,
placeholder: permittedModifyCommentTypes.includes(activityType)
? featuresSettings?.deploymentCommentSettings?.exampleComment
: undefined,
confirmButtonText,
},
});
}, [activityType, commentContent.value, featuresSettings?.deploymentCommentSettings?.exampleComment, open, scenarioActivityId]);
}, [
activityType,
commentContent.value,
confirmButtonText,
featuresSettings?.deploymentCommentSettings?.exampleComment,
open,
scenarioActivityId,
title,
]);

return (
<StyledActionIcon
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ const StyledHeaderIcon = styled(UrlIcon)(({ theme }) => ({
width: "16px",
height: "16px",
color: theme.palette.primary.main,
svg: {
width: "16px",
height: "16px",
},
}));

const StyledHeaderActionRoot = styled("div")(({ theme }) => ({
Expand All @@ -37,7 +41,7 @@ const StyledActivityItemHeader = styled("div")<{ isHighlighted: boolean; isRunni
({ theme, isHighlighted, isRunning, isActiveFound }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0.5, 0, 0.5, 0.75),
padding: theme.spacing(0.5, 0.5, 0.5, 0.75),
borderRadius: theme.spacing(0.5),
...getHeaderColors(theme, isHighlighted, isRunning, isActiveFound),
}),
Expand Down Expand Up @@ -149,6 +153,8 @@ const HeaderActivity = ({
scenarioActivityId={scenarioActivityId}
activityType={activityType}
activityAction={activityAction}
title={t("panels.actions.addComment.title", "Add comment")}
confirmButtonText={t("panels.actions.addComment.confirmButton", "Add")}
{...getEventTrackingProps({ selector: EventTrackingSelector.ScenarioActivitiesAddCommentToActivity })}
/>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ import { v4 as uuid4 } from "uuid";
import { Activity, ButtonActivity, DateActivity, UIActivity } from "../ActivitiesPanel";
import { formatDate } from "./date";

const createUiActivity = (activity: Activity) => {
const uiActivity: UIActivity = {
...activity,
isActiveFound: false,
isFound: false,
uiGeneratedId: uuid4(),
uiType: "item",
isHidden: false,
};

if (uiActivity?.attachment?.file?.status === "DELETED") {
uiActivity.additionalFields.push({ name: "", value: `File '${uiActivity.attachment.filename}' removed` });
}

return uiActivity;
};
const getLatestDateItem = (uiActivities: UIActivity[]) => {
let previousDateItem: DateActivity | undefined;

Expand Down Expand Up @@ -123,14 +139,9 @@ export const extendActivitiesWithUIData = (activitiesDataWithMetadata: Activity[
const dateLabel = recursiveDateLabelDesignation(activity, index);
const toggleItemsButton = recursiveToggleItemsButtonDesignation(activity, index);
dateLabel && uiActivities.push(dateLabel);
uiActivities.push({
...activity,
isActiveFound: false,
isFound: false,
uiGeneratedId: uuid4(),
uiType: "item",
isHidden: false,
});

uiActivities.push(createUiActivity(activity));

if (toggleItemsButton) {
initiallyHideItems(toggleItemsButton.sameItemOccurrence);
uiActivities.push(toggleItemsButton);
Expand Down
7 changes: 6 additions & 1 deletion designer/client/src/components/toolbars/activities/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,9 @@ export interface ActivityMetadataResponse {
actions: ActionMetadata[];
}

export type ModifyActivityCommentMeta = { existingComment?: string; scenarioActivityId: string; placeholder?: string };
export type ModifyActivityCommentMeta = {
existingComment?: string;
scenarioActivityId: string;
placeholder?: string;
confirmButtonText: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -94,17 +94,35 @@ const sampleActivitiesResponse: ActivitiesResponse["activities"] = [
],
type: "SCENARIO_NAME_CHANGED",
},
{
id: "524adff3-fb7d-42d1-bf92-f0903d548d00",
user: "writer",
date: "2022-11-07T11:55:53.127796Z",
scenarioVersionId: 7,
attachment: {
file: {
status: "DELETED",
},
filename: "Screenshot 2022-10-21 at 08.07.38.png",
lastModifiedBy: "writer",
lastModifiedAt: "2022-11-07T11:55:53.127796Z",
},
additionalFields: [],
overrideDisplayableName: "File removed",
type: "ATTACHMENT_ADDED",
},
];

const mockedActivities = extendActivitiesWithUIData(mergeActivityDataWithMetadata(sampleActivitiesResponse, sampleMetadataResponse));

describe(useActivitiesSearch.name, () => {
it.each<[string, string[]]>([
["atta", [mockedActivities[4].uiGeneratedId]],
["atta", [mockedActivities[4].uiGeneratedId, mockedActivities[9].uiGeneratedId]],
["3 saved", [mockedActivities[3].uiGeneratedId]],
["2024-09-27", [mockedActivities[1].uiGeneratedId]],
["tests save", [mockedActivities[3].uiGeneratedId]],
["newName: old marketing campaign", [mockedActivities[7].uiGeneratedId]],
[".png", [mockedActivities[9].uiGeneratedId]],
])("should find elements when query is '%s'", (searchQuery, expected) => {
const handleScrollToItemMock = jest.fn();
const handleUpdateScenarioActivitiesMock = jest.fn();
Expand Down
6 changes: 3 additions & 3 deletions designer/client/src/windowManager/ContentGetter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const AddCommentDialog = loadable(() => import("../components/modals/AddCommentD
fallback: <LoaderSpinner show />,
});

const ModifyExistingCommentDialog = loadable(() => import("../components/modals/ModifyExistingCommentDialog"), {
const ModifyActivityCommentDialog = loadable(() => import("../components/modals/ModifyActivityCommentDialog"), {
fallback: <LoaderSpinner show />,
});

Expand Down Expand Up @@ -97,8 +97,8 @@ const contentGetter: React.FC<WindowContentProps<WindowKind>> = (props) => {
return <ScenarioDetailsDialog {...props} />;
case WindowKind.addComment:
return <AddCommentDialog {...props} />;
case WindowKind.modifyComment:
return <ModifyExistingCommentDialog {...props} />;
case WindowKind.modifyActivityComment:
return <ModifyActivityCommentDialog {...props} />;
case WindowKind.addAttachment:
return <AddAttachmentDialog {...props} />;
default:
Expand Down
2 changes: 1 addition & 1 deletion designer/client/src/windowManager/WindowKind.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ export enum WindowKind {
viewDescription,
editDescription,
addComment,
modifyComment,
modifyActivityComment,
addAttachment,
}
20 changes: 19 additions & 1 deletion docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,19 @@

#### Highlights

(Not available yet)
##### End-user

* New Activities panel, replacing Versions, Comments and Attachments panels. Now you can browse all scenario activities on one chronological list.
* Added scenario labels. You can now organize your scenarios and find different groups of scenarios more easily.
* SpEL: added navigation through fields inside variables typed as Unknown. You can now access the data inside a variable, even if Nussknacker doesn't know its exact type during scenario authoring.
* SpEL: added conversion methods to cast or convert between data types (e.g. `String` to `Integer`).
* SpEL: various enhancements, like `#CONV.toJson` and `#CONV.toJsonString` methods, new `#BASE64` helper, possibility to treat arrays as lists, and more.
* Various UX improvements, including new UI for aggregation definitions and better validation handling in ad-hoc tests.

##### Administrator

* Flink upgrade to 1.19.1. Note: it is possible to use Nussknacker with older versions of Flink, but it requires some extra steps. See [Migration guide](MigrationGuide.md) for details.
* Performance optimisations of the serialisation of events passing through Flink's `DataStream`s.

### 1.18.0 (Not released yet)

Expand Down Expand Up @@ -80,6 +92,12 @@
* isBigDecimal/toBigDecimal/toBigDecimalOrNull
* isList/toList/toListOrNull
* isMap/toMap/toMapOrNull - the list of key-value pairs or unknown map can be converted to a map.
* [#7106](https://github.com/TouK/nussknacker/pull/7106) Fix an issue where pressing the “Esc” key did not remove focus from input fields in dialogs, which prevented the dialog window from closing
* [#7002](https://github.com/TouK/nussknacker/pull/7002) Resolve an issue with union nodes output expression when nodes were copied and pasted
* [#6994](https://github.com/TouK/nussknacker/pull/6994) Fix styling issues for form checkboxes in Firefox
* [#6721](https://github.com/TouK/nussknacker/pull/6721) Provide a popover to display additional information about count
* [#7099](https://github.com/TouK/nussknacker/pull/7099) Provide an option to embedded video to the markdown
* [#7102](https://github.com/TouK/nussknacker/pull/7102) Introduce a new UI to defining aggregations within nodes

## 1.17

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,58 @@ import org.apache.flink.api.java.typeutils.TypeExtractor
import java.lang.reflect.Type
import java.time.{LocalDate, LocalDateTime, LocalTime}
import java.util
import java.util.concurrent.atomic.AtomicBoolean

// This class contains registers TypeInfoFactory for commonly used classes in Nussknacker.
// It is a singleton as Flink's only contains a global registry for such purpose
object FlinkTypeInfoRegistrar {

private case class RegistrationEntry[T, K <: TypeInfoFactory[T]](klass: Class[T], factoryClass: Class[K])
private val typeInfoRegistrationEnabled = new AtomicBoolean(true)

private val typesToRegister = List(
private val DisableFlinkTypeInfoRegistrationEnvVarName = "NU_DISABLE_FLINK_TYPE_INFO_REGISTRATION"

// These members are package protected for purpose of TypingResultAwareTypeInformationDetection.FlinkBelow119AdditionalTypeInfo - see comment there
private[engine] case class RegistrationEntry[T](klass: Class[T], factoryClass: Class[_ <: TypeInfoFactory[T]])

private[engine] val typeInfoToRegister = List(
RegistrationEntry(classOf[LocalDate], classOf[LocalDateTypeInfoFactory]),
RegistrationEntry(classOf[LocalTime], classOf[LocalTimeTypeInfoFactory]),
RegistrationEntry(classOf[LocalDateTime], classOf[LocalDateTimeTypeInfoFactory]),
)

def ensureBaseTypesAreRegistered(): Unit =
typesToRegister.foreach { base =>
register(base)
def ensureTypeInfosAreRegistered(): Unit = {
// TypeInfo registration is available in Flink >= 1.19. For backward compatibility purpose we allow
// to disable this by either environment variable or programmatically
if (typeInfoRegistrationEnabled.get() && !typeInfoRegistrationDisabledByEnvVariable) {
typeInfoToRegister.foreach { entry =>
register(entry)
}
}
}

private def typeInfoRegistrationDisabledByEnvVariable = {
Option(System.getenv(DisableFlinkTypeInfoRegistrationEnvVarName)).exists(_.toBoolean)
}

private def register(entry: RegistrationEntry[_, _ <: TypeInfoFactory[_]]): Unit = {
private def register(entry: RegistrationEntry[_]): Unit = {
val opt = Option(TypeExtractor.getTypeInfoFactory(entry.klass))
if (opt.isEmpty) {
TypeExtractor.registerFactory(entry.klass, entry.factoryClass)
}
}

// These methods are mainly for purpose of tests in nussknacker-flink-compatibility project
// It should be used with caution as it changes the global state. They will be removed when we stop supporting Flink < 1.19
def isFlinkTypeInfoRegistrationEnabled: Boolean = typeInfoRegistrationEnabled.get()

def enableFlinkTypeInfoRegistration(): Unit = {
typeInfoRegistrationEnabled.set(true)
}

def disableFlinkTypeInfoRegistration(): Unit = {
typeInfoRegistrationEnabled.set(false)
}

class LocalDateTypeInfoFactory extends TypeInfoFactory[LocalDate] {

override def createTypeInfo(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ object TypeInformationDetection {
// We use SPI to provide implementation of TypeInformationDetection because we don't want to make
// implementation classes available in flink-components-api module.
val instance: TypeInformationDetection = {
FlinkTypeInfoRegistrar.ensureBaseTypesAreRegistered()
FlinkTypeInfoRegistrar.ensureTypeInfosAreRegistered()

val classloader = Thread.currentThread().getContextClassLoader
ServiceLoader
Expand Down
Loading

0 comments on commit 15a4f9d

Please sign in to comment.