Skip to content

Commit

Permalink
Merge pull request #972 from hngprojects/dev
Browse files Browse the repository at this point in the history
chore: Merge dev branch into staging branch
  • Loading branch information
joboy-dev authored Aug 25, 2024
2 parents 32a79d5 + d2ce65f commit 7cd4fa6
Show file tree
Hide file tree
Showing 30 changed files with 1,120 additions and 168 deletions.
48 changes: 48 additions & 0 deletions .github/workflows/regression-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Run Regression Tests

on:
schedule:
- cron: '*/15 * * * *' # Runs every 15 minutes
workflow_dispatch:


jobs:
run-newman-test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install Newman
run: |
npm install -g newman
- name: Run Newman Tests
run: |
newman run qa_tests/Boilerplate-status-page.postman_collection.json -r json --reporter-json-export=result.json --suppress-exit-code
- name: Copy result.json to server
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
source: "result.json"
target: "/home/${{ secrets.USERNAME }}/hng_boilerplate_python_fastapi_web/staging"

- name: Deploy to Server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
password: ${{ secrets.PASSWORD }}
script: |
cd hng_boilerplate_python_fastapi_web/staging
python3 update_api_status.py
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ MANIFEST
test_case1.py
api/core/dependencies/mailjet.py
tests/v1/waitlist/waitlist_test.py

result.json
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
Expand Down
56 changes: 56 additions & 0 deletions api/core/dependencies/email/templates/squeeze.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{% extends 'base.html' %}

{% block title %}Welcome{% endblock %}

{% block content %}
<table role="presentation" width="100%" style="padding: 3.5rem;">
<tr>
<td>
<div style="text-align: center; margin-bottom: 1.5rem;">
<h1 style="font-size: 1.5rem; color: #0A0A0A; font-weight: 600;">Welcome to Boilerplate Squeeze</h1>
<p style="font-size: 1.125rem; color: rgba(0, 0, 0, 0.8); font-weight: 500;">Thanks for signing up</p>
</div>

<div>
<p style="color: #111; font-size: 1.125rem; font-weight: 600;">Hi {{name}}</p>
<p style="color: rgba(17, 17, 17, 0.9); font-weight: 400;">Experience quality and innovation
like never before. Our product is made to fit your needs and make your
life easier.</p>
</div>

<div style="margin-bottom: 1.75rem;">
<h3 style="color: #0A0A0A; font-weight: 600;">Here's what you can look forward to.</h3>
<div style="margin-bottom: 1.25rem;">
<ul>
<li>
<span style="font-weight: 600;">Exclusive Offers:</span> Enjoy special promotions and
discounts available only to our members.
</li>
<li>
<span style="font-weight: 600;">Exclusive Offers:</span> Enjoy special promotions and
discounts available only to our members.
</li>
<li>
<span style="font-weight: 600;">Exclusive Offers:</span> Enjoy special promotions and
discounts available only to our members.
</li>
</ul>
</div>
</div>

<a href="{{cta_link}}" style="display: block; width: fit-content; padding: 0.5rem 2.5rem; background-color: #F97316; color: white; text-decoration: none; border-radius: 0.5rem; margin: 0 auto; text-align: center;">
Learn more about us
</a>

<!-- <div style="margin-top: 2rem;">
<p style="color: #111; font-size: 0.875rem; font-weight: 500;">Thank you for joining Boilerplate</p>
</div> -->

<div style="margin-top: 2rem;">
<p>Regards,</p>
<p>Boilerplate</p>
</div>
</td>
</tr>
</table>
{% endblock %}
2 changes: 2 additions & 0 deletions api/v1/models/billing_plan.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# app/models/billing_plan.py
from sqlalchemy import Column, String, ARRAY, ForeignKey, Numeric, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy import DateTime
from api.v1.models.base_model import BaseTableModel


