Esempio n. 1
0
async def create_action(db_session, form_directory, ax_form, action):
    """ Creates new AxAction """
    err = "marketplace_schema -> create_action"
    with ax_model.try_catch(db_session, err) as db_session:
        code = None
        action_id = str(action['guid'])
        if action['db_name']:
            action_id = action['db_name']
        action_py_path = form_directory / f'action_{action_id}.py'

        if os.path.exists(action_py_path):
            with open(action_py_path, 'r', encoding="utf-8") as action_file:
                code = action_file.read()

        new_action = AxAction()
        new_action.guid = ax_misc.guid_or_none(action['guid'])
        new_action.name = action['name']
        new_action.db_name = action['db_name']
        new_action.form_guid = ax_form.guid
        new_action.from_state_guid = ax_misc.guid_or_none(
            action['from_state_guid'])
        new_action.to_state_guid = ax_misc.guid_or_none(
            action['to_state_guid'])
        new_action.code = code
        new_action.confirm_text = action['confirm_text']
        new_action.close_modal = action['close_modal']
        new_action.icon = action['icon']
        new_action.radius = float(action['radius'] or 0)
        db_session.add(new_action)

        for role_guid in action['roles']:
            await create_action2role(db_session=db_session,
                                     action_guid=action['guid'],
                                     role_guid=role_guid)
Esempio n. 2
0
async def create_form(db_session, form):
    """ Create or update AxForm """
    err = "marketplace_schema -> create_form"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_form = db_session.query(AxForm).filter(
            AxForm.guid == ax_misc.guid_or_none(form['guid'])).first()
        new_form = None
        form_is_new = False

        if existing_form:
            existing_form.name = form['name']
            existing_form.tom_label = form['tom_label']
            existing_form.icon = form['icon']
        else:
            form_is_new = True
            new_form = AxForm()
            new_form.guid = ax_misc.guid_or_none(form['guid'])
            new_form.name = form['name']
            new_form.db_name = form['db_name']
            new_form.position = int(form['position'] or 0)
            new_form.parent = ax_misc.guid_or_none(form['parent'])
            new_form.tom_label = form['tom_label']
            new_form.icon = form['icon']
            db_session.add(new_form)

            await ax_dialects.dialect.create_data_table(
                db_session=db_session, db_name=form['db_name'])

            db_session.commit()

        ret_form = existing_form or new_form
        return form_is_new, ret_form
Esempio n. 3
0
async def create_role(db_session, ax_form, role, form_path):
    """ Create or update AxRole """
    err = "marketplace_schema -> create_role"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_role = db_session.query(AxRole).filter(
            AxRole.guid == ax_misc.guid_or_none(role['guid'])).first()
        new_role = None

        code = None
        role_db_name = re.sub(r'[\W_]+', '', role['name'])
        role_py_path = form_path / f'role_{role_db_name}.py'
        if os.path.exists(role_py_path):
            with open(role_py_path, 'r', encoding="utf-8") as role_file:
                code = role_file.read()

        if existing_role:
            existing_role.name = role['name']
            existing_role.icon = role.get('icon', None)
        else:
            new_role = AxRole()
            new_role.guid = ax_misc.guid_or_none(role['guid'])
            new_role.name = role['name']
            new_role.form_guid = ax_form.guid
            new_role.icon = role.get('icon', None)
            new_role.is_dynamic = role.get('is_dynamic', False)
            new_role.code = code
            db_session.add(new_role)
