Skip to content

Commit

Permalink
Merge pull request #2212 from auslin-aot/feature/FWF-3490-import-supp…
Browse files Browse the repository at this point in the history
…ort-webapi

FWF-3490: [Featue] Import support webapi
  • Loading branch information
arun-s-aot authored Aug 28, 2024
2 parents 6f348e1 + 41b2baf commit 01ee493
Show file tree
Hide file tree
Showing 19 changed files with 646 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/forms-flow-api-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:

strategy:
matrix:
python-version: [3.12.4]
python-version: [3.12.5]

steps:
- uses: actions/checkout@v2
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Added version to process data
Revision ID: b0ecd447cfa5
Revises: e1d88d2efbcb
Create Date: 2024-08-22 12:04:13.443862
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql


# revision identifiers, used by Alembic.
revision = 'b0ecd447cfa5'
down_revision = 'e1d88d2efbcb'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('process', sa.Column('major_version', sa.Integer(), nullable=False))
op.add_column('process', sa.Column('minor_version', sa.Integer(), nullable=False))
op.alter_column('process', 'process_data',
existing_type=sa.VARCHAR(),
type_=sa.LargeBinary(),
existing_nullable=False,
postgresql_using="process_data::bytea")
op.create_index(op.f('ix_process_major_version'), 'process', ['major_version'], unique=False)
op.create_index(op.f('ix_process_minor_version'), 'process', ['minor_version'], unique=False)
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_index(op.f('ix_process_minor_version'), table_name='process')
op.drop_index(op.f('ix_process_major_version'), table_name='process')
op.alter_column('process', 'process_data',
existing_type=sa.LargeBinary(),
type_=sa.VARCHAR(),
existing_nullable=False,
postgresql_using="process_data::bytea")
op.drop_column('process', 'minor_version')
op.drop_column('process', 'major_version')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions forms-flow-api/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,4 @@ typing_extensions==4.10.0
urllib3==2.2.1
wsproto==1.2.0
zstandard==0.22.0
lxml==5.3.0
1 change: 1 addition & 0 deletions forms-flow-api/requirements/prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ sqlalchemy_utils
markupsafe
PyJWT
redis
lxml
git+https://github.com/AOT-Technologies/forms-flow-ai.git@develop#subdirectory=forms-flow-api-utils
7 changes: 7 additions & 0 deletions forms-flow-api/src/formsflow_api/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ class BusinessErrorCode(ErrorCodeMixin, Enum):
THEME_NOT_FOUND = "The specified theme not exist", HTTPStatus.BAD_REQUEST
THEME_EXIST = "The specified theme already exist", HTTPStatus.BAD_REQUEST
ROLE_MAPPING_FAILED = "Role mapping failed", HTTPStatus.BAD_REQUEST
INVALID_FILE_TYPE = "File format not supported", HTTPStatus.BAD_REQUEST
FILE_NOT_FOUND = "The file not found", HTTPStatus.BAD_REQUEST
FORM_EXISTS = (
"Form validation failed: The Name or Path already exists. They must be unique.",
HTTPStatus.BAD_REQUEST,
)
INVALID_INPUT = "Invalid input parameter", HTTPStatus.BAD_REQUEST

def __new__(cls, message, status_code):
"""Constructor."""
Expand Down
18 changes: 17 additions & 1 deletion forms-flow-api/src/formsflow_api/models/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
validate_sort_order_and_order_by,
)
from formsflow_api_utils.utils.user_context import UserContext, user_context
from sqlalchemy import LargeBinary
from sqlalchemy.dialects.postgresql import ENUM
from sqlalchemy.sql.expression import text

Expand Down Expand Up @@ -43,12 +44,14 @@ class Process(AuditDateTimeMixin, AuditUserMixin, BaseModel, db.Model):
process_type = db.Column(
ENUM(ProcessType, name="ProcessType"), nullable=False, index=True
)
process_data = db.Column(db.String, nullable=False)
process_data = db.Column(LargeBinary, nullable=False)
status = db.Column(ENUM(ProcessStatus, name="ProcessStatus"), nullable=False)
form_process_mapper_id = db.Column(
db.Integer, db.ForeignKey("form_process_mapper.id"), nullable=True
)
tenant = db.Column(db.String(100), nullable=True)
major_version = db.Column(db.Integer, nullable=False, index=True)
minor_version = db.Column(db.Integer, nullable=False, index=True)

