Permalink
Browse files

Refactored Storage interface while implementing WindowsAzureTable#ins…

…ert() and WindowsAzureTable#update().
  • Loading branch information...
1 parent feef6cb commit 6dbed9ce7089f37cd95bf644cec9176553e6f0e9 @beberlei beberlei committed Mar 26, 2012
@@ -17,7 +17,7 @@
* <http://www.doctrine-project.org>.
*/
-namespace Doctrine\CouchDB\HTTP;
+namespace Doctrine\KeyValueStore\Http;
/**
* Connection handler using PHPs stream wrappers.
@@ -73,7 +73,7 @@ public function requiresCompositePrimaryKeys()
* @param array $data
* @return void
*/
- function insert($key, array $data)
+ function insert($className, $key, array $data)
{
try {
$this->conn->insert($this->table, array(
@@ -91,7 +91,7 @@ function insert($key, array $data)
* @param array $data
* @return void
*/
- public function update($key, array $data)
+ public function update($className, $key, array $data)
{
try {
$this->conn->update($this->table, array(
@@ -109,7 +109,7 @@ public function update($key, array $data)
* @param array|string $key
* @return void
*/
- public function delete($key)
+ public function delete($className, $key)
{
try {
$this->conn->delete($this->table, array($this->keyColumn => $key));
@@ -123,7 +123,7 @@ public function delete($key)
* @param array|string $key
* @return array
*/
- public function find($key)
+ public function find($className, $key)
{
$sql = "SELECT " . $this->dataColumn . " FROM " . $this->table . " " .
$this->keyColumn = " = ?";
@@ -73,25 +73,27 @@ private function flattenKey($key)
return $hash;
}
- public function insert($key, array $data)
+ public function insert($className, $key, array $data)
{
$key = $this->flattenKey($key);
+ $data['php_class'] = $className;
$this->cache->save($key, $data);
}
- public function update($key, array $data)
+ public function update($className, $key, array $data)
{
$key = $this->flattenKey($key);
+ $data['php_class'] = $className;
$this->cache->save($key, $data);
}
- public function delete($key)
+ public function delete($className, $key)
{
$key = $this->flattenKey($key);
$this->cache->delete($key);
}
- public function find($key)
+ public function find($className, $key)
{
$key = $this->flattenKey($key);
return $this->cache->fetch($key);
@@ -66,36 +66,40 @@ function requiresCompositePrimaryKeys();
/**
* Insert data into the storage key specified.
*
+ * @param string $className
* @param array|string $key
* @param array $data
* @return void
*/
- function insert($key, array $data);
+ function insert($className, $key, array $data);
/**
* Update data into the given key.
*
+ * @param string $className
* @param array|string $key
* @param array $data
* @return void
*/
- function update($key, array $data);
+ function update($className, $key, array $data);
/**
* Delete data at key
*
+ * @param string $className
* @param array|string $key
* @return void
*/
- function delete($key);
+ function delete($className, $key);
/**
* Find data at key
*
+ * @param string $className
* @param array|string $key
* @return array
*/
- function find($key);
+ function find($className, $key);
/**
* Return a name of the underlying storage.
@@ -0,0 +1,33 @@
+<?php
+/*
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This software consists of voluntary contributions made by many individuals
+ * and is licensed under the LGPL. For more information, see
+ * <http://www.doctrine-project.org>.
+ */
+
+namespace Doctrine\KeyValueStore\Storage\WindowsAzureTable;
+
+class SharedKeyAuthorization implements AuthorizationSchema
+{
+ /**
+ * @override
+ * {@inheritDocs}
+ */
+ public function signRequest($method, $body, array $headers)
+ {
+ return "";
+ }
+}
+
@@ -19,7 +19,8 @@
namespace Doctrine\KeyValueStore\Storage;
-use Doctrine\KeyValueStore\HTTP\HttpClient;
+use Doctrine\KeyValueStore\Http\Client;
+use Doctrine\KeyValueStore\Storage\WindowsAzureTable\AuthorizationSchema;
/**
* Storage implementation for Microsoft Windows Azure Table.
@@ -31,8 +32,36 @@
class WindowsAzureTableStorage implements Storage
{
const WINDOWS_AZURE_TABLE_BASEURL = 'https://%s.table.core.windows.net';
+
+ const METADATA_NS = 'http://schemas.microsoft.com/ado/2007/08/dataservices/metadata';
+ const DATA_NS = 'http://schemas.microsoft.com/ado/2007/08/dataservices';
+
+ /**
+ * @var string
+ */
+ const XML_TEMPLATE_ENTITY = '<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<entry xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
+ <title />
+ <updated></updated>
+ <author>
+ <name />
+ </author>
+ <id />
+ <content type="application/xml">
+ <m:properties>
+ </m:properties>
+ </content>
+</entry>';
+
+ const TYPE_INT32 = 'Edm.Int32';
+ const TYPE_INT64 = 'Edm.Int64';
+ const TYPE_DATETIME = 'Edm.DateTime';
+ const TYPE_BOOLEAN = 'Edm.Boolean';
+ const TYPE_DOUBLE = 'Edm.Double';
+ const TYPE_BINARY = 'Edm.Binary';
+
/**
- * @var \Doctrine\KeyValueStore\HTTP\HttpClient
+ * @var \Doctrine\KeyValueStore\Http\Client
*/
private $client;
@@ -46,15 +75,22 @@ class WindowsAzureTableStorage implements Storage
*/
private $baseUrl;
+ /**
+ * @var DateTime
+ */
+ private $now;
+
/**
* @param HttpClient $client
* @param AuthorizationSchema $authorization
*/
- public function __construct(HttpClient $client, $accountName, AuthorizationSchema $authorization)
+ public function __construct(Client $client, $accountName, AuthorizationSchema $authorization, \DateTime $now)
{
$this->client = $client;
$this->authorization = $authorization;
$this->baseUrl = sprintf(self::WINDOWS_AZURE_TABLE_BASEURL, $accountName);
+ $this->now = $now ? clone $now : new \DateTime();
+ $this->now->setTimeZone(new \DateTimeZone("UTC"));
}
public function supportsPartialUpdates()
@@ -72,21 +108,152 @@ public function requiresCompositePrimaryKeys()
return true;
}
- public function insert($key, array $data)
+ private function now()
+ {
+ return $this->isoDate($this->now);
+ }
+
+ private function isoDate(\DateTime $date)
+ {
+ return str_replace('+00:00', '.0000000Z', $date->format('c'));
+ }
+
+ private function createDomDocumentRequestBody()
+ {
+ $dom = new \DOMDocument('1.0', 'UTF-8');
+ $dom->loadXML(self::XML_TEMPLATE_ENTITY);
+
+ $updatedNodes = $dom->getElementsByTagName('updated');
+ $updatedNodes->item(0)->appendChild($dom->createTextNode($this->now()));
+
+ return $dom;
+ }
+
+ public function insert($className, $key, array $data)
+ {
+ $headers = array(
+ 'Content-Type' => 'application/atom+xml',
+ 'x-ms-date' => $this->now(),
+ );
+ // TODO: This sucks
+ $tableName = $className;
+
+ $dom = $this->createDomDocumentRequestBody();
+
+ $propertiesNode = $dom->getElementsByTagNameNS(self::METADATA_NS, 'properties')->item(0);
+
+ $this->serializeKeys($propertiesNode, $key);
+ $this->serializeProperties($propertiesNode, $data);
+
+ $contentNodes = $dom->getElementsByTagName('content');
+ $contentNodes->item(0)->appendChild($propertiesNode);
+ $xml = $dom->saveXML();
+
+ $url = $this->baseUrl . '/' . $tableName;
+ $this->request('POST', $url, $xml, $headers);
+ }
+
+ private function serializeKeys($propertiesNode, $key)
{
+ $keys = 0;
+ foreach ($key as $keyName => $keyValue) {
+ switch ($keys) {
+ case 0:
+ $partitionKey = $propertiesNode->ownerDocument->createElementNS(self::DATA_NS, 'PartitionKey', $keyValue);
+ $propertiesNode->appendChild($partitionKey);
+ break;
+ case 1:
+ $rowKey = $propertiesNode->ownerDocument->createElementNS(self::DATA_NS, 'RowKey', $keyValue);
+ $propertiesNode->appendChild($rowKey);
+ break;
+ default:
+ throw new \RuntimeException("Only exactly 2 composite key fields allowed.");
+ }
+ $keys++;
+ }
+ }
+ private function request($method, $url, $xml, $headers)
+ {
+ $authorizationHeader = $this->authorization->signRequest($method, "", $headers);
+ $authorizationParts = explode(":" , $authorizationHeader, 2);
+ $headers['Content-Length'] = strlen($xml);
+ $headers[$authorizationParts[0]] = ltrim($authorizationParts[1]);
+ $response = $this->client->request($method, $url, $xml, $headers);
}
- public function update($key, array $data)
+ private function serializeProperties($propertiesNode, array $data)
{
+ foreach ($data as $propertyName => $propertyValue) {
+ if ( isset($key[$propertyName])) {
+ continue;
+ }
+
+ $type = $this->getPropertyType($propertyValue);
+ $propertyValue = $this->convertPropertyValue($propertyValue, $type);
+
+ $property = $propertiesNode->ownerDocument->createElementNS(self::DATA_NS, $propertyName, $propertyValue);
+ if ($propertyValue === null) {
+ $property->setAttributeNS(self::METDATA_NS, 'null', 'true');
+ }
+
+ $propertiesNode->appendChild($property);
+ }
+ }
+
+ private function getPropertyType($propertyValue)
+ {
+ if ($propertyValue instanceof \DateTime) {
+ return self::TYPE_DATETIME;
+ }
+ return null;
+ }
+
+ private function convertPropertyValue($propertyValue, $type)
+ {
+ switch ($type) {
+ case self::TYPE_DATETIME:
+ $propertyValue = $this->isoDate($propertyValue);
+ break;
+ }
+ return $propertyValue;
+ }
+
+ public function update($className, $key, array $data)
+ {
+ $headers = array(
+ 'Content-Type' => 'application/atom+xml',
+ 'x-ms-date' => $this->now(),
+ 'If-Match' => '*',
+ );
+ // TODO: This sucks
+ $tableName = $className;
+
+ $dom = $this->createDomDocumentRequestBody();
+
+ $propertiesNode = $dom->getElementsByTagNameNS(self::METADATA_NS, 'properties')->item(0);
+
+ $this->serializeKeys($propertiesNode, $key);
+ $this->serializeProperties($propertiesNode, $data);
+ $keys = array_values($key);
+ $url = $this->baseUrl . '/' . $tableName ."(PartitionKey='" . $keys[0] . "', RowKey='" . $keys[1] . "')";
+ $idNode = $dom->getElementsByTagName('id')->item(0);
+ $idNode->appendChild($dom->createTextNode($url));
+
+ $contentNodes = $dom->getElementsByTagName('content');
+ $contentNodes->item(0)->appendChild($propertiesNode);
+ $xml = $dom->saveXML();
+ $this->request('POST', $url, $xml, $headers);
}
- public function delete($key)
+ public function delete($className, $key)
{
+ $keys = array_values($key);
+ $url = $this->baseUrl . '/' . $tableName ."(PartitionKey='" . $keys[0] . "', RowKey='" . $keys[1] . "')";
}
- public function find($key)
+ public function find($className, $key)
{
}
Oops, something went wrong.

0 comments on commit 6dbed9c

Please sign in to comment.