diff --git a/Vagrant/chef/cookbooks/default/recipes/default.rb b/Vagrant/chef/cookbooks/default/recipes/default.rb
new file mode 100644
index 0000000..3a8e1eb
--- /dev/null
+++ b/Vagrant/chef/cookbooks/default/recipes/default.rb
@@ -0,0 +1,117 @@
+execute "add dotdeb repo" do
+ user "root"
+ not_if "grep 'deb http://packages.dotdeb.org wheezy all' /etc/apt/sources.list"
+ command "echo 'deb http://packages.dotdeb.org wheezy all' >> /etc/apt/sources.list && echo 'deb-src http://packages.dotdeb.org wheezy all' >> /etc/apt/sources.list"
+end
+
+execute "add dotdeb key" do
+ user "root"
+ not_if "apt-key finger | grep 'Key fingerprint = 6572 BBEF 1B5F F28B 28B7 0683 7E3F 0700 89DF 5277'"
+ command "wget http://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg && apt-get update"
+end
+
+# Run apt-get update to create the stamp file
+execute "apt-get-update" do
+ command "apt-get update"
+ ignore_failure true
+ not_if do ::File.exists?('/var/lib/apt/periodic/update-success-stamp') end
+end
+
+# For other recipes to call to force an update
+execute "apt-get update" do
+ command "apt-get update"
+ ignore_failure true
+ action :nothing
+end
+
+# provides /var/lib/apt/periodic/update-success-stamp on apt-get update
+package "update-notifier-common" do
+ notifies :run, resources(:execute => "apt-get-update"), :immediately
+end
+
+execute "apt-get-update-periodic" do
+ command "apt-get update"
+ ignore_failure true
+ only_if do
+ File.exists?('/var/lib/apt/periodic/update-success-stamp') &&
+ File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400
+ end
+end
+
+# install the software we need
+%w(
+curl
+vim
+git
+libapache2-mod-php5
+php5-cli
+php5-curl
+php5-gd
+php5-intl
+php5-mysql
+mysql-server
+php5-mcrypt
+php5-memcached
+php-apc
+redis-server
+php5-redis
+htop
+unzip
+).each { | pkg | package pkg }
+
+# upgrade system packages
+execute "apt-get-upgrade-system" do
+ command "apt-get upgrade"
+ ignore_failure false
+ only_if do
+ File.exists?('/var/lib/apt/periodic/update-success-stamp') &&
+ File.mtime('/var/lib/apt/periodic/update-success-stamp') < Time.now - 86400
+ end
+end
+
+execute "apache-enable-mod-rewrite" do
+ user "root"
+ command "a2enmod rewrite"
+ notifies :reload, "service[apache2]"
+end
+
+execute "apache-enable-mod-ssl" do
+ user "root"
+ command "a2enmod ssl"
+ notifies :reload, "service[apache2]"
+end
+
+service "apache2" do
+ supports :restart => true, :reload => true, :status => true
+ action [ :enable, :start ]
+end
+
+execute "check if date.timezone is Europe/Berlin in /etc/php5/apache2/php.ini?" do
+ user "root"
+ not_if "grep '^date.timezone = Europe/Berlin' /etc/php5/apache2/php.ini"
+ command "sed -i 's/;date.timezone =.*/date.timezone = Europe\\/Berlin/g' /etc/php5/apache2/php.ini"
+end
+
+execute "check if date.timezone is Europe/Berlin in /etc/php5/cli/php.ini?" do
+ user "root"
+ not_if "grep '^date.timezone = Europe/Berlin' /etc/php5/cli/php.ini"
+ command "sed -i 's/;date.timezone =.*/date.timezone = Europe\\/Berlin/g' /etc/php5/cli/php.ini"
+end
+
+execute "check if memory_limit is set to the correct value in /etc/php5/apache2/php.ini?" do
+ user "root"
+ not_if "grep 'memory_limit = 256M' /etc/php5/apache2/php.ini"
+ command "sed -i 's/memory_limit =.*/memory_limit = 256M/g' /etc/php5/apache2/php.ini"
+end
+
+execute "check if memory_limit is set to the correct value /etc/php5/cli/php.ini?" do
+ user "root"
+ not_if "grep 'memory_limit = 512M' /etc/php5/cli/php.ini"
+ command "sed -i 's/memory_limit =.*/memory_limit = 512M/g' /etc/php5/cli/php.ini"
+end
+
+execute "check if max_execution_time is set to the correct value in /etc/php5/apache2/php.ini?" do
+ user "root"
+ not_if "grep 'max_execution_time = 60' /etc/php5/apache2/php.ini"
+ command "sed -i 's/max_execution_time =.*/max_execution_time = 60/g' /etc/php5/apache2/php.ini"
+end
diff --git a/Vagrant/chef/cookbooks/default/templates/default/ssl_vhost.conf.erb b/Vagrant/chef/cookbooks/default/templates/default/ssl_vhost.conf.erb
new file mode 100644
index 0000000..4a4af8c
--- /dev/null
+++ b/Vagrant/chef/cookbooks/default/templates/default/ssl_vhost.conf.erb
@@ -0,0 +1,38 @@
+
+ ServerName <%= @name %>
+ <% if @aliases %>
+ ServerAlias <% @aliases.each do |a| %><%= a %> <% end %>
+ <% end %>
+ RewriteEngine On
+ RewriteRule ^(.*) https://<%= @name %>$1 [R=301,L]
+
+
+
+ ServerName <%= @name %>
+ <% if @aliases %>
+ ServerAlias <% @aliases.each do |a| %><%= a %> <% end %>
+ <% end %>
+ DocumentRoot <%= @docroot %>
+ ">
+ Options FollowSymLinks
+ AllowOverride All
+ Order allow,deny
+ Allow from all
+
+
+ Options FollowSymLinks
+ AllowOverride None
+
+
+ # SSL settings
+ SSLEngine on
+ SSLCertificateFile /etc/ssl/certs/<%= @name %>.crt.pem
+ SSLCertificateKeyFile /etc/ssl/private/<%= @name %>.key.pem
+ BrowserMatch "MSIE [2-6]" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
+ # MSIE 7 and newer should be able to use keepalive
+ BrowserMatch "MSIE [7-9]" ssl-unclean-shutdown
+
+ LogLevel info
+ ErrorLog /var/log/apache2/<%= @name %>-error.log
+ CustomLog /var/log/apache2/<%= @name %>-access.log combined
+
\ No newline at end of file
diff --git a/Vagrant/chef/cookbooks/default/templates/default/vhost.conf.erb b/Vagrant/chef/cookbooks/default/templates/default/vhost.conf.erb
new file mode 100644
index 0000000..c4a440b
--- /dev/null
+++ b/Vagrant/chef/cookbooks/default/templates/default/vhost.conf.erb
@@ -0,0 +1,20 @@
+
+ ServerName <%= @name %>
+ <% if @aliases %>
+ ServerAlias <% @aliases.each do |a| %><%= a %> <% end %>
+ <% end %>
+ DocumentRoot <%= @docroot %>
+ ">
+ Options FollowSymLinks
+ AllowOverride All
+ Order allow,deny
+ Allow from all
+
+
+ Options FollowSymLinks
+ AllowOverride None
+
+ LogLevel info
+ ErrorLog /var/log/apache2/<%= @name %>-error.log
+ CustomLog /var/log/apache2/<%= @name %>-access.log combined
+
\ No newline at end of file
diff --git a/Vagrant/chef/cookbooks/dev/recipes/default.rb b/Vagrant/chef/cookbooks/dev/recipes/default.rb
new file mode 100644
index 0000000..e45d344
--- /dev/null
+++ b/Vagrant/chef/cookbooks/dev/recipes/default.rb
@@ -0,0 +1,96 @@
+include_recipe "default"
+
+# Install dev only software.
+%w(
+php5-xdebug
+).each { | pkg | package pkg }
+
+# Change apache user to avoid permission issues.
+execute "Change apache user in /etc/apache2/apache2.conf" do
+ user "root"
+ not_if "grep '^User vagrant' /etc/apache2/apache2.conf"
+ command "echo 'User vagrant' >> /etc/apache2/apache2.conf"
+end
+
+execute "Change apache group in /etc/apache2/apache2.conf" do
+ user "root"
+ not_if "grep '^Group vagrant' /etc/apache2/apache2.conf"
+ command "echo 'Group vagrant' >> /etc/apache2/apache2.conf"
+end
+
+execute "Set default server name in /etc/apache2/apache2.conf" do
+ user "root"
+ not_if "grep '^ServerName' /etc/apache2/apache2.conf"
+ command "echo 'ServerName sites.dev' >> /etc/apache2/apache2.conf"
+end
+
+vhosts = []
+begin
+ vhosts = data_bag("vhosts")
+rescue
+ puts "Vhost data bag is empty"
+end
+vhosts.each do | name |
+ vhost = data_bag_item("vhosts", name)
+ conffile = "/etc/apache2/sites-available/#{vhost['name']}.conf"
+ if vhost['type'] == "SSL"
+ execute "Generate certificate" do
+ user "root"
+ command "openssl req -x509 -newkey rsa:2048 -keyout /tmp/#{vhost['name']}.key.pem -out /tmp/#{vhost['name']}.crt.pem -days 365 -nodes -subj '/C=DE/ST=Bavaria/L=Munic/CN=#{vhost['name']}' && mv /tmp/#{vhost['name']}.crt.pem /etc/ssl/certs/#{vhost['name']}.crt.pem && mv /tmp/#{vhost['name']}.key.pem /etc/ssl/private/#{vhost['name']}.key.pem"
+ not_if "test -f /etc/ssl/certs/#{vhost['name']}.crt.pem && test -f /etc/ssl/private/#{vhost['name']}.key.pem"
+ end
+ template conffile do
+ user "root"
+ mode "0644"
+ source "ssl_vhost.conf.erb"
+ cookbook "default"
+ notifies :reload, "service[apache2]"
+ variables ({
+ :name => vhost['name'],
+ :aliases => vhost['aliases'],
+ :docroot => vhost['docroot']
+ })
+ end
+ else
+ template conffile do
+ user "root"
+ mode "0644"
+ source "vhost.conf.erb"
+ cookbook "default"
+ notifies :reload, "service[apache2]"
+ variables ({
+ :name => vhost['name'],
+ :aliases => vhost['aliases'],
+ :docroot => vhost['docroot']
+ })
+ end
+ end
+ execute "Remove not needed vhosts" do
+ user "root"
+ command "cd /etc/apache2/sites-available && rm `ls | grep -v '^#{vhost['name']}.conf$'` && cd /etc/apache2/sites-enabled && rm `ls | grep -v '^#{vhost['name']}.conf$'`"
+ only_if "ls /etc/apache2/sites-available | grep -v '^#{vhost['name']}.conf$'"
+ end
+ execute "Link vhost to enabled sites" do
+ user "root"
+ command "ln -s #{conffile} /etc/apache2/sites-enabled/#{vhost['name']}.conf"
+ not_if "test -L /etc/apache2/sites-enabled/#{vhost['name']}.conf"
+ end
+end
+
+# Set up Xdebug.
+xdebug = data_bag_item("config", "xdebug")
+
+template "/etc/php5/mods-available/xdebug.ini" do
+ user "root"
+ mode "0644"
+ source "xdebug.ini.erb"
+ notifies :reload, "service[apache2]"
+ variables ({
+ :hostip => xdebug['hostip']
+ })
+end
+execute "Activate Xdebug" do
+ user "root"
+ command "ln -s /etc/php5/mods-available/xdebug.ini /etc/php5/conf.d/20-xdebug.ini"
+ not_if "test -L /etc/php5/conf.d/20-xdebug.ini"
+end
diff --git a/Vagrant/chef/cookbooks/dev/templates/default/xdebug.ini.erb b/Vagrant/chef/cookbooks/dev/templates/default/xdebug.ini.erb
new file mode 100644
index 0000000..35a4285
--- /dev/null
+++ b/Vagrant/chef/cookbooks/dev/templates/default/xdebug.ini.erb
@@ -0,0 +1,12 @@
+zend_extension=/usr/lib/php5/20100525/xdebug.so
+
+[xdebug]
+xdebug.remote_enable=On
+xdebug.remote_host="<%= @hostip %>"
+xdebug.remote_port=9000
+xdebug.remote_handler="dbgp"
+xdebug.cli_color=1
+xdebug.overload_var_dump=0
+xdebug.show_mem_delta=1
+xdebug.trace_format=1
+xdebug.max_nesting_level=250
\ No newline at end of file
diff --git a/Vagrant/chef/data_bags/dev/config/xdebug.json b/Vagrant/chef/data_bags/dev/config/xdebug.json
new file mode 100755
index 0000000..acc5fea
--- /dev/null
+++ b/Vagrant/chef/data_bags/dev/config/xdebug.json
@@ -0,0 +1,4 @@
+{
+ "id": "xdebug",
+ "hostip": "10.2.254.1"
+}
\ No newline at end of file
diff --git a/Vagrant/chef/data_bags/dev/vhosts/web-api-extension.dev.json b/Vagrant/chef/data_bags/dev/vhosts/web-api-extension.dev.json
new file mode 100755
index 0000000..dad24ef
--- /dev/null
+++ b/Vagrant/chef/data_bags/dev/vhosts/web-api-extension.dev.json
@@ -0,0 +1,9 @@
+{
+ "id": "web-api-extension",
+ "type": "SSL",
+ "name": "web-api-extension.dev",
+ "aliases": [
+ "web-api-extension.dev"
+ ],
+ "docroot": "/vagrant/web"
+}
\ No newline at end of file
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 0000000..0d818f9
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,23 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ config.vm.box = "sites"
+ config.vm.box_url = "file:///Volumes/Jarlssen/98\ Development/41\ Vagrant\ Boxes/sites.jarlssen.de/sites-cheflatest-virtualbox.box"
+ config.vm.hostname = "web-api-ext-dev"
+ config.vm.synced_folder ".", "/vagrant", :nfs => true
+ config.vm.network :private_network, ip: "10.2.254.4"
+ config.vm.provision :chef_solo do |chef|
+ chef.cookbooks_path = "Vagrant/chef/cookbooks"
+ chef.data_bags_path = "Vagrant/chef/data_bags/dev"
+ # chef debug level, start vagrant like this to debug:
+ # $ CHEF_LOG_LEVEL=debug vagrant
+ chef.log_level = ENV['CHEF_LOG'] || "info"
+ # chef recipes
+ chef.add_recipe("default")
+ chef.add_recipe("dev")
+ end
+ end
diff --git a/Vagrantfile.dist b/Vagrantfile.dist
new file mode 100644
index 0000000..5e1edc8
--- /dev/null
+++ b/Vagrantfile.dist
@@ -0,0 +1,23 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ config.vm.box = "sites"
+ config.vm.box_url = "file:///Volumes/Jarlssen/98\ Development/41\ Vagrant\ Boxes/sites.jarlssen.de/sites-cheflatest-virtualbox.box"
+ config.vm.hostname = "web-api-ext-dev"
+ config.vm.synced_folder ".", "/vagrant", :nfs => true
+ config.vm.network :private_network, ip: "10.2.254.2"
+ config.vm.provision :chef_solo do |chef|
+ chef.cookbooks_path = "Vagrant/chef/cookbooks"
+ chef.data_bags_path = "Vagrant/chef/data_bags/dev"
+ # chef debug level, start vagrant like this to debug:
+ # $ CHEF_LOG_LEVEL=debug vagrant
+ chef.log_level = ENV['CHEF_LOG'] || "info"
+ # chef recipes
+ chef.add_recipe("default")
+ chef.add_recipe("dev")
+ end
+ end
diff --git a/src/Context/WebApiContext.php b/src/Context/WebApiContext.php
index f21b24b..c09cce0 100644
--- a/src/Context/WebApiContext.php
+++ b/src/Context/WebApiContext.php
@@ -20,35 +20,39 @@
* Provides web API description definitions.
*
* @author Konstantin Kudryashov
+ * @author Tomaz Ahlin (Improvements only)
*/
class WebApiContext implements ApiClientAwareContext
{
/**
* @var string
*/
- private $authorization;
+ protected $authorization;
/**
* @var ClientInterface
*/
- private $client;
+ protected $client;
/**
* @var array
*/
- private $headers = array();
+ protected $headers = array();
/**
* @var \GuzzleHttp\Message\RequestInterface
*/
- private $request;
+ protected $request;
/**
* @var \GuzzleHttp\Message\ResponseInterface
*/
- private $response;
+ protected $response;
- private $placeHolders = array();
+ /**
+ * @var array
+ */
+ protected $placeHolders = array();
/**
* {@inheritdoc}
@@ -124,7 +128,7 @@ public function iSendARequestWithValues($method, $url, TableNode $post)
}
$bodyOption = array(
- 'body' => json_encode($fields),
+ 'body' => json_encode($fields),
);
$this->request = $this->getClient()->createRequest($method, $url, $bodyOption);
if (!empty($this->headers)) {
@@ -174,8 +178,17 @@ public function iSendARequestWithFormData($method, $url, PyStringNode $body)
$body = $this->replacePlaceHolder(trim($body));
$fields = array();
- parse_str(implode('&', explode("\n", $body)), $fields);
+
+ $dataLines = explode("\n", $body);
+ foreach($dataLines as $line) {
+ $line = explode('=', $line);
+ $fields[$line[0]] = $line[1];
+ }
+
$this->request = $this->getClient()->createRequest($method, $url);
+ if (!empty($this->headers)) {
+ $this->request->addHeaders($this->headers);
+ }
/** @var \GuzzleHttp\Post\PostBodyInterface $requestBody */
$requestBody = $this->request->getBody();
foreach ($fields as $key => $value) {
@@ -227,6 +240,145 @@ public function theResponseShouldNotContain($text)
Assertions::assertNotRegExp($expectedRegexp, $actual);
}
+ /**
+ * Checks that response is json and stores the data to array.
+ *
+ * @Then /^(?:the )?response should be json$/
+ */
+ public function theResponseShouldBeJson()
+ {
+ $data = json_decode($this->response->getBody(), true);
+ Assertions::assertNotFalse($data);
+ }
+
+ /**
+ * Checks that response is json and it contains desired keys.
+ *
+ * @param string $key
+ * @param string $type
+ * @param string $subKeys
+ *
+ * @Then /^(?:the )?response should contain "([^"]*)" with "(at least|exactly)" "([^"]*)"$/
+ */
+ public function theResponseShouldContainKeys($key, $type, $subKeys)
+ {
+ $this->theResponseShouldContainOneItem($key, $type, $subKeys, false);
+ }
+
+ /**
+ * Checks that response is json and it contains desired keys and values.
+ *
+ * @param string $key
+ * @param string $type
+ * @param string $subKeys
+ * @param string $subKeyValues
+ *
+ * @Then /^(?:the )?response should contain "([^"]*)" with "(at least|exactly)" "([^"]*)" with values "([^"]*)"$/
+ */
+ public function theResponseShouldContainKeysAndValues($key, $type, $subKeys, $subKeyValues)
+ {
+ $this->theResponseShouldContainOneItem($key, $type, $subKeys, true, $subKeyValues);
+ }
+
+ /**
+ * Checks that response is json and stores the data to array.
+ *
+ * @param string $key
+ * @param string $type
+ * @param string $subKeys
+ * @param boolean $compareValues
+ * @param string $subKeyValues
+ */
+ private function theResponseShouldContainOneItem($key, $type, $subKeys, $compareValues, $subKeyValues = '')
+ {
+ $data = json_decode($this->response->getBody(), true);
+ Assertions::assertNotFalse($data);
+
+ $subKeys = array_map('trim', explode(',', $subKeys));
+ $subKeyValues = array_map('trim', explode(',', $subKeyValues));
+
+ if ($compareValues) {
+ Assertions::assertEquals(count($subKeyValues), count($subKeys));
+ }
+
+ Assertions::assertArrayHasKey('data', $data);
+
+ for ($j=0, $c=count($subKeys); $j<$c; $j++) {
+ $subKey = $subKeys[$j];
+ if ($this->isExactType($type)) {
+ Assertions::assertEquals(count($subKeys), count($data[$key]));
+ }
+ Assertions::assertArrayHasKey($subKey, $data[$key]);
+ if ($compareValues) {
+ Assertions::assertEquals($subKeyValues[$j], $data[$key][$subKey]);
+ }
+ }
+ }
+
+ /**
+ * Checks that response is json and contains the expected number of items with the desired keys.
+ *
+ * @param string $key
+ * @param integer $n
+ * @param string $type
+ * @param string $subKeys
+ *
+ * @Then /^(?:the )?response should contain "([^"]*)" with (\d+) items containing "(at least|exactly)" "([^"]*)"$/
+ */
+ public function theResponseShouldContainItemsWithKeys($key, $n, $type, $subKeys)
+ {
+ $this->theResponseShouldContainItems($key, $n, $type, $subKeys, false);
+ }
+
+ /**
+ * Checks that response is json and contains the expected number of items with the desired keys.
+ *
+ * @param string $key
+ * @param integer $n
+ * @param string $type
+ * @param string $subKeys
+ * @param boolean $compareValues
+ * @param string $subKeyValues
+ */
+ private function theResponseShouldContainItems($key, $n, $type, $subKeys, $compareValues, $subKeyValues = '')
+ {
+ $data = json_decode($this->response->getBody(), true);
+ Assertions::assertNotFalse($data);
+
+ $subKeys = array_map('trim', explode(',', $subKeys));
+ $subKeyValues = array_map('trim', explode(',', $subKeyValues));
+
+ if ($compareValues) {
+ Assertions::assertEquals(count($subKeyValues), count($subKeys));
+ }
+
+ Assertions::assertArrayHasKey($key, $data);
+ Assertions::assertEquals($n, count($data[$key]));
+
+ for ($i=0; $i<$n; $i++) {
+ for ($j=0, $c=count($subKeys); $j<$c; $j++) {
+ $subKey = $subKeys[$j];
+ Assertions::assertArrayHasKey($i, $data[$key]);
+ if ($this->isExactType($type)) {
+ Assertions::assertEquals(count($subKeys), count($data[$key][$i]));
+ }
+ Assertions::assertArrayHasKey($subKey, $data[$key][$i]);
+ if ($compareValues) {
+ Assertions::assertEquals($subKeyValues[$j], $data[$key][$i][$subKey]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param $type
+ * @returns boolean
+ */
+ private function isExactType($type)
+ {
+ return $type === 'exactly';
+ }
+
/**
* Checks that response body contains JSON from PyString.
*
@@ -245,7 +397,7 @@ public function theResponseShouldContainJson(PyStringNode $jsonString)
if (null === $etalon) {
throw new \RuntimeException(
- "Can not convert etalon to json:\n" . $this->replacePlaceHolder($jsonString->getRaw())
+ "Can not convert etalon to json:\n" . $this->replacePlaceHolder($jsonString->getRaw())
);
}