Skip to content

Commit

Permalink
support for registering custom schemas and datapackge classes
Browse files Browse the repository at this point in the history
  • Loading branch information
OriHoch committed May 12, 2017
1 parent ec82007 commit 780f36a
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 26 deletions.
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
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;
}
}
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");
}
}
5 changes: 4 additions & 1 deletion src/Datapackages/TabularDatapackage.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@

class TabularDatapackage extends DefaultDatapackage
{

protected static function handlesProfile($profile)
{
return ($profile == "tabular-data-package");
}
}
89 changes: 69 additions & 20 deletions src/Factory.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<?php namespace frictionlessdata\datapackage;

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

/**
* datapackage and resource have different classes depending on the corresponding profile
Expand Down Expand Up @@ -131,18 +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)
{
$profile = Registry::getDatapackageValidationProfile($descriptor);
if ($profile == "tabular-data-package") {
$datapackageClass = "frictionlessdata\\datapackage\\Datapackages\TabularDatapackage";
} elseif ($profile == "data-package") {
$datapackageClass = "frictionlessdata\\datapackage\\Datapackages\DefaultDatapackage";
} else {
$datapackageClass = "frictionlessdata\\datapackage\\Datapackages\CustomDatapackage";
$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;
}
}
/** @var BaseDatapackage $datapackageClass */
return $datapackageClass;
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 @@ -151,17 +185,32 @@ public static function getDatapackageClass($descriptor)
*/
public static function getResourceClass($descriptor)
{
$profile = Registry::getResourceValidationProfile($descriptor);
if ($profile == "tabular-data-resource") {
$resourceClass = "frictionlessdata\\datapackage\\Resources\TabularResource";
} elseif ($profile == "data-resource") {
$resourceClass = "frictionlessdata\\datapackage\\Resources\DefaultResource";
} else {
$resourceClass = "frictionlessdata\\datapackage\\Resources\CustomResource";
$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;
}
}
/** @var BaseResource $resourceClass */
return $resourceClass;
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
26 changes: 24 additions & 2 deletions src/Registry.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<?php namespace frictionlessdata\datapackage;
use frictionlessdata\datapackage\Resources\BaseResource;

/**
* repository of known profiles and the corresponding schemas
Expand Down Expand Up @@ -40,12 +39,26 @@ public static function getJsonSchemaFile($profile)
{
foreach (static::getAllSchemas() as $schema) {
if ($schema->id != "registry" && $schema->id == $profile) {
return realpath(dirname(__FILE__))."/Validators/schemas/".$schema->schema_path;
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
*/
Expand Down Expand Up @@ -82,6 +95,15 @@ public static function getAllSchemas()
}
}
}
// custom registered schemas
foreach (static::$registeredSchemas as $profile => $schema) {
$registry[] = (object)[
"id" => $profile,
"schema_filename" => $schema["filename"]
];
}
return $registry;
}

protected static $registeredSchemas = [];
}
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;
}
}
5 changes: 5 additions & 0 deletions src/Resources/DefaultResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ protected function getDataStream($dataSource)
{
return new DefaultDataStream($this->normalizeDataSource($dataSource, $this->basePath));
}

protected static function handlesProfile($profile)
{
return ($profile == "data-resource");
}
}
4 changes: 4 additions & 0 deletions src/Resources/TabularResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,8 @@ protected function getDataStream($dataSource)
return new TabularDataStream($this->normalizeDataSource($dataSource, $this->basePath), $this->schema());
}

protected static function handlesProfile($profile)
{
return ($profile == "tabular-data-resource");
}
}
2 changes: 1 addition & 1 deletion src/Validators/BaseValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ protected function convertValidationSchemaFilenameToUrl($filename)
if (file_exists($filename)) {
return "file://".$filename;
} else {
throw new \Exception("failed to find schema file: {$filename} for descriptor ".json_encode($this->descriptor));
throw new \Exception("failed to find schema file: '{$filename}' for descriptor ".json_encode($this->descriptor));
}
}

Expand Down
27 changes: 27 additions & 0 deletions tests/FactoryTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace frictionlessdata\datapackage\tests;

use PHPUnit\Framework\TestCase;
use frictionlessdata\datapackage\Factory;

class FactoryTest extends TestCase
{
public function testRegisterDatapackageClass()
{
Factory::registerDatapackageClass(
"frictionlessdata\\datapackage\\tests\\Mocks\\MockDefaultDatapackage"
);
$datapackage = Factory::datapackage((object)[
"name" => "my-custom-datapackage",
"myCustomDatapackage" => true,
"resources" => [
(object)["name" => "my-custom-resource", "data" => ["tests/fixtures/foo.txt"]]
]
]);
$this->assertEquals(
"frictionlessdata\\datapackage\\tests\\Mocks\\MockDefaultDatapackage",
get_class($datapackage)
);
Factory::clearRegisteredDatapackageClasses();
}
}
37 changes: 36 additions & 1 deletion tests/RegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use frictionlessdata\datapackage\Registry;
use frictionlessdata\datapackage\Factory;


class RegistryTest extends TestCase
{

Expand Down Expand Up @@ -145,7 +146,7 @@ public function testCustomSchemaMustConformToDatapackageSchema()
{
$descriptor = (object)[
"profile" => "http://json-schema.org/schema",
"resources" => [] // this is allows for json-schema.org/schema - but for datapackage it has minimum of 1
"resources" => [] // this is allowed for json-schema.org/schema - but for datapackage it has minimum of 1
];
try {
Factory::datapackage($descriptor);
Expand All @@ -158,6 +159,40 @@ public function testCustomSchemaMustConformToDatapackageSchema()
}
}

public function testRegisteredSchema()
{
$descriptor = (object)[
"name" => "custom-datapackage",
"profile" => "test-custom-profile",
"resources" => [
(object)[
"name" => "custom-resource",
"profile" => "test-custom-resource-profile",
"data" => ["foo.txt"],
"custom" => ["1", "2", "3"]
]
]
];
Registry::registerSchema(
"test-custom-profile",
"tests/fixtures/test-custom-profile.schema.json"
);
Registry::registerSchema(
"test-custom-resource-profile",
"tests/fixtures/test-custom-resource-profile.schema.json"
);
$datapackage = Factory::datapackage($descriptor, "tests/fixtures");
$this->assertInstanceOf(
"frictionlessdata\\datapackage\\Datapackages\\CustomDatapackage",
$datapackage
);
$this->assertInstanceOf(
"frictionlessdata\\datapackage\\Resources\\CustomResource",
$datapackage->resource("custom-resource")
);
Registry::clearRegisteredSchemas();
}

protected function assertDatapackageClassProfile($expectedClass, $expectedProfile, $descriptor)
{
$this->assertEquals($expectedClass, Factory::getDatapackageClass($descriptor));
Expand Down

0 comments on commit 780f36a

Please sign in to comment.