Skip to content

Commit

Permalink
improve profiles and registry support (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
OriHoch authored Jun 8, 2017
1 parent ca4fdd6 commit 77d1aa2
Show file tree
Hide file tree
Showing 32 changed files with 7,959 additions and 189 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ script:
- composer test
after_success:
- vendor/bin/coveralls
dist: trusty
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,9 @@ At the moment, the integration with frictionlessdata repo is not working, you ca
* Login with GitHub which has permissions
* click "Update"
* all releases from GitHub appear as releases on Packagist

## updating frictionlessdata schemas

The json schemas for the frictionlessdata specs are stored locally as part of the package.

They might change from time to time, to donwnload the latest specs run `composer update_registry`
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ $resource = Factory::resource((object)[
"name" => "new-resource", "data" => ["tests/fixtures/foo.txt", "tests/fixtures/baz.txt"]
])
$datapackage->addResource($resource);

// register custom datapackage classes which can override / extend core classes
Factory::registerDatapackageClass("my\\custom\\DatapackageClass");

// register custom profiles and related schema (must conform to the default datapackage schema as well)
Registry::registerSchema("my-custom-profile-id", "path/to/my-custom-profile.schema.json");
```


Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}
},
"scripts": {
"test": "phpunit --debug --coverage-clover coverage-clover.xml --bootstrap tests/autoload.php tests/"
"test": "phpunit --debug --coverage-clover coverage-clover.xml --bootstrap tests/autoload.php tests/",
"update_registry": "php update_registry.php"
}
}
11 changes: 11 additions & 0 deletions src/Datapackages/BaseDatapackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace frictionlessdata\datapackage\Datapackages;

use frictionlessdata\datapackage\Factory;
use frictionlessdata\datapackage\Registry;
use frictionlessdata\datapackage\Validators\DatapackageValidator;
use frictionlessdata\datapackage\Exceptions\DatapackageValidationFailedException;

Expand Down Expand Up @@ -29,6 +30,11 @@ public function revalidate()
}
}

public static function handlesDescriptor($descriptor)
{
return static::handlesProfile(Registry::getDatapackageValidationProfile($descriptor));
}

/**
* returns the descriptor as-is, without adding default values or normalizing
* @return object
Expand Down Expand Up @@ -101,4 +107,9 @@ protected function datapackageValidate()
{
return DatapackageValidator::validate($this->descriptor(), $this->basePath);
}

protected static function handlesProfile($profile)
{
return false;
}
}
7 changes: 7 additions & 0 deletions src/Datapackages/CustomDatapackage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
namespace frictionlessdata\datapackage\Datapackages;

class CustomDatapackage extends DefaultDatapackage
{

}
5 changes: 4 additions & 1 deletion src/Datapackages/DefaultDatapackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@

class DefaultDatapackage extends BaseDatapackage
{

protected static function handlesProfile($profile)
{
return ($profile == "data-package");
}
}
10 changes: 10 additions & 0 deletions src/Datapackages/TabularDatapackage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace frictionlessdata\datapackage\Datapackages;

class TabularDatapackage extends DefaultDatapackage
{
protected static function handlesProfile($profile)
{
return ($profile == "tabular-data-package");
}
}
2 changes: 1 addition & 1 deletion src/Exceptions/DatapackageValidationFailedException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class DatapackageValidationFailedException extends \Exception
public function __construct($validationErrors)
{
$this->validationErrors = $validationErrors;
parent::__construct("DefaultDatapackage validation failed: ".DatapackageValidationError::getErrorMessages($validationErrors));
parent::__construct("Datapackage validation failed: ".DatapackageValidationError::getErrorMessages($validationErrors));
}
}
77 changes: 73 additions & 4 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace frictionlessdata\datapackage;

use frictionlessdata\datapackage\Datapackages\BaseDatapackage;
use frictionlessdata\datapackage\Resources\BaseResource;
use JsonSchema\Validator;

/**
* datapackage and resource have different classes depending on the corresponding profile
Expand All @@ -21,7 +22,7 @@ class Factory
* - URL (must be in either 'http' or 'https' schemes)
* - local filesystem (POSIX) path
* @param mixed $source
* @param null|string $basePath
* @param null|string $basePath optional, required only if you want to use relative paths
* @return Datapackages\BaseDatapackage
* @throws Exceptions\DatapackageInvalidSourceException
* @throws Exceptions\DatapackageValidationFailedException
Expand Down Expand Up @@ -129,9 +130,53 @@ public static function validate($source, $basePath=null)
}
}

public static function registerDatapackageClass($datapackageClass)
{
static::$registeredDatapackageClasses[] = $datapackageClass;
}

public static function clearRegisteredDatapackageClasses()
{
static::$registeredDatapackageClasses = [];
}

/**
* @param $descriptor
* @return BaseDatapackage::class
*/
public static function getDatapackageClass($descriptor)
{
return Repository::getDatapackageClass($descriptor);
$datapackageClasses = array_merge(
// custom classes
static::$registeredDatapackageClasses,
// core classes
[
"frictionlessdata\\datapackage\\Datapackages\TabularDatapackage",
"frictionlessdata\\datapackage\\Datapackages\DefaultDatapackage",
]
);
$res = null;
foreach ($datapackageClasses as $datapackageClass) {
if (call_user_func([$datapackageClass, "handlesDescriptor"], $descriptor)) {
$res = $datapackageClass;
break;
}
}
if (!$res) {
// not matched by any known classes
$res = "frictionlessdata\\datapackage\\Datapackages\CustomDatapackage";
}
return $res;
}

public static function registerResourceClass($resourceClass)
{
static::$registeredResourceClasses[] = $resourceClass;
}

public static function clearRegisteredResourceClasses()
{
static::$registeredResourceClasses = [];
}

/**
Expand All @@ -140,8 +185,32 @@ public static function getDatapackageClass($descriptor)
*/
public static function getResourceClass($descriptor)
{
return Repository::getResourceClass($descriptor);
$resourceClasses = array_merge(
// custom classes
static::$registeredResourceClasses,
// core classes
[
"frictionlessdata\\datapackage\\Resources\\TabularResource",
"frictionlessdata\\datapackage\\Resources\\DefaultResource"
]
);
$res = null;
foreach ($resourceClasses as $resourceClass) {
if (call_user_func([$resourceClass, "handlesDescriptor"], $descriptor)) {
$res = $resourceClass;
break;
}
}
if (!$res) {
// not matched by any known classes
$res = "frictionlessdata\\datapackage\\Resources\\CustomResource";
}
return $res;
}

protected static $registeredDatapackageClasses = [];
protected static $registeredResourceClasses = [];

/**
* allows extending classes to add custom sources
* used by unit tests to add a mock http source
Expand Down
109 changes: 109 additions & 0 deletions src/Registry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php namespace frictionlessdata\datapackage;

/**
* repository of known profiles and the corresponding schemas
*/
class Registry
{
/**
* get the profile which should be used for validation from the given resource descriptor
*/
public static function getResourceValidationProfile($descriptor)
{
if (isset($descriptor->profile) && $descriptor->profile != "default") {
return $descriptor->profile;
} else {
return "data-resource";
}
}

/**
* get the profile which should be used for validation from the given datapackage descriptor
* corresponds to the id from the registry
*/
public static function getDatapackageValidationProfile($descriptor)
{
if (isset($descriptor->profile) && $descriptor->profile != "default") {
return $descriptor->profile;
} else {
return "data-package";
}
}

/**
* given a normalized profile - get the corresponding schema file for known schema in the registry
* returns false in case of unknown schema
* works the same for both datapackage schema and resource schemas
*/
public static function getJsonSchemaFile($profile)
{
foreach (static::getAllSchemas() as $schema) {
if ($schema->id != "registry" && $schema->id == $profile) {
if (isset($schema->schema_path)) {
return realpath(dirname(__FILE__))."/Validators/schemas/".$schema->schema_path;
} else {
return $schema->schema_filename;
}
}
}
return false;
}

public static function registerSchema($profile, $filename)
{
static::$registeredSchemas[$profile] = ["filename" => $filename];
}

public static function clearRegisteredSchemas()
{
static::$registeredSchemas = [];
}

/**
* returns array of all known schemas in the registry
*/
public static function getAllSchemas()
{
// registry schema
$registrySchemaFilename = dirname(__FILE__)."/Validators/schemas/registry.json";
$registry = [
(object)[
"id" => "registry",
"schema" => "https://specs.frictionlessdata.io/schemas/registry.json",
"schema_path" => "registry.json"
]
];
// schemas from the registry (currently contains only the datapackage scheams)
$schemaIds = [];
if (file_exists($registrySchemaFilename)) {
foreach (json_decode(file_get_contents($registrySchemaFilename)) as $schema) {
$schemaIds[] = $schema->id;
if ($schema->id == "fiscal-data-package") {
// fix a bug in the specs, see https://github.com/frictionlessdata/specs/pull/416
$schema->schema = "https://specs.frictionlessdata.io/schemas/fiscal-data-package.json";
}
$registry[] = $schema;
};
// resource schemas - currently not in the registry
foreach (["data-resource", "tabular-data-resource"] as $id) {
if (!in_array($id, $schemaIds)) {
$registry[] = (object)[
"id" => $id,
"schema" => "https://specs.frictionlessdata.io/schemas/{$id}.json",
"schema_path" => "{$id}.json"
];
}
}
}
// custom registered schemas
foreach (static::$registeredSchemas as $profile => $schema) {
$registry[] = (object)[
"id" => $profile,
"schema_filename" => $schema["filename"]
];
}
return $registry;
}

protected static $registeredSchemas = [];
}
46 changes: 0 additions & 46 deletions src/Repository.php

This file was deleted.

11 changes: 11 additions & 0 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\Registry;
use frictionlessdata\datapackage\Validators\ResourceValidationError;
use frictionlessdata\datapackage\Validators\ResourceValidator;
use frictionlessdata\datapackage\Exceptions\ResourceValidationFailedException;
Expand All @@ -25,6 +26,11 @@ public function __construct($descriptor, $basePath)
}
}

public static function handlesDescriptor($descriptor)
{
return static::handlesProfile(Registry::getResourceValidationProfile($descriptor));
}

/**
* @return object
*/
Expand Down Expand Up @@ -94,4 +100,9 @@ protected function validateResource()
* @return BaseDataStream
*/
abstract protected function getDataStream($dataSource);

protected static function handlesProfile($profile)
{
return false;
}
}
Loading

0 comments on commit 77d1aa2

Please sign in to comment.