Skip to content
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
38 changes: 36 additions & 2 deletions packages/db/src/credentials.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ describe("credentials schemas", () => {
});

describe("GoogleSearchConsoleCredentialsSchema", () => {
it("validates Google Search Console credentials", () => {
it("validates Google Search Console access token credentials", () => {
const credentials: GoogleSearchConsoleCredentials = {
accessToken: "ya29.token",
siteUrl: "https://www.example.com/",
Expand All @@ -1564,6 +1564,24 @@ describe("credentials schemas", () => {
expect(result.success).toBe(true);
});

it("validates Google Search Console OAuth credentials", () => {
const credentials: GoogleSearchConsoleCredentials = {
accessToken: "ya29.token",
authType: "oauth",
expiresAt: Date.now() + 3_600_000,
refreshToken: "1//gsc",
siteUrl: "https://www.example.com/",
type: "google_search_console",
};

const result =
GoogleSearchConsoleCredentialsSchema.safeParse(credentials);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data).toEqual(credentials);
}
});

it("rejects missing access token", () => {
const result = GoogleSearchConsoleCredentialsSchema.safeParse({
siteUrl: "https://www.example.com/",
Expand Down Expand Up @@ -2480,6 +2498,17 @@ describe("credentials schemas", () => {
apiKey: "lin_api_xxx",
type: "linear",
},
{
accessToken: "ya29.gsc",
siteUrl: "https://www.example.com/",
type: "google_search_console",
},
{
accessToken: "ya29.gsc-oauth",
expiresAt: Date.now() + 3_600_000,
refreshToken: "1//gsc",
type: "google_search_console",
},
{
accessToken: "ya29.youtube",
expiresAt: Date.now() + 3_600_000,
Expand All @@ -2490,7 +2519,12 @@ describe("credentials schemas", () => {

const oauthCreds = allCredentials.filter(isOAuthCredentials);
expect(oauthCreds.map((c) => c.type).toSorted()).toEqual(
["bigquery", "ga", "youtube_analytics"].toSorted()
[
"bigquery",
"ga",
"google_search_console",
"youtube_analytics",
].toSorted()
);

const dbCreds = allCredentials.filter(isDatabaseCredentials);
Expand Down
27 changes: 26 additions & 1 deletion packages/db/src/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,16 +348,37 @@ export const GranolaCredentialsSchema = z.object({

export type GranolaCredentials = z.infer<typeof GranolaCredentialsSchema>;

export const GoogleSearchConsoleCredentialsSchema = z.object({
export const GoogleSearchConsoleOAuthCredentialsSchema = z.object({
accessToken: requiredOpaqueString("Access token is required"),
apiBaseUrl: optionalTrimmedUrl("API base URL must be a valid URL"),
authType: z.literal("oauth").optional(),
expiresAt: z.number().int().min(0, "Expiration time must be positive"),
refreshToken: requiredOpaqueString("Refresh token is required"),
siteUrl: optionalTrimmedString("Site URL is required"),
type: z.literal("google_search_console"),
});

export const GoogleSearchConsoleAccessTokenCredentialsSchema = z.object({
accessToken: requiredOpaqueString("Access token is required"),
apiBaseUrl: optionalTrimmedUrl("API base URL must be a valid URL"),
siteUrl: optionalTrimmedString("Site URL is required"),
type: z.literal("google_search_console"),
});

export const GoogleSearchConsoleCredentialsSchema = z.union([
GoogleSearchConsoleOAuthCredentialsSchema,
GoogleSearchConsoleAccessTokenCredentialsSchema,
]);

export type GoogleSearchConsoleCredentials = z.infer<
typeof GoogleSearchConsoleCredentialsSchema
>;
export type GoogleSearchConsoleOAuthCredentials = z.infer<
typeof GoogleSearchConsoleOAuthCredentialsSchema
>;
export type GoogleSearchConsoleAccessTokenCredentials = z.infer<
typeof GoogleSearchConsoleAccessTokenCredentialsSchema
>;

export const ConfluenceCredentialsSchema = z.object({
apiToken: requiredOpaqueString("API token is required"),
Expand Down Expand Up @@ -680,7 +701,11 @@ export function isOAuthCredentials(
): credentials is
| GoogleAnalyticsOAuthCredentials
| BigQueryOAuthCredentials
| GoogleSearchConsoleOAuthCredentials
| YouTubeAnalyticsCredentials {
if (credentials.type === "google_search_console") {
return "refreshToken" in credentials && "expiresAt" in credentials;
}
if (
credentials.type !== "ga" &&
credentials.type !== "bigquery" &&
Expand Down
2 changes: 1 addition & 1 deletion packages/db/src/source-providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -761,7 +761,7 @@ export const SOURCE_PROVIDER_REGISTRY = {
sourceApiInterface: true,
testable: false,
dashboardConnectable: true,
dashboardCredentialForm: "json",
dashboardCredentialForm: "google_oauth",
publicCategory: "Marketing",
guide: {
summary:
Expand Down
Loading