Skip to content

Commit b60bd4e

Browse files
authored
Merge pull request #51 from jonrmitchell/use-relative-paths-on-save
Update the save() method to set relative paths for resources. Fixes #41.
2 parents 48e73fc + bf356c0 commit b60bd4e

4 files changed

Lines changed: 166 additions & 18 deletions

File tree

src/Datapackages/BaseDatapackage.php

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -183,20 +183,31 @@ public function save($zip_filename)
183183
{
184184
Package::isZipPresent();
185185
$zip = new ZipArchive();
186+
187+
$packageCopy = $this->copy();
188+
186189
$base = tempnam(sys_get_temp_dir(), 'datapackage-zip-');
187190
$files = [
188191
'datapackage.json' => $base.'datapackage.json',
189192
];
190193
$ri = 0;
191-
foreach ($this as $resource) {
194+
foreach ($packageCopy as $resource) {
195+
if ($resource->isRemote()) {
196+
continue;
197+
}
198+
199+
$resourceFiles = [];
192200
$fileNames = $resource->save($base.'resource-'.$ri);
193201
foreach ($fileNames as $fileName) {
194202
$relname = str_replace($base.'resource-'.$ri, '', $fileName);
195203
$files['resource-'.$ri.$relname] = $fileName;
204+
$resourceFiles[] = 'resource-'.$ri.$relname;
196205
}
206+
$resource->descriptor()->path = count($resourceFiles) == 1 ? $resourceFiles[0] : $resourceFiles;
197207
++$ri;
198208
}
199-
$this->saveDescriptor($files['datapackage.json']);
209+
$packageCopy->saveDescriptor($files['datapackage.json']);
210+
200211
register_shutdown_function(function () use ($base) {
201212
Utils::removeDir($base);
202213
});
@@ -210,19 +221,29 @@ public function save($zip_filename)
210221
}
211222
}
212223

224+
/**
225+
* Make a new Datapackage object that is a copy of the current Datapackage. Bypasses validation.
226+
* @return $this
227+
* @throws DatapackageValidationFailedException
228+
*/
229+
protected function copy()
230+
{
231+
return new static($this->descriptor, $this->basePath, true);
232+
}
233+
213234
protected $descriptor;
214235
protected $currentResourcePosition = 0;
215236
protected $basePath;
216237
protected $skipValidations = false;
217238

218-
/**
219-
* called by the resources iterator for each iteration.
220-
*
221-
* @param object $descriptor
222-
*
223-
* @return \frictionlessdata\datapackage\Resources\BaseResource
224-
* @throws \frictionlessdata\datapackage\Exceptions\ResourceValidationFailedException
225-
*/
239+
/**
240+
* called by the resources iterator for each iteration.
241+
*
242+
* @param object $descriptor
243+
*
244+
* @return \frictionlessdata\datapackage\Resources\BaseResource
245+
* @throws \frictionlessdata\datapackage\Exceptions\ResourceValidationFailedException
246+
*/
226247
protected function initResource($descriptor)
227248
{
228249
return Factory::resource($descriptor, $this->basePath, $this->skipValidations);

src/Resources/BaseResource.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ public function read($readOptions = null)
7575
return $rows;
7676
}
7777

78+
/**
79+
* Loads $this->dataStreams based on $this->path() and $this->data
80+
* @return BaseDataStream[]|null
81+
*/
7882
public function dataStreams()
7983
{
8084
if (is_null($this->dataStreams)) {
@@ -121,6 +125,25 @@ public function path()
121125
}
122126
}
123127

128+
/**
129+
* If the resource's $path is local (non-http)
130+
* @return bool
131+
*/
132+
public function isLocal()
133+
{
134+
return !$this->isRemote();
135+
}
136+
137+
/**
138+
* If the resource's $path is remote (http)
139+
* @return bool
140+
*/
141+
public function isRemote()
142+
{
143+
$path = $this->path();
144+
return Utils::isHttpSource(is_array($path) && count($path) > 0 ? $path[0] : $path);
145+
}
146+
124147
public function data()
125148
{
126149
return isset($this->descriptor()->data) ? $this->descriptor()->data : null;

tests/DatapackageTest.php

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,37 @@ public function testHttpSource()
141141
);
142142
}
143143