Expand Down Expand Up @@ -34,3 +35,4 @@ class UserSubscription(BaseTableModel):
user = relationship("User", back_populates="subscriptions")
billing_plan = relationship("BillingPlan", back_populates="user_subscriptions")
organisation = relationship("Organisation", back_populates="user_subscriptions")
billing_cycle = Column(DateTime, nullable=True)
52 changes: 50 additions & 2 deletions api/v1/routes/blog.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from api.utils.pagination import paginated_response
from api.utils.success_response import success_response
from api.v1.models.user import User
from api.v1.models.blog import Blog, BlogDislike, BlogLike
from api.v1.models.blog import Blog
from api.v1.schemas.blog import (
BlogCreate,
BlogPostResponse,
Expand All @@ -20,7 +20,7 @@
CommentRequest,
CommentUpdateResponseModel
)
from api.v1.services.blog import BlogService
from api.v1.services.blog import BlogService, BlogDislikeService, BlogLikeService
from api.v1.services.user import user_service
from api.v1.schemas.comment import CommentCreate, CommentSuccessResponse
from api.v1.services.comment import comment_service
Expand Down Expand Up @@ -118,6 +118,7 @@ def like_blog_post(
current_user: User = Depends(user_service.get_current_user),
):
"""Endpoint to add `like` to a blog post.
Existing `dislike` by the `current_user` is automatically deleted.
args:
blog_id: `str` The ID of the blog post.
Expand All @@ -137,6 +138,9 @@ def like_blog_post(
# confirm current user has NOT liked before
blog_service.check_user_already_liked_blog(blog_p, current_user)

# check for BlogDislike by current user and delete it
blog_service.delete_opposite_blog_like_or_dislike(blog_p, current_user, "like")

# update likes
new_like = blog_service.create_blog_like(
db, blog_p.id, current_user.id, ip_address=get_ip_address(request))
Expand All @@ -160,6 +164,7 @@ def dislike_blog_post(
current_user: User = Depends(user_service.get_current_user),
):
"""Endpoint to add `dislike` to a blog post.
Existing `like` by the `current_user` is automatically deleted.
args:
blog_id: `str` The ID of the blog post.
Expand All @@ -179,6 +184,9 @@ def dislike_blog_post(
# confirm current user has NOT disliked before
blog_service.check_user_already_disliked_blog(blog_p, current_user)

# check for BlogLike by current user and delete it
blog_service.delete_opposite_blog_like_or_dislike(blog_p, current_user, "dislike")

# update disikes
new_dislike = blog_service.create_blog_dislike(
db, blog_p.id, current_user.id, ip_address=get_ip_address(request))
Expand Down Expand Up @@ -299,3 +307,43 @@ async def update_blog_comment(
status_code=200,
data=jsonable_encoder(updated_blog_comment)
)


@blog.delete("/likes/{blog_like_id}",
status_code=status.HTTP_204_NO_CONTENT)
async def delete_blog_like(
blog_like_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user),
):
"""Endpoint to delete `BlogLike`
args:
blog_like_id: `str` The ID of the BlogLike object.
request: `default` Request.
db: `default` Session.
"""
blog_like_service = BlogLikeService(db)

# delete blog like
return blog_like_service.delete(blog_like_id, current_user.id)


@blog.delete("/dislikes/{blog_dislike_id}",
status_code=status.HTTP_204_NO_CONTENT)
def delete_blog_dislike(
blog_dislike_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_user),
):
"""Endpoint to delete `BlogDislike`
args:
blog_dislike_id: `str` The ID of the BlogDislike object.
request: `default` Request.
db: `default` Session.
"""
blog_dislike_service = BlogDislikeService(db)

# delete blog dislike
return blog_dislike_service.delete(blog_dislike_id, current_user.id)
23 changes: 22 additions & 1 deletion api/v1/routes/email_template.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, status
from fastapi import APIRouter, Depends, status, HTTPException
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session

Expand Down Expand Up @@ -92,3 +92,24 @@ async def delete_email_template(
"""Endpoint to delete a single template"""

email_template_service.delete(db, template_id=template_id)


@email_template.post("/{template_id}/send", response_model=success_response, status_code=200)
async def send_email_template(
template_id: str,
recipient_email: str,
db: Session = Depends(get_db),
current_user: User = Depends(user_service.get_current_super_admin),
):
"""Endpoint to send an email template to a recipient"""

send_result = email_template_service.send(db=db, template_id=template_id, recipient_email=recipient_email)

if send_result["status"] == "failure":
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=send_result["message"])

return success_response(
data=send_result,
message=f"Email sent successfully to {recipient_email}",
status_code=status.HTTP_200_OK,
)
27 changes: 15 additions & 12 deletions api/v1/routes/newsletter.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,23 @@ async def sub_newsletter(
# Save user to the database
NewsletterService.create(db, request)

link = "https://anchor-python.teams.hng.tech/"

# Send email in the background
background_tasks.add_task(
send_email,
recipient=request.email,
template_name="newsletter-subscription.html",
subject="Thank You for Subscribing to HNG Boilerplate Newsletters",
context={"link": link},
)
link = "https://anchor-python.teams.hng.tech/"

# Send email in the background
background_tasks.add_task(
send_email,
recipient=request.email,
template_name="newsletter-subscription.html",
subject="Thank You for Subscribing to HNG Boilerplate Newsletters",
context={"link": link},
)
message = "Thank you for subscribing to our newsletter."
else:
message = "You have already subscribed to our newsletter. Thank you."

return success_response(
message="Thank you for subscribing to our newsletter.",
status_code=status.HTTP_201_CREATED,
message=message,
status_code=status.HTTP_200_OK,
)


Expand Down
Loading

0 comments on commit 7cd4fa6

Please sign in to comment.