class MetadataImportServiceImpl extends AbstractMetadataService implements MetadataImportService, MetadataImportResolver {

    @Nonnull
    @Override
    @Transactional
    public MetadataImportResult imp( @Nonnull JsonNode jsonNode, @Nonnull MetadataImportParams params )
    {
        final MetadataImportResult result = new MetadataImportResult( params );
        final MetadataImportContext context = new MetadataImportContext( params, result );
        final MetadataExport metadataExport;

        if ( jsonNode instanceof ObjectNode )
        {
            final ObjectNode objectNode = (ObjectNode) jsonNode;
            objectNode.remove( VERSION_INFO_FIELD_NAME );

            if ( !params.isUpdateResourceMappings() )
            {
                objectNode.remove( FHIR_RESOURCE_MAPPINGS_FIELD_NAME );
            }
        }

        final ObjectNode objectNode = JsonNodeFactory.instance.objectNode();

        // nodes needs to be ordered according to their dependencies
        for ( final MetadataExportField field : MetadataExportField.values() )
        {
            final JsonNode node = jsonNode.get( field.getPluralFieldName() );

            if ( node != null )
            {
                objectNode.set( field.getPluralFieldName(), node );
            }
        }

        try
        {
            metadataExport = mapper.readerFor( MetadataExport.class )
                .withAttribute( MetadataImportContext.ATTR_NAME, context ).treeToValue( objectNode, MetadataExport.class );
        }
        catch ( JsonProcessingException e )
        {
            result.add( new MetadataImportMessage( MetadataImportSeverity.ERROR, "Error when processing JSON: " + e.getMessage() ) );

            throw new MetadataImportException( result );
        }

        if ( !result.isAnyError() )
        {
            removeFiltered( metadataExport, params );
            validate( metadataExport, result );
        }

        if ( !result.isAnyError() )
        {
            persist( metadataExport, result );
        }

        if ( result.isAnyError() )
        {
            throw new MetadataImportException( result );
        }

        result.setSuccess( true );
        TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter()
            {
                @Override
                public void afterCommit()
                {
                    cacheManager.getCacheNames().forEach( cacheName -> Objects.requireNonNull( cacheManager.getCache( cacheName ) ).clear() );
                }
            } );

        return result;
    }

    public  MetadataImportServiceImpl( @Nonnull MessageSource messageSource, @Nonnull MappedTrackerProgramRepository trackerProgramRepository,
        @Nonnull ProgramStageRuleRepository programStageRuleRepository,
        @Nonnull FhirResourceMappingRepository fhirResourceMappingRepository,
        @Nonnull List<? extends MetadataValidator<? extends Metadata>> validators,
        @Nonnull List<? extends MetadataRepository<? extends Metadata>> repositories,
        @Nonnull EntityManager entityManager, @Nonnull @Qualifier( "metadataCacheManager" ) CacheManager cacheManager );

}