Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/mappings/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
from apps.fyle.models import DependentFieldSetting
from fyle_integrations_imports.models import ImportLog
from fyle_integrations_imports.dataclasses import TaskSetting
from apps.sage_intacct.helpers import get_sage_intacct_connection
from apps.sage_intacct.enums import SageIntacctRestConnectionTypeEnum
from fyle_intacct_api.utils import invalidate_sage_intacct_credentials
from fyle_integrations_imports.queues import chain_import_fields_to_fyle
from workers.helpers import RoutingKeyEnum, WorkerActionEnum, publish_to_rabbitmq
from apps.sage_intacct.helpers import get_sage_intacct_connection, validate_rest_api_connection
from apps.mappings.helpers import get_project_billable_field_detail_key, is_project_billable_sync_allowed
from apps.workspaces.models import (
Configuration,
Expand Down Expand Up @@ -306,6 +306,7 @@ def initiate_import_to_fyle(workspace_id: int, run_in_rabbitmq_worker: bool = Fa
:param workspace_id: Workspace Id
:return: None
"""
validate_rest_api_connection(workspace_id=workspace_id)
mapping_settings = MappingSetting.objects.filter(workspace_id=workspace_id, import_to_fyle=True)
configuration = Configuration.objects.get(workspace_id=workspace_id)
dependent_fields = DependentFieldSetting.objects.filter(workspace_id=workspace_id, is_import_enabled=True).first()
Expand Down
27 changes: 27 additions & 0 deletions apps/sage_intacct/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import traceback
from datetime import datetime, timezone

from django.conf import settings
from django_q.models import Schedule

from apps.fyle.models import DependentFieldSetting
Expand Down Expand Up @@ -84,6 +85,7 @@ def check_interval_and_sync_dimension(workspace_id: int, **kwargs) -> bool:
"""
workspace = Workspace.objects.get(pk=workspace_id)
try:
validate_rest_api_connection(workspace_id=workspace_id)
if workspace.destination_synced_at:
time_interval = datetime.now(timezone.utc) - workspace.source_synced_at

Expand Down Expand Up @@ -137,3 +139,28 @@ def sync_dimensions(workspace_id: int, dimensions: list = []) -> None:

workspace.destination_synced_at = datetime.now()
workspace.save(update_fields=['destination_synced_at'])


def validate_rest_api_connection(workspace_id: int) -> None:
"""
Validate REST API connection for orgs and migrated them to rest api
:param workspace_id: Workspace ID
:return: None
"""
if settings.BRAND_ID != 'fyle':
return

migrated_to_rest_api = FeatureConfig.get_feature_config(workspace_id=workspace_id, key='migrated_to_rest_api')
if migrated_to_rest_api:
return

try:
sage_intacct_connection = SageIntacctRestConnector(workspace_id=workspace_id)
sage_intacct_connection.connection.locations.count()
FeatureConfig.objects.filter(workspace_id=workspace_id, migrated_to_rest_api=False).update(
migrated_to_rest_api=True,
updated_at=datetime.now(timezone.utc)
)
sync_dimensions(workspace_id=workspace_id)
except Exception as e:
logger.info('REST API is not working for workspace_id - %s, error - %s', workspace_id, e)
1 change: 1 addition & 0 deletions docker-compose-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ services:
API_URL: ${API_URL}
DATABASE_URL: postgres://postgres:postgres@db:5432/intacct_db
DB_HOST: db
BRAND_ID: fyle
FYLE_BASE_URL: 'https://sample.fyle.tech'
FYLE_CLIENT_ID: 'sample'
FYLE_CLIENT_SECRET: 'sample'
Expand Down
81 changes: 80 additions & 1 deletion tests/test_sageintacct/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
check_interval_and_sync_dimension,
is_dependent_field_import_enabled,
schedule_payment_sync,
validate_rest_api_connection
)
from apps.sage_intacct.exports.helpers import get_source_entity_id
from apps.sage_intacct.exports.journal_entries import construct_journal_entry_payload
from apps.fyle.models import ExpenseGroup
from apps.mappings.models import GeneralMapping, LocationEntityMapping
from apps.workspaces.models import Configuration, FyleCredential, Workspace
from apps.workspaces.models import Configuration, FyleCredential, Workspace, FeatureConfig
from apps.workspaces.tasks import patch_integration_settings


Expand Down Expand Up @@ -194,3 +195,81 @@ def test_construct_journal_entry_payload_with_source_entity(db, mocker, create_j

assert 'baseLocation' in payload
assert payload['baseLocation']['id'] == 'LOC123'


def test_validate_rest_api_connection_already_migrated(db, mocker):
"""
Test validate_rest_api_connection when already migrated to REST API
Should return early without attempting connection
"""
workspace_id = 1

mocker.patch('apps.sage_intacct.helpers.FeatureConfig.get_feature_config', return_value=True)
mock_connector = mocker.patch('apps.sage_intacct.helpers.SageIntacctRestConnector')
mock_sync_dimensions = mocker.patch('apps.sage_intacct.helpers.sync_dimensions')

validate_rest_api_connection(workspace_id)

mock_connector.assert_not_called()
mock_sync_dimensions.assert_not_called()


def test_validate_rest_api_connection_successful_migration(db, mocker):
"""
Test validate_rest_api_connection when REST API connection is successful
Should update FeatureConfig and call sync_dimensions
"""
workspace_id = 1

feature_config = FeatureConfig.objects.get(workspace_id=workspace_id)
feature_config.migrated_to_rest_api = False
feature_config.save()

mocker.patch('apps.sage_intacct.helpers.FeatureConfig.get_feature_config', return_value=False)

mock_connector_instance = mocker.Mock()
mock_connector_instance.connection.locations.count.return_value = 10
mock_connector = mocker.patch('apps.sage_intacct.helpers.SageIntacctRestConnector', return_value=mock_connector_instance)
mock_sync_dimensions = mocker.patch('apps.sage_intacct.helpers.sync_dimensions')

validate_rest_api_connection(workspace_id)

mock_connector.assert_called_once_with(workspace_id=workspace_id)
mock_connector_instance.connection.locations.count.assert_called_once()
mock_sync_dimensions.assert_called_once_with(workspace_id=workspace_id)

feature_config.refresh_from_db()
assert feature_config.migrated_to_rest_api is True


def test_validate_rest_api_connection_failed_connection(db, mocker):
"""
Test validate_rest_api_connection when REST API connection fails
Should log error and not update FeatureConfig
"""
workspace_id = 1

feature_config = FeatureConfig.objects.get(workspace_id=workspace_id)
feature_config.migrated_to_rest_api = False
feature_config.save()

mocker.patch('apps.sage_intacct.helpers.FeatureConfig.get_feature_config', return_value=False)

mock_connector_instance = mocker.Mock()
mock_connector_instance.connection.locations.count.side_effect = Exception('Connection failed')
mock_connector = mocker.patch('apps.sage_intacct.helpers.SageIntacctRestConnector', return_value=mock_connector_instance)
mock_sync_dimensions = mocker.patch('apps.sage_intacct.helpers.sync_dimensions')
logger_mock = mocker.patch('apps.sage_intacct.helpers.logger')

validate_rest_api_connection(workspace_id)

mock_connector.assert_called_once_with(workspace_id=workspace_id)
mock_connector_instance.connection.locations.count.assert_called_once()
mock_sync_dimensions.assert_not_called()
logger_mock.info.assert_called_once()
call_args = logger_mock.info.call_args[0]
assert 'REST API is not working' in call_args[0]
assert str(workspace_id) in str(call_args)

feature_config.refresh_from_db()
assert feature_config.migrated_to_rest_api is False
Loading