Skip to content

Commit

Permalink
Merge pull request #42 from hubcio2115/feat/41-przy-rejestracji-uytko…
Browse files Browse the repository at this point in the history
…wnikowi-powinna-tworzy-si-organizacja-pod-nazw-jego-nazwy-uytkownika

Added a organization creation when user creates an account
  • Loading branch information
hubcio2115 committed Apr 4, 2024
2 parents c5274ce + 8ecc54f commit 42708a6
Show file tree
Hide file tree
Showing 10 changed files with 1,634 additions and 81 deletions.
7 changes: 6 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dev": "next dev",
"dev:turbo": "next dev --turbo",
"lint": "next lint",
"test": "vitest",
"start": "next start"
},
"dependencies": {
Expand Down Expand Up @@ -51,25 +52,29 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@testing-library/react": "^14.2.2",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/eslint": "^8.44.2",
"@types/node": "20.8.9",
"@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^7.1.0",
"@typescript-eslint/parser": "^7.1.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.14",
"dotenv-cli": "^7.3.0",
"drizzle-kit": "^0.20.14",
"eslint": "^8.52.0",
"eslint-config-next": "^14.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jsdom": "^24.0.0",
"postcss": "^8.4.27",
"prettier": "^3.0.0",
"prettier-plugin-tailwindcss": "^0.5.4",
"tailwindcss": "^3.3.3",
"typescript": "^5.1.6"
"typescript": "^5.1.6",
"vitest": "^1.4.0"
},
"ct3aMetadata": {
"initVersion": "7.20.2"
Expand Down
16 changes: 16 additions & 0 deletions apps/web/src/lib/__test__/generateSeedForOrgName.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { expect, test } from "vitest";

import { generateSeedForOrgName } from "../utils";

test("is generated seed a whole number", () => {
const value = generateSeedForOrgName();

expect(value % 1).lt(Number.EPSILON);
});

test("is generated seed a 4 digit number", () => {
const value = generateSeedForOrgName();

expect(value).gte(1000);
expect(value).lt(10000);
});
21 changes: 21 additions & 0 deletions apps/web/src/lib/__test__/stripSpecialCharacters.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { expect, test } from "vitest";

import { stripSpecialCharacters } from "../utils";

test('stripSpecialCharacters for "xx__Pu$$yD€$stroy€r__xx" return "xxPyDstroyrxx"', () => {
const testingValue = stripSpecialCharacters("xx__Pu$$yD€$stroy€r__xx");

expect(testingValue).toBe("xxPuyDstroyrxx");
});

test('stripSpecialCharacters for "hubcio2115" leaves it alone', () => {
const testingValue = stripSpecialCharacters("hubcio2115");

expect(testingValue).toBe("hubcio2115");
});

test('stripSpecialCharacters for "k-lisowski" leaves it alone', () => {
const testingValue = stripSpecialCharacters("k-lisowski");

expect(testingValue).toBe("k-lisowski");
});
20 changes: 19 additions & 1 deletion apps/web/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs));
}

/**
* Strip a string of special characters other than "-"
* @example
* stripSpecialCharacters("xx__Pu$$yD€$stroy€r__xx"); // returns "xxPuyDstroyrxx"
* @example
* stripSpecialCharacters("hubcio2115"); // returns "hubcio2115"
* @example
* stripSpecialCharacters("k-lisowski"); // returns "k-lisowski"
*/
export function stripSpecialCharacters(str: string): string {
return str.replace(/[^a-zA-Z0-9-]/g, "");
}

/** Generate a 4 digit seed number for unique organization names */
export function generateSeedForOrgName(): number {
return Math.floor(Math.random() * 10000);
}
12 changes: 7 additions & 5 deletions apps/web/src/server/api/routers/organization.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { eq } from "drizzle-orm";

import { insertOrganizationSchema } from "~/lib/validators/organization";
import { createOrganization } from "~/server/db/organizations";
import { organizations, usersToOrganizations } from "~/server/db/schema";

import { createTRPCRouter, protectedProcedure } from "../trpc";
Expand Down Expand Up @@ -31,11 +32,12 @@ export const organizationRouter = createTRPCRouter({
createOrganization: protectedProcedure
.input(insertOrganizationSchema)
.mutation(async ({ ctx, input }) => {
const newOrganization = await ctx.db
.insert(organizations)
.values({ name: input.name, owner: ctx.session.user.id })
.returning();
const newOrganization = (await createOrganization(
ctx.db,
input.name,
ctx.session.user.id,
))[0];

return newOrganization[0];
return newOrganization;
}),
});
64 changes: 64 additions & 0 deletions apps/web/src/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@ import type { Adapter } from "next-auth/adapters";
import GoogleProvider from "next-auth/providers/google";

import { env } from "~/env.mjs";
import { generateSeedForOrgName, stripSpecialCharacters } from "~/lib/utils";
import { db } from "~/server/db";
import { createTable } from "~/server/db/schema";

import { createOrganization, getOrganizationByName } from "./db/organizations";

/**
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
* object and keep type safety.
Expand Down Expand Up @@ -55,6 +58,67 @@ export const authOptions: NextAuthOptions = {
clientSecret: env.GOOGLE_CLIENT_SECRET,
}),
],
events: {
async createUser({ user }) {
const userName = user.name;

if (!userName) {
throw new Error("Username is no present, this shouldn't happen!");
}

const splitUsername = userName.split(" ");

let newOrgName = stripSpecialCharacters(
(() => {
switch (splitUsername.length) {
case 1: {
const [name, ..._rest] = splitUsername;

return `${name!.toLowerCase()}`;
}

case 2:
case 3: {
const [firstName, lastName, ..._rest] = splitUsername;

return `${firstName!
.at(0)
?.toLowerCase()}${lastName!.toLowerCase()}`;
}

default:
throw new Error("Couldn't create a new organization from name");
}
})(),
);

let isFirstIteration = true;
for (;;) {
const isNameAvailable =
(await getOrganizationByName(db, newOrgName)).length === 0;

if (isNameAvailable) {
break;
}

if (!isFirstIteration) {
newOrgName =
newOrgName.slice(0, -4) + generateSeedForOrgName.toString();
} else {
newOrgName += generateSeedForOrgName().toString();
isFirstIteration = false;
}
}

const newOrganization = (
await createOrganization(db, newOrgName, user.id)
)[0];

if (!newOrganization) {
throw new Error("Couldn't create new organization.");
}
},
},
};

/**
Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/server/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ import { drizzle } from "drizzle-orm/vercel-postgres";
import * as schema from "./schema";

export const db = drizzle(sql, { schema });

export type DbConnection = typeof db;
25 changes: 25 additions & 0 deletions apps/web/src/server/db/organizations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use server";
import "server-only";

import { eq } from "drizzle-orm";

import type { DbConnection } from ".";
import { organizations } from "./schema";

export async function createOrganization(
db: DbConnection,
name: string,
ownerId: string,
) {
return db.insert(organizations).values({ name, owner: ownerId }).returning();
}

export async function getOrganizationByName(db: DbConnection, name: string) {
return db
.select({
id: organizations.id,
name: organizations.name,
})
.from(organizations)
.where(eq(organizations.name, name));
}
9 changes: 9 additions & 0 deletions apps/web/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import react from "@vitejs/plugin-react";
import { defineConfig } from "vitest/config";

export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
},
});
Loading

0 comments on commit 42708a6

Please sign in to comment.