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

Merge release/v0.1.2 into main #77

Merged
merged 39 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8380bcf
Merge pull request #61 from morph-data/release/v0.1.1
tslcls Jan 24, 2025
85e69a8
chore: update poetry lock
oginokairan Jan 27, 2025
2bb92e1
fix: remove project id validation for morph config
tslcls Jan 27, 2025
7a22394
feat(cli): python version selection at new command
oginokairan Jan 27, 2025
91b9f07
Merge pull request #63 from morph-data/fix/morph-config-with-project-id
shibatanaoto Jan 27, 2025
ad9c2d5
feat: context command
tslcls Jan 27, 2025
d915d40
fix: transform parquet error handling
tslcls Jan 27, 2025
1cf1e60
Merge pull request #66 from morph-data/fix/transform-parquet-output-e…
shibatanaoto Jan 27, 2025
beae40c
Merge pull request #65 from morph-data/feature/context-command
shibatanaoto Jan 27, 2025
2d1a37a
fix: morph context user info to snake case
tslcls Jan 28, 2025
71310ef
Merge pull request #67 from morph-data/fix/morph-context-user-info
shibatanaoto Jan 28, 2025
8a7dcea
Merge branch 'release/v0.1.2' into feature/python-version
oginokairan Jan 28, 2025
a35e550
feat(cli): profile flag support for morph config command
oginokairan Jan 28, 2025
25bba74
Merge pull request #68 from morph-data/feature/profile-config-support
tslcls Jan 28, 2025
3a6ef9d
chore: update dependencies
oginokairan Jan 29, 2025
147131e
fix(cli): timezone handling
oginokairan Jan 29, 2025
b3cd8cf
Add title fallback
KT83 Jan 30, 2025
c523e75
fix: title fallback typo
KT83 Jan 30, 2025
b330f59
update: add title auto detection and toc
KT83 Jan 30, 2025
499727a
fix: Add <Chat /> to custom-mdx-components.tsx
KT83 Jan 30, 2025
5cd48e9
fix: Force light mode to prevent unintended dark mode in hosted envir…
atsuki44 Jan 30, 2025
8a8a550
Merge pull request #69 from morph-data/feature/mdx-utils
KT83 Jan 30, 2025
845fa24
Merge pull request #70 from morph-data/fix/hosted-app-color-mode
KT83 Jan 30, 2025
caae1b7
add stream_chat for simple usage
shibatanaoto Jan 30, 2025
1e946ff
update starter template
shibatanaoto Jan 30, 2025
efb675e
Merge pull request #71 from morph-data/enhancement/add-stream-chat
tslcls Jan 31, 2025
9197233
Merge pull request #64 from morph-data/feature/python-version
oginokairan Jan 31, 2025
9092d9f
Merge branch 'release/v0.1.2' into enhancement/update-starter-template
shibatanaoto Jan 31, 2025
13ed75b
feat(cli): force clean frontend directory
oginokairan Jan 31, 2025
a14a598
replace create_chunk with stream_chat
shibatanaoto Jan 31, 2025
c08a4d6
Merge pull request #72 from morph-data/enhancement/update-starter-tem…
oginokairan Jan 31, 2025
58048c1
Merge branch 'release/v0.1.2' into feature/clean-frontend
oginokairan Jan 31, 2025
ffdc27a
Merge pull request #73 from morph-data/feature/clean-frontend
shibatanaoto Jan 31, 2025
3611fd8
Add page title fallback on header
KT83 Jan 31, 2025
2c02c4d
add error handling for missing OPENAI_API_KEY for chat template
shibatanaoto Jan 31, 2025
d4cae3e
Merge pull request #75 from morph-data/enhancement/update-starter-tem…
oginokairan Jan 31, 2025
3e5927f
Merge pull request #74 from morph-data/fix/page-title-fallback-on-header
KT83 Jan 31, 2025
a7ae847
update version 0.1.2
shibatanaoto Jan 31, 2025
8bbd33b
Merge pull request #78 from morph-data/chore/update-version-0.1.2
oginokairan Jan 31, 2025
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
50 changes: 32 additions & 18 deletions core/morph/api/cloud/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import configparser
import os
from functools import wraps
from typing import Any, Dict, Generic, List, Optional, Type, TypeVar, cast

