Skip to content

Commit

Permalink
add notification type config (#4271)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-g-town authored Feb 12, 2024
1 parent 92ebeac commit 9a52947
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 19 deletions.
12 changes: 12 additions & 0 deletions api/server/handlers/notifications/get_notification_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ func configFromProto(proto *porterv1.NotificationConfig) Config {
statuses = append(statuses, Status{transformProtoToStatusString[status]})
}

var types []Type
for _, t := range proto.EventTypes {
types = append(types, Type{transformProtoToTypeString[t]})
}

var mention string
if proto.SlackConfig != nil && len(proto.SlackConfig.Mentions) > 0 {
mention = proto.SlackConfig.Mentions[0]
Expand All @@ -109,6 +114,7 @@ func configFromProto(proto *porterv1.NotificationConfig) Config {
return Config{
Statuses: statuses,
Mention: mention,
Types: types,
}
}

Expand All @@ -117,3 +123,9 @@ var transformProtoToStatusString = map[porterv1.EnumNotificationStatus]string{
porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_FAILED: "failed",
porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_PROGRESSING: "progressing",
}

var transformProtoToTypeString = map[porterv1.EnumNotificationEventType]string{
porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_DEPLOY: "deploy",
porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_PREDEPLOY: "pre-deploy",
porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_BUILD: "build",
}
18 changes: 18 additions & 0 deletions api/server/handlers/notifications/update_notification_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,19 @@ type UpdateNotificationConfigRequest struct {
type Config struct {
Mention string `json:"mention"`
Statuses []Status `json:"statuses"`
Types []Type `json:"types"`
}

// Status is a wrapper object over a string for zod validation
type Status struct {
Status string `json:"status"`
}

// Type is a wrapper object over a string for zod validation
type Type struct {
Type string `json:"type"`
}

// UpdateNotificationConfigResponse is the response object for the /notifications/{notification_config_id} endpoint
type UpdateNotificationConfigResponse struct {
ID uint `json:"id"`
Expand Down Expand Up @@ -117,8 +123,14 @@ func configToProto(config Config) *porterv1.NotificationConfig {
statuses = append(statuses, transformStatusStringToProto[status.Status])
}

var types []porterv1.EnumNotificationEventType
for _, t := range config.Types {
types = append(types, transformTypeStringToProto[t.Type])
}

return &porterv1.NotificationConfig{
Statuses: statuses,
EventTypes: types,
SlackConfig: &porterv1.SlackConfig{Mentions: []string{config.Mention}},
}
}
Expand All @@ -128,3 +140,9 @@ var transformStatusStringToProto = map[string]porterv1.EnumNotificationStatus{
"failed": porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_FAILED,
"progressing": porterv1.EnumNotificationStatus_ENUM_NOTIFICATION_STATUS_PROGRESSING,
}

var transformTypeStringToProto = map[string]porterv1.EnumNotificationEventType{
"deploy": porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_DEPLOY,
"build": porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_BUILD,
"pre-deploy": porterv1.EnumNotificationEventType_ENUM_NOTIFICATION_EVENT_TYPE_PREDEPLOY,
}
16 changes: 7 additions & 9 deletions dashboard/src/components/porter/Icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,13 @@ import styled from "styled-components";
type Props = {
src: any;
height?: string;
width?: string;
opacity?: number;
};

const Icon: React.FC<Props> = ({
src,
height,
opacity,
}) => {
const Icon: React.FC<Props> = ({ src, height, width, opacity }) => {
return (
<StyledIcon src={src} height={height} opacity={opacity} />
<StyledIcon src={src} height={height} opacity={opacity} width={width} />
);
};

Expand All @@ -23,6 +20,7 @@ const StyledIcon = styled.img<{
height?: string;
opacity?: number;
}>`
height: ${props => props.height || "20px"};
opacity: ${props => props.opacity || 1};
`;
${(props) =>
props.width ? "width: 20px;" : `height: ${props.height || "20px"};`}
opacity: ${(props) => props.opacity || 1};
`;
12 changes: 11 additions & 1 deletion dashboard/src/lib/notifications/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ export const notificationConfigFormValidator = z.object({
.object({
status: z.string(),
})
.array(),
.array()
.nullish()
.default([]),
types: z
.object({
type: z.string(),
})
.array()
.nullish()
.default([]),
});
export type NotificationConfigFormData = z.infer<
typeof notificationConfigFormValidator
Expand All @@ -17,4 +26,5 @@ export type NotificationConfigFormData = z.infer<
export const emptyNotificationConfig: NotificationConfigFormData = {
mention: "",
statuses: [],
types: [],
};
82 changes: 73 additions & 9 deletions dashboard/src/main/home/integrations/SlackIntegrationList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ import {

import api from "shared/api";
import { Context } from "shared/Context";
import build from "assets/build.png";
import deploy from "assets/deploy.png";
import hash from "assets/hash-02.svg";
import pre_deploy from "assets/pre_deploy.png";
import save from "assets/save-01.svg";

type SlackIntegrationListProps = {
Expand All @@ -41,13 +44,19 @@ const statusOptions = [
{ value: "progressing", emoji: "🚀", label: "Progressing" },
];

const typeOptions = [
{ value: "deployment", icon: deploy, label: "Deploy" },
{ value: "pre-deploy", icon: pre_deploy, label: "Pre-deploy" },
{ value: "build", icon: build, label: "Build" },
];

const SlackIntegrationList: React.FC<SlackIntegrationListProps> = (props) => {
const [isDelete, setIsDelete] = useState(false);
const [deleteIndex, setDeleteIndex] = useState(-1); // guaranteed to be set when used
const { currentProject, setCurrentError } = useContext(Context);
const deleted = useRef(new Set());

const handleDelete = () => {
const handleDelete = (): void => {
api
.deleteSlackIntegration(
"<token>",
Expand Down Expand Up @@ -195,18 +204,43 @@ const NotificationConfigContainer: React.FC<
register,
} = notificationForm;

const { append, remove, fields } = useFieldArray({
const {
append: statusAppend,
remove: statusRemove,
fields: statusFields,
} = useFieldArray({
control,
name: "statuses",
});

const onAdd = (
const onAddStatuses = (
inp: IterableElement<NotificationConfigFormData["statuses"]>
): void => {
const previouslyAdded = fields.findIndex((s) => s.status === inp.status);
const previouslyAdded = statusFields.findIndex(
(s) => s.status === inp.status
);

if (previouslyAdded === -1) {
append(inp);
statusAppend(inp);
}
};

const {
append: typeAppend,
remove: typeRemove,
fields: typeFields,
} = useFieldArray({
control,
name: "types",
});

const onAddTypes = (
inp: IterableElement<NotificationConfigFormData["types"]>
): void => {
const previouslyAdded = typeFields.findIndex((s) => s.type === inp.type);

if (previouslyAdded === -1) {
typeAppend(inp);
}
};

Expand Down Expand Up @@ -258,12 +292,42 @@ const NotificationConfigContainer: React.FC<

return (
<>
<Text>Filter deployment notifications:</Text>
<Text>Filter notification types:</Text>
<Spacer y={0.5} />
<SelectableList
scroll={false}
listItems={typeOptions.map((option) => {
const selectedOptionsIdx = typeFields.findIndex(
(s) => s.type === option.value
);
return {
selectable: (
<Container row>
<Spacer inline width="1px" />
<Icon src={option.icon} width={"8px"} />
<Spacer inline width="10px" />
<Text size={12}>{option.label}</Text>
<Spacer inline x={1} />
</Container>
),
key: option.value,
onSelect: () => {
onAddTypes({ type: option.value });
},
onDeselect: () => {
typeRemove(selectedOptionsIdx);
},
isSelected: selectedOptionsIdx !== -1,
};
})}
/>
<Spacer y={0.75} />
<Text>Filter notification statuses:</Text>
<Spacer y={0.5} />
<SelectableList
scroll={false}
listItems={statusOptions.map((option) => {
const selectedOptionsIdx = fields.findIndex(
const selectedOptionsIdx = statusFields.findIndex(
(s) => s.status === option.value
);
return {
Expand All @@ -280,10 +344,10 @@ const NotificationConfigContainer: React.FC<
),
key: option.value,
onSelect: () => {
onAdd({ status: option.value });
onAddStatuses({ status: option.value });
},
onDeselect: () => {
remove(selectedOptionsIdx);
statusRemove(selectedOptionsIdx);
},
isSelected: selectedOptionsIdx !== -1,
};
Expand Down

0 comments on commit 9a52947

Please sign in to comment.