Files
hoppscotch/packages/hoppscotch-backend/src/utils.ts
Balu Babu a938be3712 feat: Introducing user-collections into self-host (HBE-98) (#18)
* feat: team module added

* feat: teamEnvironment module added

* feat: teamCollection module added

* feat: team request module added

* feat: team invitation module added

* feat: selfhost auth frontend (#15)

Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>

* feat: bringing shortcodes from central to selfhost

* chore: added review changes in resolver

* chore: commented out subscriptions

* chore: bump backend prettier version

* feat: created new user-collections module with base files

* feat: added new models for user-collection and user-request tables in schema.prisma file

* feat: mutations to create user-collections complete

* feat: added user field resolver for userCollections

* feat: added parent field resolver for userCollections

* feat: added child field resolver with pagination for userCollections

* feat: added query to fetch root user-collections with pagination for userCollections

* feat: added query to fetch user-collections for userCollections

* feat: added mutation to rename user-collections

* feat: added mutation to delete user-collections

* feat: added mutation to delete user-collections

* refactor: changed the way we fetch root and child user-collection counts for other operations

* feat: added mutation to move user-collections between root and other child collections

* refactor: abstracted orderIndex update logic into helpert function

* chore: mutation to update order root user-collections complete

* feat: user-collections order can be updated when moving it to the end of list

* feat: user-collections order update feature complete

* feat: subscriptions for user-collection module complete

* chore: removed all console.logs from user-collection.service file

* test: added tests for all field resolvers for user-collection module

* test: test cases for getUserCollection is complete

* test: test cases for getUserRootCollections is complete

* test: test cases for createUserCollection is complete

* test: test cases for renameCollection is complete

* test: test cases for moveUserCollection is complete

* test: test cases for updateUserCollectionOrder is complete

* chore: added createdOn and updatedOn fields to userCollections and userRequests schema

* chore: created function to check if title are of valid size

* refactor: simplified user-collection creation code

* chore: made changed requested in initial PR review

* chore: added requestType enum to user-collections

* refactor: created two seperate queries to fetch root REST or GQL queries

* chore: created seperate mutations and queries for REST and GQL root/child collections

* chore: migrated all input args classess into a single file

* chore: modified createUserCollection service method to work with different creation inputs args type

* chore: rewrote all test cases for user-collections service methods with new CollType

* fix: added updated and deleted subscription changes

* fix: made all the changes requested in the initial PR review

* fix: made all the changes requested in the second PR review

* chore: removed migrations from prisma directory

* fix: made all the changes requested in the third PR review

* chore: added collection type checking to updateUserCollectionOrder service method

* chore: refactored all test cases to reflect new additions to service methods

* chore: fixed issues with pnpm-lock

* chore: removed migrations from prisma directory

* chore: hopefully fixed pnpm-lock issues

* chore: removed console logs in auth controller

---------

Co-authored-by: Mir Arif Hasan <arif.ishan05@gmail.com>
Co-authored-by: Akash K <57758277+amk-dev@users.noreply.github.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
Co-authored-by: ankitsridhar16 <ankit.sridhar16@gmail.com>
2023-03-03 15:03:05 +05:30

155 lines
4.5 KiB
TypeScript

import { ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { pipe } from 'fp-ts/lib/function';
import * as O from 'fp-ts/Option';
import * as TE from 'fp-ts/TaskEither';
import * as T from 'fp-ts/Task';
import * as E from 'fp-ts/Either';
import * as A from 'fp-ts/Array';
import { TeamMemberRole } from './team/team.model';
import { User } from './user/user.model';
import { JSON_INVALID } from './errors';
/**
* A workaround to throw an exception in an expression.
* JS throw keyword creates a statement not an expression.
* This function allows throw to be used as an expression
* @param errMessage Message present in the error message
*/
export function throwErr(errMessage: string): never {
throw new Error(errMessage);
}
/**
* Prints the given value to log and returns the same value.
* Used for debugging functional pipelines.
* @param val The value to print
* @returns `val` itself
*/
export const trace = <T>(val: T) => {
console.log(val);
return val;
};
/**
* Similar to `trace` but allows for labels and also an
* optional transform function.
* @param name The label to given to the trace log (log outputs like this "<name>: <value>")
* @param transform An optional function to transform the log output value (useful for checking specific aspects or transforms (duh))
* @returns A function which takes a value, and is traced.
*/
export const namedTrace =
<T>(name: string, transform?: (val: T) => unknown) =>
(val: T) => {
console.log(`${name}:`, transform ? transform(val) : val);
return val;
};
/**
* Returns the list of required roles annotated on a GQL Operation
* @param reflector NestJS Reflector instance
* @param context NestJS Execution Context
* @returns An Option which contains the defined roles
*/
export const getAnnotatedRequiredRoles = (
reflector: Reflector,
context: ExecutionContext,
) =>
pipe(
reflector.get<TeamMemberRole[]>('requiresTeamRole', context.getHandler()),
O.fromNullable,
);
/**
* Gets the user from the NestJS GQL Execution Context.
* Usually used within guards.
* @param ctx The Execution Context to use to get it
* @returns An Option of the user
*/
export const getUserFromGQLContext = (ctx: ExecutionContext) =>
pipe(
ctx,
GqlExecutionContext.create,
(ctx) => ctx.getContext().req,
({ user }) => user,
O.fromNullable,
);
/**
* Gets a GQL Argument in the defined operation.
* Usually used in guards.
* @param argName The name of the argument to get
* @param ctx The NestJS Execution Context to use to get it.
* @returns The argument value typed as `unknown`
*/
export const getGqlArg = <ArgName extends string>(
argName: ArgName,
ctx: ExecutionContext,
) =>
pipe(
ctx,
GqlExecutionContext.create,
(ctx) => ctx.getArgs<object>(),
// We are not sure if this thing will even exist
// We pass that worry to the caller
(args) => args[argName as any] as unknown,
);
/**
* Sequences an array of TaskEither values while maintaining an array of all the error values
* @param arr Array of TaskEithers
* @returns A TaskEither saying all the errors possible on the left or all the success values on the right
*/
export const taskEitherValidateArraySeq = <A, B>(
arr: TE.TaskEither<A, B>[],
): TE.TaskEither<A[], B[]> =>
pipe(
arr,
A.map(TE.mapLeft(A.of)),
A.sequence(
TE.getApplicativeTaskValidation(T.ApplicativeSeq, A.getMonoid<A>()),
),
);
/**
* Checks to see if the email is valid or not
* @param email The email
* @see https://emailregex.com/ for information on email regex
* @returns A Boolean depending on the format of the email
*/
export const validateEmail = (email: string) => {
return new RegExp(
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
).test(email);
};
/**
* String to JSON parser
* @param {str} str The string to parse
* @returns {E.Right<T> | E.Left<"json_invalid">} An Either of the parsed JSON
*/
export function stringToJson<T>(
str: string,
): E.Right<T | any> | E.Left<string> {
try {
return E.right(JSON.parse(str));
} catch (err) {
return E.left(JSON_INVALID);
}
}
/**
*
* @param title string whose length we need to check
* @param length minimum length the title needs to be
* @returns boolean if title is of valid length or not
*/
export function isValidLength(title: string, length: number) {
if (title.length < length) {
return false;
}
return true;
}