def test_ftp_download():
    """Test downloading an Entity that points to a file on an FTP server. """

    # Another test with an external reference. This is because we only need to test FTP download; not upload. Also so we don't have to maintain an FTP server just for this purpose.
    # Make an entity that points to an FTP server file.
    entity = File(parent=project['id'], name='1KB.zip')
    fileHandle = {}
    fileHandle['externalURL'] = 'ftp://speedtest.tele2.net/1KB.zip'
    fileHandle["fileName"] = entity.name
    fileHandle["contentType"] = "application/zip"
    fileHandle["contentMd5"] = '0f343b0931126a20f133d67c2b018a3b'
    fileHandle["contentSize"] = 1024
    fileHandle[
        "concreteType"] = "org.sagebionetworks.repo.model.file.ExternalFileHandle"
    fileHandle = syn.restPOST('/externalFileHandle', json.dumps(fileHandle),
                              syn.fileHandleEndpoint)
    entity.dataFileHandleId = fileHandle['id']
    entity = syn.store(entity)

    # Download the entity and check that MD5 matches expected
    FTPfile = syn.get(entity.id,
                      downloadLocation=os.getcwd(),
                      downloadFile=True)
    assert FTPfile.md5 == utils.md5_for_file(FTPfile.path).hexdigest()
    schedule_for_cleanup(entity)
    os.remove(FTPfile.path)
def test_download_file_entity__correct_local_state(syn):
    mock_cache_path = utils.normalize_path("/i/will/show/you/the/path/yi.txt")
    file_entity = File(parentId="syn123")
    file_entity.dataFileHandleId = 123
    with patch.object(syn.cache, 'get', return_value=mock_cache_path):
        syn._download_file_entity(downloadLocation=None, entity=file_entity, ifcollision="overwrite.local",
                                  submission=None)
        assert mock_cache_path == file_entity.path
        assert os.path.dirname(mock_cache_path) == file_entity.cacheDir
        assert 1 == len(file_entity.files)
        assert os.path.basename(mock_cache_path) == file_entity.files[0]
    def init(self, syn, schedule_for_cleanup):
        self.syn = syn

        # create external file handles for https://www.synapse.org/images/logo.svg,
        project = Project(str(uuid.uuid4()))
        project = self.syn.store(project)
        schedule_for_cleanup(project)

        # create file entity from externalFileHandle
        external_file_handle_request_1 = {
            "concreteType": "org.sagebionetworks.repo.model.file.ExternalFileHandle",
            "externalURL": "https://www.synapse.org/images/logo.svg",
            "fileName": "testExternalFileHandle"
        }
        external_response_1 = self.syn.restPOST('/externalFileHandle', body=json.dumps(external_file_handle_request_1),
                                                endpoint=self.syn.fileHandleEndpoint)
        self.file_handle_id_1 = external_response_1['id']
        test_entity_1 = File(parent=project)
        test_entity_1.dataFileHandleId = self.file_handle_id_1
        test_entity_1 = self.syn.store(test_entity_1)
        self.obj_id_1 = str(test_entity_1['id'][3:])
def _copyFile(syn, entity, destinationId, version=None, update=False, setProvenance="traceback"):
    """
    Copies most recent version of a file to a specified synapse ID.

    :param entity:          A synapse ID of a File entity

    :param destinationId:   Synapse ID of a folder/project that the file wants to be copied to

    :param version:         Can specify version of a file. 
                            Default to None

    :param update:          Can choose to update files that have the same name 
                            Default to False
    
    :param setProvenance:   Has three values to set the provenance of the copied entity:
                                traceback: Sets to the source entity
                                existing: Sets to source entity's original provenance (if it exists)
                                None: No provenance is set
    """
    ent = syn.get(entity, downloadFile=False, version=version, followLink=False)
    #CHECK: If File is in the same parent directory (throw an error) (Can choose to update files)
    if not update:
        search = syn.query('select name from entity where parentId =="%s"'%destinationId)
        for i in search['results']:
            if i['entity.name'] == ent.name:
                raise ValueError('An item named "%s" already exists in this location. File could not be copied'%ent.name)
    profile = syn.getUserProfile()
    # get provenance earlier to prevent errors from being called in the end
    # If traceback, set activity to old entity
    if setProvenance == "traceback":
        act = Activity("Copied file", used=ent)
    # if existing, check if provenance exists
    elif setProvenance == "existing":
        try:
            act = syn.getProvenance(ent.id)
        except SynapseHTTPError as e:
            # Should catch the 404
            act = None
    elif setProvenance is None or setProvenance.lower() == 'none':
        act = None
    else:
        raise ValueError('setProvenance must be one of None, existing, or traceback')
    #Grab file handle createdBy annotation to see the user that created fileHandle
    fileHandleList = syn.restGET('/entity/%s/version/%s/filehandles'%(ent.id,ent.versionNumber))
    #NOTE: May not always be the first index (need to filter to make sure not PreviewFileHandle)
    #Loop through to check which dataFileHandles match and return createdBy
    # Look at convenience function
    for fileHandle in fileHandleList['list']:
        if fileHandle['id'] == ent.dataFileHandleId:
            createdBy = fileHandle['createdBy']
            break
    else:
        createdBy = None
    #CHECK: If the user created the file, copy the file by using fileHandleId else hard copy
    if profile.ownerId == createdBy:
        new_ent = File(name=ent.name, parentId=destinationId)
        new_ent.dataFileHandleId = ent.dataFileHandleId
    else:
        #CHECK: If the synapse entity is an external URL, change path and store
        if ent.externalURL is None: #and ent.path == None:
            #####If you have never downloaded the file before, the path is None
            store = True
            #This needs to be here, because if the file has never been downloaded before
            #there wont be a ent.path
            ent = syn.get(entity,downloadFile=store,version=version)
            path = ent.path
        else:
            store = False
            ent = syn.get(entity,downloadFile=store,version=version)
            path = ent.externalURL

        new_ent = File(path, name=ent.name, parentId=destinationId, synapseStore=store)
    #Set annotations here
    new_ent.annotations = ent.annotations
    #Store provenance if act is not None
    if act is not None:
        new_ent = syn.store(new_ent, activity=act)
    else:
        new_ent = syn.store(new_ent)
    #Leave this return statement for test
    return new_ent['id']