Esempio n. 4
0
async def create_state(db_session, ax_form, state):
    """ Create or update AxState """
    err = "marketplace_schema -> create_state"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_state = db_session.query(AxState).filter(
            AxState.guid == ax_misc.guid_or_none(state['guid'])).first()
        new_state = None

        if existing_state:
            existing_state.name = state['name']
            existing_state.x = float(state['x'] or 0)
            existing_state.y = float(state['y'] or 0)
        else:
            new_state = AxState()
            new_state.guid = ax_misc.guid_or_none(state['guid'])
            new_state.name = state['name']
            new_state.form_guid = ax_form.guid
            new_state.is_start = state['is_start']
            new_state.is_deleted = state['is_deleted']
            new_state.is_all = state['is_all']
            new_state.x = float(state['x'] or 0)
            new_state.y = float(state['y'] or 0)
            db_session.add(new_state)

        for role_guid in state['roles']:
            await create_state2role(db_session=db_session,
                                    state_guid=state['guid'],
                                    role_guid=role_guid)
Esempio n. 5
0
    async def mutate(self, info, **args):  # pylint: disable=missing-docstring
        err = 'Error in gql mutation - fields_schema -> CreateMessage.'
        with ax_model.try_catch(info.context['session'], err) as db_session:
            thread_guid = args.get('thread_guid')
            text = args.get('text')
            data_json = args.get('data_json')
            current_user = info.context['user']
            user_guid = current_user.get('user_id', None)
            user_email = current_user.get('email', None)
            parent = args.get('parent')

            data = None
            if data_json:
                try:
                    data = json.loads(data_json)
                except ValueError:
                    logger.exception(
                        'Error decoding data_json on creating AxMessage')

            thread = db_session.query(AxMessageThread).filter(
                AxMessageThread.guid == uuid.UUID(args.get('thread_guid'))
            ).first()
            if not thread:
                thread = AxMessageThread()
                thread.guid = uuid.UUID(thread_guid)
                db_session.add(thread)
                db_session.flush()

            new_message = AxMessage()
            new_message.author_guid = ax_misc.guid_or_none(user_guid)
            new_message.author_email = user_email
            new_message.text = text
            new_message.data_json = data
            new_message.thread_guid = thread.guid
            new_message.parent = ax_misc.guid_or_none(parent)

            db_session.add(new_message)
            db_session.flush()

            created_message = db_session.query(AxMessage).filter(
                AxMessage.guid == new_message.guid
            ).options(joinedload(AxMessage.author)).first()

            # joinedload(Event.user)

            db_session.expunge(created_message)
            db_session.expunge(created_message.author)
            ax_pubsub.publisher.publish(
                aiopubsub.Key('thread_message'), {
                    "thread_guid": str(created_message.thread_guid),
                    "ax_message": created_message
                })

            return CreateMessage(message=new_message, ok=True)
Esempio n. 6
0
async def create_role_field_perm(db_session, ax_form, perm):
    """ AxRoleFieldPermission creaes new """
    err = "marketplace_schema -> create_role_field_perm"
    with ax_model.try_catch(db_session, err) as db_session:
        new_perm = AxRoleFieldPermission()
        new_perm.role_guid = ax_misc.guid_or_none(str(perm['role_guid']))
        new_perm.state_guid = ax_misc.guid_or_none(str(perm['state_guid']))
        new_perm.field_guid = ax_misc.guid_or_none(str(perm['field_guid']))
        new_perm.form_guid = ax_form.guid
        new_perm.read = perm['read']
        new_perm.edit = perm['edit']
        db_session.add(new_perm)
Esempio n. 7
0
async def create_metric(db_session, metric):
    """ Create AxMetric if not exists """
    err = "marketplace_schema -> create_metric"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_metric = db_session.query(AxMetric).filter(
            AxMetric.guid == ax_misc.guid_or_none(metric['guid'])).first()

        if not existing_metric:
            new_metric = AxMetric()
            new_metric.guid = ax_misc.guid_or_none(metric['guid'])
            new_metric.key = metric['key']
            new_metric.value = metric['value']
            db_session.add(new_metric)
