fidor_starter_kits 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +2 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +68 -0
  6. data/Rakefile +7 -0
  7. data/fidor_starter_kits.gemspec +25 -0
  8. data/lib/fidor_starter_kits/version.rb +3 -0
  9. data/lib/fidor_starter_kits.rb +63 -0
  10. data/spec/fidor_starter_kits_spec.rb +51 -0
  11. data/spec/spec_helper.rb +8 -0
  12. data/starter_kits/golang_plain/example.go +21 -0
  13. data/starter_kits/java_servlet/.classpath +17 -0
  14. data/starter_kits/java_servlet/.project +36 -0
  15. data/starter_kits/java_servlet/.settings/.jsdtscope +12 -0
  16. data/starter_kits/java_servlet/.settings/org.eclipse.jdt.core.prefs +7 -0
  17. data/starter_kits/java_servlet/.settings/org.eclipse.wst.common.component +9 -0
  18. data/starter_kits/java_servlet/.settings/org.eclipse.wst.common.project.facet.core.xml +10 -0
  19. data/starter_kits/java_servlet/.settings/org.eclipse.wst.jsdt.ui.superType.container +1 -0
  20. data/starter_kits/java_servlet/.settings/org.eclipse.wst.jsdt.ui.superType.name +1 -0
  21. data/starter_kits/java_servlet/WebContent/META-INF/MANIFEST.MF +3 -0
  22. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/commons-codec-1.6.jar +0 -0
  23. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/commons-logging-1.1.3.jar +0 -0
  24. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/fluent-hc-4.3.5.jar +0 -0
  25. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/httpclient-4.3.5.jar +0 -0
  26. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/httpclient-cache-4.3.5.jar +0 -0
  27. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/httpcore-4.3.2.jar +0 -0
  28. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/httpmime-4.3.5.jar +0 -0
  29. data/starter_kits/java_servlet/WebContent/WEB-INF/lib/json-simple-1.1.1.jar +0 -0
  30. data/starter_kits/java_servlet/src/de/fidor/api/example/Example.java +113 -0
  31. data/starter_kits/node_tx/example.js +344 -0
  32. data/starter_kits/php_plain/README.md +14 -0
  33. data/starter_kits/php_plain/example.php +49 -0
  34. data/starter_kits/sinatra_plain/Gemfile +4 -0
  35. data/starter_kits/sinatra_plain/README.md +14 -0
  36. data/starter_kits/sinatra_plain/example.rb +38 -0
  37. metadata +137 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 58717eb772bc8ff11805649bb0f0f2fa6b28f17c
