From e04a920ab9f144800176ba98a52ce2cde0306aff Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Sat, 4 Jan 2025 22:57:33 +0530 Subject: [PATCH 01/12] documentation done for the clubs --- db.py | 18 ++++++- main.py | 8 ++++ models.py | 133 +++++++++++++++++++++++++++++++++++++++++++++++++-- mutations.py | 102 ++++++++++++++++++++++++++++++++++++--- otypes.py | 62 +++++++++++++++++++++++- queries.py | 81 ++++++++++++++++++++++--------- utils.py | 55 +++++++++++++++++++-- 7 files changed, 420 insertions(+), 39 deletions(-) diff --git a/db.py b/db.py index 2ea6c19..db8b82b 100644 --- a/db.py +++ b/db.py @@ -1,8 +1,24 @@ +""" +MongoDB Initialization Module + +This module sets up a connection to a MongoDB database and ensures that the required indexes are created. +This module connects to the MongoDB database using environment variables for authentication. +Ensures that a `unique_clubs` index is present on the `cid` field in the clubs collection. +It specifically exports the clubs collection of the database. + +Environment Variables: + `MONGO_USERNAME` (str): MongoDB username. Defaults to "username". + `MONGO_PASSWORD` (str): MongoDB password. Defaults to "password". + `MONGO_PORT` (str): MongoDB port. Defaults to "27017". + `MONGO_DATABASE` (str): MongoDB database name. Defaults to "default". + +""" + from os import getenv from pymongo import MongoClient -# get mongodb URI and database name from environment variale +# get mongodb URI and database name from environment variables MONGO_URI = "mongodb://{}:{}@mongo:{}/".format( getenv("MONGO_USERNAME", default="username"), getenv("MONGO_PASSWORD", default="password"), diff --git a/main.py b/main.py index 6934ef5..28908ab 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,11 @@ +""" +Final Setup + +This file is used to setup the final schema for the subgraph. +It imports the resolvers from the queries and mutations files and creates a final GraphQL schema. +It sets up the Fast API for the Clubs Microservice. +""" + from os import getenv import strawberry diff --git a/models.py b/models.py index 1897f10..715b73d 100644 --- a/models.py +++ b/models.py @@ -1,3 +1,14 @@ +""" +Data Models for Clubs Microservice + +This file decides what and how the Club information is stored in its MongoDB document. +It creates many custom data types for this purpose. + +It defines the following models: + Club: Represents a Club. + Socials: Represents Social Media Links of a Club. +""" + from datetime import datetime from enum import Enum from typing import Annotated, Any, List @@ -17,6 +28,8 @@ from pydantic_core import core_schema from pytz import timezone + +# A custom type for HttpUrls that validates the URL when assigned a value and converts it to a string. http_url_adapter = TypeAdapter(HttpUrl) HttpUrlString = Annotated[ str, @@ -25,15 +38,33 @@ ), ] - +# method that returns the current UTC(timezone) time def create_utc_time(): return datetime.now(timezone("UTC")) - -# for handling mongo ObjectIds class PyObjectId(ObjectId): + """ + MongoDB ObjectId handler + + This class contains clasmethods to validate and serialize ObjectIds. + ObjectIds of documents under the Clubs collection are stored under the 'id' field. + """ + @classmethod def __get_pydantic_core_schema__(cls, source_type: Any, handler): + """ + Defines custom schema for Pydantic validation + + This method is used to define the schema for the Pydantic model. + + Inputs: + source_type (Any): The source type. + handler: The handler. + + Returns: + dict: The schema for the Pydantic model. + """ + return core_schema.union_schema( [ # check if it's an instance first before doing any further work @@ -45,16 +76,53 @@ def __get_pydantic_core_schema__(cls, source_type: Any, handler): @classmethod def validate(cls, v): + """ + Validates the given ObjectId + + Inputs: + v (Any): The value to validate. + + Returns: + ObjectId: The validated ObjectId. + + Raises: + ValueError: If the given value is not a valid ObjectId. + """ + if not ObjectId.is_valid(v): raise ValueError("Invalid ObjectId") return ObjectId(v) @classmethod def __get_pydantic_json_schema__(cls, field_schema): + """ + Generates JSON schema + + This method is used to generate the JSON schema for the Pydantic model. + + Inputs: + field_schema (dict): The field schema. + """ + field_schema.update(type="string") def iiit_email_only(v: str) -> str: + """ + Email validator for IIIT-H emails + + This function validates the given email address according to valid IIIT-H email formats. + + Inputs: + v (str): The email address to validate. + + Returns: + str: The validated email address in lower case. + + Raises: + ValueError: If the given email address is not a valid IIIT-H email. + """ + valid_domains = [ "@iiit.ac.in", "@students.iiit.ac.in", @@ -67,15 +135,22 @@ def iiit_email_only(v: str) -> str: def current_year() -> int: - return datetime.now().year + """ + Returns the current year + Returns: + int: The current year. + """ + + return datetime.now().year +# Enum for the status of a Club(active or deleted) @strawberry.enum class EnumStates(str, Enum): active = "active" deleted = "deleted" - +# Enum for the categories of a Club(cultural, technical, affinity or other) @strawberry.enum class EnumCategories(str, Enum): cultural = "cultural" @@ -85,6 +160,28 @@ class EnumCategories(str, Enum): class Social(BaseModel): + """ + Social Media Links Model + + This class atrributes represent the social media links of a Club. + It also checks for duplicate URLs in the 'other_links' field. + All the attributes are optional and use the HttpUrlString type to validate and store the URLs. + + Atrributes: + website (HttpUrlString | None): The website URL of the Club. + instagram (HttpUrlString | None): The Instagram URL of the Club. + facebook (HttpUrlString | None): The Facebook URL of the Club. + youtube (HttpUrlString | None): The YouTube URL of the Club. + twitter (HttpUrlString | None): The Twitter URL of the Club. + linkedin (HttpUrlString | None): The LinkedIn URL of the Club. + discord (HttpUrlString | None): The Discord URL of the Club. + whatsapp (HttpUrlString | None): The WhatsApp URL of the Club. + other_links (List[HttpUrlString]): The other social media links of the Club. + + Raises: + ValueError: If duplicate URLs are found in the 'other_links' field. + """ + website: HttpUrlString | None = None instagram: HttpUrlString | None = None facebook: HttpUrlString | None = None @@ -104,6 +201,32 @@ def validate_unique_links(cls, value): class Club(BaseModel): + """ + Club Model + + This class represents a Club. + Its attributes store all the details of a Club. + It also validates the email address of the Club using the iiit_email_only method. + + Attributes: + id (PyObjectId): Stores the ObjectId of the Club's document in MongoDB.Uses the PyObjectId class. + cid (str): The unique identifier of the Club. + code (str): The unique short code of the Club. + state (EnumStates): The state of the Club.Uses the EnumStates enum. + category (EnumCategories): The category of the Club.Uses the EnumCategories enum. + student_body (bool): Whether the Club is a Student Body or not. + name (str): The name of the Club. + email (EmailStr): The email address of the Club. + logo (str | None): The URL of the Club's official logo. + banner (str | None): The URL of the Club's official banner. + banner_square (str | None): The URL of the Club's official square banner. + tagline (str): The tagline of the Club. + description (str): The description of the Club. + social (Social): The social media links of the Club.Uses the Social class. + created_time (datetime): The date and time when the Club was created.Uses the current_utc_time method. + updated_time (datetime): The date and time when the Club was last updated. + """ + id: PyObjectId = Field(default_factory=PyObjectId, alias="_id") cid: str = Field(..., description="Club ID") code: str = Field( diff --git a/mutations.py b/mutations.py index c682685..a1e916e 100644 --- a/mutations.py +++ b/mutations.py @@ -1,3 +1,9 @@ +""" +Mutation Resolvers + +Contains mutation resolvers that create, edit, delete and restart a club +""" + from datetime import datetime import strawberry @@ -21,9 +27,30 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: """ Create a new Club. - Checks for the 'cc' role, else raises an Error. - Checks for uniqueness of the club code. + + This resolver/method creates a new club. + For a new club to be created, prior to the creation a user with a uid same as the cid of the club should exist. + This user account will be the account of the club. + Any changes or autherization for actions regarding the club are provided to this account. + This user profile only exists on the database and not on LDAP. + + Iputs: + clubInput (FullClubInput): Used to take input almost all the fields of the Club Schema. + info (Info): Contains the user details. + + Accessibility: + Only CC can create a club. + + Returns: + SimpleClubType: Returns the created club. + + Raises Exception: + Not Authenticated or Not Authenticated to access this API : If the user is not a CC. + Club Already Exists : If a club with the same cid already exists. + Invalid Club ID/Club Email : If there does not exist a user with the same cid or email. + A club with the same code already exists : If a club with the same code already exists. """ + user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -38,7 +65,7 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: if cid_exists: raise Exception("A club with this cid already exists") - # Check whether this cid is valid or not + # Check whether there exists a user with the same cid or email clubMember = getUser(club_input["cid"], info.context.cookies) if clubMember is None: raise Exception("Invalid Club ID/Club Email") @@ -64,8 +91,40 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: @strawberry.mutation def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: """ - Mutation for editing of the club details either by that specific club or the cc. + Edit a club. + + This resolver/method edits a club. + editing of the club details is allowed to be done either by that specific club or the cc. + CC can edit any club details. + the Club can edit only few details. + If user role is ‘cc’ + If the current and the new cid(Club id) are different then this method changes the role of the user with uid same as the old cid to ‘public’ and changes the role of the user with the same uid as the new cid to ‘club’. + And then calls the update_events_members_cid method with the old cid and the new cid. + If the user role is ‘club’ + Does not let the user change the name, email, category of the club. + + Inputs: + clubInput (FullClubInput): Used to take input almost all the fields of the Club Schema. + info (Info): Contains the user details. + + Accessibility: + CC(Full Access), club(Partial Access). + + Returns: + FullClubType: Returns the edited club's details. + + Raises Exception: + Not Authenticated or Not Authenticated to access this API : If the user is not a CC or not a member of the club. + For CC: + A club with this code doesn't exist : If a club with the same code does not exist. + Invalid Club ID/Club Email : If there does not exist a user with the same cid or email. + For club: + Authentication Error! (CLUB ID CHANGED) : If the user's uid and the clubs cid are not the same. + Club Does Not Exist : If the club with the given cid does not exist. + Only CC can edit the club details: If the user is not a CC and trying to change name, email, category of the club. + """ # noqa: E501 + user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -206,8 +265,24 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: @strawberry.mutation def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: """ - Mutation for the cc to move a club to deleted state. + Delete a club. + + This method changes a club's state to "deleted". + + Input: + clubInput (SimpleClubIput): contains the club's information(its cid). + info (Info): An Info object containing the user's information. + + Accessibility: + Only CC. + + Returns: + SimpleClubType: The updated club's information. + + Raises Exception: + Not Authenticated: If the user is not authenticated. """ + user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -235,7 +310,22 @@ def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: @strawberry.mutation def restartClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: """ - Mutation for cc to move a club from deleted state to asctive state. + Restart a club. + + This method is similar to deleteClub but changes the state of the club to "active". + + Input: + clubInput (SimpleClubIput): contains the club's information(its cid). + info (Info): An Info object containing the user's information. + + Accessibility: + Only CC. + + Returns: + SimpleClubType: The updated club's information. + + Raises Exception: + Not Authenticated: If the user is not authenticated. """ user = info.context.user if user is None: diff --git a/otypes.py b/otypes.py index 20df5b4..e8ef8c8 100644 --- a/otypes.py +++ b/otypes.py @@ -1,3 +1,22 @@ +""" +Types and Inputs + +It contains both Inputs and Types for taking inputs and returning outputs. +It also contains the Context class which is used to pass the user details to the resolvers. + +Types: + Info : used to pass the user details to the resolvers. + PyObjectId : used to return ObjectId of a document. + SocialType : used to return Social handles, if they are present. + SimpleClubType : used to return only few fields defined within the Club schema. + FullClubType : used to return almost all the fields within the Club Schema. + +Inputs + SocialsInput : used to take Social handles as input.All of its fields are optional to fill. + SimpleClubInput : used to take club id as input. + FullClubInput : used to take almost all the fields within the Club Schema as input. +""" + import json from functools import cached_property from typing import Dict, List, Optional, Union @@ -12,8 +31,24 @@ # custom context class class Context(BaseContext): + """ + To pass user details + + This class is used to pass the user details to the resolvers. + It will be used through the Info type. + """ + @cached_property def user(self) -> Union[Dict, None]: + """ + Returns User Details + + It will be used in the resolvers to check the user details. + + Returns: + user (Dict): Contains User Details. + """ + if not self.request: return None @@ -22,6 +57,15 @@ def user(self) -> Union[Dict, None]: @cached_property def cookies(self) -> Union[Dict, None]: + """ + Returns Cookies Details + + It will be used in the resolvers to check the cookies details. + + Returns: + cookies (Dict): Contains Cookies Details. + """ + if not self.request: return None @@ -41,6 +85,13 @@ def cookies(self) -> Union[Dict, None]: # TYPES @strawberry.experimental.pydantic.type(model=Social) class SocialsType: + """ + Type for Social Handles + + This type is used to return Social handles, if they are present. + All of its fields(Attributes) are optional to fill and defaultly set to UNSET. + """ + website: Optional[str] = strawberry.UNSET instagram: Optional[str] = strawberry.UNSET facebook: Optional[str] = strawberry.UNSET @@ -72,7 +123,8 @@ class SocialsType: class SimpleClubType: pass - +# This type is used to return almost all the fields within the Club Schema. +# except created and updated time fields. @strawberry.experimental.pydantic.type( model=Club, fields=[ @@ -123,6 +175,14 @@ class SimpleClubInput: ], ) class FullClubInput: + """ + Full Club Details Input + + This class is used to take almost all the fields within the Club Schema as input. + It does not take created and updated time fields as input. + logo, banner, banner_square fields are optional to fill. + """ + logo: Optional[str] = strawberry.UNSET banner: Optional[str] = strawberry.UNSET banner_square: Optional[str] = strawberry.UNSET diff --git a/queries.py b/queries.py index 2c46a23..c66774a 100644 --- a/queries.py +++ b/queries.py @@ -1,3 +1,15 @@ +""" +Query Resolvers + +This file contains the 3 different query resolvers. +each resolves a different query, each providing a different set of information. + +Resolvers: + activeClubs: Returns all the currently active clubs. + allClubs: Returns a specific club. + clubs: Returns all the clubs. +""" + from typing import List import strawberry @@ -14,11 +26,20 @@ @strawberry.field def activeClubs(info: Info) -> List[SimpleClubType]: """ - Description: Returns all the currently active clubs. - Scope: Public - Return Type: List[SimpleClubType] - Input: None + For Active Clubs + + Returns all the currently active clubs + + Inputs: + info (Info): Contains the user details. + + Accessibility: + Public. + + Returns : + List[SimpleClubType] : Returns a list of all the currently active clubs. """ + results = clubsdb.find({"state": "active"}, {"_id": 0}) clubs = [ SimpleClubType.from_pydantic(Club.model_validate(result)) @@ -31,14 +52,20 @@ def activeClubs(info: Info) -> List[SimpleClubType]: @strawberry.field def allClubs(info: Info) -> List[SimpleClubType]: """ - Description: - For CC: - Returns all the currently active/deleted clubs. - For Public: - Returns all the currently active clubs. - Scope: CC (For All Clubs), Public (For Active Clubs) - Return Type: List[SimpleClubType] - Input: None + For All Clubs + + This method returns a list of all the clubs. + For CC it returns details even for deleted clubs. + For public users it returns only the active clubs. + + Inputs: + info (Info): Contains the user details. + + Accessibility: + Public(partial access), CC(Full Access). + + Returns : + List[SimpleClubType] : Returns a list of all the clubs. """ user = info.context.user if user is None: @@ -62,17 +89,25 @@ def allClubs(info: Info) -> List[SimpleClubType]: @strawberry.field def club(clubInput: SimpleClubInput, info: Info) -> FullClubType: """ - Description: Returns complete details of the club, from its club-id. - For CC: - Returns details even for deleted clubs. - For Public: - Returns details only for the active clubs. - Scope: Public - Return Type: FullClubType - Input: SimpleClubInput (cid) - Errors: - - cid doesn't exist - - if not cc and club is deleted + For Specific Club + + This method returns a specific club. + For users with role 'cc', it returns details even for a deleted club. + For public users it returns info only if it is an active club. + + Inputs: + clubInput (SimpleClubInput): Contains the club id of the club to be searched. + info (Info): Contains the user details. + + Accessibility: + Public(partial access), CC(Full Access). + + Returns: + FullClubType: Returns the club details. + + Raises Exceptions: + No club Found: If a club with the given club id is not found. + No Club Result Found: If the club is deleted and requesting user does not have a role of 'cc'. """ user = info.context.user club_input = jsonable_encoder(clubInput) diff --git a/utils.py b/utils.py index 1af7c0a..513505c 100644 --- a/utils.py +++ b/utils.py @@ -1,3 +1,9 @@ +""" +Helper functions + +This file contains methods which make queries and mutations to different microservices within the backend. +""" + import os import requests @@ -7,8 +13,23 @@ def update_role(uid, cookies=None, role="club"): """ - Function to call the updateRole mutation + Updates Role of a user + + This function is used to update the role of a user. + It makes a mutation to the Users Microservice. + Which is resolved by the updateRole function. + Updating a user's role to 'club'(or 'cc','slo','slc') makes the user a member of the club or committee. + + Inputs: + uid (str): The uid(User id) of the user whose role is to be changed. + cookies (dict): The cookies of the user.(Default: None) + role (str): The role to be updated to.(Default: 'club') + + Returns: + result of the mutation if successful. + else returns None. """ + try: query = """ mutation UpdateRole($roleInput: RoleInput!) { @@ -41,8 +62,23 @@ def update_role(uid, cookies=None, role="club"): def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: """ - Function to call the updateEventsCid & updateMembersCid mutation + Function to change the cid of events and members of a club + + It is used when a club changes their cid. + It makes 2 mutations one to the Events and another to the Members Microservice. + Which are resolved by the updateEventsCid and updateMembersCid functions. + They change the club's cid in all the events and members of the club. + + Inputs: + old_cid (str): The old cid of the club. + new_cid (str): The new cid of the club. + cookies (dict): The cookies of the user.(Default: None) + + Returns: + True if both mutations are successful. + else returns False. """ + return1, return2 = None, None try: query = """ @@ -106,8 +142,21 @@ def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: def getUser(uid, cookies=None): """ - Function to get a particular user details + Function to get a user's details + + This function is used to fetch a specific user's details. + It makes a query to the Users Microservice. + Which is resolved by the userProfile function. + + Inputs: + uid (str): The uid of the user whose details are to be fetched. + cookies (dict): The cookies of the user.(Default: None) + + Returns: + The user's details if successful. + else returns None. """ + try: query = """ query GetUserProfile($userInput: UserInput!) { From 50aca2d9670432370eff0368d958e1a3c6006ab0 Mon Sep 17 00:00:00 2001 From: notpua Date: Sat, 11 Jan 2025 17:55:42 +0530 Subject: [PATCH 02/12] docs: add pydoc-markdown configuration --- .gitignore | 1 + db.py | 16 +++-- main.py | 17 +++-- models.py | 165 ++++++++++++++------------------------------- mutations.py | 145 ++++++++++++++++++--------------------- otypes.py | 67 ++++-------------- pydoc-markdown.yml | 22 ++++++ queries.py | 80 ++++++++++------------ requirements.txt | 1 + utils.py | 74 +++++++++----------- 10 files changed, 240 insertions(+), 348 deletions(-) create mode 100644 pydoc-markdown.yml diff --git a/.gitignore b/.gitignore index f51ac09..86a90b3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .in .out __pycache__/ +build/ diff --git a/db.py b/db.py index db8b82b..20a4c96 100644 --- a/db.py +++ b/db.py @@ -1,10 +1,8 @@ """ -MongoDB Initialization Module +MongoDB Initialization Module. -This module sets up a connection to a MongoDB database and ensures that the required indexes are created. -This module connects to the MongoDB database using environment variables for authentication. -Ensures that a `unique_clubs` index is present on the `cid` field in the clubs collection. -It specifically exports the clubs collection of the database. +This module sets up the connection to the MongoDB database. +It ensures that the required indexes are created. Environment Variables: `MONGO_USERNAME` (str): MongoDB username. Defaults to "username". @@ -12,13 +10,19 @@ `MONGO_PORT` (str): MongoDB port. Defaults to "27017". `MONGO_DATABASE` (str): MongoDB database name. Defaults to "default". +Attributes: + MONGO_URI (str): MongoDB URI. + MONGO_DATABASE (str): MongoDB database name. + client (MongoClient): MongoDB client. + db (Database): MongoDB database. + clubsdb (Collection): MongoDB collection for clubs. """ from os import getenv from pymongo import MongoClient -# get mongodb URI and database name from environment variables +# get mongodb URI and database name from environment variale MONGO_URI = "mongodb://{}:{}@mongo:{}/".format( getenv("MONGO_USERNAME", default="username"), getenv("MONGO_PASSWORD", default="password"), diff --git a/main.py b/main.py index 28908ab..5badc0f 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,16 @@ """ -Final Setup +Main module for the Clubs Microservice. -This file is used to setup the final schema for the subgraph. -It imports the resolvers from the queries and mutations files and creates a final GraphQL schema. -It sets up the Fast API for the Clubs Microservice. +This module sets up the FastAPI application and integrates the Strawberry GraphQL schema. +It includes the configuration for queries, mutations, and context. + +Environment Variables: + GLOBAL_DEBUG (str): Enables or disables debug mode. Defaults to "False". + +Attributes: + DEBUG (bool): Indicates whether the application is running in debug mode. + gql_app (GraphQLRouter): The GraphQL router for handling GraphQL requests. + app (FastAPI): The FastAPI application instance. """ from os import getenv @@ -28,7 +35,7 @@ Mutation = create_type("Mutation", mutations) -# override context getter +# Returns The custom context by overriding the context getter. async def get_context() -> Context: return Context() diff --git a/models.py b/models.py index 715b73d..8cac4dd 100644 --- a/models.py +++ b/models.py @@ -1,14 +1,3 @@ -""" -Data Models for Clubs Microservice - -This file decides what and how the Club information is stored in its MongoDB document. -It creates many custom data types for this purpose. - -It defines the following models: - Club: Represents a Club. - Socials: Represents Social Media Links of a Club. -""" - from datetime import datetime from enum import Enum from typing import Annotated, Any, List @@ -28,8 +17,9 @@ from pydantic_core import core_schema from pytz import timezone - -# A custom type for HttpUrls that validates the URL when assigned a value and converts it to a string. +""" +Annotated type and validator for HTTP URLs to be stored as strings. +""" http_url_adapter = TypeAdapter(HttpUrl) HttpUrlString = Annotated[ str, @@ -38,33 +28,22 @@ ), ] -# method that returns the current UTC(timezone) time + def create_utc_time(): + """ + Returns the current time according to UTC timezone. + """ return datetime.now(timezone("UTC")) + +# for handling mongo ObjectIds class PyObjectId(ObjectId): """ - MongoDB ObjectId handler - - This class contains clasmethods to validate and serialize ObjectIds. - ObjectIds of documents under the Clubs collection are stored under the 'id' field. + Class for handling MongoDB document ObjectIds for 'id' fields in Models. """ @classmethod def __get_pydantic_core_schema__(cls, source_type: Any, handler): - """ - Defines custom schema for Pydantic validation - - This method is used to define the schema for the Pydantic model. - - Inputs: - source_type (Any): The source type. - handler: The handler. - - Returns: - dict: The schema for the Pydantic model. - """ - return core_schema.union_schema( [ # check if it's an instance first before doing any further work @@ -76,53 +55,25 @@ def __get_pydantic_core_schema__(cls, source_type: Any, handler): @classmethod def validate(cls, v): - """ - Validates the given ObjectId - - Inputs: - v (Any): The value to validate. - - Returns: - ObjectId: The validated ObjectId. - - Raises: - ValueError: If the given value is not a valid ObjectId. - """ - if not ObjectId.is_valid(v): raise ValueError("Invalid ObjectId") return ObjectId(v) @classmethod def __get_pydantic_json_schema__(cls, field_schema): - """ - Generates JSON schema - - This method is used to generate the JSON schema for the Pydantic model. - - Inputs: - field_schema (dict): The field schema. - """ - field_schema.update(type="string") def iiit_email_only(v: str) -> str: """ - Email validator for IIIT-H emails - - This function validates the given email address according to valid IIIT-H email formats. + Validates emails according to the valid forms. - Inputs: - v (str): The email address to validate. + Args: + v (str): Email to be validated. Returns: - str: The validated email address in lower case. - - Raises: - ValueError: If the given email address is not a valid IIIT-H email. + str: Valid Email. """ - valid_domains = [ "@iiit.ac.in", "@students.iiit.ac.in", @@ -135,24 +86,20 @@ def iiit_email_only(v: str) -> str: def current_year() -> int: - """ - Returns the current year - - Returns: - int: The current year. - """ - + """Returns the current year.""" return datetime.now().year -# Enum for the status of a Club(active or deleted) + @strawberry.enum class EnumStates(str, Enum): + """Enum for state of the club.""" active = "active" deleted = "deleted" -# Enum for the categories of a Club(cultural, technical, affinity or other) + @strawberry.enum class EnumCategories(str, Enum): + """Enum for category of the club.""" cultural = "cultural" technical = "technical" affinity = "affinity" @@ -161,27 +108,19 @@ class EnumCategories(str, Enum): class Social(BaseModel): """ - Social Media Links Model - - This class atrributes represent the social media links of a Club. - It also checks for duplicate URLs in the 'other_links' field. - All the attributes are optional and use the HttpUrlString type to validate and store the URLs. - - Atrributes: - website (HttpUrlString | None): The website URL of the Club. - instagram (HttpUrlString | None): The Instagram URL of the Club. - facebook (HttpUrlString | None): The Facebook URL of the Club. - youtube (HttpUrlString | None): The YouTube URL of the Club. - twitter (HttpUrlString | None): The Twitter URL of the Club. - linkedin (HttpUrlString | None): The LinkedIn URL of the Club. - discord (HttpUrlString | None): The Discord URL of the Club. - whatsapp (HttpUrlString | None): The WhatsApp URL of the Club. - other_links (List[HttpUrlString]): The other social media links of the Club. - - Raises: - ValueError: If duplicate URLs are found in the 'other_links' field. - """ + Model for storing social handles of a Club + Attributes: + website (HttpUrlString | None): Club Website URL. + instagram (HttpUrlString | None): Club Instagram handle. + facebook (HttpUrlString | None): Club Facebook . + youtube (HttpUrlString | None): Club YouTube handle. + twitter (HttpUrlString | None): Club Twitter handle. + linkedin (HttpUrlString | None): Club LinkedIn handle. + discord (HttpUrlString | None): Club Discord handle. + whatsapp (HttpUrlString | None): Club WhatsApp handle. + other_links (List[HttpUrlString]): List of other social handles and URLs + """ website: HttpUrlString | None = None instagram: HttpUrlString | None = None facebook: HttpUrlString | None = None @@ -195,6 +134,9 @@ class Social(BaseModel): @field_validator("other_links") @classmethod def validate_unique_links(cls, value): + """ + Validates that the URLs in 'other_links' are unique. + """ if len(value) != len(set(value)): raise ValueError("Duplicate URLs are not allowed in 'other_links'") return value @@ -202,31 +144,26 @@ def validate_unique_links(cls, value): class Club(BaseModel): """ - Club Model - - This class represents a Club. - Its attributes store all the details of a Club. - It also validates the email address of the Club using the iiit_email_only method. + Model for storing Club details. Attributes: - id (PyObjectId): Stores the ObjectId of the Club's document in MongoDB.Uses the PyObjectId class. - cid (str): The unique identifier of the Club. - code (str): The unique short code of the Club. - state (EnumStates): The state of the Club.Uses the EnumStates enum. - category (EnumCategories): The category of the Club.Uses the EnumCategories enum. - student_body (bool): Whether the Club is a Student Body or not. - name (str): The name of the Club. - email (EmailStr): The email address of the Club. - logo (str | None): The URL of the Club's official logo. - banner (str | None): The URL of the Club's official banner. - banner_square (str | None): The URL of the Club's official square banner. - tagline (str): The tagline of the Club. - description (str): The description of the Club. - social (Social): The social media links of the Club.Uses the Social class. - created_time (datetime): The date and time when the Club was created.Uses the current_utc_time method. - updated_time (datetime): The date and time when the Club was last updated. + id (PyObjectId): Unique ObjectId of the document of the Club. + cid (str): the Club ID. + code (str): Unique Short Code of Club. + state (EnumStates): State of the Club. + category (EnumCategories): Category of the Club. + student_body (bool): Is this a Student Body? + name (str): Name of the Club. + email (EmailStr): Email of the Club. + logo (str | None): Club Official Logo. + banner (str | None): Club Long Banner. + banner_square (str | None): Club Square Banner. + tagline (str | None): Tagline of the Club. + description (str | None): Club Description. + socials (Social): Social Handles of the Club. + created_time (datetime): Time of creation of the Club. + updated_time (datetime): Time of last update to the Club. """ - id: PyObjectId = Field(default_factory=PyObjectId, alias="_id") cid: str = Field(..., description="Club ID") code: str = Field( diff --git a/mutations.py b/mutations.py index a1e916e..6c24c97 100644 --- a/mutations.py +++ b/mutations.py @@ -1,7 +1,5 @@ """ -Mutation Resolvers - -Contains mutation resolvers that create, edit, delete and restart a club +Mutations for Clubs """ from datetime import datetime @@ -26,31 +24,26 @@ @strawberry.mutation def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: """ - Create a new Club. - - This resolver/method creates a new club. - For a new club to be created, prior to the creation a user with a uid same as the cid of the club should exist. - This user account will be the account of the club. - Any changes or autherization for actions regarding the club are provided to this account. - This user profile only exists on the database and not on LDAP. - - Iputs: - clubInput (FullClubInput): Used to take input almost all the fields of the Club Schema. - info (Info): Contains the user details. - - Accessibility: - Only CC can create a club. + Mutation for creation of a new club. - Returns: - SimpleClubType: Returns the created club. + Args: + clubInput (FullClubInput): Full details of the club. + info (Info): User metadata and cookies. - Raises Exception: - Not Authenticated or Not Authenticated to access this API : If the user is not a CC. - Club Already Exists : If a club with the same cid already exists. - Invalid Club ID/Club Email : If there does not exist a user with the same cid or email. - A club with the same code already exists : If a club with the same code already exists. + Returns: + SimpleClubType: Details of the created club. + + Rajinikanth: + Access to only CC(Clubs Council). + + Raises: + Exception: Not Authenticated + Exception: A club with this cid already exists + Exception: A club with this short code already exists + Exception: Invalid Club ID/Club Email + Exception: Error in updating the role for the club + Exception: Not Authenticated to access this API """ - user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -65,7 +58,7 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: if cid_exists: raise Exception("A club with this cid already exists") - # Check whether there exists a user with the same cid or email + # Check whether this cid is valid or not clubMember = getUser(club_input["cid"], info.context.cookies) if clubMember is None: raise Exception("Invalid Club ID/Club Email") @@ -91,40 +84,33 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: @strawberry.mutation def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: """ - Edit a club. + Mutation for editing of the club details either by that specific club or the cc - This resolver/method edits a club. - editing of the club details is allowed to be done either by that specific club or the cc. - CC can edit any club details. - the Club can edit only few details. - If user role is ‘cc’ - If the current and the new cid(Club id) are different then this method changes the role of the user with uid same as the old cid to ‘public’ and changes the role of the user with the same uid as the new cid to ‘club’. - And then calls the update_events_members_cid method with the old cid and the new cid. - If the user role is ‘club’ - Does not let the user change the name, email, category of the club. + This method is used for editing the club details. + CC can edit any club details, but the club can only edit its own details. + Only CC can change a clubs name/email and category. - Inputs: - clubInput (FullClubInput): Used to take input almost all the fields of the Club Schema. - info (Info): Contains the user details. - Accessibility: - CC(Full Access), club(Partial Access). + Args: + clubInput (FullClubInput): Full details of the club to be updated to. + Info (Info): User metadata and cookies. Returns: - FullClubType: Returns the edited club's details. - - Raises Exception: - Not Authenticated or Not Authenticated to access this API : If the user is not a CC or not a member of the club. - For CC: - A club with this code doesn't exist : If a club with the same code does not exist. - Invalid Club ID/Club Email : If there does not exist a user with the same cid or email. - For club: - Authentication Error! (CLUB ID CHANGED) : If the user's uid and the clubs cid are not the same. - Club Does Not Exist : If the club with the given cid does not exist. - Only CC can edit the club details: If the user is not a CC and trying to change name, email, category of the club. - + FullClubType: Full Details of the edited club. + + Rajinikanth: + Access to both clubs and CC(Clubs Council). + + Raises: + Exception: Not Authenticated. + Exception: A club with this code does not exist. + Exception: Invalid Club ID/Club Email. + Exception: Error in updating the role/cid. + Exception: Authentication Error! (CLUB ID CHANGED). + Exception: You dont have permission to change the name/email of the club. Please contact CC for it. + Exception: Only CC is allowed to change the category of club. + Exception: Not Authenticated to access this API. """ # noqa: E501 - user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -243,6 +229,8 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: } }, ) + + # also autofills the updated time clubsdb.update_one( {"cid": club_input["cid"]}, { @@ -265,24 +253,22 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: @strawberry.mutation def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: """ - Delete a club. - - This method changes a club's state to "deleted". + Mutation for the cc to move a club to deleted state. - Input: - clubInput (SimpleClubIput): contains the club's information(its cid). - info (Info): An Info object containing the user's information. + Args: + clubInput (SimpleClubInput): The club cid. + info (Info): User metadata and cookies. - Accessibility: - Only CC. - Returns: - SimpleClubType: The updated club's information. + SimpleClubType: Details of the deleted club. - Raises Exception: - Not Authenticated: If the user is not authenticated. - """ + Rajinikanth: + Access to only CC(Clubs Council). + Raises: + Exception: Not Authenticated. + Exception: Not Authenticated to access this API. + """ user = info.context.user if user is None: raise Exception("Not Authenticated") @@ -293,6 +279,7 @@ def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: if role not in ["cc"]: raise Exception("Not Authenticated to access this API") + # also autofills the updated time clubsdb.update_one( {"cid": club_input["cid"]}, {"$set": {"state": "deleted", "updated_time": datetime.utcnow()}}, @@ -310,22 +297,21 @@ def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: @strawberry.mutation def restartClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: """ - Restart a club. - - This method is similar to deleteClub but changes the state of the club to "active". + Mutation for cc to move a club from deleted state to active state. - Input: - clubInput (SimpleClubIput): contains the club's information(its cid). - info (Info): An Info object containing the user's information. - - Accessibility: - Only CC. + Args: + clubInput (SimpleClubInput): The club cid. + info (Info): User metadata and cookies. Returns: - SimpleClubType: The updated club's information. + SimpleClubType: Details of the restarted clubs cid. + + Rajinikanth: + Access to only CC(Clubs Council). - Raises Exception: - Not Authenticated: If the user is not authenticated. + Raises: + Exception: Not Authenticated. + Exception: Not Authenticated to access this API. """ user = info.context.user if user is None: @@ -337,6 +323,7 @@ def restartClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: if role not in ["cc"]: raise Exception("Not Authenticated to access this API") + # also autofills the updated time clubsdb.update_one( {"cid": club_input["cid"]}, {"$set": {"state": "active", "updated_time": datetime.utcnow()}}, diff --git a/otypes.py b/otypes.py index e8ef8c8..fcd396e 100644 --- a/otypes.py +++ b/otypes.py @@ -1,20 +1,5 @@ """ -Types and Inputs - -It contains both Inputs and Types for taking inputs and returning outputs. -It also contains the Context class which is used to pass the user details to the resolvers. - -Types: - Info : used to pass the user details to the resolvers. - PyObjectId : used to return ObjectId of a document. - SocialType : used to return Social handles, if they are present. - SimpleClubType : used to return only few fields defined within the Club schema. - FullClubType : used to return almost all the fields within the Club Schema. - -Inputs - SocialsInput : used to take Social handles as input.All of its fields are optional to fill. - SimpleClubInput : used to take club id as input. - FullClubInput : used to take almost all the fields within the Club Schema as input. +Types and Inputs for clubs subgraph """ import json @@ -32,23 +17,10 @@ # custom context class class Context(BaseContext): """ - To pass user details - - This class is used to pass the user details to the resolvers. - It will be used through the Info type. + Class provides user metadata and cookies from request headers. """ - @cached_property def user(self) -> Union[Dict, None]: - """ - Returns User Details - - It will be used in the resolvers to check the user details. - - Returns: - user (Dict): Contains User Details. - """ - if not self.request: return None @@ -57,15 +29,6 @@ def user(self) -> Union[Dict, None]: @cached_property def cookies(self) -> Union[Dict, None]: - """ - Returns Cookies Details - - It will be used in the resolvers to check the cookies details. - - Returns: - cookies (Dict): Contains Cookies Details. - """ - if not self.request: return None @@ -73,10 +36,10 @@ def cookies(self) -> Union[Dict, None]: return cookies -# custom info type +"""custom info type for user metadata""" Info = _Info[Context, RootValueType] -# serialize PyObjectId as a scalar type +"""A scalar type by serializing PyObjectId""" PyObjectIdType = strawberry.scalar( PyObjectId, serialize=str, parse_value=lambda v: PyObjectId(v) ) @@ -86,12 +49,8 @@ def cookies(self) -> Union[Dict, None]: @strawberry.experimental.pydantic.type(model=Social) class SocialsType: """ - Type for Social Handles - - This type is used to return Social handles, if they are present. - All of its fields(Attributes) are optional to fill and defaultly set to UNSET. + Type for return of social media handles of a club. """ - website: Optional[str] = strawberry.UNSET instagram: Optional[str] = strawberry.UNSET facebook: Optional[str] = strawberry.UNSET @@ -121,10 +80,12 @@ class SocialsType: ], ) class SimpleClubType: + """ + Type for return of user-provided club details except social handles. + """ pass -# This type is used to return almost all the fields within the Club Schema. -# except created and updated time fields. + @strawberry.experimental.pydantic.type( model=Club, fields=[ @@ -145,6 +106,7 @@ class SimpleClubType: ], ) class FullClubType: + """Type for return of all user-provided club details.""" # socials: SocialsType pass @@ -152,11 +114,13 @@ class FullClubType: # CLUBS INPUTS @strawberry.experimental.pydantic.input(model=Social, all_fields=True) class SocialsInput: + """Type for input of social media handles of a club.""" pass @strawberry.input class SimpleClubInput: + """Type for input of cid(Club id) of a club.""" cid: str @@ -176,13 +140,8 @@ class SimpleClubInput: ) class FullClubInput: """ - Full Club Details Input - - This class is used to take almost all the fields within the Club Schema as input. - It does not take created and updated time fields as input. - logo, banner, banner_square fields are optional to fill. + Type for input of all user-provided club details, pictures are optional to fill. """ - logo: Optional[str] = strawberry.UNSET banner: Optional[str] = strawberry.UNSET banner_square: Optional[str] = strawberry.UNSET diff --git a/pydoc-markdown.yml b/pydoc-markdown.yml new file mode 100644 index 0000000..f0f2bdf --- /dev/null +++ b/pydoc-markdown.yml @@ -0,0 +1,22 @@ +loaders: +- type: python + search_path: [.] +renderer: + type: hugo + get_hugo: + enabled: true + version: '0.111' + extended: true + config: + title: Clubs + theme: {clone_url: "https://github.com/alex-shpak/hugo-book.git"} + # The "book" theme only renders pages in "content/docs" into the nav. + content_directory: content/docs + default_preamble: {menu: main} + pages: + - title: Home + name: index + source: README.md + - title: API Documentation + contents: + - '*' diff --git a/queries.py b/queries.py index c66774a..f8209df 100644 --- a/queries.py +++ b/queries.py @@ -1,13 +1,5 @@ """ -Query Resolvers - -This file contains the 3 different query resolvers. -each resolves a different query, each providing a different set of information. - -Resolvers: - activeClubs: Returns all the currently active clubs. - allClubs: Returns a specific club. - clubs: Returns all the clubs. +Queries for Clubs """ from typing import List @@ -26,20 +18,17 @@ @strawberry.field def activeClubs(info: Info) -> List[SimpleClubType]: """ - For Active Clubs - - Returns all the currently active clubs - - Inputs: - info (Info): Contains the user details. + Fetches all the currently active clubs. + + Args: + info (Info): User metadata and cookies. - Accessibility: - Public. + Rajinikanth: + This method has public access and can be accessed by anyone. - Returns : - List[SimpleClubType] : Returns a list of all the currently active clubs. + Returns: + List[SimpleClubType]: List of active clubs. """ - results = clubsdb.find({"state": "active"}, {"_id": 0}) clubs = [ SimpleClubType.from_pydantic(Club.model_validate(result)) @@ -52,20 +41,20 @@ def activeClubs(info: Info) -> List[SimpleClubType]: @strawberry.field def allClubs(info: Info) -> List[SimpleClubType]: """ - For All Clubs + Fetches all the clubs - This method returns a list of all the clubs. - For CC it returns details even for deleted clubs. - For public users it returns only the active clubs. - - Inputs: - info (Info): Contains the user details. + This method returns all the clubs when accessed CC. + When accessed by public, it returns only the active clubs. + + Args: + info (Info): User metadata and cookies. + + Rajinikanth: + Access to both public and CC(Clubs Council). + + Returns: + List[SimpleClubType]: List of all clubs. - Accessibility: - Public(partial access), CC(Full Access). - - Returns : - List[SimpleClubType] : Returns a list of all the clubs. """ user = info.context.user if user is None: @@ -89,25 +78,24 @@ def allClubs(info: Info) -> List[SimpleClubType]: @strawberry.field def club(clubInput: SimpleClubInput, info: Info) -> FullClubType: """ - For Specific Club + Fetches all Club Details of a club - This method returns a specific club. - For users with role 'cc', it returns details even for a deleted club. - For public users it returns info only if it is an active club. + Used to get all the details of a deleted/active club by its cid. + Returns deleted clubs also for CC and not for public. - Inputs: - clubInput (SimpleClubInput): Contains the club id of the club to be searched. - info (Info): Contains the user details. + Args: + clubInput (SimpleClubInput): The club cid. + info (Info): User metadata and cookies. - Accessibility: - Public(partial access), CC(Full Access). + Rajinikanth: + Access to both public and CC(Clubs Council). Returns: - FullClubType: Returns the club details. - - Raises Exceptions: - No club Found: If a club with the given club id is not found. - No Club Result Found: If the club is deleted and requesting user does not have a role of 'cc'. + FullClubType: Contains all the club details. + + Raises: + Exception: If the club is not found. + Exception: If the club is deleted and the user is not CC. """ user = info.context.user club_input = jsonable_encoder(clubInput) diff --git a/requirements.txt b/requirements.txt index 2da3a8b..8820073 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,3 +28,4 @@ tomli==2.1.0 typer==0.13.1 typing-extensions>=4.12.2 uvicorn==0.32.1 +pydoc-markdown==4.8.2 diff --git a/utils.py b/utils.py index 513505c..8fa8b00 100644 --- a/utils.py +++ b/utils.py @@ -1,9 +1,3 @@ -""" -Helper functions - -This file contains methods which make queries and mutations to different microservices within the backend. -""" - import os import requests @@ -13,23 +7,19 @@ def update_role(uid, cookies=None, role="club"): """ - Updates Role of a user - - This function is used to update the role of a user. - It makes a mutation to the Users Microservice. - Which is resolved by the updateRole function. - Updating a user's role to 'club'(or 'cc','slo','slc') makes the user a member of the club or committee. - - Inputs: - uid (str): The uid(User id) of the user whose role is to be changed. - cookies (dict): The cookies of the user.(Default: None) - role (str): The role to be updated to.(Default: 'club') - + Function to call the updateRole mutation + + Makes a mutation resolved by the `updateRole` method from Users Microservice. + Used to change a user's role field. + + Args: + uid (str): User ID. + cookies (dict): Cookies from the request. Defaults to None. + role (str): Role of the user to be updated to. Defaults to 'club'. + Returns: - result of the mutation if successful. - else returns None. + dict: Response from the mutation. """ - try: query = """ mutation UpdateRole($roleInput: RoleInput!) { @@ -62,24 +52,22 @@ def update_role(uid, cookies=None, role="club"): def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: """ - Function to change the cid of events and members of a club + Function to call the updateEventsCid & updateMembersCid mutation - It is used when a club changes their cid. - It makes 2 mutations one to the Events and another to the Members Microservice. - Which are resolved by the updateEventsCid and updateMembersCid functions. - They change the club's cid in all the events and members of the club. + Makes a mutation resolved by the `updateEventsCid` method from Events Microservice. + Makes another mutation resolved by the `updateMembersCid` method from Members Microservice. + Used when club changes its cid to change its members and events data accordingly. - Inputs: - old_cid (str): The old cid of the club. - new_cid (str): The new cid of the club. - cookies (dict): The cookies of the user.(Default: None) + Args: + old_cid (str): Old CID of the club. + new_cid (str): New CID of the club. + cookies (dict): Cookies from the request. Returns: - True if both mutations are successful. - else returns False. + bool: True if both mutations are successful, False otherwise. """ - return1, return2 = None, None + # Update Events CID try: query = """ mutation UpdateEventsCid($oldCid: String!, $newCid: String!, $interCommunicationSecret: String) { @@ -107,6 +95,7 @@ def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: except Exception: return False + # Update Members CID try: query = """ mutation UpdateMembersCid($oldCid: String!, $newCid: String!, $interCommunicationSecret: String) { @@ -142,21 +131,18 @@ def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: def getUser(uid, cookies=None): """ - Function to get a user's details + Function to get a particular user details - This function is used to fetch a specific user's details. - It makes a query to the Users Microservice. - Which is resolved by the userProfile function. + Makes a query resolved by the `userProfile` method from Users Microservice. + Used to get a users details. + + Args: + uid (str): User ID of the user to be fetched. + cookies (dict): Cookies from the request. Defaults to None. - Inputs: - uid (str): The uid of the user whose details are to be fetched. - cookies (dict): The cookies of the user.(Default: None) - Returns: - The user's details if successful. - else returns None. + dict: User details as a result of the query. """ - try: query = """ query GetUserProfile($userInput: UserInput!) { From 2e63e68dfd58269683f11e83afb62aba34678469 Mon Sep 17 00:00:00 2001 From: abhiramtilakiiit Date: Sun, 12 Jan 2025 16:13:25 +0530 Subject: [PATCH 03/12] fix merge conflicts when rebasing master --- mutations.py | 25 +++++++++++++++++++------ utils.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/mutations.py b/mutations.py index 6c24c97..5bd2e55 100644 --- a/mutations.py +++ b/mutations.py @@ -8,7 +8,7 @@ from fastapi.encoders import jsonable_encoder from db import clubsdb -from models import Club +from models import Club, create_utc_time # import all models and types from otypes import ( @@ -18,7 +18,12 @@ SimpleClubInput, SimpleClubType, ) -from utils import getUser, update_events_members_cid, update_role +from utils import ( + check_remove_old_file, + getUser, + update_events_members_cid, + update_role, +) @strawberry.mutation @@ -133,6 +138,10 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: club_input["state"] = exists["state"] club_input["_id"] = exists["_id"] + check_remove_old_file(exists, club_input, "logo") + check_remove_old_file(exists, club_input, "banner") + check_remove_old_file(exists, club_input, "banner_square") + clubsdb.replace_one({"code": club_input["code"]}, club_input) if "socials" in club_input.keys(): clubsdb.update_one( @@ -160,7 +169,7 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: { "$set": { "created_time": exists["created_time"], - "updated_time": datetime.utcnow(), + "updated_time": create_utc_time(), } }, ) @@ -207,6 +216,10 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: club_input["state"] = exists["state"] club_input["_id"] = exists["_id"] + check_remove_old_file(exists, club_input, "logo") + check_remove_old_file(exists, club_input, "banner") + check_remove_old_file(exists, club_input, "banner_square") + clubsdb.replace_one({"cid": uid}, club_input) if "socials" in club_input.keys(): clubsdb.update_one( @@ -236,7 +249,7 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: { "$set": { "created_time": exists["created_time"], - "updated_time": datetime.utcnow(), + "updated_time": create_utc_time(), } }, ) @@ -282,7 +295,7 @@ def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: # also autofills the updated time clubsdb.update_one( {"cid": club_input["cid"]}, - {"$set": {"state": "deleted", "updated_time": datetime.utcnow()}}, + {"$set": {"state": "deleted", "updated_time": create_utc_time()}}, ) update_role(club_input["cid"], info.context.cookies, "public") @@ -326,7 +339,7 @@ def restartClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: # also autofills the updated time clubsdb.update_one( {"cid": club_input["cid"]}, - {"$set": {"state": "active", "updated_time": datetime.utcnow()}}, + {"$set": {"state": "active", "updated_time": create_utc_time()}}, ) update_role(club_input["cid"], info.context.cookies, "club") diff --git a/utils.py b/utils.py index 8fa8b00..ca75ca1 100644 --- a/utils.py +++ b/utils.py @@ -170,3 +170,32 @@ def getUser(uid, cookies=None): return request.json()["data"]["userProfile"] except Exception: return None + + +def delete_file(filename): + response = requests.post( + "http://files/delete-file", + params={ + "filename": filename, + "inter_communication_secret": inter_communication_secret, + }, + ) + + if response.status_code != 200: + raise Exception(response.text) + + return response.text + + +def check_remove_old_file(old_obj, new_obj, name="logo"): + old_file = old_obj.get(name) + new_file = new_obj.get(name) + + if old_file and new_file and old_file != new_file: + try: + delete_file(old_file) + except Exception as e: + print(f"Error in deleting old {name} file: {e}") + return False + + return True From 3308dce9f8a564eb4cf19bf5c2e063261ce49b88 Mon Sep 17 00:00:00 2001 From: Bhav Beri <43399374+bhavberi@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:03:52 +0530 Subject: [PATCH 04/12] Merge pull request #17 from Clubs-Council-IIITH/category Category for Clubs/Bodies Refactoring --- models.py | 4 ++++ otypes.py | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/models.py b/models.py index 8cac4dd..18626a1 100644 --- a/models.py +++ b/models.py @@ -103,6 +103,8 @@ class EnumCategories(str, Enum): cultural = "cultural" technical = "technical" affinity = "affinity" + admin = "admin" + body = "body" other = "other" @@ -174,6 +176,8 @@ class Club(BaseModel): ) # equivalent to club id = short name state: EnumStates = EnumStates.active category: EnumCategories = EnumCategories.other + + # TODO: Remove this field student_body: bool = Field(False, description="Is this a Student Body?") name: str = Field(..., min_length=5, max_length=100) diff --git a/otypes.py b/otypes.py index fcd396e..fe50ce8 100644 --- a/otypes.py +++ b/otypes.py @@ -70,7 +70,6 @@ class SocialsType: "code", "state", "category", - "student_body", "email", "logo", "banner", @@ -85,7 +84,6 @@ class SimpleClubType: """ pass - @strawberry.experimental.pydantic.type( model=Club, fields=[ @@ -94,7 +92,6 @@ class SimpleClubType: "code", "state", "category", - "student_body", "logo", "banner", "banner_square", @@ -132,7 +129,6 @@ class SimpleClubInput: "name", "email", "category", - "student_body", "tagline", "description", "socials", From cf5204a282e51c7031fca331770caada0ddcbab8 Mon Sep 17 00:00:00 2001 From: bhavberi Date: Tue, 17 Dec 2024 05:34:07 +0000 Subject: [PATCH 05/12] Apply Linting & Formatting Fixes --- otypes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/otypes.py b/otypes.py index fe50ce8..3ff4864 100644 --- a/otypes.py +++ b/otypes.py @@ -84,6 +84,7 @@ class SimpleClubType: """ pass + @strawberry.experimental.pydantic.type( model=Club, fields=[ From 10477d8e1df370b537fc6905c02614ca08413674 Mon Sep 17 00:00:00 2001 From: Bhav Beri Date: Fri, 20 Dec 2024 01:05:48 +0530 Subject: [PATCH 06/12] Remove student_body variable --- models.py | 3 --- queries.py | 1 - 2 files changed, 4 deletions(-) diff --git a/models.py b/models.py index 18626a1..27f78e9 100644 --- a/models.py +++ b/models.py @@ -177,9 +177,6 @@ class Club(BaseModel): state: EnumStates = EnumStates.active category: EnumCategories = EnumCategories.other - # TODO: Remove this field - student_body: bool = Field(False, description="Is this a Student Body?") - name: str = Field(..., min_length=5, max_length=100) email: EmailStr = Field(...) # Optional but required logo: str | None = Field(None, description="Club Official Logo pic URL") diff --git a/queries.py b/queries.py index f8209df..e7974a1 100644 --- a/queries.py +++ b/queries.py @@ -54,7 +54,6 @@ def allClubs(info: Info) -> List[SimpleClubType]: Returns: List[SimpleClubType]: List of all clubs. - """ user = info.context.user if user is None: From a531c275f421d90bb8ad8fbc4799146e0996f6fb Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Wed, 15 Jan 2025 18:24:29 +0530 Subject: [PATCH 07/12] clubs done --- models.py | 8 ++++---- mutations.py | 14 +------------- otypes.py | 18 +++++++++--------- queries.py | 13 +++---------- 4 files changed, 17 insertions(+), 36 deletions(-) diff --git a/models.py b/models.py index 27f78e9..7a39d9a 100644 --- a/models.py +++ b/models.py @@ -69,7 +69,10 @@ def iiit_email_only(v: str) -> str: Validates emails according to the valid forms. Args: - v (str): Email to be validated. + v (str): The Email to be validated. + + Raises: + ValueError: If email is not valid. Returns: str: Valid Email. @@ -136,9 +139,6 @@ class Social(BaseModel): @field_validator("other_links") @classmethod def validate_unique_links(cls, value): - """ - Validates that the URLs in 'other_links' are unique. - """ if len(value) != len(set(value)): raise ValueError("Duplicate URLs are not allowed in 'other_links'") return value diff --git a/mutations.py b/mutations.py index 5bd2e55..b9ac7e3 100644 --- a/mutations.py +++ b/mutations.py @@ -29,7 +29,7 @@ @strawberry.mutation def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: """ - Mutation for creation of a new club. + Mutation for creation of a new club by CC. Args: clubInput (FullClubInput): Full details of the club. @@ -38,9 +38,6 @@ def createClub(clubInput: FullClubInput, info: Info) -> SimpleClubType: Returns: SimpleClubType: Details of the created club. - Rajinikanth: - Access to only CC(Clubs Council). - Raises: Exception: Not Authenticated Exception: A club with this cid already exists @@ -103,9 +100,6 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: Returns: FullClubType: Full Details of the edited club. - Rajinikanth: - Access to both clubs and CC(Clubs Council). - Raises: Exception: Not Authenticated. Exception: A club with this code does not exist. @@ -275,9 +269,6 @@ def deleteClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: Returns: SimpleClubType: Details of the deleted club. - Rajinikanth: - Access to only CC(Clubs Council). - Raises: Exception: Not Authenticated. Exception: Not Authenticated to access this API. @@ -319,9 +310,6 @@ def restartClub(clubInput: SimpleClubInput, info: Info) -> SimpleClubType: Returns: SimpleClubType: Details of the restarted clubs cid. - Rajinikanth: - Access to only CC(Clubs Council). - Raises: Exception: Not Authenticated. Exception: Not Authenticated to access this API. diff --git a/otypes.py b/otypes.py index 3ff4864..ec91306 100644 --- a/otypes.py +++ b/otypes.py @@ -17,7 +17,7 @@ # custom context class class Context(BaseContext): """ - Class provides user metadata and cookies from request headers. + Class provides user metadata and cookies from request headers, has methods for doing this. """ @cached_property def user(self) -> Union[Dict, None]: @@ -36,10 +36,10 @@ def cookies(self) -> Union[Dict, None]: return cookies -"""custom info type for user metadata""" +"""custom info Type for user metadata""" Info = _Info[Context, RootValueType] -"""A scalar type by serializing PyObjectId""" +"""A scalar Type by serializing PyObjectId used for id field""" PyObjectIdType = strawberry.scalar( PyObjectId, serialize=str, parse_value=lambda v: PyObjectId(v) ) @@ -49,7 +49,7 @@ def cookies(self) -> Union[Dict, None]: @strawberry.experimental.pydantic.type(model=Social) class SocialsType: """ - Type for return of social media handles of a club. + Type used for return of social media handles of a club. """ website: Optional[str] = strawberry.UNSET instagram: Optional[str] = strawberry.UNSET @@ -80,7 +80,7 @@ class SocialsType: ) class SimpleClubType: """ - Type for return of user-provided club details except social handles. + Type used for return of user-provided club details except social handles. """ pass @@ -104,7 +104,7 @@ class SimpleClubType: ], ) class FullClubType: - """Type for return of all user-provided club details.""" + """Type used for return of all user-provided club details.""" # socials: SocialsType pass @@ -112,13 +112,13 @@ class FullClubType: # CLUBS INPUTS @strawberry.experimental.pydantic.input(model=Social, all_fields=True) class SocialsInput: - """Type for input of social media handles of a club.""" + """Input used for input of social media handles of a club.""" pass @strawberry.input class SimpleClubInput: - """Type for input of cid(Club id) of a club.""" + """Input used for input of cid(Club id) of a club.""" cid: str @@ -137,7 +137,7 @@ class SimpleClubInput: ) class FullClubInput: """ - Type for input of all user-provided club details, pictures are optional to fill. + Input used for input of all user-provided club details, pictures are optional to fill. """ logo: Optional[str] = strawberry.UNSET banner: Optional[str] = strawberry.UNSET diff --git a/queries.py b/queries.py index e7974a1..729c87c 100644 --- a/queries.py +++ b/queries.py @@ -18,14 +18,11 @@ @strawberry.field def activeClubs(info: Info) -> List[SimpleClubType]: """ - Fetches all the currently active clubs. + Fetches all the currently active clubs and is accessible to all. Args: info (Info): User metadata and cookies. - Rajinikanth: - This method has public access and can be accessed by anyone. - Returns: List[SimpleClubType]: List of active clubs. """ @@ -45,13 +42,11 @@ def allClubs(info: Info) -> List[SimpleClubType]: This method returns all the clubs when accessed CC. When accessed by public, it returns only the active clubs. + Access to both public and CC(Clubs Council). Args: info (Info): User metadata and cookies. - Rajinikanth: - Access to both public and CC(Clubs Council). - Returns: List[SimpleClubType]: List of all clubs. """ @@ -81,14 +76,12 @@ def club(clubInput: SimpleClubInput, info: Info) -> FullClubType: Used to get all the details of a deleted/active club by its cid. Returns deleted clubs also for CC and not for public. + Accessible to both public and CC(Clubs Council). Args: clubInput (SimpleClubInput): The club cid. info (Info): User metadata and cookies. - Rajinikanth: - Access to both public and CC(Clubs Council). - Returns: FullClubType: Contains all the club details. From af45b7ef4c4e7bc62e84846858400fd4319c36b6 Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Wed, 15 Jan 2025 18:53:51 +0530 Subject: [PATCH 08/12] final touches --- db.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/db.py b/db.py index 20a4c96..72b6e9f 100644 --- a/db.py +++ b/db.py @@ -4,13 +4,10 @@ This module sets up the connection to the MongoDB database. It ensures that the required indexes are created. -Environment Variables: - `MONGO_USERNAME` (str): MongoDB username. Defaults to "username". - `MONGO_PASSWORD` (str): MongoDB password. Defaults to "password". - `MONGO_PORT` (str): MongoDB port. Defaults to "27017". - `MONGO_DATABASE` (str): MongoDB database name. Defaults to "default". - Attributes: + MONGO_USERNAME (str): An environment variable having MongoDB username. Defaults to "username". + MONGO_PASSWORD (str): An environment variable having MongoDB password. Defaults to "password". + MONGO_PORT (str): MongoDB port. Defaults to "27017". MONGO_URI (str): MongoDB URI. MONGO_DATABASE (str): MongoDB database name. client (MongoClient): MongoDB client. From b12809ccd4fabad1a1391c1613343de6a12de285 Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Thu, 16 Jan 2025 13:29:29 +0530 Subject: [PATCH 09/12] final finaltouch --- .gitignore | 2 +- main.py | 4 +--- otypes.py | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 86a90b3..b3e360d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.venv/ +*venv/ .vscode/ .in .out diff --git a/main.py b/main.py index 5badc0f..8ade6a4 100644 --- a/main.py +++ b/main.py @@ -4,10 +4,8 @@ This module sets up the FastAPI application and integrates the Strawberry GraphQL schema. It includes the configuration for queries, mutations, and context. -Environment Variables: - GLOBAL_DEBUG (str): Enables or disables debug mode. Defaults to "False". - Attributes: + GLOBAL_DEBUG (str): Environment variable that Enables or Disables debug mode. Defaults to "False". DEBUG (bool): Indicates whether the application is running in debug mode. gql_app (GraphQLRouter): The GraphQL router for handling GraphQL requests. app (FastAPI): The FastAPI application instance. diff --git a/otypes.py b/otypes.py index ec91306..3274059 100644 --- a/otypes.py +++ b/otypes.py @@ -39,7 +39,7 @@ def cookies(self) -> Union[Dict, None]: """custom info Type for user metadata""" Info = _Info[Context, RootValueType] -"""A scalar Type by serializing PyObjectId used for id field""" +"""A scalar Type for serializing PyObjectId, used for id field""" PyObjectIdType = strawberry.scalar( PyObjectId, serialize=str, parse_value=lambda v: PyObjectId(v) ) From 0a0692b4b9a8e726be3526557be47039c8de88ea Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Wed, 22 Jan 2025 01:29:22 +0530 Subject: [PATCH 10/12] Default values included --- models.py | 26 +++++++++++++------------- utils.py | 22 +++++++++++++++++++++- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/models.py b/models.py index 7a39d9a..8503bd6 100644 --- a/models.py +++ b/models.py @@ -116,14 +116,14 @@ class Social(BaseModel): Model for storing social handles of a Club Attributes: - website (HttpUrlString | None): Club Website URL. - instagram (HttpUrlString | None): Club Instagram handle. - facebook (HttpUrlString | None): Club Facebook . - youtube (HttpUrlString | None): Club YouTube handle. - twitter (HttpUrlString | None): Club Twitter handle. - linkedin (HttpUrlString | None): Club LinkedIn handle. - discord (HttpUrlString | None): Club Discord handle. - whatsapp (HttpUrlString | None): Club WhatsApp handle. + website (HttpUrlString | None): Club Website URL. Defaults to None. + instagram (HttpUrlString | None): Club Instagram handle. Defaults to None. + facebook (HttpUrlString | None): Club Facebook. Defaults to None. + youtube (HttpUrlString | None): Club YouTube handle. Defaults to None. + twitter (HttpUrlString | None): Club Twitter handle. Defaults to None. + linkedin (HttpUrlString | None): Club LinkedIn handle. Defaults to None. + discord (HttpUrlString | None): Club Discord handle. Defaults to None. + whatsapp (HttpUrlString | None): Club WhatsApp handle. Defaults to None. other_links (List[HttpUrlString]): List of other social handles and URLs """ website: HttpUrlString | None = None @@ -157,11 +157,11 @@ class Club(BaseModel): student_body (bool): Is this a Student Body? name (str): Name of the Club. email (EmailStr): Email of the Club. - logo (str | None): Club Official Logo. - banner (str | None): Club Long Banner. - banner_square (str | None): Club Square Banner. - tagline (str | None): Tagline of the Club. - description (str | None): Club Description. + logo (str | None): Club Official Logo. Defaults to None. + banner (str | None): Club Long Banner. Defaults to None. + banner_square (str | None): Club Square Banner. Defaults to None. + tagline (str | None): Tagline of the Club. Defaults to None. + description (str | None): Club Description. Defaults to None. socials (Social): Social Handles of the Club. created_time (datetime): Time of creation of the Club. updated_time (datetime): Time of last update to the Club. diff --git a/utils.py b/utils.py index ca75ca1..66eb6b6 100644 --- a/utils.py +++ b/utils.py @@ -61,7 +61,7 @@ def update_events_members_cid(old_cid, new_cid, cookies=None) -> bool: Args: old_cid (str): Old CID of the club. new_cid (str): New CID of the club. - cookies (dict): Cookies from the request. + cookies (dict): Cookies from the request. Defaults to None. Returns: bool: True if both mutations are successful, False otherwise. @@ -173,6 +173,15 @@ def getUser(uid, cookies=None): def delete_file(filename): + """ + Method for deleting a file from the files microservice + + Args: + filename (str): Name of the file to be deleted + + Returns: + Response from the files microservice + """ response = requests.post( "http://files/delete-file", params={ @@ -188,6 +197,17 @@ def delete_file(filename): def check_remove_old_file(old_obj, new_obj, name="logo"): + """ + Method to remove old files. + + Args: + old_obj (dict): Old object containing the old file + new_obj (dict): New object containing the new file + name (str): Name of the file to be removed. Defaults to "logo" as mostly they are images of club logo's + + Returns: + bool: True if the old file is removed, False otherwise + """ old_file = old_obj.get(name) new_file = new_obj.get(name) From 8ec7550bb1f437f028e0f8efc29a7c9d37301c43 Mon Sep 17 00:00:00 2001 From: rithikreddypalla Date: Wed, 22 Jan 2025 01:47:32 +0530 Subject: [PATCH 11/12] Apply Linting & Formatting Fixes --- models.py | 4 ++++ mutations.py | 4 +--- otypes.py | 19 ++++++++++++++++--- queries.py | 2 +- utils.py | 6 +++--- 5 files changed, 25 insertions(+), 10 deletions(-) diff --git a/models.py b/models.py index 8503bd6..da9604b 100644 --- a/models.py +++ b/models.py @@ -96,6 +96,7 @@ def current_year() -> int: @strawberry.enum class EnumStates(str, Enum): """Enum for state of the club.""" + active = "active" deleted = "deleted" @@ -103,6 +104,7 @@ class EnumStates(str, Enum): @strawberry.enum class EnumCategories(str, Enum): """Enum for category of the club.""" + cultural = "cultural" technical = "technical" affinity = "affinity" @@ -126,6 +128,7 @@ class Social(BaseModel): whatsapp (HttpUrlString | None): Club WhatsApp handle. Defaults to None. other_links (List[HttpUrlString]): List of other social handles and URLs """ + website: HttpUrlString | None = None instagram: HttpUrlString | None = None facebook: HttpUrlString | None = None @@ -166,6 +169,7 @@ class Club(BaseModel): created_time (datetime): Time of creation of the Club. updated_time (datetime): Time of last update to the Club. """ + id: PyObjectId = Field(default_factory=PyObjectId, alias="_id") cid: str = Field(..., description="Club ID") code: str = Field( diff --git a/mutations.py b/mutations.py index b9ac7e3..39b43cf 100644 --- a/mutations.py +++ b/mutations.py @@ -2,8 +2,6 @@ Mutations for Clubs """ -from datetime import datetime - import strawberry from fastapi.encoders import jsonable_encoder @@ -108,7 +106,7 @@ def editClub(clubInput: FullClubInput, info: Info) -> FullClubType: Exception: Authentication Error! (CLUB ID CHANGED). Exception: You dont have permission to change the name/email of the club. Please contact CC for it. Exception: Only CC is allowed to change the category of club. - Exception: Not Authenticated to access this API. + Exception: Not Authenticated to access this API. """ # noqa: E501 user = info.context.user if user is None: diff --git a/otypes.py b/otypes.py index 3274059..042bfda 100644 --- a/otypes.py +++ b/otypes.py @@ -19,6 +19,7 @@ class Context(BaseContext): """ Class provides user metadata and cookies from request headers, has methods for doing this. """ + @cached_property def user(self) -> Union[Dict, None]: if not self.request: @@ -51,6 +52,7 @@ class SocialsType: """ Type used for return of social media handles of a club. """ + website: Optional[str] = strawberry.UNSET instagram: Optional[str] = strawberry.UNSET facebook: Optional[str] = strawberry.UNSET @@ -82,6 +84,7 @@ class SimpleClubType: """ Type used for return of user-provided club details except social handles. """ + pass @@ -104,7 +107,10 @@ class SimpleClubType: ], ) class FullClubType: - """Type used for return of all user-provided club details.""" + """ + Type used for return of all user-provided club details. + """ + # socials: SocialsType pass @@ -112,13 +118,19 @@ class FullClubType: # CLUBS INPUTS @strawberry.experimental.pydantic.input(model=Social, all_fields=True) class SocialsInput: - """Input used for input of social media handles of a club.""" + """ + Input used for input of social media handles of a club. + """ + pass @strawberry.input class SimpleClubInput: - """Input used for input of cid(Club id) of a club.""" + """ + Input used for input of cid(Club id) of a club. + """ + cid: str @@ -139,6 +151,7 @@ class FullClubInput: """ Input used for input of all user-provided club details, pictures are optional to fill. """ + logo: Optional[str] = strawberry.UNSET banner: Optional[str] = strawberry.UNSET banner_square: Optional[str] = strawberry.UNSET diff --git a/queries.py b/queries.py index 729c87c..3592cd2 100644 --- a/queries.py +++ b/queries.py @@ -19,7 +19,7 @@ def activeClubs(info: Info) -> List[SimpleClubType]: """ Fetches all the currently active clubs and is accessible to all. - + Args: info (Info): User metadata and cookies. diff --git a/utils.py b/utils.py index 66eb6b6..5d12500 100644 --- a/utils.py +++ b/utils.py @@ -175,10 +175,10 @@ def getUser(uid, cookies=None): def delete_file(filename): """ Method for deleting a file from the files microservice - + Args: filename (str): Name of the file to be deleted - + Returns: Response from the files microservice """ @@ -199,7 +199,7 @@ def delete_file(filename): def check_remove_old_file(old_obj, new_obj, name="logo"): """ Method to remove old files. - + Args: old_obj (dict): Old object containing the old file new_obj (dict): New object containing the new file From d3b516ac926c1dcf38272a7a964f84e968c071db Mon Sep 17 00:00:00 2001 From: notpua Date: Fri, 24 Jan 2025 11:16:14 +0530 Subject: [PATCH 12/12] docs: add different pages --- pydoc-markdown.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pydoc-markdown.yml b/pydoc-markdown.yml index f0f2bdf..e4d523b 100644 --- a/pydoc-markdown.yml +++ b/pydoc-markdown.yml @@ -17,6 +17,20 @@ renderer: - title: Home name: index source: README.md - - title: API Documentation + - title: Main contents: - - '*' + - 'main' + - 'db' + - title: Models + contents: + - 'models' + - 'otypes' + - title: Mutations + contents: + - 'mutations' + - title: Queries + contents: + - 'queries' + - title: Utils + contents: + - 'utils'