@classmethod
@user_context
Expand All @@ -72,6 +75,8 @@ def update(self, process_info: dict):
"modified_by",
"form_process_mapper_id",
"modified",
"major_version",
"minor_version",
],
process_info,
)
Expand Down Expand Up @@ -120,3 +125,14 @@ def find_all_process(
limit = total_count if limit is None else limit
query = query.paginate(page=page_no, per_page=limit, error_out=False)
return query.items, total_count

@classmethod
def get_latest_version(cls, process_name):
"""Get latest version of process."""
query = (
cls.query.filter(cls.name == process_name)
.order_by(cls.major_version.desc(), cls.minor_version.desc())
.first()
)

return query
2 changes: 2 additions & 0 deletions forms-flow-api/src/formsflow_api/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from formsflow_api.resources.form_process_mapper import API as FORM_API
from formsflow_api.resources.formio import API as FORMIO_API
from formsflow_api.resources.groups import API as KEYCLOAK_GROUPS_API
from formsflow_api.resources.import_support import API as IMPORT_API
from formsflow_api.resources.ipaas import API as INTEGRATION_API
from formsflow_api.resources.metrics import API as APPLICATION_METRICS_API
from formsflow_api.resources.process import API as PROCESS_API
Expand Down Expand Up @@ -63,3 +64,4 @@
API.add_namespace(FORM_EMBED_API, path="/embed")
API.add_namespace(INTEGRATION_API, path="/integrations")
API.add_namespace(THEME_CUSTOMIZATION_API, path="/themes")
API.add_namespace(IMPORT_API, path="/import")
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ class ExportById(Resource):
"""Resource to support export by mapper_id."""

@staticmethod
@auth.require
@auth.has_one_of_roles([CREATE_DESIGNS])
@profiletime
@API.response(200, "OK:- Successful request.", model=export_response_model)
@API.response(
Expand Down
46 changes: 46 additions & 0 deletions forms-flow-api/src/formsflow_api/resources/import_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""API endpoints for managing import."""

from http import HTTPStatus

from flask import request
from flask_restx import Namespace, Resource
from formsflow_api_utils.utils import (
CREATE_DESIGNS,
auth,
cors_preflight,
profiletime,
)

from formsflow_api.services import ImportService

API = Namespace("Import", description="Import")


@cors_preflight("POST,OPTIONS")
@API.route("", methods=["POST", "OPTIONS"])
class Import(Resource):
"""Resource to support import."""

@staticmethod
@auth.has_one_of_roles([CREATE_DESIGNS])
@profiletime
@API.response(200, "OK:- Successful request.")
@API.response(
400,
"BAD_REQUEST:- Invalid request.",
)
@API.response(
401,
"UNAUTHORIZED:- Authorization header not provided or an invalid token passed.",
)
@API.response(
403,
"FORBIDDEN:- Authorization will not help.",
)
def post():
"""Import."""
import_service = ImportService()
return (
import_service.import_form_workflow(request),
HTTPStatus.OK,
)
2 changes: 1 addition & 1 deletion forms-flow-api/src/formsflow_api/resources/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
CREATE_FILTERS,
MANAGE_ALL_FILTERS,
PERMISSION_DETAILS,
VIEW_FILTERS,
VIEW_DESIGNS,
VIEW_FILTERS,
auth,
cors_preflight,
profiletime,
Expand Down
6 changes: 6 additions & 0 deletions forms-flow-api/src/formsflow_api/schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
)

from .form_history_logs import FormHistorySchema
from .import_support import (
ImportEditRequestSchema,
ImportRequestSchema,
form_schema,
form_workflow_schema,
)
from .process import (
ProcessDataSchema,
ProcessListRequestSchema,
Expand Down
118 changes: 118 additions & 0 deletions forms-flow-api/src/formsflow_api/schemas/import_support.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""This manages Import Schema."""

from marshmallow import EXCLUDE, Schema, fields

form_workflow_schema = {
"title": "Form Workflow Schema",
"type": "object",
"properties": {
"forms": {
"type": "array",
"properties": {
"formTitle": {"type": "string"},
"formDescription": {"type": "string"},
"anonymous": {"type": "boolean"},
"type": {"type": "string"},
"content": {"type": "object"},
},
"required": [
"formTitle",
"formDescription",
"content",
"anonymous",
"type",
],
},
"workflows": {
"type": "array",
"properties": {
"processKey": {"type": "string"},
"processName": {"type": "string"},
"type": {"type": "string"},
"content": {"type": "string"},
},
"required": ["content", "processKey", "processName", "type"],
},
"authorizations": {
"type": "array",
"properties": {
"APPLICATION": {
"type": "array",
"properties": {
"resourceId": {"type": "string"},
"resourceDetails": {"type": "object"},
"roles": {"type": "array"},
"userName": {"type": "string"},
},
"required": ["resourceId", "resourceDetails", "roles", "userName"],
},
"FORM": {
"type": "array",
"properties": {
"resourceId": {"type": "string"},
"resourceDetails": {"type": "object"},
"roles": {"type": "array"},
"userName": {"type": "string"},
},
"required": ["resourceId", "resourceDetails", "roles", "userName"],
},
"DESIGNER": {
"type": "array",
"properties": {
"resourceId": {"type": "string"},
"resourceDetails": {"type": "object"},
"roles": {"type": "array"},
"userName": {"type": "string"},
},
"required": ["resourceId", "resourceDetails", "roles", "userName"],
},
},
"required": ["APPLICATION", "FORM", "DESIGNER"],
},
},
"required": ["forms", "workflows", "rules", "authorizations"],
}

form_schema = {
"title": "Form Schema",
"type": "object",
"properties": {
"forms": {
"type": "array",
"properties": {
"title": {"type": "string"},
"name": {"type": "string"},
"path": {"type": "boolean"},
"type": {"type": "string"},
"components": {"type": "array"},
},
"required": ["title", "name", "path", "type", "components"],
}
},
"additionalProperties": False,
}


class ImportRequestSchema(Schema):
"""This class manages import request schema."""

class Meta: # pylint: disable=too-few-public-methods
"""Exclude unknown fields in the deserialized output."""

unknown = EXCLUDE

import_type = fields.Str(data_key="importType", required=True)
action = fields.Str(data_key="action", required=True)


class ImportEditRequestSchema(Schema):
"""This class manages import edit request schema."""

class Meta: # pylint: disable=too-few-public-methods
"""Exclude unknown fields in the deserialized output."""

unknown = EXCLUDE

mapper_id = fields.Str(data_key="mapperId", required=True)
forms = fields.Dict(data_key="forms")
workflows = fields.Dict(data_key="workflows")
5 changes: 5 additions & 0 deletions forms-flow-api/src/formsflow_api/schemas/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def load_process_type(self, value): # check & delete this if not needed

def get_process_data(self, obj):
"""This method is to get the process data."""
obj.process_data = obj.process_data.decode("utf-8")
if obj.process_type.value == "LOWCODE":
return json.loads(obj.process_data)
return obj.process_data
Expand Down Expand Up @@ -104,6 +105,8 @@ class Meta: # pylint: disable=too-few-public-methods
data_key="modifiedTo", format="%Y-%m-%dT%H:%M:%S+00:00"
)
sort_order = fields.Str(data_key="sortOrder", required=False)
major_version = fields.Int(data_key="majorVersion")
minor_version = fields.Int(data_key="minorVersion")


class ProcessRequestSchema(Schema):
Expand All @@ -121,6 +124,8 @@ class Meta: # pylint: disable=too-few-public-methods
form_process_mapper_id = fields.Int(
data_key="formProcessMapperId", required=False, allow_none=True
)
major_version = fields.Int(data_key="majorVersion")
minor_version = fields.Int(data_key="minorVersion")

def load(self, data, *args, **kwargs):
"""Load method for deserializing data."""
Expand Down
2 changes: 2 additions & 0 deletions forms-flow-api/src/formsflow_api/services/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from formsflow_api.services.form_embed import CombineFormAndApplicationCreate
from formsflow_api.services.form_history_logs import FormHistoryService
from formsflow_api.services.form_process_mapper import FormProcessMapperService
from formsflow_api.services.import_support import ImportService
from formsflow_api.services.process import ProcessService
from formsflow_api.services.theme import ThemeCustomizationService
from formsflow_api.services.user import UserService
Expand All @@ -31,4 +32,5 @@
"FormHistoryService",
"CombineFormAndApplicationCreate",
"ThemeCustomizationService",
"ImportService",
]
Loading

0 comments on commit 01ee493

Please sign in to comment.