Skip to content

Commit

Permalink
interfaces for working with resources - adding / deleting (#12)
Browse files Browse the repository at this point in the history
* updated to tableschema 0.1.5 + modify tests accordingly
* close #9 - added interfaces for working with resources, adding / deleting
  • Loading branch information
OriHoch authored May 11, 2017
1 parent e62a464 commit ca4fdd6
Show file tree
Hide file tree
Showing 19 changed files with 349 additions and 36 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use frictionlessdata\datapackage;
// get a datapackage object
$datapackage = datapackage\Factory::datapackage("tests/fixtures/multi_data_datapackage.json");

// iterate over the data
// iterate over the data - it will raise exceptions in case of any problems
foreach ($datapackage as $resource) {
print("-- ".$resource->name()." --");
$i = 0;
Expand All @@ -45,6 +45,23 @@ if (count($validationErrors) == 0) {
} else {
print(datapackage\Validators\DatapackageValidationError::getErrorMessages($validationErrors));
}

// get and manipulate resources
$resources = $datapackage->resources();
$resources["resource-name"]->name() == "resource-name"
$resources["another-resource-name"] // BaseResource based object (e.g. DefaultResource / TabularResource)

// get a single resource by name
$datapackage->resource("resource-name")

// delete a resource by name - will raise exception in case of validation failure for the new descriptor
$datapackage->deleteResource("resource-name");

// add a resource - will raise exception in case of validation error for the new descriptor
$resource = Factory::resource((object)[
"name" => "new-resource", "data" => ["tests/fixtures/foo.txt", "tests/fixtures/baz.txt"]
])
$datapackage->addResource($resource);
```


Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"require": {
"php": ">=5.4",
"justinrainbow/json-schema": "^5.2",
"frictionlessdata/tableschema": "^0.1.3"
"frictionlessdata/tableschema": "^0.1.5"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35",
Expand Down
7 changes: 5 additions & 2 deletions src/DataStreams/TabularDataStream.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
use frictionlessdata\tableschema\DataSources\CsvDataSource;
use frictionlessdata\tableschema\Schema;
use frictionlessdata\tableschema\Table;
use frictionlessdata\tableschema\Exceptions\TableRowValidationException;
use frictionlessdata\tableschema\Exceptions\FieldValidationException;
use frictionlessdata\tableschema\Exceptions\DataSourceException;


class TabularDataStream extends BaseDataStream
Expand Down Expand Up @@ -39,7 +40,9 @@ public function rewind() {
public function current() {
try {
return $this->table->current();
} catch (TableRowValidationException $e) {
} catch (DataSourceException $e) {
throw new DataStreamValidationException($e->getMessage());
} catch (FieldValidationException $e) {
throw new DataStreamValidationException($e->getMessage());
}
}
Expand Down
50 changes: 49 additions & 1 deletion src/Datapackages/BaseDatapackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ public function __construct($descriptor, $basePath=null)
{
$this->descriptor = $descriptor;
$this->basePath = $basePath;
$validationErrors = DatapackageValidator::validate($this->descriptor());
$this->revalidate();
}

public function revalidate()
{
$this->rewind();
$validationErrors = $this->datapackageValidate();
if (count($validationErrors) > 0) {
throw new DatapackageValidationFailedException($validationErrors);
}
Expand All @@ -32,6 +38,43 @@ public function descriptor()
return $this->descriptor;
}

public function resources()
{
$resources = [];
foreach ($this as $resource) {
$resources[$resource->name()] = $resource;
}
return $resources;
}

public function resource($name)
{
return $this->resources()[$name];
}

public function deleteResource($name)
{
$resourceDescriptors = [];
foreach ($this->descriptor->resources as $resourceDescriptor) {
if ($resourceDescriptor->name != $name) {
$resourceDescriptors[] = $resourceDescriptor;
}
}
$this->descriptor->resources = $resourceDescriptors;
$this->revalidate();
}

public function addResource($resource)
{
$resourceDescriptors = [];
foreach ($this->descriptor->resources as $resourceDescriptor) {
$resourceDescriptors[] = $resourceDescriptor;
}
$resourceDescriptors[] = $resource->descriptor();
$this->descriptor->resources = $resourceDescriptors;
$this->revalidate();
}

// standard iterator functions - to iterate over the resources
public function rewind() {$this->currentResourcePosition = 0;}
public function current() { return $this->initResource($this->descriptor()->resources[$this->currentResourcePosition]); }
Expand All @@ -53,4 +96,9 @@ protected function initResource($descriptor)
{
return Factory::resource($descriptor, $this->basePath);
}

protected function datapackageValidate()
{
return DatapackageValidator::validate($this->descriptor(), $this->basePath);
}
}
17 changes: 14 additions & 3 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php namespace frictionlessdata\datapackage;
use frictionlessdata\datapackage\Resources\BaseResource;
use JsonSchema\Validator;

/**
Expand Down Expand Up @@ -128,16 +129,19 @@ public static function validate($source, $basePath=null)
}
}

protected static function getDatapackageClass($descriptor)
public static function getDatapackageClass($descriptor)
{
return Repository::getDatapackageClass($descriptor);
}

protected static function getResourceClass($descriptor)
/**
* @param $descriptor
* @return BaseResource::class
*/
public static function getResourceClass($descriptor)
{
return Repository::getResourceClass($descriptor);
}

/**
* allows extending classes to add custom sources
* used by unit tests to add a mock http source
Expand Down Expand Up @@ -190,9 +194,16 @@ protected static function loadSource($source, $basePath)
// http sources don't allow relative paths, hence basePath should remain null
$basePath = null;
} else {
// not a json string and not a url - assume it's a file path
if (empty($basePath)) {
// no basePath
// - assume source is the absolute path of the file
// - set it's directory as the basePath
$basePath = dirname($source);
} else {
// got a basePath
// - try to prepend it to the source and see if such a file exists
// - if not - assume it's an absolute path
$absPath = $basePath.DIRECTORY_SEPARATOR.$source;
if (file_exists($absPath)) {
$source = $absPath;
Expand Down
11 changes: 9 additions & 2 deletions src/Repository.php
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
<?php namespace frictionlessdata\datapackage;
use frictionlessdata\datapackage\Resources\BaseResource;

/**
* repository of known profiles and the corresponding classes and validation requirements
*/
class Repository
{
/**
* @param $descriptor
* @return BaseResource::class
*/
public static function getResourceClass($descriptor)
{
if (static::getResourceValidationProfile($descriptor) == "tabular-data-resource") {
return "frictionlessdata\\datapackage\\Resources\\TabularResource";
$resourceClass = "frictionlessdata\\datapackage\\Resources\\TabularResource";
} else {
return "frictionlessdata\\datapackage\\Resources\\DefaultResource";
$resourceClass = "frictionlessdata\\datapackage\\Resources\\DefaultResource";
}
/** @var BaseResource $resourceClass */
return $resourceClass;
}

public static function getResourceValidationProfile($descriptor)
Expand Down
35 changes: 27 additions & 8 deletions src/Resources/BaseResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace frictionlessdata\datapackage\Resources;

use frictionlessdata\datapackage\DataStreams\BaseDataStream;
use frictionlessdata\datapackage\Validators\ResourceValidationError;
use frictionlessdata\datapackage\Validators\ResourceValidator;
use frictionlessdata\datapackage\Exceptions\ResourceValidationFailedException;
use frictionlessdata\datapackage\Utils;
Expand All @@ -18,7 +19,7 @@ public function __construct($descriptor, $basePath)
{
$this->basePath = $basePath;
$this->descriptor = $descriptor;
$validationErrors = ResourceValidator::validate($this->descriptor());
$validationErrors = $this->validateResource();
if (count($validationErrors) > 0) {
throw new ResourceValidationFailedException($validationErrors);
}
Expand Down Expand Up @@ -47,29 +48,47 @@ public function key() { return $this->currentDataPosition; }
public function next() { $this->currentDataPosition++; }
public function valid() { return isset($this->descriptor()->data[$this->currentDataPosition]); }

protected $descriptor;
protected $basePath;
protected $currentDataPosition = 0;
public static function validateDataSource($dataSource, $basePath=null)
{
$errors = [];
$dataSource = static::normalizeDataSource($dataSource, $basePath);
if (!Utils::isHttpSource($dataSource) && !file_exists($dataSource)) {
$errors[] = new ResourceValidationError(
ResourceValidationError::SCHEMA_VIOLATION,
"data source file does not exist or is not readable: {$dataSource}"
);
}
return $errors;
}

/**
* allows extending classes to add custom sources
* used by unit tests to add a mock http source
*
* @param string $dataSource
* @param string $basePath
* @return string
*/
protected function normalizeDataSource($dataSource)
public static function normalizeDataSource($dataSource, $basePath=null)
{
if (!empty($this->basePath) && !Utils::isHttpSource($dataSource)) {
if (!empty($basePath) && !Utils::isHttpSource($dataSource)) {
// TODO: support JSON pointers
$absPath = $this->basePath.DIRECTORY_SEPARATOR.$dataSource;
$absPath = $basePath.DIRECTORY_SEPARATOR.$dataSource;
if (file_exists($absPath)) {
$dataSource = $absPath;
}
}
return $dataSource;
}

protected $descriptor;
protected $basePath;
protected $currentDataPosition = 0;

protected function validateResource()
{
return ResourceValidator::validate($this->descriptor(), $this->basePath);
}

/**
* @param string $dataSource
* @return BaseDataStream
Expand Down
2 changes: 1 addition & 1 deletion src/Resources/DefaultResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class DefaultResource extends BaseResource
*/
protected function getDataStream($dataSource)
{
return new DefaultDataStream($this->normalizeDataSource($dataSource));
return new DefaultDataStream($this->normalizeDataSource($dataSource, $this->basePath));
}
}
2 changes: 1 addition & 1 deletion src/Resources/TabularResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function schema()
*/
protected function getDataStream($dataSource)
{
return new TabularDataStream($this->normalizeDataSource($dataSource), $this->schema());
return new TabularDataStream($this->normalizeDataSource($dataSource, $this->basePath), $this->schema());
}

}
24 changes: 21 additions & 3 deletions src/Validators/BaseValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,26 @@

abstract class BaseValidator extends SchemaValidator
{
public function __construct($descriptor, $basePath=null)
{
$this->basePath = $basePath;
parent::__construct($descriptor);
}

/**
* validate a descriptor
* @param object $descriptor
* @return SchemaValidationError[]
* @param string $basePath
* @return \frictionlessdata\tableschema\SchemaValidationError[]
*/
public static function validate($descriptor)
public static function validate($descriptor, $basePath=null)
{
$validator = new static($descriptor);
$validator = new static($descriptor, $basePath);
return $validator->getValidationErrors();
}

protected $basePath;

/**
* should be implemented properly by extending classes
* should return the profile used for validation
Expand Down Expand Up @@ -96,7 +105,16 @@ protected function validateSchema()
*/
protected function addError($code, $extraDetails=null)
{
// modified from parent function to support changing the error class
$errorClass = $this->getSchemaValidationErrorClass();
$this->errors[] = new $errorClass($code, $extraDetails);
}

protected function validateKeys()
{
// this can be used to do further validations on $this->descriptor
// it will run only in case validateSchema succeeded
// so no need to check if attribute exists or in correct type
// the parent SchemaValidator does some checks specific to table schema - so don't call the parent function
}
}
15 changes: 15 additions & 0 deletions src/Validators/DatapackageValidator.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace frictionlessdata\datapackage\Validators;

use frictionlessdata\datapackage\Repository;
use frictionlessdata\datapackage\Validators\ResourceValidator;

/**
* validate a datapackage descriptor
Expand All @@ -17,4 +18,18 @@ protected function getValidationProfile()
{
return Repository::getDatapackageValidationProfile($this->descriptor);
}

protected function validateKeys()
{
foreach ($this->descriptor->resources as $resourceDescriptor) {
foreach ($this->resourceValidate($resourceDescriptor) as $error) {
$this->errors[] = $error;
}
}
}

protected function resourceValidate($resourceDescriptor)
{
return ResourceValidator::validate($resourceDescriptor, $this->basePath);
}
}
Loading

0 comments on commit ca4fdd6

Please sign in to comment.