from morph.api.cloud.base import MorphApiBaseClient, MorphClientResponse
Expand All @@ -10,6 +11,18 @@
MORPH_API_BASE_URL = "https://api.morph-data.io/v0"


def validate_project_id(method):
@wraps(method)
def wrapper(self, *args, **kwargs):
if not hasattr(self, "project_id") or not self.project_id:
raise ValueError(
"No project id found. Please fill project_id in morph_project.yml"
)
return method(self, *args, **kwargs)

return wrapper


class MorphApiKeyClientImpl(MorphApiBaseClient):
def __init__(self):
# Initialize default values
Expand All @@ -22,16 +35,9 @@ def __init__(self):
project = load_project(find_project_root_dir())
if project is None:
raise ValueError("No project found.")
if project.project_id is None:
raise ValueError(
"No project id found. Please fill project_id in morph_project.yml"
)
if project.profile is None:
raise ValueError(
"No profile found. Please fill profile in morph_project.yml"
)
profile = project.profile or "default"

self.project_id = os.environ.get("MORPH_PROJECT_ID", project.project_id)
self.project_id = os.environ.get("MORPH_PROJECT_ID", project.project_id or "")

self.api_key = os.environ.get("MORPH_API_KEY", "")
if not self.api_key:
Expand All @@ -42,14 +48,14 @@ def __init__(self):
)
config = configparser.ConfigParser()
config.read(config_path)
if not config.has_section(project.profile):
if not config.has_section(profile):
raise ValueError(
f"No profile '{project.profile}' found in the credentials file."
f"No profile '{profile}' found in the credentials file."
)
self.api_key = config.get(project.profile, "api_key", fallback="")
self.api_key = config.get(profile, "api_key", fallback="")

if not self.api_key:
raise ValueError(f"No API key found for profile '{project.profile}'.")
raise ValueError(f"No API key found for profile '{profile}'.")

