Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore: Enable seed editing #37

Merged
merged 4 commits into from
Dec 2, 2024
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
13 changes: 8 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# GITHUB_CLIENT_ID=
# GITHUB_CLIENT_SECRET=
# AZURE_CLIENT_ID=
# AZURE_CLIENT_SECRET=

# APOLLO_API_KEY=
# POSTGRES_USER=myuser
# POSTGRES_PASSWORD=mypassword
# POSTGRES_DB=instant_mock_db
# POSTGRES_HOST=postgres_db
# POSTGRES_PORT=5432
# BACKEND_PORT=3008
# FRONTEND_DEV_SERVER_PORT=3009
24 changes: 16 additions & 8 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import './loadEnv';
import 'reflect-metadata';
import {EntityManager, EntityRepository, MikroORM, RequestContext,} from '@mikro-orm/core';
import {
EntityManager,
EntityRepository,
MikroORM,
RequestContext,
} from '@mikro-orm/core';
import cors from 'cors';
import express from 'express';
import figlet from 'figlet';
import fs from 'fs';
import path from 'path';
import supertokens from 'supertokens-node';
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import * as Undici from 'undici';
import figlet from 'figlet';
import {getWebsiteDomain, SuperTokensConfig} from './config/supertokens';
import Client from './graphql/client';
import {authMiddleware} from './middleware/auth';
import {ApolloApiKey} from './models/apolloApiKey';
import {Seed} from './models/seed';
import {SeedGroup} from './models/seedGroup';
import apolloApiKeysRoutes from './routes/apolloApiKey';
import avatarRoutes from './routes/avatar';
import authRoutes from './routes/auth';
import avatarRoutes from './routes/avatar';
import graphqlRoutes from './routes/graphql';
import graphsRoutes from './routes/graphs';
import proposalsRoutes from './routes/proposals';
import seedGroupsRoutes from './routes/seedGroups';
import seedsRoutes from './routes/seeds';
import {logger} from './utilities/logger';
import fs from 'fs';

const isTypescript = __filename.endsWith('.ts');
const ProxyAgent = Undici.ProxyAgent;
Expand Down Expand Up @@ -80,7 +85,7 @@ const initializeApp = async () => {
const mikroOrmConfig = {
...(await import(
`./mikro-orm.${process.env.MIKRO_ORM_DRIVER || 'sqlite'}${isTypescript ? '.ts' : '.js'}`
).then((module) => module.default)),
).then((module) => module.default)),
};

