LabFile PixelPalette (21bcs092)
LabFile PixelPalette (21bcs092)
KATRA
JAMMU & KASHMIR – 182 320
0
INDEX
S No. Title Page No
1. Multiple Test cases generation for single function 3
and for multiple functions in a program/ file.
3. JUNIT - Testing peer's code and maintaining Test 5
Report/ log
4. JUNIT - Using other annotations and functions 7
available in JUNIT
5. Design Patterns 8
a) Singleton (Creational)
b) Decorator (Structural)
c) Behavioural (Observer)
d) Fly Weight (Structural)
6. Use Case Diagram for Pixel Palette 20
7. SRS Document 23
8. DFDs 33
9. Sequence Diagram for Pixel Palette 34
10. Activity Diagram for Pixel Palette 35
11. Function Oriented Design for Pixel Palette 36
12. Documented Code of Pixel Palette 38
13. Test Case & Test Plan 72
1
1. JUNIT - Multiple Test cases generation for single function and for multiple functions in a
program/ file.
2
3
3. JUNT - Testing peer's code and maintaining Test Report/ log
4
5
4. JUNIT - Using other annotations and functions available in JUNIT
6
5. Design Patterns
// Product Interface
interface Product {
void display();
}
// Concrete Products
class ConcreteProductA implements Product {
@Override
public void display() {
System.out.println("This is Concrete Product A.");
}
}
// Factory Interface
interface Factory {
Product factoryMethod();
}
// Concrete Factories
class ConcreteFactoryA implements Factory {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// Client Code
public class FactoryMethodExample {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.factoryMethod();
productA.display();
private Builder() {}
// Setter methods
public Builder setId(int id)
{
this.id = id;
return this;
}
public Builder setName(String name)
{
this.name = name;
return this;
}
public Builder setAddress(String address)
{
this.address = address;
return this;
}
@Override
9
public String toString()
{
return "id = " + this.id + ", name = " + this.name +
", address = " + this.address;
}
}
public StudentReceiver()
{
t1.start();
t2.start();
}
// Driver class
public class BuilderDemo {
public static void main(String args[])
{
StudentReceiver sr = new StudentReceiver();
System.out.println(sr.getStudent());
}
}
==================================================================
// Concrete prototype
class Circle implements Shape {
private String color;
// Client code
class ShapeClient {
private Shape shapePrototype;
// Main class
public class PrototypeExample {
public static void main(String[] args) {
// Create a concrete prototype (a red circle).
Shape circlePrototype = new Circle("red");
OUTPUT
Drawing a red circle.
==================================================================
// Helper class
class Singleton {
// Static variable reference of single_instance
// of type Singleton
private static Singleton single_instance = null;
// Constructor
// Here we will be creating private constructor
// restricted to this class itself
private Singleton()
{
s = "Hello I am a string part of Singleton
class";
}
// Static method
// Static method to create instance of Singleton class
13
public static synchronized Singleton getInstance()
{
if (single_instance == null)
single_instance = new Singleton();
return single_instance;
}
}
// Class 2
// Main class
class GFG {
// Main driver method
public static void main(String args[])
{
// Instantiating Singleton class with variable x
Singleton x = Singleton.getInstance();
// Condition check
if (x == y && y == z) {
// Print statement
System.out.println(
14
"Three objects point to the same memory
location on the heap i.e, to the same object");
}
else {
// Print statement
System.out.println(
"Three objects DO NOT point to the same
memory location on the heap");
}
}
}
OUTPUT
==================================================================
// Class 1
// Class 1 will be implementing the Shape interface
// Rectangle.java
public class Rectangle implements Shape {
// Class 2
// Abstract class
// ShapeDecorator.java
public abstract class ShapeDecorator implements Shape {
// Protected variable
protected Shape decoratedShape;
// Method 1
// Abstract class method
public ShapeDecorator(Shape decoratedShape)
{
// This keyword refers to current object itself
this.decoratedShape = decoratedShape;
}
// Method 2 - draw()
// Outside abstract class
public void draw() { decoratedShape.draw(); }
}
// Class 3
// Concrete class extending the abstract class
// RedShapeDecorator.java
public class RedShapeDecorator extends ShapeDecorator {
// Main class
public class DecoratorPatternDemo {
Shape redCircle
= new RedShapeDecorator(new Circle());
Shape redRectangle
= new RedShapeDecorator(new Rectangle());
// Display message
System.out.println("Circle with normal border");
// Display message
System.out.println("\nCircle of red border");
// Call 2
redCircle.draw();
// Display message
System.out.println("\nRectangle of red border");
// Call 3
redRectangle.draw();
}
}
OUTPUT
Software Requirements
Specification
for
PIXEL PALETTE
Version 1.0 approved
Prepared by
Utkersh Uppal,21bcs098
13-03-2024
21
Contents
1. INTRODUCTION 3
1.1 Document Purpose 3
1.2 Product Scope 3
1.3 Definitions, Acronyms And Abbrevations 3
1.4 Refrences And Refrences 3
1.5 Overview 3
2. OVERALL DESCRIPTION 4
2.1 Product Perspective 4
2.2 Product Functionalities 4
2.3 User Characterstics 4
2.4 General Constraints 5
2.5 Assumptions And Dependencies 5
3.SPECIFIC REQUIREMENTS 6
3.1. External Interface Requirements 6
3.1.1 User Interfaces 6
3.1.2 Hardware Interfaces 6
3.1.3 Software Interfaces 6
3.2 Functional Requirements 6
3.2.1 Authentication 6
3.2.2 Image Upload 6
3.3 Use Case Model 6
3.3.1 Use Case 1: User Authentication 6
3.3.2 Use Case 2: Community Image Showcase 7
3.3.3 Use Case 3: Advanced Image Search 7
3.3.4 Use Case 4: Image Processing functionalities 8
3.3.5 Use Case 5: Payment Integration 8
3.4 Performance Requirements 9
3.5 Design constraints 9
3.5 Other Requirements 10
4. APPENDICES 12
22
1. INTRODUCTION
1.1 DOCUMENT PURPOSE
The purpose of this Software Requirements Specification (SRS) document is to provide a
detailed description of the requirements for the development of an AI image SaaS platform. This
platform will offer advanced image processing capabilities, secure payment infrastructure,
advanced image search functionalities, and support for various AI features.
1.5 OVERVIEW
The AI Image SaaS platform is an innovative solution designed to meet the diverse image
processing needs of users ranging from hobbyists and professionals to businesses and
researchers. Leveraging cutting-edge technologies such as Next.js, TypeScript, MongoDB,
Clerk, Cloudinary, Stripe, Shadcn, and TailwindCSS, the platform offers a comprehensive suite
of features for image transformation, customization, and enhancement.
23
2. OVERALL DESCRIPTION
2.1 PRODUCT PERSPECTIVE
The AI image SaaS platform(pixel palette) will serve as a standalone web application, offering
various image processing functionalities and payment integration. It will interact with external
services such as Stripe for payment processing and Cloudinary for image storage and
processing.:
● Home Page: Allows anyone to browse and view images without requiring login.
● Upload Page: Enables users to upload images.
● User Sign-In Page: Provides login functionality for users .
● Signup Page: Allows new users to register.
24
2.5 ASSUMPTIONS AND DEPENDENCIES
● Assumptions:
● Users have a basic understanding of how to navigate and interact with web
applications.
● Users have access to a stable internet connection.
● Dependencies:
● Express services are relied upon for user authentication, data storage, and
backend functionality.
● Next.js framework is used for frontend development, requiring adherence to its
conventions and best practices.
25
3.Specific Requirements
3.1. External Interface Requirements
3.1.1 User Interfaces
● Home Page: Allows anyone to view images without logging in.
● Upload Page: Enables users to upload images.
● Sign-In Page: Allows both users to log in.
● Sign-Up Page: Enables new user registration.
● Admin-page: Checks the user data stored in MongoDB
● Steps:
1. User navigates to the sign-in page.
26
2. User enters username and password.
3. System verifies the credentials.
4. If authentication is successful, user is redirected to the respective dashboard.
● Exception Scenarios:
● If the user enters an incorrect username or password:
● The system displays an error message indicating invalid credentials.
● User is prompted to retry authentication.
● Exception Scenarios:
● If the user is not signed in:
o The system prompts the user to sign in before accessing the community
image showcase
● Steps:
1. User navigates to the advanced image search page.
2. User enters keywords or selects objects to search for.
3. System retrieves relevant images based on the search criteria.
4. User views the search results
● Exception Scenarios:
27
● If the search criteria yield no results:
o The system displays a message indicating no matching images were found.
● Exception Scenarios:
● If the payment transaction fails:
o The system prompts the user to retry the payment or try an alternative payment
method.
● If there's a communication error with the Stripe payment gateway:
28
o The system displays a message indicating the issue and advises the user to try
again later.
1. Technology Stack Compatibility: The platform must be developed using the specified
technologies (Next.js, TypeScript, MongoDB, Clerk, Cloudinary, Stripe, Shadcn, TailwindCSS) to
ensure compatibility, integration, and optimal performance.
2. Scalability: The architecture must support scalability to accommodate a growing user base
and increasing image processing demands. It should be capable of handling concurrent user
requests efficiently.
3. Security Compliance: The platform must adhere to industry-standard security practices and
comply with relevant regulations (e.g., GDPR) to protect user data, ensure secure
authentication, and handle payment transactions securely.
5. Data Privacy: User data, including images and personal information, must be handled with
strict confidentiality and protected against unauthorized access, manipulation, or disclosure.
6. Payment Gateway Integration: The platform must integrate seamlessly with the Stripe
payment gateway to facilitate secure credit purchases for image transformations.
7. Third-Party API Dependencies: Integration with external APIs (e.g., Cloudinary for image
storage and processing) should be reliable and robust, considering potential API rate limits,
downtime, or changes in API functionality.
29
8. User Experience (UX): The platform's user interface should be intuitive, responsive, and
optimized for usability to enhance user satisfaction and engagement.
2. Database Server
● Dedicated database server with sufficient CPU, RAM, and storage resources to handle
data storage and retrieval.
● Database Management System (DBMS): MongoDB for storing user data, image
metadata, and system configurations.
30
8. Hardware Security
● Physical security measures for protecting server hardware from unauthorized access or
tampering.
● Network security protocols (e.g., firewalls, intrusion detection/prevention systPixel
Palette) to safeguard against cyber threats and attacks.
By meeting these hardware requirements, the AI Image SaaS platform can ensure reliable
performance, scalability, and security for its users.
31
8. Data Flow Diagrams
32
9. Sequence Diagrams
33
10. Activity Diagram
34
35
11. Function Oriented Design for Pixel Palette
36
12. Documented Code of Pixel Palette
37
let evt: WebhookEvent;
const user = {
clerkId: id,
email: email_addresses[0].email_address,
username: username!,
firstName: first_name,
lastName: last_name,
photo: image_url,
};
const user = {
firstName: first_name,
lastName: last_name,
username: username!,
photo: image_url,
};
39
File name: api/webhooks/stripe/route.ts
try {
// Attempt to construct a Stripe event object from the request body,
signature, and secret
event = stripe.webhooks.constructEvent(body, sig, endpointSecret);
} catch (err) {
// Handle errors during event construction
console.error("Error constructing Stripe event:", err); // Log the error for
debugging
return NextResponse.json({ message: "Webhook error", error: err }); // Return
error response
}
40
amount: amount_total ? amount_total / 100 : 0, // Convert cents to dollars
(assuming amount_total is in cents)
plan: metadata?.plan || "", // Extract plan details from metadata (or set to
empty string)
credits: Number(metadata?.credits) || 0, // Extract credit details from
metadata (or set to 0)
buyerId: metadata?.buyerId || "", // Extract buyer ID from metadata (or set
to empty string)
createdAt: new Date(), // Set transaction creation date
};
// For other event types or if no event type matches, return a generic 200
response
return new Response("", { status: 200 });
}
41
File: Checkout.tsx
// This line is required for React components to access server-side data during
the initial render on the server
"use client";
interface CheckoutProps {
plan: string;
amount: number;
credits: number;
buyerId: string;
}
// Load Stripe.js library using the Stripe publishable key from the environment
variable
useEffect(() => {
loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!);
}, []);
useEffect(() => {
// Check for query parameters in the URL that indicate success or cancellation
of a Stripe checkout
const query = new URLSearchParams(window.location.search);
if (query.get("success")) {
toast({
title: "Order placed!",
description: "You will receive an email confirmation",
duration: 5000,
className: "success-toast",
});
}
if (query.get("canceled")) {
toast({
title: "Order canceled!",
description: "Continue to shop around and checkout when you're ready",
42
duration: 5000,
className: "error-toast",
});
}
}, []);
return (
<form action={onCheckout} method="POST">
<section>
<Button
type="submit"
role="link"
className="w-full rounded-full bg-purple-gradient bg-cover"
>
Buy Credit
</Button>
</section>
</form>
);
};
43
File: Collection.tsx
44
});
return (
<>
{/* Rendering the collection heading and Search component if enabled */}
<div className="collection-heading">
<h2 className="h2-bold text-dark-600">Recent Edits</h2>
{hasSearch && <Search />}
</div>
47
const onUploadErrorHandler = (result: any) => {
// Display error toast
toast({
title: "Something went wrong!!",
description: "Please try again",
duration: 5000,
className: "error-toast"
})
}
</CldUploadWidget>
)
}
49
File: Search.tsx
"use client";
useEffect(() => {
// Debounce functionality to reduce unnecessary URL updates
const delayDebounceFn = setTimeout(() => {
// If there's a query, add it to the URL
if (query) {
const newUrl = formUrlQuery({
searchParams: searchParams.toString(),
key: "query",
value: query,
});
// Push the updated URL to the browser's history
router.push(newUrl, { scroll: false });
} else {
// If query is empty, remove the 'query' parameter from the URL
const newUrl = removeKeysFromQuery({
searchParams: searchParams.toString(),
keysToRemove: ["query"],
});
// Push the updated URL to the browser's history
router.push(newUrl, { scroll: false });
}
}, 300);
50
// Render the search input field and icon
return (
<div className="search">
{/* Search icon */}
<Image
src="/assets/icons/search.svg"
alt="search"
width={24}
height={24}
/>
51
File: TransformationForm.tsx
"use client"
// TransformationForm component
52
const TransformationForm = ({ action, data = null, userId, type, creditBalance,
config = null }: TransformationFormProps) => {
if (newImage) {
form.reset()
setImage(data)
router.push(`/transformations/${newImage._id}`)
}
} catch (error) {
console.log(error)
}
}
if (updatedImage) {
router.push(`/transformations/${updatedImage._id}`)
}
} catch (error) {
console.log(error);
}
}
}
54
setIsSubmitting(false)
}
setNewTransformation(transformationType.config);
return onChangeField(value)
}
// debounce function will wait for a specified time and then pass on the
values to be processed
debounce(() => {
setNewTransformation((prevState: any) => ({
...prevState,
[type]: {
...prevState?.[type],
[fieldName === 'prompt' ? 'prompt' : 'to']: value
}
}))
}, 1000)();
return onChangeField(value)
}
settransformationConfig(deepMergeObjects(newTransformation,
transformationConfig))
setNewTransformation(null)
/>
)}
</div>
)}
<div className="media-uploader-field">
<CustomField
control={form.control}
name="publicId"
className="flex size-full flex-col"
render={({ field }) => (
<MediaUploader
onValueChange={field.onChange}
setImage={setImage}
publicId={field.value}
image={image}
type={type}
/>
)}
/>
<TransformedImage
image={image}
type={type}
title={form.getValues().title}
isTransforming={isTransforming}
setIsTransforming={setIsTransforming}
transformationConfig={transformationConfig}
/>
</div>
59
File: TransformedImage.tsx
return (
<div className='flex flex-col gap-4'>
{/* Title and download button */}
<div className='flex-between '>
<h3 className='h3-bold text-dark-600'>Transformed</h3>
{hasDownload && (
<button className='download-btn'
onClick={downloadHandler}
>
<Image
src="/assets/icons/download.svg"
alt="Download"
width={24}
height={24}
className="pb-[6px]"
/>
</button>
)}
</div>
60
{/* Check if image exists and transformation configuration is provided
*/}
{image?.publicId && transformationConfig ? (
<div className='relative'>
{/* Render Cloudinary Image component */}
<CldImage
width={getImageSize(type, image, "width")}
height={getImageSize(type, image, "height")}
src={image?.publicId}
alt={image.title}
sizes={"(max-width: 767px) 100vw, 50vw"}
placeholder={dataUrl as PlaceholderValue}
className="transformed-image"
// Callback for image load event
onLoad={()=>{
setIsTransforming && setIsTransforming(false)
}}
// Callback for image error event
onError={()=>{
// Debounce the error handling function
debounce(()=>{
setIsTransforming && setIsTransforming(false)
},8000)()
}}
{...transformationConfig}
/>
{/* Render transforming loader if image is transforming */}
{isTransforming && (
<div className="transforming-loader">
<Image
src="/assets/icons/spinner.svg"
width={50}
height={50}
alt="spinner"
/>
<p className="text-white/80">Please wait...</p>
</div>
)}
</div>
) : (
// Render placeholder if image or transformation config is missing
<div className='transformed-placeholder'>
Transformed Image
</div>
)}
</div>
)
}
61
// Export the TransformedImage component
export default TransformedImage
File: sidebar.tsx
return (
// Sidebar container
<aside className="sidebar">
<div className="flex size-full flex-col gap-4">
{/* Logo */}
<Link href="/" className="sidebar-logo">
<Image src="/assets/images/Clerk_pixel.jpeg" alt="logo"
width={300} height={28} />
</Link>
return (
// Rendering individual navigation link
<li key={link.route}
className={`sidebar-nav_element group ${isActive ? 'bg-purple-gradient text-white'
: 'text-gray-700'
}`}>
<Link className="sidebar-link"
href={link.route}>
<Image
62
src={link.icon}
alt="logo"
width={24}
height={24}
className={`${isActive &&
'brightness-200'}`}
/>
{link.label}
</Link>
</li>
)
})}
</ul>
return (
<li key={link.route}
className={`sidebar-nav_element group ${isActive ? 'bg-purple-gradient text-white'
: 'text-gray-700'
}`}>
<Link className="sidebar-link"
href={link.route}>
<Image
src={link.icon}
alt="logo"
width={24}
height={24}
className={`${isActive &&
'brightness-200'}`}
/>
{link.label}
</Link>
</li>
)
})}
File: image.actions.ts
"use server"
if(!author){
throw new Error("User not found in the database!!");
}
64
// Create a new image document in the database
const newImage = await Image.create({
...image,
author: author._id,
});
return JSON.parse(JSON.stringify(newImage));
} catch (error) {
handleError(error);
}
}
return JSON.parse(JSON.stringify(updatedImage));
} catch (error) {
handleError(error);
}
}
// Find and populate the user data associated with the image
const image = await populateUser(await Image.findById(imageId));
return JSON.parse(JSON.stringify(image));
} catch (error) {
handleError(error);
}
}
// Get images from the database, populate user data, and apply pagination
const images = await populateUser(Image.find(query))
.sort({ updatedAt: -1 })
.skip(skipAmount)
.limit(limit);
return {
data: JSON.parse(JSON.stringify(images)),
totalPage: Math.ceil(totalImages / limit),
};
} catch (error) {
handleError(error);
}
}
// Get images by a specific user, populate user data, and apply pagination
const images = await populateUser(Image.find({ author: userId }))
.sort({ updatedAt: -1 })
.skip(skipAmount)
.limit(limit);
return {
data: JSON.parse(JSON.stringify(images)),
totalPages: Math.ceil(totalImages / limit),
};
} catch (error) {
handleError(error);
}
}
File: transaction.action.ts
"use server"
import { redirect } from 'next/navigation'; // Importing the redirect function
from the next/navigation module
import Stripe from "stripe"; // Importing the Stripe library for payment
processing
import { handleError } from '../utils'; // Importing a utility function for error
handling
import { connectToDatabase } from '../database/mongoose'; // Importing a function
to connect to the database
import Transaction from '../database/models/transaction.model'; // Importing the
Transaction model from the database
import { updateCredits } from './user.actions'; // Importing a function to update
user credits from user.actions
68
const session = await stripe.checkout.sessions.create({
line_itPixel Palette: [
{
price_data: {
currency: 'usd',
unit_amount: amount,
product_data: {
name: transaction.plan, // Name of the product or plan
}
},
quantity: 1 // Quantity of the product
}
],
metadata: {
plan: transaction.plan, // Metadata including plan name
credits: transaction.credits, // Credits associated with the transaction
buyerId: transaction.buyerId, // ID of the buyer
},
mode: 'payment', // Mode of the session (payment in this case)
success_url: `${process.env.NEXT_PUBLIC_SERVER_URL}/profile`, // URL to
redirect on successful payment
cancel_url: `${process.env.NEXT_PUBLIC_SERVER_URL}/`, // URL to redirect on
canceled payment
});
69
File: user.actions.ts
"use server"
import { redirect } from 'next/navigation'; // Importing the redirect function
from the next/navigation module
import Stripe from "stripe"; // Importing the Stripe library for payment
processing
import { handleError } from '../utils'; // Importing a utility function for error
handling
import { connectToDatabase } from '../database/mongoose'; // Importing a function
to connect to the database
import Transaction from '../database/models/transaction.model'; // Importing the
Transaction model from the database
import { updateCredits } from './user.actions'; // Importing a function to update
user credits from user.actions
70
redirect(session.url!); // Redirecting to the checkout session URL
}
71
13. Test Case & Test Plan
2 User can log into their User is successfully User logged in Pass
account logged in successfully
3 User can view the Credits and user images Credits and images Pass
transformations displayed on the user displayed
performed and the profile
remaining credits on the
user profile
6 Credits can be bought by The user should be User gets redirected Pass
the user to perform directed to the credits successfully and
various transformations page where various credits packs are
credit packs should be visible on the page
visible on the credits
page
7 User can complete the The payment gateway Payment gateway Pass
payment through stripe through stripe should world correctly
work correctly
72