Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fullstack Exam (React.js + .NetCore) #10

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.node_modules/
3 changes: 2 additions & 1 deletion BackEnd/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,5 @@ id_token.txt
pipeline/locust/.env

# Ignore auto-generated minified JS
js/*.min.js
js/*.min.js
node_modules/
2 changes: 2 additions & 0 deletions BackEnd/ContactsAPI/ContactsAPI/ContactsAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FirebaseAdmin" Version="2.3.0" />
<PackageReference Include="Google.Cloud.Firestore" Version="3.3.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.5" />
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.4.0" />
Expand Down
81 changes: 66 additions & 15 deletions BackEnd/ContactsAPI/ContactsAPI/Controllers/ContactController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,96 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ContactsAPI.Models;
using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
using FirebaseAdmin;
using Google.Cloud.Firestore;

namespace ContactsAPI.Controllers
{
[Route("api/[controller]")]
public class ContactController : Controller
{
// GET: api/<controller>
private readonly FirestoreDb _firestoreDb;

public ContactController()
{
var projectId = "react-firebase-37768";
_firestoreDb = FirestoreDb.Create(projectId);
}

// GET: api/Contacts
[HttpGet]
public IEnumerable<string> Get()
public async Task<ActionResult<IEnumerable<Contact>>> GetContacts()
{
return new string[] { "value1", "value2" };
var contactsRef = _firestoreDb.Collection("contacts");
var snapshot = await contactsRef.GetSnapshotAsync();
var contacts = snapshot.Documents.Select(doc =>
{
var contact = doc.ConvertTo<Contact>();
contact.Id = doc.Id;
return contact;
}).ToList();
return contacts;
}

// GET api/<controller>/5
// GET api/Contacts/5
[HttpGet("{id}")]
public string Get(int id)
public async Task<ActionResult<Contact>> GetContact(string id)
{
return "value";
var contactRef = _firestoreDb.Collection("contacts").Document(id);
var snapshot = await contactRef.GetSnapshotAsync();

if (!snapshot.Exists)
{
return NotFound();
}

var contact = snapshot.ConvertTo<Contact>();
return contact;
}

// POST api/<controller>
// POST: api/Contacts
[HttpPost]
public void Post([FromBody]string value)
public async Task<ActionResult<Contact>> CreateContact([FromBody] Contact contact)
{
var contactsRef = _firestoreDb.Collection("contacts");
var docRef = await contactsRef.AddAsync(contact);
contact.Id = docRef.Id;

return CreatedAtAction(nameof(GetContact), new { id = contact.Id }, contact);
}

// PUT api/<controller>/5
// PUT api/Contacts/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
public async Task<IActionResult> UpdateContact(string id, Contact updatedContact)
{
var contactRef = _firestoreDb.Collection("contacts").Document(id);
var snapshot = await contactRef.GetSnapshotAsync();

if (!snapshot.Exists)
{
return NotFound();
}

await contactRef.SetAsync(updatedContact, SetOptions.Overwrite);
return NoContent();
}

// DELETE api/<controller>/5
// DELETE api/Contacts/5
[HttpDelete("{id}")]
public void Delete(int id)
public async Task<IActionResult> DeleteContact(string id)
{
var contactRef = _firestoreDb.Collection("contacts").Document(id);
var snapshot = await contactRef.GetSnapshotAsync();

if (!snapshot.Exists)
{
return NotFound();
}

await contactRef.DeleteAsync();
return NoContent();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "react-firebase-37768",
"private_key_id": "5c975e74d188f2aae891b58976809df257c09fd0",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDCylHehGs3uSMQ\n/q38Z0hLJAFuZntuRt8Y1pbV3wicytUk9I7Uyt5uP/VhrYCbL3nO7ZGH4XNWa270\nybkhSNhbEX6rZ9QCdz03zuKPgSGNIh1LbDcGjkLHlD7+Nq0ciGeCc2Py59/gxapw\n6pIWkwcAKJ0fYqB+oQcAU7ZWxnKL3YB14rs5YO49WMPiKsreljVn6Dhoap0YvfoD\nBpqAxOwM6TGTh+gHRBOBlEyhIbhJJq7FIAMXZ3GF3Yj54sNZhGnFmEnKxAd1z9/z\nMtNAe0kSz1dSU7gg+fKZVucZUNZOUUyhFDves1S4CsfRn1ManrDjfOZKZdpdZIEW\nclLAylj7AgMBAAECggEABHedXwwMZW/ChR5CITrBJrABtGV3STUo4qnxNaBy0x23\nu6w8JlcsuLyQxRe0jlqNYZiDMOmTupSmK8kAjOndxGDnVckAzLmtVr1Q5tegTFtO\n7q7hqC3VDDyzISNh3vil3xEEZsq4kLAX05UdmpTuoStNRy383kqVHYXN9bmBYQG+\nJIDfIvVbntIzdFm/sdMzWxnewYfqW3eQZrba4qgZNa4AMtdglwDtdq8yE8rvjXxy\nJsoPKb+u8/0PE6/56PImBr4HMtG+Xr5ZSFleWDdBCbE3dU8HJsiGXD5YJFxa020H\nK1vMbTrTIwgwo+sAf9zdiLHRozO5DkK3oGVvPKaJoQKBgQD6FoqivZPjgIMiahlA\n33KSErQnBVPeic2chjk2anDHGAkWGae1nevTE5477Qy5nr71zgF6MYk4JmsIDzTk\nr/gJCjhI7XDxpL7VdUyOXCkqaQ0DDLUotYLkg9gaOJBh1kIQmE5Nh+jSQPTudTBh\nWohuG5aX1ZOJI4F4MxBgAqWiKwKBgQDHZSH+SEc8yHJ0PRngmEe3YUIPGsGZs+m+\nsj20OT+OxEvxxenVAgn+EYg+3SDb2OeHXEHM5AVHkNb8GwrvtUp3Z2UfjJmRY+97\neCr2cT+ubWubjJABDzQXqWN7gyh+C2qZIwJDU66vHXAte9WUb0pQJCf+6RfEMvEw\n5T7sbRtMcQKBgQCdTWxqLKBG6uCQ1veHbITeCFG0cY9tXHQtzedSqOTC9gVRF1X7\n0JcyrxZKTzI/1u/6IFehimLrJI8SBK6aYWy8EueQR3lRJPS7wwoiSSnDeUwr7ORN\n9M2d+qgE5is6vs/TLFmkPqZdCWqGSsPhmFZUG5o/aqFgEysudcAJA+E2/QKBgFvQ\nHJv66hp57gHKFL1PQU6WURWla1n79vihGDqBVdvGVXXpfOdhPfqoVgUkSzoQIWQ5\nve67XBb9AZEqXpnL75MVoAGr+xZwXtiKl7XMpdcKeU8xpSlnt8PhcnZwCVgop5gF\n/8fCo5/svTekWs8zg5NtYqFE8XWdQ2QRWy0AfF5xAoGAehUJzab6Owd4atT/PT4k\nIHKBX5YIVkne0tBpcz/LgPn7JtuOv0f0lDQPfdpH0K06AqP7hfQ+0j/zB9OgRyVy\nTDJ3VapXa+wyqyD9bZbSe1koNSOM5XBa+yxCJn2mWLrtSCDOkU0N1NT8mn6xNPWO\ngXFM6MbH5e2O2jVuosUd0l0=\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-44o03@react-firebase-37768.iam.gserviceaccount.com",
"client_id": "112073152501532901433",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-44o03%40react-firebase-37768.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
25 changes: 23 additions & 2 deletions BackEnd/ContactsAPI/ContactsAPI/Models/Contact.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,32 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Google.Cloud.Firestore;

namespace ContactsAPI.Models
{
[FirestoreData]
public class Contact
{
// Insert Contact Fields Here
[FirestoreProperty]
public string Id { get; set; }

[FirestoreProperty]
public string Name { get; set; }

[FirestoreProperty]
public string Email { get; set; }

[FirestoreProperty]
public int PhoneNumber { get; set; }

[FirestoreProperty]
public string Company { get; set; }

[FirestoreProperty]
public string Title { get; set; }

[FirestoreProperty]
public string Group { get; set; }
}
}
}
24 changes: 24 additions & 0 deletions BackEnd/ContactsAPI/ContactsAPI/Services/FirebaseService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.IO;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;

namespace ContactManagerAPI.Services
{
public class FirebaseService
{
public FirebaseService()
{
if (FirebaseApp.DefaultInstance == null)
{
var serviceAccountPath = "FirebaseCredentials/react-firebase-37768-firebase-adminsdk-44o03-5c975e74d1.json";
var serviceAccount = File.ReadAllText(serviceAccountPath);
var credential = GoogleCredential.FromJson(serviceAccount);

FirebaseApp.Create(new AppOptions
{
Credential = credential
});
}
}
}
}
27 changes: 26 additions & 1 deletion BackEnd/ContactsAPI/ContactsAPI/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using System.IO;
using Microsoft.AspNetCore.Cors.Infrastructure;

namespace ContactsAPI
{
Expand All @@ -28,6 +32,15 @@ public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();

services.AddCors(options =>
{
options.AddPolicy("AllowAll", new CorsPolicyBuilder()
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
.Build());
});

services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
Expand All @@ -43,6 +56,16 @@ public void ConfigureServices(IServiceCollection services)
},
});
});

// Set GOOGLE_APPLICATION_CREDENTIALS environment variable
string firebaseServiceAccountPath = "FirebaseCredentials/react-firebase-37768-firebase-adminsdk-44o03-5c975e74d1.json";
Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS", Path.GetFullPath(firebaseServiceAccountPath));

// Initialize Firebase Admin SDK
FirebaseApp.Create(new AppOptions
{
Credential = GoogleCredential.GetApplicationDefault(),
});
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
Expand All @@ -69,6 +92,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

app.UseRouting();

app.UseCors("AllowAll");

app.UseAuthorization();

app.UseEndpoints(endpoints =>
Expand All @@ -77,4 +102,4 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
});
}
}
}
}
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Contact Management Tool

This project is a full-stack contact management tool that allows users to efficiently manage their contacts. The main features of the application include adding new contacts, editing existing contacts, deleting contacts, and marking contacts as favorites. The application also provides sorting functionality, a search input for finding specific contacts, and pagination for easy navigation. A snackbar notification is displayed whenever a contact is successfully added, updated, or deleted. The application is designed using Material UI and tested using Jest.

## Getting Started

### Frontend

To run the frontend, follow these steps:

1. Open a terminal and navigate to the `contacts-app` directory:
cd contacts-app

2. Install the required dependencies:
npm install

3. Start the frontend development server:

The frontend will be running at [http://localhost:3000/](http://localhost:3000/).

### Backend

To run the backend, follow these steps:

1. Open the `ContactsAPI.sln` file in Visual Studio Code.

2. Run the IIS Express server.

The backend will be running at [https://localhost:44305/](https://localhost:44305/).
2 changes: 2 additions & 0 deletions contacts-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
.node_modules/
Loading