unkeyed / unkey

Open source API management platform
https://go.unkey.com
Other
2.91k stars 290 forks source link

[KEY-256] Make tinybird optional on the landing page #483

Closed chronark closed 10 months ago

chronark commented 10 months ago

From SyncLinear.com | KEY-256

lukasjaronis commented 10 months ago

Temp fix at: /apps/(landing)/page.tsx

const [workspaces, apis, keys] = await Promise.all([
  db
    .select({ count: sql<number>`count(*)` })
    .from(schema.workspaces)
    .then((res) => res.at(0)?.count ?? 0),
  db
    .select({ count: sql<number>`count(*)` })
    .from(schema.apis)
    .then((res) => res.at(0)?.count ?? 0),
  db
    .select({ count: sql<number>`count(*)` })
    .from(schema.keys)
    .then((res) => res.at(0)?.count ?? 0)
  // getTotalVerifications({}).then((res) => {
  //   return res.data.reduce((acc, curr) => acc + curr.verifications, 0);
  // }),
]);
function NumbersServed() {
  return (
    <div className="mt-24 rounded-4xl sm:mt-32 lg:mt-32">
      <Container className="">
        <FadeIn className="flex items-center gap-x-8">
          <h2 className="mb-8 text-2xl font-semibold tracking-wider text-center text-black font-display sm:text-left">
            We serve
          </h2>
          <div className="flex-auto h-px" />
        </FadeIn>
        <FadeInStagger faster>
          <StatList>
            <StatListItem
              value={Intl.NumberFormat("en", { notation: "compact" }).format(workspaces)}
              label="Workspaces"
            />
            <StatListItem
              value={Intl.NumberFormat("en", { notation: "compact" }).format(apis)}
              label="APIs"
            />
            <StatListItem
              value={Intl.NumberFormat("en", { notation: "compact" }).format(keys)}
              label="Keys"
            />
            {/* <StatListItem
              value={Intl.NumberFormat("en", { notation: "compact" }).format(totalVerifications)}
              label="Verifications"
            /> */}
          </StatList>
        </FadeInStagger>
      </Container>
    </div>
  );
}
lukasjaronis commented 10 months ago

Tinybird functions are called in various places.

It depends on how you want to handle Tinybird as an optional part of the dev environment @chronark, but a sleek solution imo is to mock the Tinybird class. I would suggest adding a more specific env var rather than just checking against 'development'.

const meta = z.object({
  name: z.string(),
  type: z.string(),
});

const pipeResponseWithoutData = z.object({
  meta: z.array(meta),
  // rows: z.number().optional(),
  // rows_before_limit_at_least: z.number().optional(),
  // statistics: z
  //   .object({
  //     elapsed: z.number().optional(),
  //     rows_read: z.number().optional(),
  //     bytes_read: z.number().optional(),
  //   })
  //   .optional(),
});

class DudTinyBird {
  constructor() {}

  public buildPipe<
    TParameters extends Record<string, unknown>,
    TData extends Record<string, unknown>
  >(req: {
    pipe: string;
    parameters?: z.ZodSchema<TParameters>;
    data: z.ZodSchema<TData, any, any>;
    opts?: {
      cache?: RequestCache;
      revalidate?: number;
    };
  }): (
    params: TParameters
  ) => Promise<z.infer<typeof pipeResponseWithoutData> & { data: TData[] }> {
    const outputSchema = pipeResponseWithoutData.setKey(
      "data",
      z.array(req.data)
    );

    return async () => {
      const res = outputSchema.safeParse({ data: [], meta: [] })
      if (!res.success) {
        throw new Error(res.error.message)
      }
      return res.data
    }
  }
}

const tb =
  env().VERCEL_ENV === "development"
    ? new DudTinyBird()
    : new Tinybird({ token: env().TINYBIRD_TOKEN });

I wanted to preserve the core structure of buildPipe in this quick wip, but let me know your thoughts!

(this does work)

Or... we can apply a patch directly to the package which checks if the token is empty, if it is, we can ignore the fetch call and return an empty data set + put in a log to warn the user there is no token so it will return no data. I think that's probably a better approach.

chronark commented 10 months ago

Definitely prefer a Noop class here cause tinybird would basically break all pages with charts

Let me add that to the upstream https://github.com/chronark/zod-bird and then we can use it here

chronark commented 10 months ago

ok @chronark/zod-bird latest now has a NoopTinybird

import { Tinybird, NoopTinybird } from "@chronark/zod-bird"

const tb =
  env().VERCEL_ENV === "development"
    ? new NoopTinybird()
    : new Tinybird({ token: env().TINYBIRD_TOKEN });
chronark commented 10 months ago

Sorry had to expedite this myself for james' video it's now optional