144+
public function testHttpSourceSaveAndLoad()
145+
{
146+
$package = Mocks\MockFactory::datapackage('mock-http://simple_valid_datapackage_mock_http_data.json');
147+
148+
$filename = tempnam(sys_get_temp_dir(), 'datapackage-php-tests-').'.zip';
149+
//save the datapackage
150+
if (is_file($filename)) {
151+
unlink($filename);
152+
}
153+
$package->save($filename);
154+
155+
//load the new package
156+
$package2 = Mocks\MockFactory::datapackage($filename);
157+
158+
$this->assertDatapackage(
159+
(object) [
160+
'name' => 'datapackage-name',
161+
'resources' => [
162+
(object) [
163+
'name' => 'resource-name',
164+
'path' => ['mock-http://foo.txt', 'mock-http://foo.txt'],
165+
],
166+
],
167+
],
168+
['resource-name' => ['foo', 'foo']],
169+
$package2
170+
);
171+
172+
unlink($filename);
173+
}
174+
144175
public function testMultiDataDatapackage()
145176
{
146177
$out = [];
@@ -476,6 +507,29 @@ public function testCreateEditDatapackageDescriptor()
476507
$zip->close();
477508
unlink($filename);
478509
$tempdir = $tempdir.DIRECTORY_SEPARATOR;
510+
511+
//after saving to disk, the paths are updated
512+
$expectedDatapackageDescriptor = (object) [
513+
'name' => 'my-datapackage-name',
514+
'resources' => [
515+
(object) [
516+
'name' => 'my-default-resource',
517+
'path' => ["resource-0-data-0", "resource-0-data-1"],
518+
],
519+
(object) [
520+
'name' => 'my-renamed-tabular-resource',
521+
'path' => "resource-1.csv",
522+
'profile' => 'tabular-data-resource',
523+
'schema' => (object) [
524+
'fields' => [
525+
(object) ['name' => 'id', 'type' => 'integer'],
526+
(object) ['name' => 'name', 'type' => 'string'],
527+
],
528+
],
529+
],
530+
],
531+
];
532+
479533
$this->assertEquals($expectedDatapackageDescriptor, json_decode(file_get_contents($tempdir.'datapackage.json')));
480534
$this->assertEquals('foo', file_get_contents($tempdir.'resource-0-data-0'));
481535
$this->assertEquals("testing 改善\n", file_get_contents($tempdir.'resource-0-data-1'));
@@ -484,6 +538,43 @@ public function testCreateEditDatapackageDescriptor()
484538
Utils::removeDir($tempdir);
485539
}
486540

541+
public function testSaveAndLoadZip()
542+
{
543+
//generate a csv file
544+
$csv_filepath = tempnam(sys_get_temp_dir(),'example-csv');
545+
546+
//create example csv
547+
file_put_contents($csv_filepath, "name,email\nJohn Doe,john@example.com");
548+
549+
//create a new datapackage object
550+
$package = Package::create(['name' => 'csv-example','profile' => 'tabular-data-package']);
551+
552+
//add a csv file
553+
$package->addResource('example.csv', [
554+
"profile" => "tabular-data-resource",
555+
"schema" => ["fields" => [["name" => "name", "type" => "string"],["name" => "email", "type" => "string"]]],
556+
"path" => $csv_filepath
557+
]);
558+
559+
//save the datapackage
560+
$filename = tempnam(sys_get_temp_dir(), 'datapackage-php-tests-').'.zip';
561+
if (is_file($filename)) {
562+
unlink($filename);
563+
}
564+
$package->save($filename);
565+
566+
//delete example csv
567+
unlink($csv_filepath);
568+
569+
//load the new package
570+
$package2 = Package::load($filename);
571+
572+
//assert you get expected content back out
573+
$this->assertEquals([['name' => 'John Doe', 'email' => 'john@example.com']], $package2->resource('example.csv')->read());
574+
575+
unlink($filename);
576+
}
577+
487578
public function testLoadDatapackageZip()
488579
{
489580
$package = Package::load(dirname(__FILE__).'/fixtures/datapackage_zip.zip');
@@ -626,21 +717,21 @@ public function testCsvDialect()
626717
'CommitteeTypeDesc' => 'ועדה משותפת',
627718
'Email' => null,
628719
'StartDate' => Carbon::__set_state(array(
629-
'date' => '2004-08-12 00:00:00.000000',
630-
'timezone_type' => 3,
631-
'timezone' => 'UTC',
632-
)),
720+
'date' => '2004-08-12 00:00:00.000000',
721+
'timezone_type' => 3,
722+
'timezone' => 'UTC',
723+
)),
633724
'FinishDate' => null,
634725
'AdditionalTypeID' => null,
635726
'AdditionalTypeDesc' => null,
636727
'ParentCommitteeID' => null,
637728
'CommitteeParentName' => null,
638729
'IsCurrent' => true,
639730
'LastUpdatedDate' => Carbon::__set_state(array(
640-
'date' => '2015-03-20 12:02:57.000000',
641-
'timezone_type' => 3,
642-
'timezone' => 'UTC',
643-
)),
731+
'date' => '2015-03-20 12:02:57.000000',
732+
'timezone_type' => 3,
733+
'timezone' => 'UTC',
734+
)),
644735
), $row);
645736
}
646737
++$rowNum;

tests/Mocks/MockDefaultResource.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,17 @@ protected function validateResource()
4141
{
4242
return MockResourceValidator::validate($this->descriptor(), $this->basePath);
4343
}
44+
45+
46+
/**
47+
* @inheritDoc
48+
* @return bool
49+
*/
50+
public function isRemote()
51+
{
52+
$path = $this->path();
53+
$path_to_check = is_array($path) && count($path) > 0 ? $path[0] : $path;
54+
return strpos($path_to_check, 'mock-http://') === 0 || parent::isRemote();
55+
}
56+
4457
}

0 commit comments

Comments
 (0)