def get_headers(self) -> Dict[str, Any]:
return {
Expand All @@ -61,28 +67,28 @@ def get_headers(self) -> Dict[str, Any]:
def get_base_url(self) -> str:
return self.api_url

@validate_project_id
def find_database_connection(self) -> MorphClientResponse:
path = f"project/{self.project_id}/connection"
return self.request(method="GET", path=path)

@validate_project_id
def find_external_connection(self, connection_slug: str) -> MorphClientResponse:
path = f"external-connection/{connection_slug}"
return self.request(method="GET", path=path)

def list_external_connections(self) -> MorphClientResponse:
path = "external-connection"
query = {"withAuth": True}
return self.request(method="GET", path=path, query=query)

@validate_project_id
def list_env_vars(self) -> MorphClientResponse:
path = "env-vars"
return self.request(method="GET", path=path)

@validate_project_id
def override_env_vars(self, env_vars: List[EnvVarObject]) -> MorphClientResponse:
path = "env-vars/override"
body = {"envVars": [env_var.model_dump() for env_var in env_vars]}
return self.request(method="POST", path=path, data=body)

@validate_project_id
def list_fields(
self,
table_name: str,
Expand All @@ -106,6 +112,13 @@ def check_api_secret(self) -> MorphClientResponse:
path = "api-secret/check"
return self.request(method="GET", path=path)

@validate_project_id
def verify_api_secret(self) -> MorphClientResponse:
path = "api-secret/verify"
body = {"projectId": self.project_id}
return self.request(method="POST", path=path, data=body)

@validate_project_id
def initiate_deployment(
self, project_id: str, image_build_log: str, image_checksum: str
) -> MorphClientResponse:
Expand All @@ -118,6 +131,7 @@ def initiate_deployment(

return self.request(method="POST", path=path, data=body)

@validate_project_id
def execute_deployment(
self, user_function_deployment_id: str
) -> MorphClientResponse:
Expand Down
15 changes: 11 additions & 4 deletions core/morph/api/cloud/types.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
from typing import List

from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict, Field
from pydantic.alias_generators import to_snake


# ================================================
# User
# ================================================
class UserInfo(BaseModel):
user_id: str
user_id: str = Field(alias="userId")
username: str
email: str
first_name: str
last_name: str
first_name: str = Field(alias="firstName")
last_name: str = Field(alias="lastName")
roles: List[str]

model_config = ConfigDict(
alias_generator=to_snake,
populate_by_name=True,
from_attributes=True,
)


# ================================================
# EnvVar
Expand Down
5 changes: 5 additions & 0 deletions core/morph/api/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
{% endif %}

<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: "class",
};
</script>
</head>
<body>
{% inertia_body %}
Expand Down
19 changes: 19 additions & 0 deletions core/morph/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def run(

@cli.command("clean")
@params.verbose
@params.force
@click.pass_context
@global_flags
@requires.preflight
Expand Down Expand Up @@ -197,3 +198,21 @@ def init(
task = InitTask(ctx.obj["flags"])
results = task.run()
return results, True


@cli.command("context")
@params.output
@click.pass_context
@global_flags
@requires.preflight
@requires.postflight
def context(
ctx: click.Context, **kwargs: Dict[str, Union[str, int, bool]]
) -> Tuple[None, bool]:
"""Print or save the user information context."""
from morph.task.context import ContextTask

task = ContextTask(ctx.obj["flags"])
task.run()

return None, True
14 changes: 14 additions & 0 deletions core/morph/cli/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,17 @@ def convert_value(val: str) -> Any:
is_flag=True,
help="Disable cache.",
)

output = click.option(
"--output",
"-o",
type=str,
help="Specify output file path.",
)

force = click.option(
"--force",
"-f",
is_flag=True,
help="Force execution.",
)
4 changes: 3 additions & 1 deletion core/morph/frontend/template/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,22 @@
"devDependencies": {
"@inertiajs/react": "^1.2.0",
"@mdx-js/rollup": "^3.1.0",
"@stefanprobst/rehype-extract-toc": "^2.2.1",
"@types/mdx": "^2.0.13",
"@types/node": "^20.14.11",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^7.15.0",
"@typescript-eslint/parser": "^7.15.0",
"@morph-data/components": "0.1.0",
"@morph-data/components": "0.1.2-beta.3",
"@vitejs/plugin-react-swc": "^3.5.0",
"class-variance-authority": "^0.7.1",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react-refresh": "^0.4.7",
"react-error-boundary": "^4.1.2",
"rehype-pretty-code": "^0.14.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "^4.0.0",
"shiki": "^1.24.1",
"typescript": "^5.2.2",
Expand Down
2 changes: 2 additions & 0 deletions core/morph/frontend/template/src/custom-mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Embed,
Grid,
LLM,
Chat,
Metrics,
MetricsGrid,
Panel,
Expand Down Expand Up @@ -39,6 +40,7 @@ export const customMDXComponents: MDXComponents = {
Accordion,
Callout,
LLM,
Chat,
DatePicker,
DateRangePicker,
};
33 changes: 29 additions & 4 deletions core/morph/frontend/template/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import "vite/modulepreload-polyfill";
import { createRoot } from "react-dom/client";
import { createInertiaApp } from "@inertiajs/react";
import React, { StrictMode } from "react";
import React, { StrictMode, useMemo } from "react";
import { PageSkeleton } from "./page-skeleton.tsx";
import "@morph-data/components/css";
import { MDXComponents } from "mdx/types";
import type { Toc } from "@stefanprobst/rehype-extract-toc";
import { customMDXComponents } from "./custom-mdx-components.tsx";
import { AdminPage } from "./admin/AdminPage.tsx";
import { ErrorPage } from "./error-page.tsx";
Expand All @@ -17,7 +18,11 @@ type MDXProps = {

export type MDXComponent = (props: MDXProps) => JSX.Element;

type PageModule = { default: MDXComponent }; // types MDX default export
type PageModule = {
default: MDXComponent;
title?: string;
tableOfContents?: Toc;
}; // types MDX default export
type Pages = Record<string, PageModule>;

const pages: Pages = import.meta.glob<true, string, PageModule>(
Expand All @@ -39,15 +44,24 @@ const normalizePath = (filePath: string) => {
const routes = Object.entries(pages).map(([filePath, module]) => {
// Extract the exported title from the MDX file
const title = (() => {
if ("title" in module) {
if (module.title) {
return String(module.title);
}

if (module.tableOfContents && module.tableOfContents.length > 0) {
const firstHeading = module.tableOfContents[0];
if (firstHeading) {
return firstHeading.value;
}
}

return "Untitled";
})();

return {
path: normalizePath(filePath),
title,
toc: module.tableOfContents,
};
});

Expand All @@ -61,11 +75,22 @@ document.addEventListener("DOMContentLoaded", () => {
}) => {
useRefresh();

const firstHeading = useMemo(() => {
if (
pageModule?.tableOfContents &&
pageModule.tableOfContents.length > 0
) {
return pageModule.tableOfContents[0];
}
return null;
}, []);

return (
<PageSkeleton
routes={routes}
title={name}
title={pageModule?.title || firstHeading?.value || "Untitled"}
showAdminPage={showAdminPage}
toc={pageModule?.tableOfContents}
>
{name === "morph" ? (
<AdminPage />
Expand Down
20 changes: 17 additions & 3 deletions core/morph/frontend/template/src/page-skeleton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
DropdownMenuItem,
Button,
DropdownMenuSeparator,
Toc,
} from "@morph-data/components";
import { ErrorBoundary, FallbackProps } from "react-error-boundary";
import { Link } from "@inertiajs/react";
import { Toc as TocEntries } from "@stefanprobst/rehype-extract-toc";

function fallbackRender({ error }: FallbackProps) {
// Call resetErrorBoundary() to reset the error boundary and retry the render.
Expand All @@ -24,6 +26,7 @@ type PageSkeletonProps = React.PropsWithChildren<{
routes: Array<{ path: string; title: string }>;
title: string;
showAdminPage: boolean;
toc?: TocEntries;
}>;

export const PageSkeleton: React.FC<PageSkeletonProps> = (props) => {
Expand Down Expand Up @@ -52,10 +55,14 @@ export const PageSkeleton: React.FC<PageSkeletonProps> = (props) => {
</svg>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<DropdownMenuContent align="start" className="w-60">
{props.routes.map((route) => (
<Link href={route.path} key={route.path}>
<DropdownMenuItem>{route.title}</DropdownMenuItem>
<DropdownMenuItem>
<span className="truncate max-w-full w-full">
{route.title}
</span>
</DropdownMenuItem>
</Link>
))}
{props.showAdminPage && (
Expand All @@ -81,7 +88,14 @@ export const PageSkeleton: React.FC<PageSkeletonProps> = (props) => {
</a>
</div>
</div>
<div className="mt-4 p-2">{props.children}</div>
<div className="mt-4 p-2">
<div className="grid gap-4 grid-cols-[1fr_32px] lg:grid-cols-[1fr_180px]">
<div className="p-2">{props.children}</div>
<div>
<Toc toc={props.toc} className="sticky top-10 right-10 h-fit" />
</div>
</div>
</div>
</div>
</ErrorBoundary>
);
Expand Down
Loading