DI.orm = await MikroORM.init(mikroOrmConfig);
Expand Down Expand Up @@ -117,16 +122,19 @@ const initializeApp = async () => {
cors({
origin: (origin, callback) => {
const allowedOrigins = [getWebsiteDomain()];
const regex = /^(https:\/\/[a-zA-Z0-9-]+\.narrative\.tech|https?:\/\/localhost(:\d+)?)$/;
if (!origin || allowedOrigins.includes(origin) || regex.test(origin)) callback(null, true);
const regex =
/^(https:\/\/[a-zA-Z0-9-]+\.narrative\.tech|https?:\/\/localhost(:\d+)?|https:\/\/[a-zA-Z0-9-]+\.xspecs\.io)$/;

if (!origin || allowedOrigins.includes(origin) || regex.test(origin))
callback(null, true);
else callback(new Error('Not allowed by CORS'));
},
allowedHeaders: [
'content-type',
...supertokens.getAllCORSHeaders(),
'seed-group',
],
methods: ['GET', 'PUT', 'POST', 'DELETE'],
methods: ['GET', 'PUT', 'POST', 'DELETE', 'PATCH'],
credentials: true,
})
);
Expand Down
126 changes: 100 additions & 26 deletions frontend/src/components/ui/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import {Seed} from '@/models/Seed';
import {ApolloSandbox} from '@apollo/sandbox/react';
import {HandleRequest} from '@apollo/sandbox/src/helpers/postMessageRelayHelpers';
import {zodResolver} from '@hookform/resolvers/zod';
import {ChevronsUpDown, LogOut, Plus, Settings, Trash} from 'lucide-react';
import React, {useCallback, useEffect, useState} from 'react';
import {ChevronsUpDown, Plus, Settings, Trash} from 'lucide-react';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {useForm} from 'react-hook-form';
import {useNavigate} from 'react-router';
import {z} from 'zod';
Expand All @@ -22,7 +22,6 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from './alert-dialog';
import {Avatar, AvatarFallback, AvatarImage} from './avatar';
import {Button} from './button';
import {
Card,
Expand All @@ -48,12 +47,6 @@ import {
DialogHeader,
DialogTitle,
} from './dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from './dropdown-menu';
import {
Form,
FormControl,
Expand Down Expand Up @@ -118,6 +111,9 @@ const Home = () => {
const [selectedTab, setSelectedTab] = useState('sandbox');
const [selectedVariant, setSelectedVariant] = useState(null);
const [variants, setVariants] = useState([]);
const [isEditing, setIsEditing] = useState(false);
const matchArgumentsRef = useRef<HTMLPreElement>(null);
const responseRef = useRef<HTMLPreElement>(null);

const serverBaseUrl = getApiBaseUrl();

Expand Down Expand Up @@ -488,6 +484,69 @@ const Home = () => {
setIsDeleteDialogOpen(true);
};

const handleCancel = () => {
setIsEditing(false);
if (matchArgumentsRef.current) {
matchArgumentsRef.current.textContent = JSON.stringify(
seedToView.operationMatchArguments,
null,
2
);
}
if (responseRef.current) {
responseRef.current.textContent = JSON.stringify(
seedToView.seedResponse,
null,
2
);
}
};

const handleSave = async () => {
try {
const updatedMatchArguments =
matchArgumentsRef.current?.textContent || '';
const updatedResponse = responseRef.current?.textContent || '';

const payload = {
id: seedToView.id,
operationName: seedToView.operationName,
seedResponse: JSON.parse(updatedResponse),
operationMatchArguments: JSON.parse(updatedMatchArguments),
seedGroupId: selectedSeedGroup.id,
graphId: seedToView.graphId,
variantName: seedToView.variantName,
oldOperationMatchArguments: seedToView.operationMatchArguments,
};

console.log('server base url: ', serverBaseUrl);
const response = await fetch(`${serverBaseUrl}/api/seeds`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});

if (!response.ok) {
throw new Error('Failed to update seed data');
}
setIsEditing(false);
toast({
title: 'Seed Updated Successfully',
description: 'Your changes have been saved.',
});
} catch (error) {
console.log('Error updating seed:', error);
toast({
title: 'Error Updating Seed',
description:
'An error occurred while saving your changes. Please try again.',
variant: 'destructive',
});
}
};

return (
<Tabs value={selectedTab} onValueChange={setSelectedTab} className="w-full">
<Toaster />
Expand Down Expand Up @@ -892,13 +951,6 @@ const Home = () => {
)}
/>
<div className="flex space-x-2">
<Button
id="cancel-seed-button"
type="button"
variant="secondary"
>
Discard
</Button>
<Button id="save-seed-button" type="submit">
Save seed
</Button>
Expand All @@ -917,25 +969,47 @@ const Home = () => {
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-center mb-2">
<span>{`Operation name: ${seedToView?.operationName}`}</span>
<div className="flex items-center justify-between mb-2">
<span>{`Operation name: ${seedToView.operationName}`}</span>
{!isEditing ? (
<Button onClick={() => setIsEditing(true)}>
Edit seed
</Button>
) : (
<div className="flex space-x-2">
<Button
id="discard-changes-button"
type="button"
variant="secondary"
onClick={handleCancel}
>
Discard Changes
</Button>
<Button
id="save-changes-button"
onClick={handleSave}
>
Save Changes
</Button>
</div>
)}
</div>
<div>
<h3 className="font-semibold">Matching Arguments</h3>
<pre
// ref={matchArgumentsRef}
// contentEditable={isEditing}
ref={matchArgumentsRef}
contentEditable={isEditing}
suppressContentEditableWarning={true}
className={`bg-gray-100 p-4 rounded overflow-auto focus:border-primary focus:outline-none ${
false
isEditing
? 'border-[1px] border-[hsl(var(--primary))]'
: 'border-[1px] border-transparent'
}`}
style={{minHeight: '100px'}}
>
<code className="text-sm">
{JSON.stringify(
seedToView?.operationMatchArguments,
seedToView.operationMatchArguments,
null,
2
)}
Expand All @@ -945,18 +1019,18 @@ const Home = () => {
<div>
<h3 className="font-semibold">Response</h3>
<pre
// ref={responseRef}
// contentEditable={isEditing}
ref={responseRef}
contentEditable={isEditing}
suppressContentEditableWarning={true}
className={`bg-gray-100 p-4 rounded overflow-auto focus:border-primary focus:outline-none ${
false
isEditing
? 'border-[1px] border-[hsl(var(--primary))]'
: 'border-[1px] border-transparent'
}`}
style={{minHeight: '100px'}}
>
<code className="text-sm">
{JSON.stringify(seedToView?.seedResponse, null, 2)}
{JSON.stringify(seedToView.seedResponse, null, 2)}
</code>
</pre>
</div>
Expand Down