Skip to content

Commit

Permalink
feat:Add flags in different environment
Browse files Browse the repository at this point in the history
  • Loading branch information
killbond007 committed Jan 4, 2022
1 parent b111c7d commit db37ea4
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 39 deletions.
3 changes: 2 additions & 1 deletion src/api-gateway/api-gateway.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ type FlagDetails {
clientSideAvailability: ClientSideAvailability!
deleted: Boolean!
description: String!
environment: ID!
fallthrough: Fallthrough!
key: String!
name: String!
Expand Down Expand Up @@ -89,7 +90,7 @@ type Mutation {
subscribeToNewsletter(email: String!, firstName: String, lastName: String): Empty!
upsertApiTokens(_id: String!, launchRoomToken: String!): ApiTokens
upsertEnvironment(_id: ID, deletedAt: DateTime, launchRoomToken: String, name: String!, project: ID): Environment
upsertFlag(archived: Boolean, description: String, fallthrough: FallthroughInput, key: ID!, name: String, offVariation: Int, on: Boolean, rules: [RuleInput!], variationsBoolean: [Boolean!], variationsJson: [String!], variationsNumber: [Int!], variationsString: [String!], workspaceId: ID!): Boolean!
upsertFlag(archived: Boolean, description: String, environment: ID!, fallthrough: FallthroughInput, key: ID!, name: String, offVariation: Int, on: Boolean, rules: [RuleInput!], variationsBoolean: [Boolean!], variationsJson: [String!], variationsNumber: [Int!], variationsString: [String!], workspaceId: ID!): Boolean!
upsertProject(_id: ID, deletedAt: DateTime, name: String!, workspace: ID!): Project
}

Expand Down
35 changes: 31 additions & 4 deletions src/api-gateway/resolvers/environment-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,46 @@ export class EnvironmentsResolver {
@Mutation(() => Environment, { nullable: true })
async upsertEnvironment(
@Args() { _id, name, project, deletedAt }: UpsertEnvironmentRequest,
@Ctx() { model: { environmentModel, projectModel, apiTokens } }: IContext
@Ctx()
{
model: { environmentModel, projectModel, apiTokens, flagModel },
}: IContext
): Promise<EnvironmentDoc | null> {
if (_id) {
return environmentModel.findOneAndUpdate(
{ _id },
{ name, project, deletedAt }
);
}
const workspace = await projectModel.findOne({ _id: project });
const currentProject = await projectModel.findOne({ _id: project });
const apiToken = await apiTokens.create({
workspace: workspace?.workspace,
workspace: currentProject?.workspace,
});
const environment = await environmentModel.create({
name,
project,
apiToken,
});

const prevEnvironment = await environmentModel.findOne({
project,
deletedAt: null,
});
if (prevEnvironment) {
const flags = await flagModel
.find({
workspace: currentProject?.workspace,
environment: prevEnvironment._id,
})
.lean();

return environmentModel.create({ name, project, apiToken });
await Promise.all(
flags.map(async (flag) => {
delete flag._id;
await flagModel.create({ ...flag, environment: environment._id });
})
);
}
return environment;
}
}
54 changes: 38 additions & 16 deletions src/api-gateway/resolvers/flag-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ class FlagDetails {
@Field(() => ClientSideAvailability)
clientSideAvailability: ClientSideAvailability;

@Field(() => ID)
environment: boolean;

@Field(() => Boolean)
archived: boolean;
}
Expand Down Expand Up @@ -281,6 +284,9 @@ class UpFlagDetailsArgs {
@Field(() => FallthroughInput, { nullable: true })
fallthrough: FallthroughInput;

@Field(() => ID)
environment: string;

@Field(() => Boolean, { nullable: true })
archived: boolean;
}
Expand Down Expand Up @@ -341,7 +347,8 @@ export class FlagResolver {
@Mutation(() => Boolean)
async upsertFlag(
@Args() detail: UpFlagDetailsArgs,
@Ctx() { model: { flagModel, userWorkspace }, userId }: IContext
@Ctx()
{ model: { flagModel, userWorkspace, environmentModel }, userId }: IContext
): Promise<boolean> {
const {
key,
Expand All @@ -351,11 +358,15 @@ export class FlagResolver {
offVariation,
fallthrough,
archived,
environment,
} = detail;

await assertWorkspace(userWorkspace, userId, workspaceId);

const flag = await flagModel.findOne({ key, workspace: workspaceId });
const flag = await flagModel.findOne({
key,
workspace: workspaceId,
environment,
});
if (flag) {
const updated = {} as Record<string, unknown>;

Expand All @@ -381,11 +392,13 @@ export class FlagResolver {
if (archived !== undefined) {
updated.archived = archived;
}
updated.environment = environment;

await flagModel.findOneAndUpdate(
{
key,
workspace: workspaceId,
environment,
},
updated
);
Expand All @@ -401,20 +414,29 @@ export class FlagResolver {
variationsJson?.map((value) => JSON.parse(value)) ||
variationsNumber ||
variationsString;

await flagModel.create({
workspace: workspaceId,
clientSideAvailability: {
usingMobileKey: true,
usingEnvironmentId: true,
},
isOn: true,
salt: "",
sel: "",
targets: [],
variations,
...detail,
const currentEnvironment = await environmentModel.findById(environment);
const environments = await environmentModel.find({
project: currentEnvironment?.project,
deletedAt: null,
});
await Promise.all(
environments.map(async (value) =>
flagModel.create({
workspace: workspaceId,
clientSideAvailability: {
usingMobileKey: true,
usingEnvironmentId: true,
},
isOn: true,
salt: "",
sel: "",
targets: [],
variations,
...detail,
environment: value._id,
})
)
);
}

return true;
Expand Down
6 changes: 5 additions & 1 deletion src/model/flag-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import {
} from "@typegoose/typegoose";
import { TimeStamps } from "@typegoose/typegoose/lib/defaultClasses";
import { Workspace } from "@/model/workspace-model";
import { Environment } from "@/model/environment-model";

@index({ key: 1, workspace: 1 }, { unique: true })
@index({ key: 1, workspace: 1, environment: 1 }, { unique: true })
@modelOptions({ options: { allowMixed: Severity.ALLOW } })
export class Flag extends TimeStamps {
@prop()
Expand Down Expand Up @@ -94,6 +95,9 @@ export class Flag extends TimeStamps {
@prop()
description?: string;

@prop({ ref: Environment })
environment: Ref<Environment>;

@prop({ default: false })
archived?: boolean;
}
Expand Down
41 changes: 30 additions & 11 deletions src/server/server-routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { apolloSSR } from "@/shared/common/apollo-ssr";
import { setEmailPasswordIdentityProviderRoutes } from "@/shared/onefx-auth-provider/email-password-identity-provider/email-password-identity-provider-handler";
import { setApiGateway } from "@/api-gateway/api-gateway";
import { setSdkApiRoutes } from "@/server/sdk-api/sdk-api-routes";
import { MyServer } from "./start-server";
import { Environment as EnvironmentDoc } from "@/model/environment-model";
import { MyServer } from "./start-server";

export function setServerRoutes(server: MyServer): void {
// Health checks
Expand Down Expand Up @@ -38,16 +38,35 @@ export function setServerRoutes(server: MyServer): void {
workspace: userWorkspace?.workspace,
deletedAt: null,
});
const environments = await Promise.all(
projects.map(
async (project): Promise<EnvironmentDoc[]> =>
server.model.environmentModel.find({
project: project._id,
deletedAt: null,
})
)
);
ctx.setState("base.currentEnvironment", environments?.flat()[0]);

if (projects.length === 0) {
const project = await server.model.projectModel.create({
name: "Default",
workspace: userWorkspace?.workspace,
});
const apiToken = await server.model.apiTokens.create({
workspace: userWorkspace?.workspace,
});
const environment = await server.model.environmentModel.create({
name: "Prod",
project: project._id,
apiToken,
});

ctx.setState("base.currentEnvironment", environment);
} else {
const environments = await Promise.all(
projects.map(
async (project): Promise<EnvironmentDoc[]> =>
server.model.environmentModel.find({
project: project._id,
deletedAt: null,
})
)
);

ctx.setState("base.currentEnvironment", environments?.flat()[0]);
}

ctx.body = await apolloSSR(ctx, {
VDom: <AppContainer />,
Expand Down
6 changes: 3 additions & 3 deletions src/shared/common/top-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const TopBar = (): JSX.Element => {

const currentProject = useMemo(
() =>
projects?.find((project) => project._id === currentEnvironment.project),
projects?.find((project) => project?._id === currentEnvironment?.project),
[projects, currentEnvironment]
);

Expand Down Expand Up @@ -109,11 +109,11 @@ export const TopBar = (): JSX.Element => {
<CommonMargin />
<div>
<Space>
{currentProject && (
{currentProject && currentEnvironment._id && (
<>
<Typography.Text>{currentProject.name}</Typography.Text>
<Select
defaultValue={currentProject.name}
defaultValue={currentEnvironment._id}
style={{ width: 200 }}
onChange={(id) =>
dispatch({
Expand Down
1 change: 1 addition & 0 deletions src/shared/feature-flags/data/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const flagsStatus = gql`
variation
}
variations
environment
on
}
}
Expand Down
11 changes: 10 additions & 1 deletion src/shared/feature-flags/flags-status-table-controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ import React from "react";
import { useFlagsStatus } from "@/shared/feature-flags/hooks/use-flags-status";
import { useSelector } from "react-redux";
import { useUpsertFlag } from "@/shared/flag-details/hooks/use-upsert-flag";
import { RootState } from "@/client/javascripts/main";
import { FlagsStatusTable } from "./flags-status-table";
import { WorkspaceIdContext, RefetchContext } from "./context";

export const FlagsStatusTableController: React.FC = () => {
const workspaceId = useSelector(
(state: { base: { workspaceId: string } }) => state.base.workspaceId
);
const currentEnvironment = useSelector(
(state: RootState) => state.base.currentEnvironment
);

const { flagsStatus, refetch, loading } = useFlagsStatus({
workspaceId,
skip: 0,
Expand All @@ -22,7 +27,11 @@ export const FlagsStatusTableController: React.FC = () => {
<WorkspaceIdContext.Provider value={workspaceId}>
<RefetchContext.Provider value={refetch}>
<FlagsStatusTable
data={flagsStatus?.flags || []}
data={
flagsStatus?.flags?.filter(
(value) => value.environment === currentEnvironment._id
) || []
}
archived={flagsStatus?.archived || false}
loading={loading}
upsertFlag={upsertFlag}
Expand Down
16 changes: 14 additions & 2 deletions src/shared/feature-flags/flags-status-table.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-disable camelcase */
import React from "react";
import { useSelector } from "react-redux";
import { Link } from "onefx/lib/react-router-dom";
import { t } from "onefx/lib/iso-i18n";
import Table from "antd/lib/table/Table";
Expand All @@ -25,6 +26,7 @@ import {
} from "@/shared/feature-flags/context";
import { CommonMargin } from "@/shared/common/common-margin";
import { VarIcon } from "@/shared/common/icons/var-icon";
import { RootState } from "@/client/javascripts/main";
import { NewFlagController } from "./new-flag-controller";

type Props = {
Expand All @@ -42,6 +44,9 @@ export const FlagsStatusTable: React.FC<Props> = ({
}) => {
const refetch = React.useContext(RefetchContext);
const workspaceId = React.useContext(WorkspaceIdContext);
const environment = useSelector(
(state: RootState) => state.base.currentEnvironment?._id
);

const columns: ColumnsType<FlagsStatus_flagsStatus_flags> = [
{
Expand Down Expand Up @@ -98,11 +103,17 @@ export const FlagsStatusTable: React.FC<Props> = ({
render(value, record) {
return (
<Switch
key={environment}
defaultChecked={value}
onChange={async () => {
try {
await upsertFlag({ workspaceId, key: record.key, on: !value });

await upsertFlag({
environment,
workspaceId,
key: record.key,
on: !value,
});
refetch();
notification.success({ message: t("notification.update") });
} catch (e) {
notification.error({ message: e.message });
Expand Down Expand Up @@ -151,6 +162,7 @@ export const FlagsStatusTable: React.FC<Props> = ({
onConfirm={async () => {
try {
await upsertFlag({
environment,
workspaceId,
key,
archived: !archived,
Expand Down
6 changes: 6 additions & 0 deletions src/shared/feature-flags/new-flag-form.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { useContext } from "react";
import { useSelector } from "react-redux";
import Form from "antd/lib/form";
import Input from "antd/lib/input";
import InputNumber from "antd/lib/input-number";
Expand All @@ -15,6 +16,7 @@ import { CommonMargin } from "@/shared/common/common-margin";
import { styled } from "onefx/lib/styletron-react";
import { t } from "onefx/lib/iso-i18n";
import { margin } from "polished";
import { RootState } from "@/client/javascripts/main";
import { RefetchContext, WorkspaceIdContext } from "./context";
import { VarIcon } from "../common/icons/var-icon";
import { UpsertFlagVariables } from "../flag-details/data/__generated__/UpsertFlag";
Expand Down Expand Up @@ -49,6 +51,9 @@ export const NewFlagForm: React.FC<Props> = ({
}) => {
const workspaceId = useContext(WorkspaceIdContext);
const refetch = useContext(RefetchContext);
const environment = useSelector(
(state: RootState) => state.base.currentEnvironment?._id
);

const [form] = Form.useForm();

Expand Down Expand Up @@ -99,6 +104,7 @@ export const NewFlagForm: React.FC<Props> = ({
workspaceId,
key: values.key as string,
on: true,
environment,
variationsBoolean: values.variationsBoolean as boolean[],
variationsJson: values.variationsJson as string[],
variationsNumber: values.variationsNumber as number[],
Expand Down
Loading

0 comments on commit db37ea4

Please sign in to comment.