Esempio n. 8
0
async def create_action2role(db_session, action_guid, role_guid):
    """ Create or update AxAction2Role """
    err = "marketplace_schema -> create_action2role"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_a2r = db_session.query(AxAction2Role).filter(
            AxAction2Role.action_guid == ax_misc.guid_or_none(
                action_guid)).filter(AxAction2Role.role_guid ==
                                     ax_misc.guid_or_none(role_guid)).first()

        if not existing_a2r:
            new_a2r = AxAction2Role()
            new_a2r.action_guid = ax_misc.guid_or_none(action_guid)
            new_a2r.role_guid = ax_misc.guid_or_none(role_guid)
            db_session.add(new_a2r)
Esempio n. 9
0
async def create_tom_references(db_session, ax_form, form_directory):
    """ # Ax1tomReference[] - only insert if not exists """
    err = "marketplace_schema -> create_tom_references"
    with ax_model.try_catch(db_session, err) as db_session:
        tom_yaml_path = form_directory / 'tom_reference.yaml'
        if not os.path.exists(tom_yaml_path):
            return False

        with open(tom_yaml_path, 'r', encoding="utf-8") as tom_yaml_file:
            tom_yaml = yaml.load(tom_yaml_file)
            for ref in tom_yaml:
                new_ref = Ax1tomReference()
                new_ref.form_guid = ax_form.guid
                new_ref.field_guid = ax_misc.guid_or_none(ref['field_guid'])
                new_ref.row_guid = ax_misc.guid_or_none(ref['row_guid'])
                new_ref.child_guid = ax_misc.guid_or_none(ref['child_guid'])
                new_ref.field_guid = ax_misc.guid_or_none(ref['field_guid'])
                db_session.add(new_ref)
Esempio n. 10
0
async def create_folder(db_session, folder):
    """ Create or update AxForm that is folder """
    err = "marketplace_schema -> create_folder"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_folder = db_session.query(AxForm).filter(
            AxForm.guid == ax_misc.guid_or_none(folder['guid'])).filter(
                AxForm.is_folder.is_(True)).first()

        if existing_folder:
            existing_folder.name = folder['name']
        else:
            new_folder = AxForm()
            new_folder.guid = ax_misc.guid_or_none(folder['guid'])
            new_folder.name = folder['name']
            new_folder.is_folder = True
            new_folder.position = int(folder['position'] or 0)
            new_folder.parent = ax_misc.guid_or_none(folder['parent'])
            db_session.add(new_folder)
Esempio n. 11
0
 async def resolve_ax_role(self, info, guid, update_time=None):
     """Get AxRole with code"""
     del update_time
     err = 'Error in GQL query - resolve_ax_role.'
     with ax_model.try_catch(info.context['session'], err, no_commit=True):
         query = Role.get_query(info)  # SQLAlchemy query
         role_guid = ax_misc.guid_or_none(guid)
         ret_role = query.filter(AxRole.guid == role_guid).first()
         return ret_role
Esempio n. 12
0
async def create_field(db_session, ax_form, field):
    """ Create or update AxField """
    err = "marketplace_schema -> create_field"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_field = db_session.query(AxField).filter(
            AxField.guid == ax_misc.guid_or_none(field['guid'])).first()

        if existing_field:
            existing_field.name = field['name']
            existing_field.options_json = field['options_json']
            existing_field.private_options_json = field['private_options_json']
            existing_field.is_required = field['is_required']
            existing_field.is_whole_row = field['is_whole_row']
            existing_field.parent = ax_misc.guid_or_none(field['parent'])
            existing_field.position = int(field['position'] or 0)
        else:
            new_field = AxField()
            new_field.guid = ax_misc.guid_or_none(field['guid'])
            new_field.form_guid = ax_form.guid
            new_field.name = field['name']
            new_field.db_name = field['db_name']
            new_field.options_json = field['options_json']
            new_field.private_options_json = field['private_options_json']
            new_field.field_type_tag = field['field_type_tag']
            new_field.is_tab = field['is_tab']
            new_field.is_required = field['is_required']
            new_field.is_whole_row = field['is_whole_row']
            new_field.parent = ax_misc.guid_or_none(field['parent'])
            new_field.position = int(field['position'] or 0)
            db_session.add(new_field)

            # Field is not tab
            if field['field_type_tag']:
                ax_field_type = db_session.query(AxFieldType).filter(
                    AxFieldType.tag == field['field_type_tag']).first()

                if not ax_field_type.is_virtual:
                    await ax_dialects.dialect.add_column(
                        db_session=db_session,
                        table=ax_form.db_name,
                        db_name=field['db_name'],
                        type_name=ax_field_type.value_type)

                    db_session.commit()
