Skip to content

Commit

Permalink
stringCompare
Browse files Browse the repository at this point in the history
  • Loading branch information
N2D4 committed Dec 28, 2024
1 parent 58fcce5 commit c733fee
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 261 deletions.
8 changes: 7 additions & 1 deletion apps/backend/scripts/verify-data-integrity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PrismaClient } from "@prisma/client";
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { filterUndefined } from "@stackframe/stack-shared/dist/utils/objects";
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
import { deindent } from "@stackframe/stack-shared/dist/utils/strings";
Expand Down Expand Up @@ -67,6 +67,11 @@ async function main() {
});
console.log(`Found ${projects.length} projects, iterating over them.`);

// we wanna do the internal project first
const internalProject = projects.pop() ?? throwErr("No projects found");
if (internalProject.id !== "internal") throwErr("Last project is not the internal project? This is a bug.");
projects.unshift(internalProject);

for (let i = 0; i < projects.length; i++) {
const projectId = projects[i].id;
await recurse(`[project ${i + 1}/${projects.length}] ${projectId} ${projects[i].displayName}`, async (recurse) => {
Expand All @@ -88,6 +93,7 @@ async function main() {
},
}),
]);
if (users.is_paginated) throwErr("Users are paginated? Please update the verify-data-integrity.ts script to handle this.");

