Skip to content

Commit

Permalink
able to delete and add tags to project
Browse files Browse the repository at this point in the history
  • Loading branch information
Mubangizi committed Jul 12, 2024
1 parent af4deec commit fa8ed70
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 12 deletions.
8 changes: 8 additions & 0 deletions api_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2529,6 +2529,14 @@ paths:
type: string
project_type:
type: string
tags_add:
type: array
items:
type: string
tags_remove:
type: array
items:
type: string
responses:
200:
description: "Success"
Expand Down
18 changes: 17 additions & 1 deletion app/controllers/project.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import datetime
import json
from types import SimpleNamespace
import uuid
from app.helpers.cost_modal import CostModal
from app.helpers.alias import create_alias
from app.helpers.admin import is_authorised_project_user, is_owner_or_admin, is_current_or_admin, is_admin
from app.helpers.role_search import has_role
from app.helpers.activity_logger import log_activity
from app.helpers.kube import create_kube_clients, delete_cluster_app, disable_project, enable_project, check_kube_error_code
from app.helpers.tags import add_tags_to_project, create_tags, remove_tags_from_project
from app.models.billing_invoice import BillingInvoice
from app.models.project_users import ProjectUser
from app.models.user import User
Expand All @@ -15,6 +17,7 @@
from app.schemas import ProjectSchema, AppSchema, ProjectUserSchema, ClusterSchema
from app.helpers.decorators import admin_required
import datetime
from app.schemas.tags import TagSchema
from flask_restful import Resource, request
from kubernetes import client
from flask_jwt_extended import jwt_required, get_jwt_identity, get_jwt_claims
Expand Down Expand Up @@ -57,6 +60,7 @@ def post(self):
message=f'''project with name {
validated_project_data["name"]} already exists'''
), 409

try:
validated_project_data['alias'] =\
create_alias(validated_project_data['name'])
Expand Down Expand Up @@ -131,6 +135,9 @@ def post(self):
)
project.users.append(new_role)

if validated_project_data['tags']:
tags = create_tags(validated_project_data['tags'])

saved = project.save()

if not saved:
Expand Down Expand Up @@ -491,7 +498,7 @@ def patch(self, project_id):
current_user_roles = get_jwt_claims()['roles']

project_schema = ProjectSchema(
only=("name", "description", "organisation", "project_type"), partial=True)
only=("name", "description", "organisation", "project_type", "tags_add", "tags_remove"), partial=True)

project_data = request.get_json()

Expand Down Expand Up @@ -523,6 +530,15 @@ def patch(self, project_id):
if not is_authorised_project_user(project, current_user_id, 'admin'):
return dict(status='fail', message='unauthorised'), 403

if validate_project_data.get('tags_add'):
add_tags_to_project(
validate_project_data['tags_add'], project)
validate_project_data.pop('tags_add', None)
if validate_project_data.get('tags_remove'):
remove_tags_from_project(
validate_project_data['tags_remove'], project)
validate_project_data.pop('tags_remove', None)

updated = Project.update(project, **validate_project_data)

if not updated:
Expand Down
6 changes: 3 additions & 3 deletions app/controllers/tags.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

from flask_restful import Resource, request
from flask_jwt_extended import jwt_required
from app.schemas.tags import TagSchema
from app.schemas.tags import TagSchema, TagsDetailSchema
from app.models.tags import Tag
from app.helpers.decorators import admin_required

Expand Down Expand Up @@ -62,11 +62,11 @@ class TagsDetailView(Resource):

@jwt_required
def get(self, tag_id):
tag_id_schema = TagSchema()
tag_schema = TagsDetailSchema()

tag = Tag.get_by_id(tag_id)

tags_data = tag_id_schema.dump(tag)
tags_data = tag_schema.dump(tag)