Esempio n. 13
0
async def create_grid(db_session, form_directory, ax_form, grid):
    """ Create or update AxGrid """
    err = "marketplace_schema -> create_grid"
    with ax_model.try_catch(db_session, err) as db_session:
        code = None
        existing_grid = db_session.query(AxGrid).filter(
            AxGrid.guid == ax_misc.guid_or_none(grid['guid'])).first()
        new_grid = None

        grid_py_path = form_directory / f'grid_{grid["db_name"]}.py'
        if os.path.exists(grid_py_path):
            with open(grid_py_path, 'r', encoding="utf-8") as grid_file:
                code = grid_file.read()

        if existing_grid:
            existing_grid.name = grid['name']
            existing_grid.db_name = grid['db_name']
            existing_grid.position = int(grid['position'] or 0)
            existing_grid.options_json = grid['options_json']
            existing_grid.code = code
            existing_grid.is_default_view = grid['is_default_view']
        else:
            new_grid = AxGrid()
            new_grid.guid = ax_misc.guid_or_none(grid['guid'])
            new_grid.name = grid['name']
            new_grid.db_name = grid['db_name']
            new_grid.position = int(grid['position'] or 0)
            new_grid.options_json = grid['options_json']
            new_grid.code = code
            new_grid.form_guid = ax_form.guid
            new_grid.is_default_view = grid['is_default_view']
            db_session.add(new_grid)

    for column in grid['columns']:
        await create_column(db_session=db_session,
                            grid_guid=grid['guid'],
                            column=column)

    return existing_grid or new_grid
Esempio n. 14
0
async def create_column(db_session, grid_guid, column):
    """ Create or update AxColumn """
    err = "marketplace_schema -> create_column"
    with ax_model.try_catch(db_session, err) as db_session:
        existing_column = db_session.query(AxColumn).filter(
            AxColumn.guid == ax_misc.guid_or_none(column['guid'])).first()
        new_column = None

        if existing_column:
            existing_column.position = int(column['position'] or 0)
            existing_column.options_json = column['options_json']
            existing_column.column_type = column['column_type']
            existing_column.aggregation_type = column['aggregation_type']
        else:
            new_column = AxColumn()
            new_column.guid = ax_misc.guid_or_none(column['guid'])
            new_column.position = int(column['position'] or 0)
            new_column.options_json = column['options_json']
            new_column.field_guid = ax_misc.guid_or_none(column['field_guid'])
            new_column.grid_guid = ax_misc.guid_or_none(grid_guid)
            new_column.column_type = column['column_type']
            new_column.aggregation_type = column['aggregation_type']
            db_session.add(new_column)
Esempio n. 15
0
async def create_page(db_session, page, package_directory, ax_root_guid,
                      app_root_guid):
    """ Create AxPage from application package data """
    err = "marketplace_schema -> create_page"
    with ax_model.try_catch(db_session, err) as db_session:
        code = None
        page_name = str(page['guid'])
        if page['db_name']:
            page_name = page['db_name']
        page_md_path = package_directory / '_pages' / f'page_{page_name}.md'
        if os.path.exists(page_md_path):
            with open(page_md_path, 'r', encoding="utf-8") as page_file:
                code = page_file.read()

        existing_page = db_session.query(AxPage).filter(
            AxPage.guid == ax_misc.guid_or_none(page['guid'])).first()

        if existing_page:
            existing_page.name = page['name']
            existing_page.db_name = page['db_name']
            existing_page.position = int(page['position'] or 0)
            existing_page.parent = ax_misc.guid_or_none(page['parent'])
            existing_page.code = code
        else:
            cur_parent = page['parent']
            if page['guid'] == app_root_guid:
                cur_parent = ax_root_guid

            new_page = AxPage()
            new_page.guid = ax_misc.guid_or_none(page['guid'])
            new_page.name = page['name']
            new_page.db_name = page['db_name']
            new_page.position = int(page['position'] or 0)
            new_page.parent = ax_misc.guid_or_none(cur_parent)
            new_page.code = code
            db_session.add(new_page)