for (let j = 0; j < users.items.length; j++) {
const user = users.items[j];
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/src/lib/openapi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { yupNumber, yupObject, yupString } from '@stackframe/stack-shared/dist/s
import { StackAssertionError, throwErr } from '@stackframe/stack-shared/dist/utils/errors';
import { HttpMethod } from '@stackframe/stack-shared/dist/utils/http';
import { typedEntries, typedFromEntries } from '@stackframe/stack-shared/dist/utils/objects';
import { deindent } from '@stackframe/stack-shared/dist/utils/strings';
import { deindent, stringCompare } from '@stackframe/stack-shared/dist/utils/strings';
import * as yup from 'yup';

export function parseOpenAPI(options: {
Expand Down Expand Up @@ -34,7 +34,7 @@ export function parseOpenAPI(options: {
)]
))
.filter(([_, handlersByMethod]) => Object.keys(handlersByMethod).length > 0)
.sort(([_a, handlersByMethodA], [_b, handlersByMethodB]) => ((Object.values(handlersByMethodA)[0] as any).tags[0] ?? "").localeCompare(((Object.values(handlersByMethodB)[0] as any).tags[0] ?? ""))),
.sort(([_a, handlersByMethodA], [_b, handlersByMethodB]) => stringCompare((Object.values(handlersByMethodA)[0] as any).tags[0] ?? "", (Object.values(handlersByMethodB)[0] as any).tags[0] ?? "")),
),
};
}
Expand Down
10 changes: 3 additions & 7 deletions apps/backend/src/lib/permissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { KnownErrors } from "@stackframe/stack-shared";
import { ProjectsCrud } from "@stackframe/stack-shared/dist/interface/crud/projects";
import { TeamPermissionDefinitionsCrud, TeamPermissionsCrud } from "@stackframe/stack-shared/dist/interface/crud/team-permissions";
import { StackAssertionError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { typedToLowercase, typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
import { stringCompare, typedToLowercase, typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
import { PrismaTransaction } from "./types";

export const fullPermissionInclude = {
Expand Down Expand Up @@ -167,11 +167,7 @@ export async function listUserTeamPermissions(
}

return finalResults
.sort((a, b) => {
if (a.team_id !== b.team_id) return a.team_id.localeCompare(b.team_id);
if (a.user_id !== b.user_id) return a.user_id.localeCompare(b.user_id);
return a.id.localeCompare(b.id);
})
.sort((a, b) => stringCompare(a.team_id, b.team_id) || stringCompare(a.user_id, b.user_id) || stringCompare(a.id, b.id))
.filter(p => options.permissionId ? p.id === options.permissionId : true);
}

Expand Down Expand Up @@ -344,7 +340,7 @@ export async function listTeamPermissionDefinitions(

const systemPermissions = Object.values(DBTeamSystemPermission).map(db => teamPermissionDefinitionJsonFromTeamSystemDbType(db, projectConfig));

return [...nonSystemPermissions, ...systemPermissions];
return [...nonSystemPermissions, ...systemPermissions].sort((a, b) => stringCompare(a.id, b.id));
}

export async function createTeamPermissionDefinition(
Expand Down
179 changes: 7 additions & 172 deletions apps/backend/src/lib/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { UsersCrud } from "@stackframe/stack-shared/dist/interface/crud/users";
import { getNodeEnvironment } from "@stackframe/stack-shared/dist/utils/env";
import { StackAssertionError, captureError, throwErr } from "@stackframe/stack-shared/dist/utils/errors";
import { deepPlainEquals, isNotNull, omit } from "@stackframe/stack-shared/dist/utils/objects";
import { typedToLowercase, typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
import { stringCompare, typedToLowercase, typedToUppercase } from "@stackframe/stack-shared/dist/utils/strings";
import { generateUuid } from "@stackframe/stack-shared/dist/utils/uuids";
import { fullPermissionInclude, teamPermissionDefinitionJsonFromDbType, teamPermissionDefinitionJsonFromRawDbType, teamPermissionDefinitionJsonFromTeamSystemDbType } from "./permissions";
import { ensureSharedProvider, ensureStandardProvider } from "./request-checks";
Expand Down Expand Up @@ -108,7 +108,7 @@ export function projectPrismaToCrud(
}
})
.filter((provider): provider is Exclude<typeof provider, undefined> => !!provider)
.sort((a, b) => a.id.localeCompare(b.id));
.sort((a, b) => stringCompare(a.id, b.id));

const passwordAuth = prisma.config.authMethodConfigs.find((config) => config.passwordConfig && config.enabled);
const otpAuth = prisma.config.authMethodConfigs.find((config) => config.otpConfig && config.enabled);
Expand Down Expand Up @@ -137,7 +137,7 @@ export function projectPrismaToCrud(
domain: domain.domain,
handler_path: domain.handlerPath,
}))
.sort((a, b) => a.domain.localeCompare(b.domain)),
.sort((a, b) => stringCompare(a.domain, b.domain)),
oauth_providers: oauthProviders,
enabled_oauth_providers: oauthProviders.filter(provider => provider.enabled),
email_config: (() => {
Expand Down Expand Up @@ -167,12 +167,12 @@ export function projectPrismaToCrud(
team_creator_default_permissions: prisma.config.permissions.filter(perm => perm.isDefaultTeamCreatorPermission)
.map(teamPermissionDefinitionJsonFromDbType)
.concat(prisma.config.teamCreateDefaultSystemPermissions.map(db => teamPermissionDefinitionJsonFromTeamSystemDbType(db, prisma.config)))
.sort((a, b) => a.id.localeCompare(b.id))
.sort((a, b) => stringCompare(a.id, b.id))
.map(perm => ({ id: perm.id })),
team_member_default_permissions: prisma.config.permissions.filter(perm => perm.isDefaultTeamMemberPermission)
.map(teamPermissionDefinitionJsonFromDbType)
.concat(prisma.config.teamMemberDefaultSystemPermissions.map(db => teamPermissionDefinitionJsonFromTeamSystemDbType(db, prisma.config)))
.sort((a, b) => a.id.localeCompare(b.id))
.sort((a, b) => stringCompare(a.id, b.id))
.map(perm => ({ id: perm.id })),
}
};
Expand All @@ -195,69 +195,6 @@ export function listManagedProjectIds(projectUser: UsersCrud["Admin"]["Read"]) {
return managedProjectIds;
}

/**
*
* @param projectId export const fullProjectInclude = {
config: {
include: {
oauthProviderConfigs: {
include: {
proxiedOAuthConfig: true,
standardOAuthConfig: true,
},
},
emailServiceConfig: {
include: {
proxiedEmailServiceConfig: true,
standardEmailServiceConfig: true,
},
},
permissions: {
include: {
parentEdges: {
include: {
parentPermission: true,
},
},
},
},
authMethodConfigs: {
include: {
oauthProviderConfig: {
include: {
proxiedOAuthConfig: true,
standardOAuthConfig: true,
},
},
otpConfig: true,
passwordConfig: true,
passkeyConfig: true,
}
},
connectedAccountConfigs: {
include: {
oauthProviderConfig: {
include: {
proxiedOAuthConfig: true,
standardOAuthConfig: true,
},
},
}
},
domains: true,
},
},
configOverride: true,
_count: {
select: {
users: true, // Count the users related to the project
},
},
} as const satisfies Prisma.ProjectInclude;
* @returns
*/

export function getProjectQuery(projectId: string): RawQuery<ProjectsCrud["Admin"]["Read"] | null> {
const OAuthProviderConfigSelectSql = Prisma.sql`
(
Expand Down Expand Up @@ -430,108 +367,6 @@ export function getProjectQuery(projectId: string): RawQuery<ProjectsCrud["Admin
) AS "row_data_json"
`,
postProcess: (queryResult) => {

/**
* export function projectPrismaToCrud(
prisma: Prisma.ProjectGetPayload<{ include: typeof fullProjectInclude }>
): ProjectsCrud["Admin"]["Read"] {
const oauthProviders = prisma.config.authMethodConfigs
.map((config) => {
if (config.oauthProviderConfig) {
const providerConfig = config.oauthProviderConfig;
if (providerConfig.proxiedOAuthConfig) {
return {
id: typedToLowercase(providerConfig.proxiedOAuthConfig.type),
enabled: config.enabled,
type: "shared",
} as const;
} else if (providerConfig.standardOAuthConfig) {
return {
id: typedToLowercase(providerConfig.standardOAuthConfig.type),
enabled: config.enabled,
type: "standard",
client_id: providerConfig.standardOAuthConfig.clientId,
client_secret: providerConfig.standardOAuthConfig.clientSecret,
facebook_config_id: providerConfig.standardOAuthConfig.facebookConfigId ?? undefined,
microsoft_tenant_id: providerConfig.standardOAuthConfig.microsoftTenantId ?? undefined,
} as const;
} else {
throw new StackAssertionError(`Exactly one of the provider configs should be set on provider config '${config.id}' of project '${prisma.id}'`, { prisma });
}
}
})
.filter((provider): provider is Exclude<typeof provider, undefined> => !!provider)
.sort((a, b) => a.id.localeCompare(b.id));
const passwordAuth = prisma.config.authMethodConfigs.find((config) => config.passwordConfig && config.enabled);
const otpAuth = prisma.config.authMethodConfigs.find((config) => config.otpConfig && config.enabled);
const passkeyAuth = prisma.config.authMethodConfigs.find((config) => config.passkeyConfig && config.enabled);
return {
id: prisma.id,
display_name: prisma.displayName,
description: prisma.description ?? "",
created_at_millis: prisma.createdAt.getTime(),
user_count: prisma._count.users,
is_production_mode: prisma.isProductionMode,
config: {
id: prisma.config.id,
allow_localhost: prisma.config.allowLocalhost,
sign_up_enabled: prisma.config.signUpEnabled,
credential_enabled: !!passwordAuth,
magic_link_enabled: !!otpAuth,
passkey_enabled: !!passkeyAuth,
create_team_on_sign_up: prisma.config.createTeamOnSignUp,
client_team_creation_enabled: prisma.config.clientTeamCreationEnabled,
client_user_deletion_enabled: prisma.config.clientUserDeletionEnabled,
legacy_global_jwt_signing: prisma.config.legacyGlobalJwtSigning,
domains: prisma.config.domains
.map((domain) => ({
domain: domain.domain,
handler_path: domain.handlerPath,
}))
.sort((a, b) => a.domain.localeCompare(b.domain)),
oauth_providers: oauthProviders,
enabled_oauth_providers: oauthProviders.filter(provider => provider.enabled),
email_config: (() => {
const emailServiceConfig = prisma.config.emailServiceConfig;
if (!emailServiceConfig) {
throw new StackAssertionError(`Email service config should be set on project '${prisma.id}'`, { prisma });
}
if (emailServiceConfig.proxiedEmailServiceConfig) {
return {
type: "shared"
} as const;
} else if (emailServiceConfig.standardEmailServiceConfig) {
const standardEmailConfig = emailServiceConfig.standardEmailServiceConfig;
return {
type: "standard",
host: standardEmailConfig.host,
port: standardEmailConfig.port,
username: standardEmailConfig.username,
password: standardEmailConfig.password,
sender_email: standardEmailConfig.senderEmail,
sender_name: standardEmailConfig.senderName,
} as const;
} else {
throw new StackAssertionError(`Exactly one of the email service configs should be set on project '${prisma.id}'`, { prisma });
}
})(),
team_creator_default_permissions: prisma.config.permissions.filter(perm => perm.isDefaultTeamCreatorPermission)
.map(teamPermissionDefinitionJsonFromDbType)
.concat(prisma.config.teamCreateDefaultSystemPermissions.map(teamPermissionDefinitionJsonFromTeamSystemDbType))
.sort((a, b) => a.id.localeCompare(b.id))
.map(perm => ({ id: perm.id })),
team_member_default_permissions: prisma.config.permissions.filter(perm => perm.isDefaultTeamMemberPermission)
.map(teamPermissionDefinitionJsonFromDbType)
.concat(prisma.config.teamMemberDefaultSystemPermissions.map(teamPermissionDefinitionJsonFromTeamSystemDbType))
.sort((a, b) => a.id.localeCompare(b.id))
.map(perm => ({ id: perm.id })),
}
};
}
*/
if (queryResult.length !== 1) {
throw new StackAssertionError(`Expected 1 project with id ${projectId}, got ${queryResult.length}`, { queryResult });
}
Expand All @@ -544,7 +379,7 @@ export function getProjectQuery(projectId: string): RawQuery<ProjectsCrud["Admin
const teamPermissions = [
...row.ProjectConfig.Permissions.map((perm: any) => teamPermissionDefinitionJsonFromRawDbType(perm)),
...Object.values(TeamSystemPermission).map(systemPermission => teamPermissionDefinitionJsonFromTeamSystemDbType(systemPermission, row.ProjectConfig)),
].sort((a, b) => a.id.localeCompare(b.id, 'en'));
].sort((a, b) => stringCompare(a.id, b.id));

const oauthProviderAuthMethods = row.ProjectConfig.AuthMethodConfigs
.map((authMethodConfig: any) => {
Expand Down Expand Up @@ -572,7 +407,7 @@ export function getProjectQuery(projectId: string): RawQuery<ProjectsCrud["Admin
}
})
.filter(isNotNull)
.sort((a: any, b: any) => a.id.localeCompare(b.id));
.sort((a: any, b: any) => stringCompare(a.id, b.id));

return {
id: row.id,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useRouter } from "@/components/router";
import { SearchBar } from "@/components/search-bar";
import { useUser } from "@stackframe/stack";
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
import { stringCompare } from "@stackframe/stack-shared/dist/utils/strings";
import { Button, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@stackframe/stack-ui";
import { useEffect, useMemo, useState } from "react";

Expand Down Expand Up @@ -33,7 +34,7 @@ export default function PageClient() {
if (sort === "recency") {
return a.createdAt > b.createdAt ? -1 : 1;
} else {
return a.displayName.localeCompare(b.displayName);
return stringCompare(a.displayName, b.displayName);
}
});
}, [rawProjects, sort, search]);
Expand Down
Loading

0 comments on commit c733fee

Please sign in to comment.