4
+ data.tar.gz: bdbfa6ba4a1f1dcd8603bc3a7f3869ad3d42a31d
5
+ SHA512:
6
+ metadata.gz: cceabe1545dcc5aa4fa14c6e5233c2d54263760d7641755d1c15e1900c188b5a6ea4f9974086aa959df50b8b1c87c86b40060dc5a61f3ca33fc6ec0dc11d1d71
7
+ data.tar.gz: 4000eef19c578f9c5f031828592fb37a8853b069afde6d2a4c7f49804a974b40d503a819659ec2bd7e272598509a260bf42d6183c3cc0a671fbfa9a8a9921df4
data/.gitignore ADDED
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ tmp
16
+ *.log
17
+ database.yml
18
+ settings.yml
19
+ secrets.yml
20
+ /coverage/*
21
+ .ruby-version
22
+ .ruby-gemset
23
+ *.swp
24
+ node_modules
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Georg Leciejewski
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # FidorStarterKits
2
+
3
+ This repo features fidor application examples. The apps use oAuth to
4
+ authenticate with fidor, so you must register an app at fidor first to get your
5
+ personal app credentials.
6
+ Afterwards just copy on of the /starter_kits, add your app credentials to the
7
+ example source and start to play.
8
+
9
+ For Ruby Heros, this repo is also available as ruby gem and provides a tiny
10
+ helper for app creation, see Install & Usage.
11
+
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'fidor_starter_kits'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install fidor_starter_kits
26
+
27
+ ## Usage
28
+
29
+ Create a zipped app with credentials and the fidor url:
30
+
31
+ ```ruby
32
+
33
+ opts = {
34
+ app_name: 'sinatra_plain',
35
+ client_id: 'my-client-id',
36
+ client_secret: 'my-client-secret',
37
+ app_url: 'http://my-app-url:3002',
38
+ fidor_oauth_url: 'https://fidor-oauth-url.de/oauth',
39
+ fidor_api_url: 'https://fidor-api-url.de/api_sandbox'
40
+ }
41
+
42
+ zip_file_path = FidorStarterKits.build(opts)
43
+ # => /tmp/sinatra_plain-xyz/sinatra_plain.zip
44
+ # => mv / cp / download is up to you babee
45
+
46
+ ```
47
+ ## Build your own starter kit
48
+
49
+ As a quickstart for new developers we zip and download the examples in our
50
+ application manager. Before the following placeholders inside in your main
51
+ example.xy file are substituted with the according values from the app:
52
+
53
+ <APP_URL> # default http://localhost:8000/example.php
54
+ <CLIENT_ID>
55
+ <CLIENT_SECRET>
56
+ <FIDOR_OAUTH_URL> # e.g https://fidor.com/oauth
57
+ <FIDOR_API_URL>
58
+
59
+ So just add those to example.[rb, php, ..] and see existing examples and specs
60
+ for a reference.
61
+
62
+ ## Contributing
63
+
64
+ 1. Fork it ( http://github.com/schorsch/fidor_starter_kits/fork )
65
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
66
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
67
+ 4. Push to the branch (`git push origin my-new-feature`)
68
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec'
3
+ require 'rspec/core/rake_task'
4
+
5
+ desc 'Run specs'
6
+ RSpec::Core::RakeTask.new
7
+ task :default => :spec
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fidor_starter_kits/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fidor_starter_kits"
8
+ spec.version = FidorStarterKits::VERSION
9
+ spec.authors = ["Georg Leciejewski"]
10
+ spec.email = ["dev@fidor.de"]
11
+ spec.summary = %q{Starter Kits for building fidor apps.}
12
+ spec.description = %q{Fidor application examples for different languages }
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency "rubyzip"
22
+ spec.add_development_dependency "bundler", "~> 1.5"
23
+ spec.add_development_dependency "rake"
24
+ spec.add_development_dependency 'rspec'
25
+ end
@@ -0,0 +1,3 @@
1
+ module FidorStarterKits
2
+ VERSION = '0.1.2'
3
+ end
@@ -0,0 +1,63 @@
1
+ require 'fidor_starter_kits/version'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'zip'
5
+
6
+ module FidorStarterKits
7
+
8
+ STARTER_KITS = %w{ node_tx golang_plain php_plain sinatra_plain java_servlet }
9
+
10
+ class << self
11
+
12
+ # Directory path to starter kits
13
+ def path
14
+ File.expand_path('../starter_kits', File.dirname(__FILE__))
15
+ end
16
+
17
+ # Check if an app with the given name exists in the starter kits directory
18
+ # @param [String] app_name
19
+ # @return [Boolean]
20
+ def exists?(app_name)
21
+ File.exists? File.join(path, app_name)
22
+ end
23
+
24
+ # @param [Hash] opts options for building a starter kit
25
+ # @options opts [String] :app_name directory name of a starter kit
26
+ # @options opts [String] :client_id app client id
27
+ # @options opts [String] :client_secret app client secret
28
+ # @options opts [String] :app_url full url to the application
29
+ # @options opts [String] :fidor_oauth_url full url to the oauth endpoints e.g http://fidor.de/oauth
30
+ # @options opts [String] :fidor_api_url full url to the api or api_sandbox no trailing slashes e.g http://fidor.de/api_sandbox
31
+ # @return [Nil | String] path to zipped example im /tmp folder or nil if app does not exists
32
+ def build(opts)
33
+ app_name = opts[:app_name]
34
+ return if !app_name || !exists?(app_name) || !app_name.in?(STARTER_KITS)
35
+
36
+ # move example to a safe location
37
+ example_src_path = File.join(path, app_name)
38
+ tmp_src_dir = Dir.mktmpdir(app_name)
39
+ FileUtils.copy_entry example_src_path, tmp_src_dir
40
+
41
+ # read example files and replace placeholder with id/secret
42
+ example_files = File.join(tmp_src_dir, "**", "[Ee]xample.*")
43
+
44
+ Dir.glob(example_files) do |example_file|
45
+ content = File.read(example_file)
46
+ %w(client_id client_secret app_url fidor_oauth_url fidor_api_url).each do |field|
47
+ content.gsub!("<#{field.upcase}>", opts[field.to_sym]) if opts[field.to_sym]
48
+ end
49
+ File.write(example_file, content)
50
+ end
51
+
52
+ # create zip file in tmp dir
53
+ zip_file_path = File.join(tmp_src_dir, "#{app_name}.zip")
54
+ Zip::File.open(zip_file_path, Zip::File::CREATE) do |zipfile|
55
+ Dir.glob(File.join(tmp_src_dir, '**', '**'), File::FNM_DOTMATCH).each do |file|
56
+ zipfile.add(file.sub("#{tmp_src_dir}/", ''), file) unless file.end_with? '.'
57
+ end
58
+ end
59
+ zip_file_path
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe FidorStarterKits do
4
+
5
+ describe '.exists?' do
6
+ it 'is true' do
7
+ expect( FidorStarterKits.exists?('sinatra_plain') ).to be
8
+ end
9
+ it 'is false' do
10
+ expect( FidorStarterKits.exists?('sinatra_plain') ).to be
11
+ end
12
+ end
13
+
14
+ describe '.path' do
15
+ it 'exists' do
16
+ expect( File.exists?( FidorStarterKits.path) ).to be
17
+ end
18
+ end
19
+
20
+ describe '.build' do
21
+
22
+ it 'creates zip file' do
23
+ opts = {
24
+ app_name: 'sinatra_plain',
25
+ client_id: '123',
26
+ client_secret: '12345',
27
+ app_url: 'localhost'
28
+ }
29
+ res = FidorStarterKits.build(opts)
30
+ expect( File.exists?(res) ).to be
31
+ end
32
+
33
+ it 'replaces placeholders in example.rb' do
34
+ opts = {
35
+ app_name: 'sinatra_plain',
36
+ client_id: 'my-client-id',
37
+ client_secret: 'my-client-secret',
38
+ app_url: 'my-app-url',
39
+ fidor_oauth_url: 'fidor-oauth-url',
40
+ fidor_api_url: 'fidor-api-url'
41
+ }
42
+ res = FidorStarterKits.build(opts)
43
+ content = File.read(File.join(File.dirname(res), 'example.rb'))
44
+ expect( content ).to include 'my-client-id'
45
+ expect( content ).to include 'my-client-secret'
46
+ expect( content ).to include 'my-app-url'
47
+ expect( content ).to include 'fidor-api-url'
48
+ expect( content ).to include 'fidor-oauth-url'
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ $:.unshift(File.dirname(__FILE__))
3
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'rspec'
6
+ require 'fidor_starter_kits'
7
+ RSpec.configure do |config|
8
+ end
@@ -0,0 +1,21 @@
1
+ import (
2
+ "fmt"
3
+ "io/ioutil"
4
+ "net/http"
5
+ )
6
+
7
+
8
+ func main() {
9
+ http.HandleFunc("/", viewHandler)
10
+ http.ListenAndServe(":8080", nil)
11
+ }
12
+
13
+ func viewHandler(w http.ResponseWriter, r *http.Request) {
14
+
15
+ app_url := "<APP-URL>" //default: http://localhost:8080
16
+ client_id := "<CLIENT-ID>"
17
+ client_secret := "<CLIENT-SECRET>"
18
+ fidor_url := "<FIDOR-URL>"
19
+
20
+ fmt.Fprintf(w, "<h1>You made it!</h1><div>%s</div>", p)
21
+ }
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <classpath>
3
+ <classpathentry kind="src" path="src"/>
4
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 7 [1.7.0_51]">
5
+ <attributes>
6
+ <attribute name="owner.project.facets" value="java"/>
7
+ </attributes>
8
+ </classpathentry>
9
+ <classpathentry kind="con" path="org.eclipse.jst.server.core.container/org.eclipse.jst.server.tomcat.runtimeTarget/Apache Tomcat v7.0">
10
+ <attributes>
11
+ <attribute name="owner.project.facets" value="jst.web"/>
12
+ </attributes>
13
+ </classpathentry>
14
+ <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/>
15
+ <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/>
16
+ <classpathentry kind="output" path="build/classes"/>
17
+ </classpath>
@@ -0,0 +1,36 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>JavaServlet</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ <buildCommand>
9
+ <name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
10
+ <arguments>
11
+ </arguments>
12
+ </buildCommand>
13
+ <buildCommand>
14
+ <name>org.eclipse.jdt.core.javabuilder</name>
15
+ <arguments>
16
+ </arguments>
17
+ </buildCommand>
18
+ <buildCommand>
19
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
20
+ <arguments>
21
+ </arguments>
22
+ </buildCommand>
23
+ <buildCommand>
24
+ <name>org.eclipse.wst.validation.validationbuilder</name>
25
+ <arguments>
26
+ </arguments>
27
+ </buildCommand>
28
+ </buildSpec>
29
+ <natures>
30
+ <nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
31
+ <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
32
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
33
+ <nature>org.eclipse.jdt.core.javanature</nature>
34
+ <nature>org.eclipse.wst.jsdt.core.jsNature</nature>
35
+ </natures>
36
+ </projectDescription>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <classpath>
3
+ <classpathentry kind="src" path="WebContent"/>
4
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
5
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.WebProject">
6
+ <attributes>
7
+ <attribute name="hide" value="true"/>
8
+ </attributes>
9
+ </classpathentry>
10
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
11
+ <classpathentry kind="output" path=""/>
12
+ </classpath>
@@ -0,0 +1,7 @@
1
+ eclipse.preferences.version=1
2
+ org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
3
+ org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
4
+ org.eclipse.jdt.core.compiler.compliance=1.7
5
+ org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
6
+ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
7
+ org.eclipse.jdt.core.compiler.source=1.7
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project-modules id="moduleCoreId" project-version="1.5.0">
3
+ <wb-module deploy-name="JavaServlet">
4
+ <wb-resource deploy-path="/" source-path="/WebContent" tag="defaultRootSource"/>
5
+ <wb-resource deploy-path="/WEB-INF/classes" source-path="/src"/>
6
+ <property name="context-root" value="JavaServlet"/>
7
+ <property name="java-output-path" value="/JavaServlet/build/classes"/>
8
+ </wb-module>
9
+ </project-modules>
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <faceted-project>
3
+ <runtime name="Apache Tomcat v7.0"/>
4
+ <fixed facet="jst.web"/>
5
+ <fixed facet="wst.jsdt.web"/>
6
+ <fixed facet="java"/>
7
+ <installed facet="java" version="1.7"/>
8
+ <installed facet="jst.web" version="3.0"/>
9
+ <installed facet="wst.jsdt.web" version="1.0"/>
10
+ </faceted-project>
@@ -0,0 +1 @@
1
+ org.eclipse.wst.jsdt.launching.baseBrowserLibrary
@@ -0,0 +1,3 @@
1
+ Manifest-Version: 1.0
2
+ Class-Path:
3
+
@@ -0,0 +1,113 @@
1
+ package de.fidor.api.example;
2
+
3
+ import java.io.IOException;
4
+ import java.io.PrintWriter;
5
+ import java.net.URI;
6
+ import java.net.URISyntaxException;
7
+
8
+ import javax.servlet.ServletException;
9
+ import javax.servlet.annotation.WebServlet;
10
+ import javax.servlet.http.HttpServlet;
11
+ import javax.servlet.http.HttpServletRequest;
12
+ import javax.servlet.http.HttpServletResponse;
13
+
14
+ import org.apache.http.HttpResponse;
15
+ import org.apache.http.client.ClientProtocolException;
16
+ import org.apache.http.client.HttpClient;
17
+ import org.apache.http.client.methods.HttpUriRequest;
18
+ import org.apache.http.client.methods.RequestBuilder;
19
+ import org.apache.http.impl.client.HttpClients;
20
+ import org.apache.http.util.EntityUtils;
21
+ import org.json.simple.JSONObject;
22
+ import org.json.simple.parser.JSONParser;
23
+ import org.json.simple.parser.ParseException;
24
+
25
+ @WebServlet("/Example")
26
+ public class Example extends HttpServlet {
27
+ private static final long serialVersionUID = 1L;
28
+ private final String app_url = "<APP_URL>";
29
+ private final String client_id = "<CLIENT_ID>";
30
+ private final String client_secret = "<CLIENT_SECRET>";
31
+ private final String fidor_oauth_url = "<FIDOR_API_URL>/oauth";
32
+ private final String fidor_api_url = "<FIDOR_API_URL>";
33
+ private HttpClient httpClient;
34
+ private JSONParser parser;
35
+
36
+ /**
37
+ * @see HttpServlet#HttpServlet()
38
+ */
39
+ public Example() {
40
+ super();
41
+ httpClient = HttpClients.createDefault();
42
+ parser = new JSONParser();
43
+ }
44
+
45
+ /**
46
+ * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
47
+ */
48
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
49
+ String code = request.getParameter("code");
50
+ if(code == null) {
51
+ response.sendRedirect(getCodeUrl());
52
+ } else {
53
+ try {
54
+ String token = getToken(code);
55
+ if(token != null) {
56
+ HttpResponse userResponse = httpClient.execute(RequestBuilder.get().setUri(getUserUri(token)).build());
57
+ String body = EntityUtils.toString(userResponse.getEntity());
58
+ JSONObject user = (JSONObject)parser.parse(body);
59
+
60
+ response.setContentType("text/html");
61
+ PrintWriter writer = response.getWriter();
62
+ writer.append(
63
+ "<h2>Hello " + user.get("email") + "</h2>"
64
+ + "<i>May i present the access token response:</i>"
65
+ + "<blockquote>" + body + "</blockquote>"
66
+ + "<p>Now use the access token in <br> <a href='" + getAccountUrl(token) +"'>" + getAccountUrl(token) +"</a></p>");
67
+ }
68
+ } catch (URISyntaxException e) {
69
+ e.printStackTrace();
70
+ } catch (ParseException e) {
71
+ e.printStackTrace();
72
+ }
73
+
74
+ }
75
+ }
76
+
77
+ private URI getUserUri(String token) throws URISyntaxException {
78
+ return new URI(fidor_api_url + "/users/current?access_token=" + token);
79
+ }
80
+
81
+ private String getToken(String code) throws URISyntaxException, ClientProtocolException, IOException, ParseException {
82
+ HttpUriRequest req = (HttpUriRequest) RequestBuilder.post()
83
+ .setUri(getTokenUri())
84
+ .addParameter("client_id", client_id)
85
+ .addParameter("redirect_uri", app_url)
86
+ .addParameter("code", code)
87
+ .addParameter("client_secret", client_secret)
88
+ .build();
89
+
90
+ HttpResponse resp = httpClient.execute(req);
91
+ String body = EntityUtils.toString(resp.getEntity());
92
+ Object jsonResponse = parser.parse(body);
93
+ if(jsonResponse instanceof JSONObject) {
94
+ JSONObject responseObject = (JSONObject)jsonResponse;
95
+ return responseObject.get("access_token").toString();
96
+ } else {
97
+ return null;
98
+ }
99
+ }
100
+
101
+ private URI getTokenUri() throws URISyntaxException {
102
+ return new URI(fidor_oauth_url + "/token");
103
+ }
104
+
105
+ private String getCodeUrl() {
106
+ return fidor_oauth_url + "/authorize?client_id=" + client_id + "&redirect_uri=" + app_url;
107
+ }
108
+
109
+ private String getAccountUrl(String token) {
110
+ return fidor_api_url + "/accounts?access_token=" + token;
111
+ }
112
+
113
+ }
@@ -0,0 +1,344 @@
1
+ // This is an example node.js app that demonstrates the use of the Fidor
2
+ // API to retrieve information about transactions executed by a user's
3
+ // customer.
4
+ //
5
+ // This sample intentionally does not require any further dependencies.
6
+ // We tried to keep things as simple as possible in order to illustrate the
7
+ // underlying mechanisms of the API.
8
+ //
9
+ // Using this code
10
+ // ---------------
11
+ //
12
+ // In order to use this code, a sample app needs to be installed in the
13
+ // Fidor App Manager. Settings for this app are displayed in the app
14
+ // manager an need to be transfered into the config below:
15
+
16
+ var fidor_config = {
17
+ app_port : 3141, // you might want to change this to match your app_url
18
+ app_url : "<APP_URL>",
19
+ client_id : "<CLIENT_ID>",
20
+ client_secret : "<CLIENT_SECRET>",
21
+ fidor_api_url : "<FIDOR_API_URL>"
22
+ }
23
+
24
+ // This app can be started by executing:
25
+ //
26
+ // node server.js
27
+ //
28
+ // Once the app is running, it can be accessed on:
29
+ //
30
+ // http://localhost:3001
31
+ //
32
+ // or on whatever port is configured above.
33
+ //
34
+ // The app checks whether an OAuth access token is available for the
35
+ // user, if not, the user is redirected to the OAuth endpoit. After
36
+ // successful authentication and authorization, the OAuth endpoint
37
+ // redirects the user back to the app. Specifically to:
38
+ //
39
+ // http://localhost:3001/code
40
+ //
41
+ // This redirect will contain a query with the OAuth code. The app uses
42
+ // this code to request an OAuth access-token. The retrieved token is stored with
43
+ // the users session and is used to authenticate API requests.
44
+
45
+ var http = require('http')
46
+ var url = require('url')
47
+ var querystring = require('querystring')
48
+
49
+
50
+
51
+ // the actual call to the api.
52
+ // @param accessToken : OAuth access_token for this user.
53
+ // @param cb : callback function(err, transactions)
54
+ function apiGetTransactions(accessToken, cb) {
55
+ // URL endpoint to call to retrieve transactions.
56
+ var tx_url = fidor_config.fidor_api_url+
57
+ "/transactions?access_token="+
58
+ accessToken
59
+
60
+ var get = http.get(tx_url, function(res){
61
+ // response may come in numerous chunks, we need to collect
62
+ // them and reassemble the entire answer when all data has
63
+ // been retrieved.
64
+ var data = new Buffer(0)
65
+ res.on('data', function(chunk){
66
+ data = Buffer.concat([data, chunk])
67
+ })
68
+ res.on('end', function() {
69
+ if (res.statusCode !== 200) {
70
+ cb(data.toString(), null)
71
+ return
72
+ }
73
+ var d = JSON.parse(data)
74
+ cb(d.error, d.transactions)
75
+ })
76
+ })
77
+
78
+ get.on('error', function(e) {
79
+ cb (e, null)
80
+ })
81
+ }
82
+
83
+ //
84
+ // The handle for the Apps /transactions endpoint.
85
+ // @param request : http request object
86
+ // @param response : http response
87
+ //
88
+ function getTransactions(request, response) {
89
+ var cookie_token = getCookie(request, "oauth_token")
90
+ // if we don't have a token for this user already, redirect
91
+ // the user to the OAuth server
92
+ if (!cookie_token) {
93
+ var oauth_url = fidor_config.fidor_api_url+"/oauth/authorize?client_id="+
94
+ fidor_config.client_id+"&redirect_uri="+fidor_config.app_url+":"+
95
+ fidor_config.app_port+"/code"
96
+ response.writeHead(307, {"location" : oauth_url})
97
+ response.end()
98
+ return
99
+ }
100
+
101
+ // trade in the key we stored in the cookie for the actual oauth token.
102
+ var oauth_token = getToken(cookie_token)
103
+
104
+ // call the api with the OAuth token:
105
+ apiGetTransactions(oauth_token, function (err, transactions) {
106
+ if (err) {
107
+ // 500 Server Error in case of any problems
108
+
109
+ console.log(">>>error")
110
+ console.log(err)
111
+ console.log("<<<error")
112
+
113
+ response.writeHead(500, "Borked.")
114
+ response.end("Borked.")
115
+ return
116
+ }
117
+ // if everything went well, dump the received json.
118
+ response.writeHead(200, {"Content-Type": "application/json; charset=utf-8"})
119
+ response.write(JSON.stringify(transactions, null, " "))
120
+ response.end()
121
+
122
+ })
123
+ }
124
+
125
+ // Trades the `code` we received from the OAuth server via client
126
+ // redirect for an actual access_token. To do this, the code needs to be
127
+ // send to the correct OAuth endppoint together with client_id and
128
+ // client_secret.
129
+ function getOAuthToken(code, cb) {
130
+ var oauth_url = url.parse(fidor_config.fidor_api_url)
131
+
132
+ // where to send the data ...
133
+ var postOptions = {
134
+ method: "POST",
135
+ path : oauth_url.path+"/oauth/token",
136
+ port : oauth_url.port,
137
+ host : oauth_url.hostname
138
+ }
139
+
140
+ // ... what to send
141
+ var postData = {
142
+ code : code,
143
+ client_id : fidor_config.client_id,
144
+ client_secret : fidor_config.client_secret
145
+ }
146
+ postData = querystring.stringify(postData)
147
+
148
+ var token_request = http.request(postOptions, function (res) {
149
+ // collect the data chunks we received and reassemble them
150
+ // on request end ...
151
+ var data = new Buffer(0)
152
+ res.on('data', function(chunk) {
153
+ data = Buffer.concat([data, chunk])
154
+ })
155
+
156
+ res.on('end', function() {
157
+ var oauth_response = JSON.parse(data)
158
+ cb(oauth_response.error, oauth_response.access_token)
159
+ })
160
+ })
161
+
162
+ token_request.on('error', function(e) {
163
+ cb(e, null)
164
+ })
165
+
166
+ token_request.write(postData);
167
+ token_request.end()
168
+ }
169
+
170
+ // handler we provide to handle our user being redirected back
171
+ // to us from the OAuth server with the OAuth `code` in the
172
+ // query of the url.
173
+ function setOAuthToken(request, response) {
174
+ var u = url.parse(request.url)
175
+ var code = querystring.parse(u.query)["code"]
176
+
177
+ // if the request does not contain a ?code=adsfasdfasdf
178
+ // query, it's not valid.
179
+ if (!code) {
180
+ response.writeHead(400, "Bad Request")
181
+ response.end()
182
+ return
183
+ }
184
+
185
+ // exchange the code for a token ...
186
+ getOAuthToken(code, function (err, token) {
187
+ if (err) {
188
+ console.log(">>>error")
189
+ console.log(err)
190
+ console.log("<<<error")
191
+
192
+ response.writeHead(500, "Borked.")
193
+ response.end("Borked.")
194
+ return
195
+ }
196
+
197
+ // once we have the token, we store it in our app (see below). The
198
+ // OAuth token is stored under a random key. We set this key in the
199
+ // user's cookie in order to retrieve the access_token whenever we
200
+ // may need it in the future without leaking the actual access_token
201
+ // via the cookie
202
+
203
+ var token_key = storeToken(token)
204
+ setCookie(response, "oauth_token", token_key)
205
+
206
+ // send the user back to the transactions url, this time, with a
207
+ // valid access_token (via the cookie we just set)
208
+ response.writeHead(307, {"location" : "/transactions"})
209
+ response.end()
210
+ })
211
+ }
212
+
213
+ // handler for all http requests to our app ...
214
+ function listener(req, resp) {
215
+ // reject everything but GET.
216
+ if (req.method !== "GET") {
217
+ resp.writeHead(403, "Forbidden")
218
+ }
219
+
220
+ var u = url.parse(req.url)
221
+ switch (u.pathname) {
222
+ case "/":
223
+ console.log("start page ...")
224
+ resp.writeHead(200, {"Content-Type" : "text/html"})
225
+ resp.end(hello_template)
226
+ break
227
+ case "/transactions":
228
+ console.log("transactions ...")
229
+ getTransactions(req, resp)
230
+ break
231
+ // utility code to remove cookies in order to force OAuth.
232
+ case "/code":
233
+ console.log("received code redirect ...")
234
+ setOAuthToken(req, resp)
235
+ break
236
+ case "/clear_all_cookies":
237
+ console.log("clearing cookies")
238
+ clearCookies(req, resp)
239
+ resp.writeHead(307, {"location": "/"})
240
+ resp.end()
241
+ break
242
+ case "/clear_cookie":
243
+ console.log("clearing oauth cookie")
244
+
245
+ var key = getCookie(req, "oauth_token")
246
+ deleteToken(key)
247
+
248
+ clearCookie(resp, "oauth_token")
249
+ resp.writeHead(307, {"location": "/"})
250
+ resp.end()
251
+ break
252
+ default:
253
+ console.log("unknown: "+u.path)
254
+ resp.end()
255
+ }
256
+
257
+ }
258
+
259
+ // start the server
260
+ var server = http.createServer(listener)
261
+ server.listen(fidor_config.app_port)
262
+
263
+ console.log("listening on : "+fidor_config.app_port)
264
+
265
+
266
+
267
+ var hello_template = ""+
268
+ "<DOCTYPE html>" +
269
+ "<html>" +
270
+ "<head></head>" +
271
+ "<body>" +
272
+ " <h1>Welcome to Transaction Getter!</h1>" +
273
+ " <p><a href='/transactions'>Get Transactions</a></p>" +
274
+ " <p><a href='/clear_cookie'>Clear Cookie</a></p>" +
275
+ " <p><a href='/clear_all_cookies'>Clear All Cookies</a></p>" +
276
+ "</body>" +
277
+ "</html>"
278
+
279
+ // Code for setting and clearing cookies. Typically a framework would
280
+ // handle the intricacies of cookie handling, but we opted not to
281
+ // require any additional dependancies for this example so we need to
282
+ // handle cookies manually...
283
+
284
+ function setCookie(response, key, value) {
285
+ response.setHeader("Set-Cookie", key+"="+value)
286
+ }
287
+
288
+ function clearCookie(response, key) {
289
+ console.log("clearing: "+key)
290
+ if (key.map) {
291
+ key = key.map(function (k) {
292
+ return k+"=delete; expires=Thu, 01 Jan 1970 00:00:00 GMT"
293
+ })
294
+ } else {
295
+ key = key+"=delete; expires=Thu, 01 Jan 1970 00:00:00 GMT"
296
+ }
297
+ console.log(key)
298
+ response.setHeader("Set-Cookie", key)
299
+ }
300
+
301
+ function clearCookies(request, response){
302
+ clearCookie(response, Object.keys(getCookies(request)))
303
+ }
304
+
305
+
306
+ function getCookies(request) {
307
+ var cookies = {}
308
+ var c = request.headers["cookie"]
309
+ if (c) {
310
+ c.split(';').forEach(function(cookie){
311
+ var c = cookie.split("=")
312
+ cookies[c[0].trim()] = c[1]
313
+ })
314
+ }
315
+ return cookies
316
+ }
317
+
318
+ function getCookie(request, key) {
319
+ var cookies = getCookies(request)
320
+ return cookies[key]
321
+ }
322
+
323
+ // token storage
324
+
325
+
326
+ var tokens = {}
327
+
328
+ function storeToken (token) {
329
+ var key = ""
330
+ for (var i =0 ; i!= 16; ++i) {
331
+ key += Math.floor((Math.random() * 256)).toString(16)
332
+ }
333
+ tokens[key] = token
334
+ return key
335
+ }
336
+ function getToken(key) {
337
+ return tokens[key]
338
+ }
339
+
340
+ function deleteToken (key) {
341
+ var tok = tokens[key]
342
+ delete tokens[key]
343
+ return tok
344
+ }
@@ -0,0 +1,14 @@
1
+ # Plain PHP Login - Example
2
+
3
+ A single view showing how to get an access token
4
+
5
+ ## Usage
6
+
7
+ Start your local php server:
8
+
9
+ php -S localhost:8000
10
+
11
+ And point your browser to http://localhost:8000/example.php
12
+
13
+ If you run the example under a different url, please update the url in the
14
+ source file
@@ -0,0 +1,49 @@
1
+ <?php
2
+
3
+ $app_url = "<APP_URL>"; # default http://localhost:8000/example.php
4
+ $app_id = "<CLIENT_ID>";
5
+ $app_secret = "<CLIENT_SECRET>";
6
+ $fidor_oauth_url = "<FIDOR_OAUTH_URL>"; # e.g https://fidor.com/api_sandbox/oauth
7
+ $fidor_api_url = "<FIDOR_API_URL>"; # e.g https://fidor.com/api_sandbox vs /api
8
+
9
+ $code = $_REQUEST["code"];
10
+ # 1. redirect to authorize url
11
+ if(empty($code)) {
12
+ $dialog_url = $fidor_oauth_url . "/authorize?" .
13
+ "client_id=". $app_id .
14
+ "&redirect_uri=" . urlencode($app_url);
15
+
16
+ echo("<script> top.location.href='" . $dialog_url . "'</script>");
17
+ }
18
+ # 2. build url to get the access token
19
+ $token_url = $fidor_oauth_url . "/token";
20
+
21
+ $data = array('client_id' => $app_id,
22
+ 'client_secret' => $app_secret,
23
+ 'code' => $code,
24
+ 'redirect_uri' => urlencode($app_url)
25
+ );
26
+ // use key 'http' even if you send the request to https://...
27
+ $options = array(
28
+ 'http' => array(
29
+ 'header' => "Content-type: application/x-www-form-urlencoded\r\n",
30
+ 'method' => 'POST',
31
+ 'content' => http_build_query($data),
32
+ ),
33
+ );
34
+ # get the access_token, use PHP5 internal POST method
35
+ $context = stream_context_create($options);
36
+ $resp = json_decode(file_get_contents($token_url, false, $context));
37
+
38
+ # 3. GET Data e.g.info about current user
39
+ $usr_url = $fidor_api_url . "/users/current?access_token=" . $resp->access_token;
40
+ $user = json_decode(file_get_contents($usr_url));
41
+ $transactions_url = $fidor_api_url . "/transactions?access_token=" . $resp->access_token;
42
+ echo( "<h2>Hello " . $user->email . "</h2>
43
+ <i>May i present the access token response:</i>
44
+ <blockquote>");
45
+ print_r($resp);
46
+ echo("</blockquote>
47
+ <p>Now use the access token in <br> <a href='" . $transactions_url . "'>".$transactions_url."</a></p>");
48
+
49
+ ?>
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gem 'activesupport'
3
+ gem 'sinatra'
4
+ gem 'curb'
@@ -0,0 +1,14 @@
1
+ = Plain Login - Example
2
+
3
+ A single view showing how to get an access token
4
+
5
+ == Usage
6
+
7
+ This uses bundler to install the required gems:
8
+
9
+ cd sinatra_plain
10
+ bundle install
11
+ ruby example.rb
12
+
13
+ # or run on different port than :4567 provided by WEBrick
14
+ ruby example.rb -p 3004
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+ require 'active_support/json'
4
+ require 'net/http'
5
+
6
+ get '/' do
7
+ # settings
8
+ @app_url = '<APP_URL>' # default for local installs: http://localhost:4567
9
+ @client_id = '<CLIENT_ID>'
10
+ @client_secret = '<CLIENT_SECRET>'
11
+ @fidor_oauth_url = '<FIDOR_OAUTH_URL>' # e.g https://fidor.com/oauth
12
+ @fidor_api_url = '<FIDOR_API_URL>' # e.g https://fidor.com/api_sandbox, https://fidor.com/api
13
+
14
+ # 1. redirect to authorize url
15
+ unless code = params["code"]
16
+ dialog_url = "#{@fidor_oauth_url}/authorize?client_id=#{@client_id}&redirect_uri=#{CGI::escape(@app_url)}"
17
+ redirect dialog_url
18
+ end
19
+
20
+ # 2. get the access token, with code returned from auth dialog above
21
+ token_url = URI("#{@fidor_oauth_url}/token")
22
+ # GET and parse access_token response json
23
+ res = Net::HTTP.post_form(token_url, 'client_id' => @client_id,
24
+ 'redirect_uri' => CGI::escape(@app_url),
25
+ 'code' =>code,
26
+ 'client_secret'=>@client_secret)
27
+ resp = ActiveSupport::JSON.decode(res.body)
28
+
29
+ # GET current user
30
+ usr_url = "#{@fidor_api_url}/users/current?access_token=#{resp['access_token']}"
31
+ user = ActiveSupport::JSON.decode( Net::HTTP.get URI(usr_url) )
32
+ account_url = "#{@fidor_api_url}/accounts?access_token=#{resp['access_token']}"
33
+ "<h2>Hello #{user['email']}</h2>
34
+ <i>May i present the access token response:</i>
35
+ <blockquote>#{resp.inspect}</blockquote>
36
+ <p>Now use the access token in <br> <a href='#{account_url}'>#{account_url}</a></p>
37
+ "
38
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fidor_starter_kits
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Georg Leciejewski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-10-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rubyzip
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: 'Fidor application examples for different languages '
70
+ email:
71
+ - dev@fidor.de
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - fidor_starter_kits.gemspec
82
+ - lib/fidor_starter_kits.rb
83
+ - lib/fidor_starter_kits/version.rb
84
+ - spec/fidor_starter_kits_spec.rb
85
+ - spec/spec_helper.rb
86
+ - starter_kits/golang_plain/example.go
87
+ - starter_kits/java_servlet/.classpath
88
+ - starter_kits/java_servlet/.project
89
+ - starter_kits/java_servlet/.settings/.jsdtscope
90
+ - starter_kits/java_servlet/.settings/org.eclipse.jdt.core.prefs
91
+ - starter_kits/java_servlet/.settings/org.eclipse.wst.common.component
92
+ - starter_kits/java_servlet/.settings/org.eclipse.wst.common.project.facet.core.xml
93
+ - starter_kits/java_servlet/.settings/org.eclipse.wst.jsdt.ui.superType.container
94
+ - starter_kits/java_servlet/.settings/org.eclipse.wst.jsdt.ui.superType.name
95
+ - starter_kits/java_servlet/WebContent/META-INF/MANIFEST.MF
96
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/commons-codec-1.6.jar
97
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/commons-logging-1.1.3.jar
98
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/fluent-hc-4.3.5.jar
99
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/httpclient-4.3.5.jar
100
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/httpclient-cache-4.3.5.jar
101
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/httpcore-4.3.2.jar
102
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/httpmime-4.3.5.jar
103
+ - starter_kits/java_servlet/WebContent/WEB-INF/lib/json-simple-1.1.1.jar
104
+ - starter_kits/java_servlet/src/de/fidor/api/example/Example.java
105
+ - starter_kits/node_tx/example.js
106
+ - starter_kits/php_plain/README.md
107
+ - starter_kits/php_plain/example.php
108
+ - starter_kits/sinatra_plain/Gemfile
109
+ - starter_kits/sinatra_plain/README.md
110
+ - starter_kits/sinatra_plain/example.rb
111
+ homepage: ''
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.2.2
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Starter Kits for building fidor apps.
135
+ test_files:
136
+ - spec/fidor_starter_kits_spec.rb
137
+ - spec/spec_helper.rb