Esempio n. 16
0
async def check_existing_form(db_session, package_directory, form_db_name):
    """ Checks if AxForm with such db_name already exists. Aborts installation
        if found """
    form_directory = package_directory / form_db_name
    yaml_path = form_directory / f'{form_db_name}.yaml'
    form_guid = None
    with open(yaml_path, 'r', encoding="utf-8") as form_yaml_file:
        form_yaml = yaml.load(form_yaml_file)
        form_guid = form_yaml["AxForm"]["guid"]

    existing_form = db_session.query(AxForm).filter(
        AxForm.db_name == form_db_name).first()

    if existing_form.guid != ax_misc.guid_or_none(form_guid):
        err = (f'Can not create AxForm [{form_db_name}]. Form with same '
               f'db_name already exists.\nInstallation is aborted ☠️')
        logger.exception(err)
        await terminal_log(f'\n\n\n{err}')
        raise Exception(err)
Esempio n. 17
0
    async def create_message(thread_guid, text):
        thread = None
        if thread_guid and thread_guid != 'None':
            thread = db_session.query(AxMessageThread).filter(
                AxMessageThread.guid == uuid.UUID(str(thread_guid))).first()
        if not thread:
            thread = AxMessageThread()
            thread.guid = uuid.UUID(str(thread_guid))
            db_session.add(thread)
            db_session.flush()

        new_message = AxMessage()
        new_message.author_guid = ax_misc.guid_or_none(current_user_guid)
        new_message.author_email = current_user_email
        new_message.text = text
        new_message.data_json = None
        new_message.thread_guid = thread.guid
        db_session.add(new_message)
        db_session.flush()
    async def mutate(self, info, **args):  # pylint: disable=missing-docstring
        # avatar_tmp = args.get('avatar_tmp')
        guid = args.get('guid')

        err = 'Error in gql mutation - users_schema -> ChangeUserPassword.'
        with ax_model.try_catch(info.context['session'], err) as db_session:
            ax_user = db_session.query(AxUser).filter(
                AxUser.guid == uuid.UUID(guid)).first()

            current_user = info.context['user']
            user_guid = current_user.get('user_id',
                                         None) if current_user else None
            if not ax_user or ax_user.guid != ax_misc.guid_or_none(user_guid):
                return None

            ax_user.password = pbkdf2_sha256.hash(args.get('password'))
            ax_user.password_must_change = False
            db_session.flush()

            return ChangeUserPassword(user=ax_user, ok=True)
Esempio n. 19
0
async def retrieve_user(request, payload, *args, **kwargs):
    """ Get user info. This info is transfered into routes with inject_user """
    del request, args, kwargs
    if payload:
        user_id = payload.get('user_id') or None

        if not ax_misc.string_is_guid(user_id):
            return None

        email = await ax_cache.cache.get(f'user_email_{user_id}')

        if not email:
            msg = "Auth -> retrieve_user"
            with ax_model.scoped_session(msg) as db_session:
                user = db_session.query(AxUser).filter(
                    AxUser.guid == ax_misc.guid_or_none(user_id)
                ).first()

                if user is not None:
                    # raise exceptions.AuthenticationFailed("User not found.")
                    await check_if_admin(
                        user_guid=user_id, db_session=db_session)
                    await write_perm_cache(
                        db_session=db_session, user_guid=user_id)
                    await write_info_cache(user)

        email = await ax_cache.cache.get(f'user_email_{user_id}')
        short_name = await ax_cache.cache.get(f'user_short_name_{user_id}')
        is_admin = await ax_cache.cache.get(f'user_is_admin_{user_id}')

        user = {
            "user_id": str(user_id),
            "is_admin": is_admin,
            "short_name": short_name,
            "email": email
        }
        return user
    else:
        return None