return dict(
status="success",
Expand Down
52 changes: 52 additions & 0 deletions app/helpers/tags.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from app.models.tags import ProjectTag, Tag


def create_tags(tag_names):
"""
Create tags
"""
none_existing_tags = []
existing_tags = []
for tag in tag_names:
tag = tag.strip()
tag_rec = Tag.find_first(name=tag)
if not tag_rec:
none_existing_tags.append(Tag(name=tag))
else:
existing_tags.append(tag_rec)
if none_existing_tags:
Tag.bulk_save(none_existing_tags)

new_tags = [Tag.find_first(name=tag.name)
for tag in none_existing_tags]
if new_tags:
existing_tags.append(new_tags)
return existing_tags


def add_tags_to_project(tag_names, project):
tags = create_tags(tag_names)
project_tags = []
for tag in tags:
project_tag = ProjectTag.find_first(
tag_id=tag.id, project_id=project.id)
if not project_tag:
project_tags.append(ProjectTag(
tag_id=tag.id, project_id=project.id))

if project_tags:
saved_tags = ProjectTag.bulk_save(project_tags)
if not saved_tags:
return False
return True

def remove_tags_from_project(tag_names, project):
for tag in tag_names:
existing_tag = Tag.find_first(name=tag)
if not existing_tag:
continue
project_tag = ProjectTag.find_first(
tag_id=existing_tag.id, project_id=project.id)
if project_tag:
project_tag.delete()
return True
2 changes: 1 addition & 1 deletion app/models/model_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def check_exists(cls, **kwargs):

if result > 0:
return False
return False
return True

@classmethod
def get_by_id(cls, id):
Expand Down
1 change: 1 addition & 0 deletions app/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Project(ModelMixin):
deleted = db.Column(db.Boolean, default=False)
disabled = db.Column(db.Boolean, default=False)
admin_disabled = db.Column(db.Boolean, default=False)
tags = relationship('ProjectTag', back_populates='project')

def is_followed_by(self, user):
return any(follower.user_id == user.id for follower in self.followers)
Expand Down
21 changes: 21 additions & 0 deletions app/models/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,25 @@ class Tag(ModelMixin):
server_default=sa_text("uuid_generate_v4()"))
name = db.Column(db.String)
deleted = db.Column(db.Boolean, default=False)
is_super_tag = db.Column(db.Boolean, default=False)
projects = db.relationship("ProjectTag", back_populates="tag")
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())

def __repr__(self):
return f"<Tag {self.name}>"


class ProjectTag(ModelMixin):
__tablename__ = "project_tag"

id = db.Column(UUID(as_uuid=True), primary_key=True,
server_default=sa_text("uuid_generate_v4()"))
project_id = db.Column(UUID(as_uuid=True), db.ForeignKey("project.id"))
tag_id = db.Column(UUID(as_uuid=True), db.ForeignKey("tag.id"))

date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
project = db.relationship("Project", back_populates="tags")
tag = db.relationship("Tag", back_populates="projects")

def __repr__(self):
return f"<ProjectTag {self.project.name}, {self.tag.name}>"
9 changes: 9 additions & 0 deletions app/schemas/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
from app.models.user import User


class ProjectListSchema(Schema):
id = fields.UUID(dump_only=True)
name = fields.String()
description = fields.String()


class ProjectSchema(Schema):

id = fields.UUID(dump_only=True)
Expand Down Expand Up @@ -32,6 +38,9 @@ class ProjectSchema(Schema):
admin_disabled = fields.Boolean(dump_only=True)
prometheus_url = fields.Method("get_prometheus_url", dump_only=True)
is_following = fields.Method("get_is_following", dump_only=True)
tags = fields.Nested("TagsProjectsSchema", many=True, dump_only=True)
tags_add = fields.List(fields.String, load_only=True)
tags_remove = fields.List(fields.String, load_only=True)

def get_is_following(self, obj):
# Assuming current_user is available in the view context
Expand Down
29 changes: 22 additions & 7 deletions app/schemas/tags.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
from app.schemas.project import ProjectListSchema
from marshmallow import Schema, fields, validate, pre_load


class TagSchema(Schema):

id = fields.UUID(dump_only=True)
name = fields.String(required=True, error_message={
"required": "name is required"},
validate=[
validate.Regexp(
regex=r'^(?!\s*$)', error='name should be a valid string'
),
])
name = fields.String(required=True)
is_super_tag = fields.Boolean()
date_created = fields.Date(dump_only=True)


class TagsProjectsSchema(TagSchema):
name = fields.Method("get_name", dump_only=True)
id = fields.Method("get_id", dump_only=True)
is_super_tag = fields.Method("get_is_super_tag", dump_only=True)

def get_id(self, obj):
return str(obj.tag.id)

def get_name(self, obj):
return obj.tag.name

def get_is_super_tag(self, obj):
return obj.tag.is_super_tag


class TagsDetailSchema(TagSchema):
projects = fields.Nested(ProjectListSchema, many=False, dump_only=True)
38 changes: 38 additions & 0 deletions migrations/versions/403631504272_.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"""empty message
Revision ID: 403631504272
Revises: c7f9222b60b8
Create Date: 2024-07-04 22:38:45.147122
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = '403631504272'
down_revision = 'c7f9222b60b8'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('project_tag',
sa.Column('id', postgresql.UUID(as_uuid=True), server_default=sa.text('uuid_generate_v4()'), nullable=False),
sa.Column('project_id', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('tag_id', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('date_created', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['project_id'], ['project.id'], ),
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
sa.PrimaryKeyConstraint('id')
)
op.add_column('tag', sa.Column('is_super_tag', sa.Boolean(), nullable=True))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('tag', 'is_super_tag')
op.drop_table('project_tag')
# ### end Alembic commands ###

0 comments on commit fa8ed70

Please sign in to comment.