Esempio n. 20
0
async def insert_data(db_session, ax_form, form_directory):
    """ Inserts data from package """
    err = "marketplace_schema -> insert_data"
    with ax_model.try_catch(db_session, err) as db_session:
        data_yaml_path = form_directory / 'data.yaml'
        if not os.path.exists(data_yaml_path):
            return False

        with open(data_yaml_path, 'r', encoding="utf-8") as data_yaml_file:
            data_yaml = yaml.load(data_yaml_file)
            for row in data_yaml:
                tobe_form = db_session.query(AxForm).filter(
                    AxForm.guid == ax_form.guid).first()

                for field in tobe_form.db_fields:
                    if field.db_name in row.keys():
                        field.value = clean_yaml_value(row=row, field=field)
                        field.needs_sql_update = True

                await ax_dialects.dialect.insert(db_session=db_session,
                                                 form=tobe_form,
                                                 to_state_name=row["axState"],
                                                 new_guid=ax_misc.guid_or_none(
                                                     row['guid']))
Esempio n. 21
0
    async def mutate(self, info, **args):  # pylint: disable=missing-docstring
        err = "marketplace_schema -> CreateMarketplaceApplication"
        code_name = args.get('db_name')
        root_page_db_name = args.get('root_page')
        unique_guid = str(uuid.uuid4())
        include_data = args.get('include_data')
        create_readme = args.get('create_readme')

        with ax_model.try_catch(info.context['session'], err) as db_session:
            # Create tmp folder for new package
            archive_directory = ax_misc.tmp_root_dir / unique_guid
            package_directory = archive_directory / 'archive_me' / code_name
            dir_to_zip = archive_directory / 'archive_me'
            os.makedirs(package_directory)

            # Get AxForms and folders to dump
            root_folder = db_session.query(AxForm).filter(
                AxForm.guid == ax_misc.guid_or_none(
                    str(args.get('folder_guid')))).first()
            if not root_folder:
                raise Exception('Cant find app root folder')

            # AxPage[]
            # Get AxPage if db_name is provided
            pages_data = []
            root_page = None
            root_page_guid = None
            if root_page_db_name:
                root_page = db_session.query(AxPage).filter(
                    AxPage.db_name == root_page_db_name).first()

                if not root_page:
                    raise Exception('Cant find app root page')

                # Сreate readme.md
                if create_readme:
                    await do_create_readme(root_page=root_page,
                                           package_directory=package_directory)

                root_page_guid = str(root_page.guid)
                pages_data = await dump_pages(
                    db_session=db_session,
                    package_directory=package_directory,
                    root_page=root_page)

            all_forms = await get_child_forms(db_session=db_session,
                                              form=root_folder)
            app_forms, app_folders = await get_folders_and_forms(all_forms)

            # Create app.yaml
            app_yaml_path = package_directory / 'ax_app.yaml'
            with open(app_yaml_path, 'w', encoding="utf-8") as yaml_file:
                app_info = {
                    "Code name": code_name,
                    "Root folder": args.get('folder_guid'),
                    "Forms": await get_forms_db_names(app_forms),
                    "Folders": await get_folders_dump(app_folders),
                    "Root page": root_page_guid,
                    "Pages": pages_data,
                    "Ax version": os.environ.get('AX_VERSION')
                }
                yaml.dump(app_info, yaml_file)

            for form in app_forms:
                await dump_form(db_session=db_session,
                                package_directory=package_directory,
                                form=form,
                                include_data=include_data)

            # version_str = str(app_version).replace(',', '-')
            # app_file_name = f'{code_name}-{version_str}.zip'
            app_file_name = f'{code_name}.zip'
            output_file_path = archive_directory / app_file_name

            await ax_misc.zip_folder(output_filename=output_file_path,
                                     source_dir=dir_to_zip)

            shutil.rmtree(dir_to_zip)

            url = f'/api/file/null/null/null/{unique_guid}/{app_file_name}'
            return CreateMarketplaceApplication(download_url=url, ok=True)
    async def mutate(self, info, **args):  # pylint: disable=missing-docstring
        import backend.schema as ax_schema
        err = 'Error in gql mutation - form_schema -> CreateTab'
        with ax_model.try_catch(info.context['session'], err) as db_session:
            current_user = info.context['user']

            form_guid = args.get('form_guid')
            name = args.get('name')
            tag = args.get('tag')
            position = args.get('position')
            positions = args.get('positions')
            parent = args.get('parent')

            cur_name = None
            cur_db_name = None
            name_is_checked = False
            cur_num = 1

            ax_field_type = db_session.query(AxFieldType).filter(
                AxFieldType.tag == tag).first()

            ax_form = db_session.query(AxForm).filter(
                AxForm.guid == uuid.UUID(form_guid)).first()

            # If db table already have {db_name} column -> add digit to db_name
            while name_is_checked is False:
                error_flag = False
                if cur_num > 1:
                    cur_name = name + " " + str(cur_num)
                    cur_db_name = ax_field_type.default_db_name + str(cur_num)
                else:
                    cur_name = name
                    cur_db_name = ax_field_type.default_db_name

                for field in ax_form.fields:
                    if field.name == cur_name or field.db_name == cur_db_name:
                        error_flag = True

                if error_flag is True:
                    cur_num = cur_num + 1
                else:
                    name_is_checked = True
                    break

            if not positions:
                positions = []
            if not parent:
                for fld in ax_form.fields:
                    if fld.is_tab and not parent:
                        parent = fld.guid
            if not position:
                flds_num = 0
                for fld in ax_form.fields:
                    if str(fld.parent) == str(parent):
                        flds_num += 1
                position = flds_num

            ax_field = AxField()
            ax_field.name = cur_name
            ax_field.db_name = cur_db_name
            ax_field.form_guid = ax_form.guid
            ax_field.value_type = ax_field_type.value_type
            ax_field.field_type_tag = ax_field_type.tag
            ax_field.options_json = "{}"
            ax_field.position = position
            ax_field.parent = ax_misc.guid_or_none(parent)

            if ax_field_type.is_always_whole_row:
                ax_field.is_whole_row = True

            db_session.add(ax_field)

            if not ax_field_type.is_virtual:
                await ax_dialects.dialect.add_column(
                    db_session=db_session,
                    table=ax_form.db_name,
                    db_name=ax_field.db_name,
                    type_name=ax_field_type.value_type)

            db_session.flush()

            # Update positions of all fields that are lower then created field
            for field in ax_form.fields:
                for pos in positions:
                    if field.guid == uuid.UUID(pos.guid):
                        current_parent = None
                        if pos.parent != '#':
                            current_parent = uuid.UUID(pos.parent)
                        field.position = pos.position
                        field.parent = current_parent

            # Run after_create if needed
            if ax_field.field_type.is_backend_available:
                tag = ax_field.field_type_tag
                field_py = globals().get(f'AxField{tag}', None)
                if field_py and hasattr(field_py, "after_field_create"):
                    method_to_call = getattr(field_py, "after_field_create")
                    await method_to_call(db_session=db_session,
                                         field=ax_field,
                                         before_form=ax_form,
                                         tobe_form=ax_form,
                                         action=None,
                                         current_user=current_user)

            db_session.commit()
            ax_schema.init_schema(db_session)  # re-create GQL schema
            roles = ax_form.roles

            ok = True
            return CreateField(field=ax_field, roles=roles, ok=ok)