├── .bowerrc
├── .csslintrc
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── Dockerfile
├── LICENSE
├── README.md
├── Vagrantfile
├── application.js
├── bootstrap.sh
├── bower.json
├── config.js
├── config
├── config.js
├── env
│ ├── all.js
│ ├── development.js
│ └── production.js
└── init.js
├── dashboard.css
├── gruntfile.js
├── humans.txt
├── index.html
├── karma.conf.js
├── modules
├── apis
│ ├── apis.client.module.js
│ ├── config
│ │ ├── apis.client.config.js
│ │ ├── apis.client.constants.js
│ │ └── apis.client.routes.js
│ ├── controllers
│ │ └── apis.client.controller.js
│ ├── directives
│ │ └── pluginform.client.directive.js
│ ├── services
│ │ └── apis.client.service.js
│ ├── tests
│ │ └── apis.client.controller.test.js
│ └── views
│ │ ├── create-api.client.view.html
│ │ ├── edit-api.client.view.html
│ │ ├── list-apis.client.view.html
│ │ ├── plugin.form.directive.html
│ │ ├── view-api-plugin.client.view.html
│ │ └── view-api.client.view.html
├── consumers
│ ├── config
│ │ ├── consumers.client.config.js
│ │ └── consumers.client.routes.js
│ ├── consumers.client.module.js
│ ├── controllers
│ │ └── consumers.client.controller.js
│ ├── services
│ │ └── consumers.client.service.js
│ ├── tests
│ │ └── consumers.client.controller.test.js
│ └── views
│ │ ├── create-consumer.client.view.html
│ │ ├── crud-plugin-consumer.client.view.html
│ │ ├── edit-consumer.client.view.html
│ │ ├── list-consumers.client.view.html
│ │ ├── view-api-plugin-consumer.client.view.html
│ │ └── view-consumer.client.view.html
└── core
│ ├── config
│ ├── core.client.constants.js
│ └── core.client.routes.js
│ ├── controllers
│ ├── header.client.controller.js
│ └── home.client.controller.js
│ ├── core.client.module.js
│ ├── css
│ └── core.css
│ ├── img
│ ├── brand
│ │ ├── favicon.ico
│ │ └── logo.png
│ └── loaders
│ │ └── loader.gif
│ ├── services
│ └── menus.client.service.js
│ ├── tests
│ ├── header.client.controller.test.js
│ └── home.client.controller.test.js
│ └── views
│ ├── header.client.view.html
│ └── home.client.view.html
├── package.json
├── robots.txt
└── test
├── .jshintrc
├── karma.conf.js
└── spec
└── controllers
├── about.js
├── apis.js
├── consumers.js
└── main.js
/.bowerrc:
--------------------------------------------------------------------------------
1 | {
2 | "directory": "lib"
3 | }
4 |
--------------------------------------------------------------------------------
/.csslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "adjoining-classes": false,
3 | "box-model": false,
4 | "box-sizing": false,
5 | "floats": false,
6 | "font-sizes": false,
7 | "important": false,
8 | "known-properties": false,
9 | "overqualified-elements": false,
10 | "qualified-headings": false,
11 | "regex-selectors": false,
12 | "unique-headings": false,
13 | "universal-selector": false,
14 | "unqualified-attributes": false
15 | }
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 |
8 | [*]
9 |
10 | # Change these settings to your own preference
11 | indent_style = space
12 | indent_size = 2
13 |
14 | # We recommend you to keep these unchanged
15 | end_of_line = lf
16 | charset = utf-8
17 | trim_trailing_whitespace = true
18 | insert_final_newline = true
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .tmp
4 | .sass-cache
5 | lib/
6 | .vagrant
7 | npm-debug.log
8 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment.
3 | "browser": true, // Standard browser globals e.g. `window`, `document`.
4 | "esnext": true, // Allow ES.next specific features such as `const` and `let`.
5 | "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.).
6 | "camelcase": false, // Permit only camelcase for `var` and `object indexes`.
7 | "curly": false, // Require {} for every new block or scope.
8 | "eqeqeq": true, // Require triple equals i.e. `===`.
9 | "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
10 | "latedef": true, // Prohibit variable use before definition.
11 | "newcap": true, // Require capitalization of all constructor functions e.g. `new F()`.
12 | "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`.
13 | "quotmark": "single", // Define quotes to string values.
14 | "regexp": true, // Prohibit `.` and `[^...]` in regular expressions.
15 | "undef": true, // Require all non-global variables be declared before they are used.
16 | "unused": false, // Warn unused variables.
17 | "strict": true, // Require `use strict` pragma in every file.
18 | "trailing": true, // Prohibit trailing whitespaces.
19 | "smarttabs": false, // Suppresses warnings about mixed tabs and spaces
20 | "globals": { // Globals variables.
21 | "jasmine": true,
22 | "angular": true,
23 | "ApplicationConfiguration": true
24 | },
25 | "predef": [ // Extra globals.
26 | "define",
27 | "require",
28 | "exports",
29 | "module",
30 | "describe",
31 | "before",
32 | "beforeEach",
33 | "after",
34 | "afterEach",
35 | "it",
36 | "inject",
37 | "expect",
38 | "URI"
39 | ],
40 | "indent": 4, // Specify indentation spacing
41 | "devel": true, // Allow development statements e.g. `console.log();`.
42 | "noempty": true // Prohibit use of empty blocks.
43 | }
44 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 'iojs'
5 | - '0.12'
6 | - '0.10'
7 | before_script:
8 | - 'npm install -g bower grunt-cli'
9 | - 'bower install'
10 |
--------------------------------------------------------------------------------
/.yo-rc.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:wheezy
2 |
3 | MAINTAINER mike@mikangali.com
4 |
5 | WORKDIR /app
6 |
7 | # Install meanjs tools
8 | RUN npm install -g grunt-cli
9 | RUN npm install -g bower
10 |
11 | # Get mean quick start app
12 | RUN git clone https://github.com/rsdevigo/jungle.git .
13 |
14 | # ADD package.json /app
15 | # ADD .bowerrc /app
16 |
17 | # Install Mean.JS packages
18 | RUN npm install
19 |
20 | # Manually trigger bower. Why doesnt this work via npm install?
21 | RUN bower install --config.interactive=false --allow-root
22 |
23 | # currently only works for development
24 | ENV NODE_ENV development
25 | ENV KONGURL http://localhost:8001
26 |
27 | RUN echo "'use strict'; angular.module('core').constant('KONGURL', ['$KONGURL']);" > modules/core/config/core.client.constants.js
28 |
29 | RUN cat modules/core/config/core.client.constants.js
30 |
31 | # Expose ports: server (3000), livereload (35729)
32 | EXPOSE 3000 35729
33 | CMD ["grunt", "serve"]
34 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JUNGLE
2 |
3 | [](https://gitter.im/rsdevigo/jungle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4 | [](https://travis-ci.org/rsdevigo/jungle)
5 |
6 | 
7 |
8 | GUI to [KONG](http://getkong.org).
9 |
10 | ## Prerequisites
11 | - Nodejs
12 | - Npm
13 | - Grunt
14 | - Bower
15 |
16 | ## Install dependencies
17 | ```
18 | $ [sudo] npm install
19 | $ bower install
20 | ```
21 |
22 | ## Configure the Kong url
23 | In ./modules/core/config/core.client.constants.js change the KONGURL.
24 |
25 | ## Build & development
26 |
27 | - Run `grunt` for building and `grunt serve` for preview.
28 |
29 | - Open the address http://localhost:3000
30 |
31 | ## Testing
32 |
33 | Running `grunt test` will run the unit tests with karma.
34 |
35 | ## Gitter chat & Community
36 |
37 | [](https://gitter.im/rsdevigo/jungle?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
38 |
39 | ## Roadmap for version 0.0.1
40 |
41 | - Error treatment for messages from server.
42 | - Paginate results using the *next* attribute of response from server.
43 | - Add the all plugins in PLUGINSAVAILABLE. 
44 | - Create a active plugin per consumer in API section.
45 | - Add favicon and Jungle logo 
46 | - Dockerfile 
47 | - Write the tests 
48 | - Add travis webhook 
49 |
50 |
51 | ## License
52 |
53 | ```
54 | Copyright 2015 Jungle Contributors
55 |
56 | Licensed under the Apache License, Version 2.0 (the "License");
57 | you may not use this file except in compliance with the License.
58 | You may obtain a copy of the License at
59 |
60 | http://www.apache.org/licenses/LICENSE-2.0
61 |
62 | Unless required by applicable law or agreed to in writing, software
63 | distributed under the License is distributed on an "AS IS" BASIS,
64 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
65 | See the License for the specific language governing permissions and
66 | limitations under the License.
67 | ```
68 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | # -*- mode: ruby -*-
2 | # vi: set ft=ruby :
3 |
4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
5 | VAGRANTFILE_API_VERSION = "2"
6 |
7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
8 | # All Vagrant configuration is done here. The most common configuration
9 | # options are documented and commented below. For a complete reference,
10 | # please see the online documentation at vagrantup.com.
11 |
12 | # Every Vagrant virtual environment requires a box to build off of.
13 | config.vm.box = "ubuntu/trusty64"
14 | config.vm.provision :shell, path: "bootstrap.sh"
15 |
16 | # Disable automatic box update checking. If you disable this, then
17 | # boxes will only be checked for updates when the user runs
18 | # `vagrant box outdated`. This is not recommended.
19 | # config.vm.box_check_update = false
20 |
21 | # Create a forwarded port mapping which allows access to a specific port
22 | # within the machine from a port on the host machine. In the example below,
23 | # accessing "localhost:8080" will access port 80 on the guest machine.
24 | # config.vm.network "forwarded_port", guest: 8000, host: 8000
25 | # config.vm.network "forwarded_port", guest: 8001, host: 8001
26 | config.vm.network "forwarded_port", guest: 3000, host: 3000
27 |
28 | # Create a private network, which allows host-only access to the machine
29 | # using a specific IP.
30 | # config.vm.network "private_network", ip: "192.168.33.10"
31 |
32 | # Create a public network, which generally matched to bridged network.
33 | # Bridged networks make the machine appear as another physical device on
34 | # your network.
35 | # config.vm.network "public_network"
36 |
37 | # If true, then any SSH connections made will enable agent forwarding.
38 | # Default value: false
39 | # config.ssh.forward_agent = true
40 |
41 | # Share an additional folder to the guest VM. The first argument is
42 | # the path on the host to the actual folder. The second argument is
43 | # the path on the guest to mount the folder. And the optional third
44 | # argument is a set of non-required options.
45 | # config.vm.synced_folder "workspace", "/home/vagrant/workspace"
46 |
47 | # Provider-specific configuration so you can fine-tune various
48 | # backing providers for Vagrant. These expose provider-specific options.
49 | # Example for VirtualBox:
50 | #
51 | config.vm.provider "virtualbox" do |vb|
52 | # # Don't boot with headless mode
53 | # vb.gui = true
54 | #
55 | # # Use VBoxManage to customize the VM. For example to change memory:
56 | vb.name = "jungle"
57 | vb.memory = 1024
58 | # vb.customize ["modifyvm", :id, "--memory", "1024"]
59 | end
60 | #
61 | # View the documentation for the provider you're using for more
62 | # information on available options.
63 |
64 | # Enable provisioning with CFEngine. CFEngine Community packages are
65 | # automatically installed. For example, configure the host as a
66 | # policy server and optionally a policy file to run:
67 | #
68 | # config.vm.provision "cfengine" do |cf|
69 | # cf.am_policy_hub = true
70 | # # cf.run_file = "motd.cf"
71 | # end
72 | #
73 | # You can also configure and bootstrap a client to an existing
74 | # policy server:
75 | #
76 | # config.vm.provision "cfengine" do |cf|
77 | # cf.policy_server_address = "10.0.2.15"
78 | # end
79 |
80 | # Enable provisioning with Puppet stand alone. Puppet manifests
81 | # are contained in a directory path relative to this Vagrantfile.
82 | # You will need to create the manifests directory and a manifest in
83 | # the file default.pp in the manifests_path directory.
84 | #
85 | # config.vm.provision "puppet" do |puppet|
86 | # puppet.manifests_path = "manifests"
87 | # puppet.manifest_file = "site.pp"
88 | # end
89 |
90 | # Enable provisioning with chef solo, specifying a cookbooks path, roles
91 | # path, and data_bags path (all relative to this Vagrantfile), and adding
92 | # some recipes and/or roles.
93 | #
94 | # config.vm.provision "chef_solo" do |chef|
95 | # chef.cookbooks_path = "../my-recipes/cookbooks"
96 | # chef.roles_path = "../my-recipes/roles"
97 | # chef.data_bags_path = "../my-recipes/data_bags"
98 | # chef.add_recipe "mysql"
99 | # chef.add_role "web"
100 | #
101 | # # You may also specify custom JSON attributes:
102 | # chef.json = { mysql_password: "foo" }
103 | # end
104 |
105 | # Enable provisioning with chef server, specifying the chef server URL,
106 | # and the path to the validation key (relative to this Vagrantfile).
107 | #
108 | # The Opscode Platform uses HTTPS. Substitute your organization for
109 | # ORGNAME in the URL and validation key.
110 | #
111 | # If you have your own Chef Server, use the appropriate URL, which may be
112 | # HTTP instead of HTTPS depending on your configuration. Also change the
113 | # validation key to validation.pem.
114 | #
115 | # config.vm.provision "chef_client" do |chef|
116 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME"
117 | # chef.validation_key_path = "ORGNAME-validator.pem"
118 | # end
119 | #
120 | # If you're using the Opscode platform, your validator client is
121 | # ORGNAME-validator, replacing ORGNAME with your organization name.
122 | #
123 | # If you have your own Chef Server, the default validation client name is
124 | # chef-validator, unless you changed the configuration.
125 | #
126 | # chef.validation_client_name = "ORGNAME-validator"
127 | end
128 |
--------------------------------------------------------------------------------
/application.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Start by defining the main module and adding the module dependencies
4 | angular.module(ApplicationConfiguration.applicationModuleName, ApplicationConfiguration.applicationModuleVendorDependencies);
5 |
6 | // Setting HTML5 Location Mode
7 | angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
8 | function($locationProvider) {
9 | $locationProvider.hashPrefix('!');
10 | }
11 | ]);
12 |
13 | //Then define the init function for starting up the application
14 | angular.element(document).ready(function() {
15 | //Fixing facebook bug with redirect
16 | if (window.location.hash === '#_=_') window.location.hash = '#!';
17 |
18 | //Then init the app
19 | angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
20 | });
--------------------------------------------------------------------------------
/bootstrap.sh:
--------------------------------------------------------------------------------
1 | echo "========================="
2 | echo "Provisioning VM..."
3 | echo "========================="
4 |
5 | cd /home/vagrant
6 |
7 | echo "Installing mongodb..."
8 | sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
9 | echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list
10 | sudo apt-get update
11 | sudo apt-get install -y mongodb-org
12 | sudo service mongod start
13 |
14 | echo "Adding node.js repository..."
15 | curl -sL https://deb.nodesource.com/setup | sudo bash -
16 |
17 | echo "Installing apps..."
18 | sudo apt-get install -y emacs24-nox unzip bash nodejs git
19 | sudo npm install -g npm grunt-cli
20 |
21 | echo "Installing liquid prompt..."
22 | git clone https://github.com/nojhan/liquidprompt.git
23 | echo "source ~/liquidprompt/liquidprompt" >> .bashrc
24 |
25 | echo "Installing Yeoman & generator..."
26 | sudo npm install -g yo generator-meanjs
27 |
28 | echo "========================="
29 | echo "Provisioning finished"
30 | echo "========================="
31 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jungle",
3 | "version": "0.0.1",
4 | "description": "GUI of Kong API Gateway",
5 | "dependencies": {
6 | "bootstrap": "~3",
7 | "angular": "~1.2",
8 | "angular-resource": "~1.2",
9 | "angular-mocks": "~1.2",
10 | "angular-cookies": "~1.2",
11 | "angular-animate": "~1.2",
12 | "angular-touch": "~1.2",
13 | "angular-sanitize": "~1.2",
14 | "angular-bootstrap": "~0.11.2",
15 | "angular-ui-utils": "~0.1.1",
16 | "angular-ui-router": "*",
17 | "font-awesome": "*",
18 | "angular-breadcrumb": "*",
19 | "uri.js":"*",
20 | "angular-xeditable": "*",
21 | "ngstorage":"*",
22 | "ngInfiniteScroll":"*"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Init the application configuration module for AngularJS application
4 | var ApplicationConfiguration = (function() {
5 | // Init module configuration options
6 | var applicationModuleName = 'jungle';
7 | var applicationModuleVendorDependencies = ['ngResource', 'ngCookies', 'ngAnimate', 'ngTouch', 'ngSanitize', 'ui.router', 'ui.bootstrap', 'ui.utils', 'ncy-angular-breadcrumb', 'xeditable', 'ngStorage', 'infinite-scroll'];
8 |
9 | // Add a new vertical module
10 | var registerModule = function(moduleName, dependencies) {
11 | // Create angular module
12 | angular.module(moduleName, dependencies || []);
13 |
14 | // Add the module to the AngularJS configuration file
15 | angular.module(applicationModuleName).requires.push(moduleName);
16 | };
17 |
18 | return {
19 | applicationModuleName: applicationModuleName,
20 | applicationModuleVendorDependencies: applicationModuleVendorDependencies,
21 | registerModule: registerModule
22 | };
23 | })();
--------------------------------------------------------------------------------
/config/config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 | var _ = require('lodash'),
7 | glob = require('glob');
8 |
9 | /**
10 | * Load app configurations
11 | */
12 | module.exports = _.extend(
13 | require('./env/all'),
14 | require('./env/' + process.env.NODE_ENV) || {}
15 | );
16 |
17 | /**
18 | * Get files by glob patterns
19 | */
20 | module.exports.getGlobbedFiles = function(globPatterns, removeRoot) {
21 | // For context switching
22 | var _this = this;
23 |
24 | // URL paths regex
25 | var urlRegex = new RegExp('^(?:[a-z]+:)?\/\/', 'i');
26 |
27 | // The output array
28 | var output = [];
29 |
30 | // If glob pattern is array so we use each pattern in a recursive way, otherwise we use glob
31 | if (_.isArray(globPatterns)) {
32 | globPatterns.forEach(function(globPattern) {
33 | output = _.union(output, _this.getGlobbedFiles(globPattern, removeRoot));
34 | });
35 | } else if (_.isString(globPatterns)) {
36 | if (urlRegex.test(globPatterns)) {
37 | output.push(globPatterns);
38 | } else {
39 | glob(globPatterns, {
40 | sync: true
41 | }, function(err, files) {
42 | if (removeRoot) {
43 | files = files.map(function(file) {
44 | return file.replace(removeRoot, '');
45 | });
46 | }
47 |
48 | output = _.union(output, files);
49 | });
50 | }
51 | }
52 |
53 | return output;
54 | };
55 |
56 | /**
57 | * Get the modules JavaScript files
58 | */
59 | module.exports.getJavaScriptAssets = function(includeTests) {
60 | var output = this.getGlobbedFiles(this.assets.lib.js.concat(this.assets.js));
61 |
62 | // To include tests
63 | if (includeTests) {
64 | output = _.union(output, this.getGlobbedFiles(this.assets.tests));
65 | }
66 |
67 | return output;
68 | };
69 |
70 | /**
71 | * Get the modules CSS files
72 | */
73 | module.exports.getCSSAssets = function() {
74 | var output = this.getGlobbedFiles(this.assets.lib.css.concat(this.assets.css));
75 | return output;
76 | };
--------------------------------------------------------------------------------
/config/env/all.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | assets: {
5 | lib: {
6 | css: [
7 | 'lib/bootstrap/dist/css/bootstrap.css',
8 | 'lib/bootstrap/dist/css/bootstrap-theme.css',
9 | 'lib/font-awesome/css/font-awesome.css'
10 | ],
11 | js: [
12 | 'lib/angular/angular.js',
13 | 'lib/angular-resource/angular-resource.js',
14 | 'lib/angular-cookies/angular-cookies.js',
15 | 'lib/angular-animate/angular-animate.js',
16 | 'lib/angular-touch/angular-touch.js',
17 | 'lib/angular-sanitize/angular-sanitize.js',
18 | 'lib/angular-ui-router/release/angular-ui-router.js',
19 | 'lib/angular-ui-utils/ui-utils.js',
20 | 'lib/angular-bootstrap/ui-bootstrap-tpls.js',
21 | 'lib/angular-breadcrumb/release/angular-breadcrumb.js',
22 | 'lib/angular-xeditable/dist/js/xeditable.js',
23 | 'lib/ngstorage/ngStorage.js',
24 | 'lib/ngInfiniteScroll/build/ng-infinite-scroll.js'
25 | ]
26 | },
27 | css: [
28 | 'modules/**/css/*.css',
29 | 'dashboard.css'
30 | ],
31 | js: [
32 | 'config.js',
33 | 'application.js',
34 | 'modules/*/*.js',
35 | 'modules/*/*[!tests]*/*.js'
36 | ],
37 | tests: [
38 | 'lib/angular-mocks/angular-mocks.js',
39 | 'modules/*/tests/*.js'
40 | ]
41 | }
42 | };
--------------------------------------------------------------------------------
/config/env/development.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 |
5 | };
6 |
--------------------------------------------------------------------------------
/config/env/production.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | assets: {
5 | lib: {
6 | css: [
7 | 'lib/bootstrap/dist/css/bootstrap.min.css',
8 | 'lib/bootstrap/dist/css/bootstrap-theme.min.css',
9 | ],
10 | js: [
11 | 'lib/angular/angular.min.js',
12 | 'lib/angular-resource/angular-resource.js',
13 | 'lib/angular-cookies/angular-cookies.js',
14 | 'lib/angular-animate/angular-animate.js',
15 | 'lib/angular-touch/angular-touch.js',
16 | 'lib/angular-sanitize/angular-sanitize.js',
17 | 'lib/angular-ui-router/release/angular-ui-router.min.js',
18 | 'lib/angular-ui-utils/ui-utils.min.js',
19 | 'lib/angular-bootstrap/ui-bootstrap-tpls.min.js'
20 | ]
21 | },
22 | css: 'dist/application.min.css',
23 | js: 'dist/application.min.js'
24 | }
25 | };
26 |
--------------------------------------------------------------------------------
/config/init.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 | var glob = require('glob'),
7 | chalk = require('chalk');
8 |
9 | /**
10 | * Module init function.
11 | */
12 | module.exports = function() {
13 | /**
14 | * Before we begin, lets set the environment variable
15 | * We'll Look for a valid NODE_ENV variable and if one cannot be found load the development NODE_ENV
16 | */
17 | glob('./config/env/' + process.env.NODE_ENV + '.js', {
18 | sync: true
19 | }, function(err, environmentFiles) {
20 | if (!environmentFiles.length) {
21 | if (process.env.NODE_ENV) {
22 | console.error(chalk.red('No configuration file found for "' + process.env.NODE_ENV + '" environment using development instead'));
23 | } else {
24 | console.error(chalk.red('NODE_ENV is not defined! Using default development environment'));
25 | }
26 |
27 | process.env.NODE_ENV = 'development';
28 | } else {
29 | console.log(chalk.black.bgWhite('Application loaded using the "' + process.env.NODE_ENV + '" environment configuration'));
30 | }
31 | });
32 |
33 | };
--------------------------------------------------------------------------------
/dashboard.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Base structure
3 | */
4 |
5 | /* Move down content because we have a fixed navbar that is 50px tall */
6 | body {
7 | padding-top: 50px;
8 | }
9 |
10 |
11 | /*
12 | * Global add-ons
13 | */
14 |
15 | .sub-header {
16 | padding-bottom: 10px;
17 | border-bottom: 1px solid #eee;
18 | }
19 |
20 | /*
21 | * Top navigation
22 | * Hide default border to remove 1px line.
23 | */
24 | .navbar-fixed-top {
25 | border: 0;
26 | }
27 |
28 | /*
29 | * Sidebar
30 | */
31 |
32 | /* Hide for mobile, show later */
33 | .sidebar {
34 | display: none;
35 | }
36 | @media (min-width: 768px) {
37 | .sidebar {
38 | position: fixed;
39 | top: 51px;
40 | bottom: 0;
41 | left: 0;
42 | z-index: 1000;
43 | display: block;
44 | padding: 20px;
45 | overflow-x: hidden;
46 | overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
47 | background-color: #f5f5f5;
48 | border-right: 1px solid #eee;
49 | }
50 | }
51 |
52 | /* Sidebar navigation */
53 | .nav-sidebar {
54 | margin-right: -21px; /* 20px padding + 1px border */
55 | margin-bottom: 20px;
56 | margin-left: -20px;
57 | }
58 | .nav-sidebar > li > a {
59 | padding-right: 20px;
60 | padding-left: 20px;
61 | }
62 | .nav-sidebar > .active > a,
63 | .nav-sidebar > .active > a:hover,
64 | .nav-sidebar > .active > a:focus {
65 | color: #fff;
66 | background-color: #428bca;
67 | }
68 |
69 |
70 | /*
71 | * Main content
72 | */
73 |
74 | .main {
75 | padding: 20px;
76 | }
77 | @media (min-width: 768px) {
78 | .main {
79 | padding-right: 40px;
80 | padding-left: 40px;
81 | }
82 | }
83 | .main .page-header {
84 | margin-top: 0;
85 | }
86 |
87 |
88 | /*
89 | * Placeholder dashboard ideas
90 | */
91 |
92 | .placeholders {
93 | margin-bottom: 30px;
94 | text-align: center;
95 | }
96 | .placeholders h4 {
97 | margin-bottom: 0;
98 | }
99 | .placeholder {
100 | margin-bottom: 20px;
101 | }
102 | .placeholder img {
103 | display: inline-block;
104 | border-radius: 50%;
105 | }
106 |
107 |
108 | /*
109 | * Table
110 | */
111 |
112 | th.sortable {
113 | cursor: pointer;
114 | }
115 | .table-2-column th, .table-2-column td {
116 | width: 50%;
117 | }
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | // Generated on 2015-06-10 using generator-angular 0.11.1
2 | 'use strict';
3 |
4 | // # Globbing
5 | // for performance reasons we're only matching one level down:
6 | // 'test/spec/{,*/}*.js'
7 | // use this if you want to recursively match all subfolders:
8 | // 'test/spec/**/*.js'
9 |
10 | module.exports = function (grunt) {
11 |
12 | // Load grunt tasks automatically
13 | require('load-grunt-tasks')(grunt);
14 |
15 | // Time how long tasks take. Can help when optimizing build times
16 | require('time-grunt')(grunt);
17 |
18 | // Configurable paths for the application
19 | var appConfig = {
20 | app: '.',
21 | dist: './dist'
22 | };
23 |
24 | // Define the configuration for all the tasks
25 | grunt.initConfig({
26 |
27 | // Project settings
28 | yeoman: appConfig,
29 |
30 | // Watches files for changes and runs tasks based on the changed files
31 | watch: {
32 | bower: {
33 | files: ['bower.json'],
34 | tasks: ['wiredep']
35 | },
36 | js: {
37 | files: ['<%= yeoman.app %>/{,*/}*.js'],
38 | tasks: ['newer:jshint:all'],
39 | options: {
40 | livereload: '<%= connect.options.livereload %>'
41 | }
42 | },
43 | jsTest: {
44 | files: ['test/spec/{,*/}*.js'],
45 | tasks: ['newer:jshint:test', 'karma']
46 | },
47 | styles: {
48 | files: ['<%= yeoman.app %>/modules/{,*/}*.css'],
49 | tasks: ['newer:copy:styles', 'autoprefixer']
50 | },
51 | gruntfile: {
52 | files: ['Gruntfile.js']
53 | },
54 | livereload: {
55 | options: {
56 | livereload: '<%= connect.options.livereload %>'
57 | },
58 | files: [
59 | '<%= yeoman.app %>/{,*/}*.html',
60 | '.tmp/styles/{,*/}*.css',
61 | '<%= yeoman.app %>/modules/{,*/}*.{png,jpg,jpeg,gif,webp,svg}'
62 | ]
63 | }
64 | },
65 |
66 | // The actual grunt server settings
67 | connect: {
68 | options: {
69 | port: 3000,
70 | // Change this to '0.0.0.0' to access the server from outside.
71 | hostname: '0.0.0.0',
72 | livereload: 35729
73 | },
74 | livereload: {
75 | options: {
76 | open: true,
77 | middleware: function (connect) {
78 | return [
79 | connect.static('.tmp'),
80 | connect.static(appConfig.app)
81 | ];
82 | }
83 | }
84 | },
85 | test: {
86 | options: {
87 | port: 9001,
88 | middleware: function (connect) {
89 | return [
90 | connect.static('.tmp'),
91 | connect.static(appConfig.app)
92 | ];
93 | }
94 | }
95 | },
96 | dist: {
97 | options: {
98 | open: true,
99 | base: '<%= yeoman.dist %>'
100 | }
101 | }
102 | },
103 |
104 | // Make sure code styles are up to par and there are no obvious mistakes
105 | jshint: {
106 | options: {
107 | jshintrc: '.jshintrc',
108 | reporter: require('jshint-stylish')
109 | },
110 | all: {
111 | src: [
112 | 'Gruntfile.js',
113 | '<%= yeoman.app %>/modules/**/*.js',
114 | 'application.js',
115 | 'config.js',
116 | ]
117 | },
118 | test: {
119 | options: {
120 | jshintrc: 'test/.jshintrc'
121 | },
122 | src: ['test/spec/{,*/}*.js']
123 | }
124 | },
125 |
126 | csslint: {
127 | options: {
128 | csslintrc: '.csslintrc',
129 | },
130 | all : {
131 | src:'<%= applicationCSSFiles %>'
132 | }
133 |
134 | },
135 |
136 | // Empties folders to start fresh
137 | clean: {
138 | dist: {
139 | files: [{
140 | dot: true,
141 | src: [
142 | '.tmp',
143 | '<%= yeoman.dist %>/{,*/}*',
144 | '!<%= yeoman.dist %>/.git{,*/}*'
145 | ]
146 | }]
147 | },
148 | server: '.tmp'
149 | },
150 |
151 | // Add vendor prefixed styles
152 | autoprefixer: {
153 | options: {
154 | browsers: ['last 1 version']
155 | },
156 | server: {
157 | options: {
158 | map: true,
159 | },
160 | files: [{
161 | expand: true,
162 | cwd: '.tmp/styles/',
163 | src: '{,*/}*.css',
164 | dest: '.tmp/styles/'
165 | }]
166 | },
167 | dist: {
168 | files: [{
169 | expand: true,
170 | cwd: '.tmp/styles/',
171 | src: '{,*/}*.css',
172 | dest: '.tmp/styles/'
173 | }]
174 | }
175 | },
176 |
177 | // Automatically inject Bower components into the app
178 | wiredep: {
179 | app: {
180 | src: ['<%= yeoman.app %>/index.html'],
181 | ignorePath: /\.\.\//
182 | },
183 | test: {
184 | devDependencies: true,
185 | src: '<%= karma.unit.configFile %>',
186 | ignorePath: /\.\.\//,
187 | fileTypes:{
188 | js: {
189 | block: /(([\s\t]*)\/{2}\s*?bower:\s*?(\S*))(\n|\r|.)*?(\/{2}\s*endbower)/gi,
190 | detect: {
191 | js: /'(.*\.js)'/gi
192 | },
193 | replace: {
194 | js: '\'{{filePath}}\','
195 | }
196 | }
197 | }
198 | }
199 | },
200 |
201 | // Renames files for browser caching purposes
202 | filerev: {
203 | dist: {
204 | src: [
205 | '<%= yeoman.dist %>/{,*/}*.js',
206 | '<%= yeoman.dist %>/modules/{,*/}*.css',
207 | '<%= yeoman.dist %>/modules/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
208 | '<%= yeoman.dist %>/styles/fonts/*'
209 | ]
210 | }
211 | },
212 |
213 | env: {
214 | dev: {
215 | NODE_ENV: 'development'
216 | },
217 | prod: {
218 | NODE_ENV: 'production'
219 | }
220 | },
221 |
222 |
223 | cssmin: {
224 | dist: {
225 | files: {
226 | '<%= yeoman.dist %>/application.min.css': '<%= applicationCSSFiles %>'
227 | }
228 | }
229 | },
230 | uglify: {
231 | dist: {
232 | options: {
233 | mangle: false
234 | },
235 | files: {
236 | '<%= yeoman.dist %>/application.min.js': 'dist/application.js'
237 | }
238 | }
239 | },
240 |
241 | htmlmin: {
242 | dist: {
243 | options: {
244 | collapseWhitespace: true,
245 | conservativeCollapse: true,
246 | collapseBooleanAttributes: true,
247 | removeCommentsFromCDATA: true,
248 | removeOptionalTags: true
249 | },
250 | files: [{
251 | expand: true,
252 | cwd: '<%= yeoman.app %>',
253 | src: ['*.html', '<%= yeoman.app %>/modules/*/views/{,*/}*.html'],
254 | dest: '<%= yeoman.dist %>'
255 | }]
256 | }
257 | },
258 |
259 | // ng-annotate tries to make the code safe for minification automatically
260 | // by using the Angular long form for dependency injection.
261 | ngAnnotate: {
262 | dist: {
263 | files: {
264 | '<%= yeoman.dist %>/application.js': '<%= applicationJavaScriptFiles %>'
265 | }
266 | }
267 | },
268 |
269 | // Copies remaining files to places other tasks can use
270 | copy: {
271 | dist: {
272 | files: [{
273 | expand: true,
274 | dot: true,
275 | cwd: '<%= yeoman.app %>',
276 | dest: '<%= yeoman.dist %>',
277 | src: [
278 | 'modules/*/img/{,*/}*.{png,jpg,jpeg,gif,webp,svg,ico}'
279 | ]
280 | },
281 | {
282 | expand: true,
283 | dot: true,
284 | cwd: '<%= yeoman.app %>',
285 | dest: '<%= yeoman.dist %>/fonts/',
286 | flatten: true,
287 | src: [
288 | '<%= yeoman.app %>/lib/*/fonts/{,*/}*.*'
289 | ]
290 | }]
291 | },
292 | styles: {
293 | expand: true,
294 | cwd: '<%= yeoman.app %>/styles',
295 | dest: '.tmp/styles/',
296 | src: '{,*/}*.css'
297 | }
298 | },
299 |
300 | // Run some tasks in parallel to speed up the build process
301 | concurrent: {
302 | server: [
303 | 'copy:styles'
304 | ],
305 | test: [
306 | 'copy:styles'
307 | ],
308 | dist: [
309 | 'copy:styles',
310 | 'imagemin',
311 | 'svgmin'
312 | ]
313 | },
314 |
315 | // Test settings
316 | karma: {
317 | unit: {
318 | configFile: 'karma.conf.js',
319 | singleRun: true
320 | }
321 | },
322 |
323 | htmlrefs: {
324 | dist: {
325 | /** @required - string including grunt glob variables */
326 | src: '<%= yeoman.app %>/index.html',
327 | /** @optional - string directory name*/
328 | dest: '<%= yeoman.dist %>/index.html',
329 | }
330 | }
331 | });
332 |
333 |
334 | grunt.task.registerTask('loadConfig', 'Task that loads the config into a grunt option.', function() {
335 | var init = require('./config/init')();
336 | var config = require('./config/config');
337 |
338 | grunt.config.set('applicationJavaScriptFiles', config.assets.lib.js.concat(config.assets.js));
339 | grunt.config.set('applicationCSSFiles', config.assets.lib.css.concat(config.assets.css));
340 | grunt.log.warn(config.assets.lib.css.concat(config.assets.css));
341 | });
342 |
343 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) {
344 | if (target === 'dist') {
345 | return grunt.task.run(['connect:dist:keepalive']);
346 | }
347 |
348 | grunt.task.run([
349 | 'clean:server',
350 | 'wiredep',
351 | 'concurrent:server',
352 | 'autoprefixer:server',
353 | 'connect:livereload',
354 | 'watch'
355 | ]);
356 | });
357 |
358 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) {
359 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
360 | grunt.task.run(['serve:' + target]);
361 | });
362 |
363 | grunt.registerTask('test', [
364 | 'env:dev',
365 | 'clean:server',
366 | 'connect:test',
367 | 'karma:unit'
368 | ]);
369 |
370 | grunt.registerTask('build', ['clean:dist','env:dev', 'jshint','wiredep','loadConfig', 'ngAnnotate', 'uglify', 'cssmin', 'htmlmin', 'copy:dist','htmlrefs']);
371 |
372 | grunt.registerTask('default', [
373 | 'build',
374 | ]);
375 | };
376 |
--------------------------------------------------------------------------------
/humans.txt:
--------------------------------------------------------------------------------
1 | # humanstxt.org/
2 | # The humans responsible & technology colophon
3 |
4 | # TEAM
5 |
6 | -- --
7 |
8 | # THANKS
9 |
10 |
11 |
12 | # TECHNOLOGY COLOPHON
13 |
14 | HTML5, CSS3
15 | jQuery, Modernizr
16 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | JUNGLE
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Module dependencies.
5 | */
6 | var applicationConfiguration = require('./config/config');
7 |
8 | // Karma configuration
9 | module.exports = function(config) {
10 | config.set({
11 | // Frameworks to use
12 | frameworks: ['jasmine'],
13 |
14 | // List of files / patterns to load in the browser
15 | files: applicationConfiguration.assets.lib.js.concat(applicationConfiguration.assets.js, applicationConfiguration.assets.tests),
16 |
17 | // Test results reporter to use
18 | // Possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
19 | //reporters: ['progress'],
20 | reporters: ['progress'],
21 |
22 | // Web server port
23 | port: 8080,
24 |
25 | // Enable / disable colors in the output (reporters and logs)
26 | colors: true,
27 |
28 | // Level of logging
29 | // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
30 | logLevel: config.LOG_WARN,
31 |
32 | // Enable / disable watching file and executing tests whenever any file changes
33 | autoWatch: true,
34 |
35 | // Start these browsers, currently available:
36 | // - Chrome
37 | // - ChromeCanary
38 | // - Firefox
39 | // - Opera
40 | // - Safari (only Mac)
41 | // - PhantomJS
42 | // - IE (only Windows)
43 | browsers: ['PhantomJS'],
44 |
45 | plugins: [
46 | "karma-phantomjs-launcher",
47 | "karma-jasmine"
48 | ],
49 |
50 | // If browser does not capture in given timeout [ms], kill it
51 | captureTimeout: 60000,
52 |
53 | // Continuous Integration mode
54 | // If true, it capture browsers, run tests and exit
55 | singleRun: true
56 | });
57 | };
--------------------------------------------------------------------------------
/modules/apis/apis.client.module.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use applicaion configuration module to register a new module
4 | ApplicationConfiguration.registerModule('apis');
--------------------------------------------------------------------------------
/modules/apis/config/apis.client.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Configuring the Articles module
4 | angular.module('apis').run(['Menus', 'editableOptions',
5 | function(Menus, editableOptions) {
6 | // Set top bar menu items
7 | Menus.addMenuItem('topbar', 'Apis', 'apis', 'dropdown', '/apis(/create)?');
8 | Menus.addSubMenuItem('topbar', 'apis', 'List Apis', 'apis');
9 | Menus.addSubMenuItem('topbar', 'apis', 'New Api', 'apis/create');
10 | editableOptions.theme = 'bs3';
11 | }
12 | ]);
--------------------------------------------------------------------------------
/modules/apis/config/apis.client.constants.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Setting up route
4 | angular.module('apis').constant('PLUGINSAVAILABLE', [
5 |
6 | {
7 | name: 'http-log',
8 | label: 'Http Log',
9 | docUrl: 'http://getkong.org/plugins/http-log/',
10 | schema: [
11 | {
12 | 'name':'config.http_endpoint',
13 | 'type' : 'string',
14 | 'label': 'Http endpoint'
15 | },
16 | {
17 | 'name':'config.timeout',
18 | 'type' : 'integer',
19 | 'label': 'Timeout'
20 | },
21 | {
22 | 'name':'config.keepalive',
23 | 'type' : 'integer',
24 | 'label': 'Keepalive'
25 | },
26 | {
27 | 'name':'config.method',
28 | 'type' : 'enum',
29 | 'label': 'Method',
30 | 'values': [
31 | { 'label' : 'POST', 'value' : 'POST'},
32 | { 'label' : 'PATCH', 'value' : 'PATCH'},
33 | { 'label' : 'PUT', 'value' : 'PUT'}
34 | ]
35 | }
36 | ]
37 | },
38 | {
39 | name: 'udp-log',
40 | label: 'UDP Log',
41 | docUrl: 'http://getkong.org/plugins/udp-log/',
42 | schema: [
43 | {
44 | 'name':'config.host',
45 | 'type' : 'string',
46 | 'label': 'Host'
47 | },
48 | {
49 | 'name':'config.timeout',
50 | 'type' : 'integer',
51 | 'label': 'Timeout'
52 | },
53 | {
54 | 'name':'config.port',
55 | 'type' : 'integer',
56 | 'label': 'Port'
57 | }
58 | ]
59 | },
60 | {
61 | name: 'tcp-log',
62 | label: 'TCP Log',
63 | docUrl: 'http://getkong.org/plugins/tcp-log/',
64 | schema: [
65 | {
66 | 'name':'config.host',
67 | 'type' : 'string',
68 | 'label': 'Host'
69 | },
70 | {
71 | 'name':'config.timeout',
72 | 'type' : 'integer',
73 | 'label': 'Timeout'
74 | },
75 | {
76 | 'name':'config.port',
77 | 'type' : 'integer',
78 | 'label': 'Port'
79 | },
80 | {
81 | 'name':'config.keepalive',
82 | 'type' : 'integer',
83 | 'label': 'Keepalive'
84 | }
85 | ]
86 | },
87 | {
88 | name: 'file-log',
89 | label: 'File Log',
90 | docUrl: 'http://getkong.org/plugins/file-log/',
91 | schema: [
92 | {
93 | 'name':'config.path',
94 | 'type' : 'string',
95 | 'label': 'The output path file'
96 | }
97 | ]
98 | },
99 | {
100 | name: 'basic-auth',
101 | label: 'Basic Authentication',
102 | docUrl: 'http://getkong.org/plugins/basic-authentication/',
103 | schema: [
104 | {
105 | 'name':'config.hide_credentials',
106 | 'type' : 'boolean',
107 | 'label': 'Hide Credentials'
108 | }
109 | ],
110 | api : {
111 | routes : [
112 | {
113 | 'action': 'list',
114 | 'route': 'consumers/:username/basic-auth',
115 | 'method': 'GET',
116 | 'params': ['username']
117 | },
118 | {
119 | 'action': 'create',
120 | 'route': 'consumers/:username/basic-auth',
121 | 'method': 'POST',
122 | 'params': ['username']
123 | },
124 | {
125 | 'action': 'view',
126 | 'route': 'consumers/:username/basic-auth/:id',
127 | 'method': 'GET',
128 | 'params': ['username', 'id']
129 | },
130 | {
131 | 'action': 'update',
132 | 'route': 'consumers/:username/basic-auth/:id',
133 | 'method': 'PATCH',
134 | 'params': ['username', 'id']
135 | },
136 | {
137 | 'action': 'delete',
138 | 'route': 'consumers/:username/basic-auth/:id',
139 | 'method': 'DELETE',
140 | 'params': ['username', 'id']
141 | }
142 | ],
143 | dao : [
144 | {
145 | 'name':'username',
146 | 'type' : 'string',
147 | 'label': 'Username'
148 | },
149 | {
150 | 'name':'password',
151 | 'type' : 'string',
152 | 'label': 'Password'
153 | },
154 | ]
155 | }
156 |
157 | },
158 | {
159 | name: 'key-auth',
160 | label: 'Key Authentication',
161 | docUrl: 'http://getkong.org/plugins/key-authentication/',
162 | schema: [
163 | {
164 | 'name':'config.hide_credentials',
165 | 'type' : 'boolean',
166 | 'label': 'Hide Credentials'
167 | },
168 | {
169 | 'name':'config.key_names',
170 | 'type' : 'string',
171 | 'label': 'Key Names'
172 | }
173 | ],
174 | api :
175 | {
176 | routes : [
177 | {
178 | 'action': 'list',
179 | 'route': 'consumers/:username/key-auth',
180 | 'method': 'GET',
181 | 'params': ['username']
182 | },
183 | {
184 | 'action': 'create',
185 | 'route': 'consumers/:username/key-auth',
186 | 'method': 'POST',
187 | 'params': ['username']
188 | },
189 | {
190 | 'action': 'view',
191 | 'route': 'consumers/:username/key-auth/:id',
192 | 'method': 'GET',
193 | 'params': ['username', 'id']
194 | },
195 | {
196 | 'action': 'update',
197 | 'route': 'consumers/:username/key-auth/:id',
198 | 'method': 'PATCH',
199 | 'params': ['username', 'id']
200 | },
201 | {
202 | 'action': 'delete',
203 | 'route': 'consumers/:username/key-auth/:id',
204 | 'method': 'DELETE',
205 | 'params': ['username', 'id']
206 | }
207 | ],
208 | dao : [
209 | {
210 | 'name':'key',
211 | 'type' : 'string',
212 | 'label': 'Key'
213 | }
214 | ]
215 | }
216 |
217 | },
218 | {
219 | name: 'cors',
220 | label: 'CORS',
221 | docUrl: 'http://getkong.org/plugins/cors/',
222 | schema: [
223 | {
224 | 'name':'config.origin',
225 | 'type' : 'string',
226 | 'label': 'Origin'
227 | },
228 | {
229 | 'name':'config.methods',
230 | 'type' : 'string',
231 | 'label': 'Method'
232 | },
233 | {
234 | 'name':'config.headers',
235 | 'type' : 'string',
236 | 'label': 'Headers'
237 | },
238 | {
239 | 'name':'config.exposed_headers',
240 | 'type' : 'string',
241 | 'label': 'Exposed Headers'
242 | },
243 | {
244 | 'name':'config.credentials',
245 | 'type' : 'boolean',
246 | 'label': 'Credentials'
247 | },
248 | {
249 | 'name':'config.max_age',
250 | 'type' : 'integer',
251 | 'label': 'Max age'
252 | }
253 | ]
254 | },
255 | {
256 | name: 'ssl',
257 | label: 'SSL',
258 | docUrl: 'http://getkong.org/plugins/ssl/',
259 | schema: [
260 | {
261 | 'name':'config.cert',
262 | 'type' : 'string',
263 | 'label': 'Certificate file path'
264 | },
265 | {
266 | 'name':'config.key',
267 | 'type' : 'string',
268 | 'label': 'Certificate key path'
269 | },
270 | {
271 | 'name':'config.only_https',
272 | 'type' : 'boolean',
273 | 'label': 'Only HTTPS'
274 | }
275 | ]
276 | },
277 | {
278 | name: 'request-transformer',
279 | label: 'Request Transformer',
280 | docUrl: 'http://getkong.org/plugins/request-transformer/',
281 | schema: [
282 | {
283 | 'name':'config.add.headers',
284 | 'type' : 'string',
285 | 'label': 'Headers to add'
286 | },
287 | {
288 | 'name':'config.add.querystring',
289 | 'type' : 'string',
290 | 'label': 'Parameters to add in request querystring'
291 | },
292 | {
293 | 'name':'config.add.form',
294 | 'type' : 'string',
295 | 'label': 'Values to add in request body'
296 | },
297 | {
298 | 'name':'config.remove.headers',
299 | 'type' : 'string',
300 | 'label': 'Headers to remove'
301 | },
302 | {
303 | 'name':'config.remove.querystring',
304 | 'type' : 'string',
305 | 'label': 'Parameters to remove in request querystring'
306 | },
307 | {
308 | 'name':'config.remove.form',
309 | 'type' : 'string',
310 | 'label': 'Values to remove in request body'
311 | }
312 | ]
313 | },
314 | {
315 | name: 'response-transformer',
316 | label: 'Response Transformer',
317 | docUrl: 'http://getkong.org/plugins/response-transformer/',
318 | schema: [
319 | {
320 | 'name':'config.add.headers',
321 | 'type' : 'string',
322 | 'label': 'Headers to add'
323 | },
324 | {
325 | 'name':'config.add.json',
326 | 'type' : 'string',
327 | 'label': 'Values to add to a JSON response body'
328 | },
329 | {
330 | 'name':'config.remove.headers',
331 | 'type' : 'string',
332 | 'label': 'Headers to remove'
333 | },
334 | {
335 | 'name':'config.remove.json',
336 | 'type' : 'string',
337 | 'label': 'Values to remove to a JSON response body'
338 | }
339 | ]
340 | },
341 | {
342 | name: 'ratelimiting',
343 | label: 'Rate Limiting',
344 | docUrl: 'http://getkong.org/plugins/rate-limiting/',
345 | schema: [
346 | {
347 | 'name':'limit',
348 | 'type' : 'integer',
349 | 'label': 'Limit'
350 | },
351 | {
352 | 'name':'period',
353 | 'type' : 'enum',
354 | 'label': 'Period',
355 | 'values': [
356 | { 'label' : 'Second', 'value' : 'second'},
357 | { 'label' : 'Minute', 'value' : 'minute'},
358 | { 'label' : 'Hour', 'value' : 'hour'},
359 | { 'label' : 'Day', 'value' : 'day'},
360 | { 'label' : 'Month', 'value' : 'month'},
361 | { 'label' : 'Year', 'value' : 'year'}
362 | ]
363 | }
364 | ]
365 | },
366 | {
367 | name: 'request-size-limiting',
368 | label: 'Request Size Limiting',
369 | docUrl: 'http://getkong.org/plugins/request-size-limiting/',
370 | schema: [
371 | {
372 | 'name':'config.allowed_payload_size',
373 | 'type' : 'integer',
374 | 'label': 'Allowed request payload size in megabytes'
375 | }
376 | ]
377 | }
378 | ]);
--------------------------------------------------------------------------------
/modules/apis/config/apis.client.routes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Setting up route
4 | angular.module('apis').config(['$stateProvider',
5 | function($stateProvider) {
6 | // Apis state routing
7 | $stateProvider.
8 | state('listApis', {
9 | url: '/apis',
10 | templateUrl: 'modules/apis/views/list-apis.client.view.html',
11 | ncyBreadcrumb: {
12 | label: 'List Apis',
13 | parent: 'home'
14 | },
15 | reloadOnSearch: true
16 | }).
17 | state('createApi', {
18 | url: '/apis/create',
19 | templateUrl: 'modules/apis/views/create-api.client.view.html',
20 | ncyBreadcrumb: {
21 | label: 'Create API',
22 | parent: 'home'
23 | },
24 | reloadOnSearch: true
25 | }).
26 | state('viewApi', {
27 | url: '/apis/:apiId',
28 | templateUrl: 'modules/apis/views/view-api.client.view.html',
29 | ncyBreadcrumb: {
30 | label: 'View API',
31 | parent: 'listApis'
32 | },
33 | reloadOnSearch: true
34 | }).
35 | state('editApi', {
36 | url: '/apis/:apiId/edit',
37 | templateUrl: 'modules/apis/views/edit-api.client.view.html',
38 | ncyBreadcrumb: {
39 | label: 'Edit API',
40 | parent: 'viewApi'
41 | },
42 | reloadOnSearch: true
43 | }).
44 | state('viewPluginApi', {
45 | url: '/apis/:apiId/plugins',
46 | templateUrl: 'modules/apis/views/view-api-plugin.client.view.html',
47 | ncyBreadcrumb: {
48 | label: 'List Plugins for API',
49 | parent: 'viewApi'
50 | },
51 | reloadOnSearch: true
52 | });
53 | }
54 | ]);
--------------------------------------------------------------------------------
/modules/apis/controllers/apis.client.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Apis controller
4 | angular.module('apis').controller('ApisController', ['$scope', '$stateParams', '$location', '$state', 'Apis', 'Plugins', 'PLUGINSAVAILABLE',
5 | function($scope, $stateParams, $location, $state, Apis, Plugins, PLUGINSAVAILABLE) {
6 |
7 | // Create new Api
8 | $scope.create = function() {
9 | // Create new Api object
10 | var api = new Apis ({
11 | name: this.name,
12 | request_host: this.request_host,
13 | request_path: this.request_path,
14 | strip_request_path: this.strip_request_path,
15 | preserve_host: this.preserve_host,
16 | upstream_url : this.upstream_url
17 | });
18 |
19 | // Redirect after save
20 | api.$save(function(response) {
21 | $location.path('apis/' + response.id);
22 |
23 | // Clear form fields
24 | $scope.name = '';
25 | $scope.request_host = '';
26 | $scope.request_path = '';
27 | $scope.strip_request_path = '';
28 | $scope.preserve_host = '';
29 | $scope.upstream_url = '';
30 | }, function(errorResponse) {
31 | $scope.error = errorResponse.data;
32 | });
33 | };
34 |
35 | // Remove existing Api
36 | $scope.remove = function(api) {
37 | if ( api ) {
38 | api.$remove();
39 |
40 | for (var i in $scope.apis) {
41 | if ($scope.apis [i] === api) {
42 | $scope.apis.splice(i, 1);
43 | }
44 | }
45 | } else {
46 |
47 | $scope.api.$remove(function() {
48 | $location.path('apis');
49 | }, function(errorResponse) {
50 | $location.path('apis');
51 | });
52 | }
53 | };
54 |
55 | // Update existing Api
56 | $scope.update = function() {
57 | var api = $scope.api;
58 | api.$update(function() {
59 | $location.path('apis/' + api.id);
60 | }, function(errorResponse) {
61 | $scope.error = errorResponse.data;
62 | });
63 | };
64 |
65 | // Find a list of Apis
66 | $scope.find = function() {
67 | $scope.apis = Apis.query($location.search());
68 | $scope.scroll= {busy : false};
69 | };
70 |
71 | // Find existing Api
72 | $scope.findOne = function() {
73 | $scope.api = Apis.get({
74 | apiId: $stateParams.apiId
75 | });
76 | };
77 |
78 | $scope.listPluginByApi = function() {
79 | $scope.pluginAvailable = PLUGINSAVAILABLE;
80 | $scope.findOne();
81 | $scope.plugins = Plugins.query({
82 | apiId: $stateParams.apiId
83 | });
84 | };
85 |
86 |
87 | $scope.initPluginForm = function() {
88 | $scope.pluginAvailable = PLUGINSAVAILABLE;
89 | $scope.currentPlugin = null;
90 | $scope.value = {};
91 | };
92 |
93 | $scope.removePlugin = function(plugin) {
94 | if ( plugin ) {
95 | var pluginResource = new Plugins(plugin);
96 | pluginResource.$remove();
97 |
98 | for (var i in $scope.plugins.data) {
99 | if ($scope.plugins.data [i] === plugin) {
100 | $scope.plugins.data.splice(i, 1);
101 | }
102 | }
103 | }
104 | };
105 |
106 | $scope.createPlugin = function() {
107 | if ($scope.currentPlugin !== null) {
108 | var opt = $scope.value;
109 | opt.name = $scope.currentPlugin.name;
110 | opt.api_id = $stateParams.apiId;
111 | var plugin = new Plugins(opt);
112 | plugin.$save(function(response) {
113 | $scope.initPluginForm();
114 | $state.go($state.current, {}, {reload: true});
115 | }, function(errorResponse) {
116 | $scope.error = errorResponse.data;
117 | });
118 | }
119 | };
120 |
121 | $scope.saveApi = function(data, id) {
122 | var api = new Apis (data);
123 | api.id = id;
124 | api.$update(function() {
125 | for (var i in $scope.apis.data) {
126 | if ($scope.apis.data [i] . id === id) {
127 | angular.extend($scope.apis.data [i], data);
128 | }
129 | }
130 | }, function(errorResponse) {
131 | $scope.error = errorResponse.data;
132 | });
133 | };
134 |
135 | $scope.nextPage = function() {
136 | if ($scope.scroll.busy){
137 | $scope.scroll.busy = false;
138 | return;
139 | }
140 |
141 | $scope.scroll.busy = true;
142 |
143 |
144 | if (undefined === $scope.apis.next){
145 | $scope.scroll.busy = false;
146 | return;
147 | }
148 |
149 | var offset = new URI($scope.apis.next).search(true).offset;
150 | Apis.query({offset: offset}, function(apis){
151 | $scope.apis.next = apis.next;
152 | $scope.apis.data = $scope.apis.data.concat(apis.data);
153 | $scope.scroll.busy = false;
154 | });
155 | };
156 |
157 | // Set sorting default configuration
158 | $scope.sortType = 'name';
159 | $scope.sortReverse = false;
160 | }
161 | ]);
162 |
--------------------------------------------------------------------------------
/modules/apis/directives/pluginform.client.directive.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('apis').directive('pluginForm', [
4 | function() {
5 | return {
6 | templateUrl: 'modules/apis/views/plugin.form.directive.html'
7 | };
8 | }
9 | ]);
--------------------------------------------------------------------------------
/modules/apis/services/apis.client.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Apis service used to communicate Apis REST endpoints
4 | angular.module('apis').factory('Apis', ['$resource', '$localStorage',
5 | function($resource, $localStorage) {
6 | return $resource($localStorage.kongurl+'/apis/:apiId', { apiId: '@id'
7 | }, {
8 | query: {
9 | method: 'GET',
10 | isArray: false
11 | },
12 | update: {
13 | method: 'PATCH'
14 | }
15 | });
16 | }
17 | ]);
18 |
19 | //Plugin service used to communicate Apis REST endpoints
20 | angular.module('apis').factory('Plugins', ['$resource', '$localStorage',
21 | function($resource, $localStorage) {
22 | return $resource($localStorage.kongurl+'/apis/:apiId/plugins/:pluginId', { apiId: '@api_id', pluginId: '@id'
23 | }, {
24 | query: {
25 | method: 'GET',
26 | isArray: false
27 | },
28 | update: {
29 | method: 'PATCH'
30 | }
31 | });
32 | }
33 | ]);
34 |
35 | angular.module('apis').factory('PluginsConfigurations', ['$resource', 'KONGURL',
36 | function($resource, KONGURL) {
37 | return $resource(KONGURL+'/plugins_configurations', { pluginId: '@id'
38 | }, {
39 | query: {
40 | method: 'GET',
41 | isArray: false
42 | }
43 | });
44 | }
45 | ]);
--------------------------------------------------------------------------------
/modules/apis/tests/apis.client.controller.test.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 | // Apis Controller Spec
4 | describe('Apis Controller Tests', function() {
5 | // Initialize global variables
6 | var ApisController,
7 | scope,
8 | $httpBackend,
9 | $stateParams,
10 | $location,
11 | $KONGURL,
12 | $localStorage;
13 |
14 | // The $resource service augments the response object with methods for updating and deleting the resource.
15 | // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
16 | // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
17 | // When the toEqualData matcher compares two objects, it takes only object properties into
18 | // account and ignores methods.
19 | beforeEach(function() {
20 | jasmine.addMatchers({
21 | toEqualData: function(util, customEqualityTesters) {
22 | return {
23 | compare: function(actual, expected) {
24 | return {
25 | pass: angular.equals(actual, expected)
26 | };
27 | }
28 | };
29 | }
30 | });
31 | });
32 |
33 | // Then we can start by loading the main application module
34 | beforeEach(module(ApplicationConfiguration.applicationModuleName));
35 |
36 | // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
37 | // This allows us to inject a service but then attach it to a variable
38 | // with the same name as the service.
39 | beforeEach(inject(function($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_, _KONGURL_, _$localStorage_) {
40 | // Set a new global scope
41 | scope = $rootScope.$new();
42 |
43 | // Point global variables to injected services
44 | $stateParams = _$stateParams_;
45 | $httpBackend = _$httpBackend_;
46 | $location = _$location_;
47 | $KONGURL = _KONGURL_;
48 | $localStorage = _$localStorage_;
49 | $localStorage.kongurl = _KONGURL_;
50 | $httpBackend.when('GET', 'modules/core/views/home.client.view.html').respond(200, {});
51 | // Initialize the Apis controller.
52 | ApisController = $controller('ApisController', {
53 | $scope: scope
54 | });
55 | }));
56 |
57 | it('$scope.find() should create an array with at least one Api object fetched from XHR', inject(function(Apis) {
58 | // Create sample Api using the Apis service
59 | var sampleApi = new Apis({
60 | name: 'New Api',
61 | request_host: 'api',
62 | path: '/path',
63 | strip_request_path: true,
64 | preserve_host: true,
65 | upstream_url: 'http://api.com'
66 | });
67 |
68 | // Create a sample Apis array that includes the new Api
69 | var sampleApis = { data: [sampleApi] };
70 |
71 | // Set GET response
72 | $httpBackend.expectGET($KONGURL+'/apis').respond(sampleApis);
73 |
74 | // Run controller functionality
75 | scope.find();
76 | $httpBackend.flush();
77 |
78 | // Test scope value
79 | expect(scope.apis).toEqualData(sampleApis);
80 | }));
81 |
82 | it('$scope.findOne() should create an array with one Api object fetched from XHR using a apiId URL parameter', inject(function(Apis) {
83 | // Define a sample Api object
84 | var sampleApi = new Apis({
85 | name: 'New Api',
86 | request_host: 'api',
87 | request_path: '/path',
88 | strip_request_path: true,
89 | preserve_host: true,
90 | upstream_url: 'http://api.com'
91 | });
92 |
93 | // Set the URL parameter
94 | $stateParams.apiId = '525a8422f6d0f87f0e407a33';
95 |
96 | // Set GET response
97 | $httpBackend.expectGET(/apis\/([0-9a-fA-F]{24})$/).respond(sampleApi);
98 |
99 | // Run controller functionality
100 | scope.findOne();
101 | $httpBackend.flush();
102 |
103 | // Test scope value
104 | expect(scope.api).toEqualData(sampleApi);
105 | }));
106 |
107 | it('$scope.create() with valid form data should send a POST request with the form input values and then locate to new object URL', inject(function(Apis) {
108 | // Create a sample Api object
109 | var sampleApiPostData = new Apis({
110 | name: 'New Api',
111 | request_host: 'api',
112 | request_path: '/path',
113 | strip_request_path: true,
114 | preserve_host: true,
115 | upstream_url: 'http://api.com'
116 | });
117 |
118 | // Create a sample Api response
119 | var sampleApiResponse = new Apis({
120 | id: '525cf20451979dea2c000001',
121 | name: 'New Api',
122 | request_host: 'api',
123 | request_path: '/path',
124 | strip_request_path: true,
125 | preserve_host: true,
126 | upstream_url: 'http://api.com'
127 | });
128 |
129 | // Fixture mock form input values
130 | scope.name = 'New Api';
131 | scope.request_host = 'api';
132 | scope.request_path = '/path';
133 | scope.strip_request_path = true;
134 | scope.preserve_host = true;
135 | scope.upstream_url = 'http://api.com';
136 |
137 | // Set POST response
138 | $httpBackend.expectPOST($KONGURL+'/apis', sampleApiPostData).respond(sampleApiResponse);
139 | $httpBackend.when('GET', 'modules/apis/views/view-api.client.view.html').respond(200, {});
140 | // Run controller functionality
141 | scope.create();
142 | $httpBackend.flush();
143 |
144 | // Test form inputs are reset
145 | expect(scope.name).toEqual('');
146 |
147 | // Test URL redirection after the Api was created
148 | expect($location.path()).toBe('/apis/' + sampleApiResponse.id);
149 | }));
150 |
151 | it('$scope.update() should update a valid Api', inject(function(Apis) {
152 | // Define a sample Api put data
153 | var sampleApiPutData = new Apis({
154 | id: '525cf20451979dea2c000001',
155 | name: 'New Api',
156 | request_host: 'api',
157 | request_path: '/path',
158 | strip_request_path: true,
159 | upstream_url: 'http://api.com'
160 | });
161 |
162 | // Mock Api in scope
163 | scope.api = sampleApiPutData;
164 |
165 | // Set PUT response
166 | $httpBackend.expectPATCH(/apis\/([0-9a-fA-F]{24})$/).respond();
167 | $httpBackend.when('GET', 'modules/apis/views/view-api.client.view.html').respond(200, {});
168 |
169 | // Run controller functionality
170 | scope.update();
171 | $httpBackend.flush();
172 |
173 | // Test URL location to new object
174 | expect($location.path()).toBe('/apis/' + sampleApiPutData.id);
175 | }));
176 |
177 | it('$scope.remove() should send a DELETE request with a valid apiId and remove the Api from the scope', inject(function(Apis) {
178 | // Create new Api object
179 | var sampleApi = new Apis({
180 | id: '525a8422f6d0f87f0e407a33'
181 | });
182 |
183 | // Create new Apis array and include the Api
184 | scope.apis = [sampleApi];
185 |
186 | // Set expected DELETE response
187 | $httpBackend.expectDELETE(/apis\/([0-9a-fA-F]{24})$/).respond(204);
188 |
189 | // Run controller functionality
190 | scope.remove(sampleApi);
191 | $httpBackend.flush();
192 |
193 | // Test array after successful delete
194 | expect(scope.apis.length).toBe(0);
195 | }));
196 | });
197 | }());
198 |
--------------------------------------------------------------------------------
/modules/apis/views/create-api.client.view.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/modules/apis/views/edit-api.client.view.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/modules/apis/views/list-apis.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{error}}
5 |
6 |
9 |
15 |
16 |
17 |
18 | ID
19 |
20 |
21 | |
22 |
23 | Name
24 |
25 |
26 | |
27 |
28 | Request Host
29 |
30 |
31 | |
32 |
33 | Request Path
34 |
35 |
36 | |
37 |
38 | Upstream Url
39 |
40 |
41 | |
42 |
43 | Strip Request Path
44 |
45 |
46 | |
47 |
48 | Preserve Host
49 |
50 |
51 | |
52 | Action |
53 |
54 |
55 |
56 |
57 | {{api.id}}
58 | |
59 |
60 |
61 | {{api.name}}
62 |
63 | |
64 |
65 |
66 | {{api.request_host}}
67 |
68 | |
69 |
70 |
71 | {{api.request_path}}
72 |
73 | |
74 |
75 |
76 | {{api.upstream_url}}
77 |
78 | |
79 |
80 |
81 | {{ api.strip_request_path && "ON" || "OFF" }}
82 |
83 | |
84 |
85 |
86 | {{ api.preserve_host && "ON" || "OFF" }}
87 |
88 | |
89 |
90 |
98 |
104 | |
105 |
106 |
107 |
108 |
109 | No Apis yet, why don't you
create one?
110 |
111 |
112 |
--------------------------------------------------------------------------------
/modules/apis/views/plugin.form.directive.html:
--------------------------------------------------------------------------------
1 | Check out the Documents for {{currentPlugin.name}} in {{currentPlugin.docUrl}}
2 |
3 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/modules/apis/views/view-api-plugin.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 | Attribute |
17 | Value |
18 |
19 |
20 | Consumer ID |
21 | {{plugin.consumer_id}} |
22 |
23 |
24 | {{key}} |
25 | {{value}} |
26 |
27 |
28 |
29 |
30 |
31 |
32 | No active plugins found in the API, why don't you add one ?
33 |
34 |
35 |
--------------------------------------------------------------------------------
/modules/apis/views/view-api.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
16 |
17 |
18 |
19 | Attribute |
20 | Value |
21 |
22 |
23 | request_host |
24 | {{api.request_host}} |
25 |
26 |
27 | request_path |
28 | {{api.request_path}} |
29 |
30 |
31 | upstream_url |
32 | {{api.upstream_url}} |
33 |
34 |
35 | strip_request_path |
36 | {{api.strip_request_path}} |
37 |
38 |
39 | preserve_host |
40 | {{api.preserve_host}} |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/modules/consumers/config/consumers.client.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Configuring the Articles module
4 | angular.module('consumers').run(['Menus', 'editableOptions',
5 | function(Menus, editableOptions) {
6 | // Set top bar menu items
7 | Menus.addMenuItem('topbar', 'Consumers', 'consumers', 'dropdown', '/consumers(/create)?');
8 | Menus.addSubMenuItem('topbar', 'consumers', 'List Consumers', 'consumers');
9 | Menus.addSubMenuItem('topbar', 'consumers', 'New Consumer', 'consumers/create');
10 | editableOptions.theme = 'bs3';
11 | }
12 | ]);
--------------------------------------------------------------------------------
/modules/consumers/config/consumers.client.routes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Setting up route
4 | angular.module('consumers').config(['$stateProvider',
5 | function($stateProvider) {
6 | // Consumers state routing
7 | $stateProvider.
8 | state('listConsumers', {
9 | url: '/consumers',
10 | templateUrl: 'modules/consumers/views/list-consumers.client.view.html',
11 | ncyBreadcrumb: {
12 | label: 'List Consumers',
13 | parent: 'home'
14 | }
15 | }).
16 | state('createConsumer', {
17 | url: '/consumers/create',
18 | templateUrl: 'modules/consumers/views/create-consumer.client.view.html',
19 | ncyBreadcrumb: {
20 | label: 'Create Consumer',
21 | parent: 'home'
22 | }
23 | }).
24 | state('viewConsumer', {
25 | url: '/consumers/:consumerId',
26 | templateUrl: 'modules/consumers/views/view-consumer.client.view.html',
27 | ncyBreadcrumb: {
28 | label: 'View Consumer',
29 | parent: 'listConsumers'
30 | }
31 | }).
32 | state('editConsumer', {
33 | url: '/consumers/:consumerId/edit',
34 | templateUrl: 'modules/consumers/views/edit-consumer.client.view.html',
35 | ncyBreadcrumb: {
36 | label: 'Edit Consumer',
37 | parent: 'viewConsumer'
38 | }
39 | }).
40 | state('viewPluginConsumer', {
41 | url: '/consumers/:consumerId/plugins',
42 | templateUrl: 'modules/consumers/views/view-api-plugin-consumer.client.view.html',
43 | ncyBreadcrumb: {
44 | label: 'List Plugins for Consumer',
45 | parent: 'viewConsumer'
46 | }
47 | }).
48 | state('crudPluginConsumer', {
49 | url: '/consumers/:consumerId/plugins/:pluginName',
50 | templateUrl: 'modules/consumers/views/crud-plugin-consumer.client.view.html',
51 | ncyBreadcrumb: {
52 | label: 'Manage Plugin API',
53 | parent: 'viewConsumer'
54 | }
55 | });
56 | }
57 | ]);
--------------------------------------------------------------------------------
/modules/consumers/consumers.client.module.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use applicaion configuration module to register a new module
4 | ApplicationConfiguration.registerModule('consumers');
--------------------------------------------------------------------------------
/modules/consumers/controllers/consumers.client.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Consumers controller
4 | angular.module('consumers').controller('ConsumersController', ['$scope', '$stateParams', '$location', '$state', '$filter', '$http', 'Consumers', 'PLUGINSAVAILABLE', 'PluginsConfigurations', 'Apis', 'Plugins', '$localStorage',
5 | function($scope, $stateParams, $location, $state, $filter, $http, Consumers, PLUGINSAVAILABLE, PluginsConfigurations, Apis, Plugins, $localStorage) {
6 |
7 | // Create new Consumer
8 | $scope.create = function() {
9 | // Create new Consumer object
10 | var consumer = new Consumers ({
11 | username: this.username,
12 | custom_id: this.custom_id
13 | });
14 |
15 | // Redirect after save
16 | consumer.$save(function(response) {
17 | $location.path('consumers/' + response.id);
18 |
19 | // Clear form fields
20 | $scope.username = '';
21 | $scope.custom_id = '';
22 | }, function(errorResponse) {
23 | $scope.error = errorResponse.data;
24 | });
25 | };
26 |
27 | // Remove existing Consumer
28 | $scope.remove = function(consumer) {
29 | if ( consumer ) {
30 | consumer.$remove();
31 |
32 | for (var i in $scope.consumers) {
33 | if ($scope.consumers [i] === consumer) {
34 | $scope.consumers.splice(i, 1);
35 | }
36 | }
37 | } else {
38 | $scope.consumer.$remove(function() {
39 | $location.path('consumers');
40 | });
41 | }
42 | };
43 |
44 | // Update existing Consumer
45 | $scope.update = function() {
46 | var consumer = $scope.consumer;
47 |
48 | consumer.$update(function() {
49 | $location.path('consumers/' + consumer.id);
50 | }, function(errorResponse) {
51 | $scope.error = errorResponse.data;
52 | });
53 | };
54 |
55 | // Find a list of Consumers
56 | $scope.find = function() {
57 | $scope.consumers = Consumers.query($location.search());
58 | $scope.scroll= {busy : false};
59 | };
60 |
61 | // Find existing Consumer
62 | $scope.findOne = function() {
63 | $scope.pluginAvailable = PLUGINSAVAILABLE;
64 | $scope.consumer = Consumers.get({
65 | consumerId: $stateParams.consumerId
66 | });
67 | };
68 |
69 | $scope.listPluginByConsumer = function() {
70 | $scope.pluginAvailable = PLUGINSAVAILABLE;
71 | $scope.findOne();
72 | $scope.apis = Apis.query({size: 100000});
73 | $scope.plugins = PluginsConfigurations.query({
74 | consumer_id : $stateParams.consumerId
75 | });
76 | };
77 |
78 |
79 | $scope.initPluginForm = function() {
80 | $scope.pluginAvailable = PLUGINSAVAILABLE;
81 | $scope.currentPlugin = null;
82 | $scope.apis = Apis.query({size: 100000});
83 | $scope.api_id = null;
84 | $scope.value = {};
85 | };
86 |
87 | $scope.removePlugin = function(plugin) {
88 | if ( plugin ) {
89 | var pluginResource = new Plugins(plugin);
90 | pluginResource.$remove();
91 |
92 | for (var i in $scope.plugins.data) {
93 | if ($scope.plugins.data [i] === plugin) {
94 | $scope.plugins.data.splice(i, 1);
95 | }
96 | }
97 | }
98 | };
99 |
100 | $scope.createPlugin = function() {
101 | if ($scope.currentPlugin !== null) {
102 | var plugin = new Plugins({
103 | value: $scope.value,
104 | name: $scope.currentPlugin.name,
105 | consumer_id: $stateParams.consumerId,
106 | api_id: $scope.api_id.id
107 | });
108 | plugin.$save(function(response) {
109 | $scope.initPluginForm();
110 | $state.go($state.current, {}, {reload: true});
111 | }, function(errorResponse) {
112 | $scope.error = errorResponse.data;
113 | });
114 | }
115 | };
116 |
117 | $scope.saveConsumer = function(data, id) {
118 | var consumer = new Consumers (data);
119 | consumer.id = id;
120 | consumer.$update(function() {
121 | for (var i in $scope.consumers.data) {
122 | if ($scope.consumers.data [i] . id === id) {
123 | angular.extend($scope.consumers.data [i], data);
124 | }
125 | }
126 | }, function(errorResponse) {
127 | $scope.error = errorResponse.data;
128 | });
129 | };
130 |
131 | $scope.hasApi = function(element) {
132 | return undefined !== element.api;
133 | };
134 |
135 | $scope.crudPlugin = function() {
136 |
137 | $scope.plugin = $filter('filter')(PLUGINSAVAILABLE,
138 | function(element){
139 | return $stateParams.pluginName === element.name;
140 | }
141 | );
142 |
143 | $scope.plugin = $scope.plugin[0];
144 |
145 | if ($scope.plugin.api === undefined) {
146 | $location.path('consumers/' + $stateParams.consumerId);
147 | }
148 |
149 | var routeList = $filter('filter')($scope.plugin.api.routes, {action: 'list'})[0];
150 |
151 | var route = routeList.route.replace(':username', $stateParams.consumerId);
152 |
153 | $http.get($localStorage.kongurl+'/'+route).
154 | success(function(data, status){
155 | $scope.items = data;
156 | });
157 |
158 | };
159 |
160 | $scope.addRow = function() {
161 | $scope.inserted = {id: null};
162 | angular.forEach($scope.plugin.api.dao, function(field){
163 | $scope.inserted[field.name] = '';
164 | });
165 |
166 | $scope.items.data.push($scope.inserted);
167 | };
168 |
169 | $scope.saveItem = function(data, id, index) {
170 | var routeList, route;
171 | if (id === null) {
172 | routeList = $filter('filter')($scope.plugin.api.routes, {action: 'create'})[0];
173 |
174 | route = routeList.route.replace(':username', $stateParams.consumerId);
175 |
176 | $http.post($localStorage.kongurl+'/'+route, data).
177 | success(function(data, status){
178 | $scope.items.data[index].id = data.id;
179 | });
180 |
181 | } else {
182 | routeList = $filter('filter')($scope.plugin.api.routes, {action: 'update'})[0];
183 |
184 | route = routeList.route.replace(':username', $stateParams.consumerId);
185 |
186 | route = route.replace(':id', id);
187 |
188 | $http.patch($localStorage.kongurl+'/'+route, data);
189 | }
190 | };
191 |
192 | $scope.removeItem = function(index) {
193 |
194 | var id = $scope.items.data[index].id;
195 |
196 | var routeList = $filter('filter')($scope.plugin.api.routes, {action: 'delete'})[0];
197 |
198 | var route = routeList.route.replace(':username', $stateParams.consumerId);
199 |
200 | route = route.replace(':id', id);
201 |
202 | $http.delete($localStorage.kongurl+'/'+route).
203 | success(function(data, status){
204 | $scope.items.data.splice(index, 1);
205 | });
206 | };
207 |
208 | $scope.nextPage = function() {
209 | if ($scope.scroll.busy){
210 | $scope.scroll.busy = false;
211 | return;
212 | }
213 |
214 | $scope.scroll.busy = true;
215 |
216 |
217 | if (undefined === $scope.consumers.next){
218 | $scope.scroll.busy = false;
219 | return;
220 | }
221 |
222 | var offset = new URI($scope.consumers.next).search(true).offset;
223 | Consumers.query({offset: offset}, function(consumers){
224 | $scope.consumers.next = consumers.next;
225 | $scope.consumers.data = $scope.consumers.data.concat(consumers.data);
226 | $scope.scroll.busy = false;
227 | });
228 | };
229 |
230 | // Set sorting default configuration
231 | $scope.sortType = 'username';
232 | $scope.sortReverse = false;
233 | }
234 | ]);
235 |
--------------------------------------------------------------------------------
/modules/consumers/services/consumers.client.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Consumers service used to communicate Consumers REST endpoints
4 | angular.module('consumers').factory('Consumers', ['$resource', '$localStorage',
5 | function($resource, $localStorage) {
6 | return $resource($localStorage.kongurl+'/consumers/:consumerId', { consumerId: '@id'
7 | }, {
8 | query: {
9 | method: 'GET',
10 | isArray: false
11 | },
12 | update: {
13 | method: 'PATCH'
14 | }
15 | });
16 | }
17 | ]);
--------------------------------------------------------------------------------
/modules/consumers/tests/consumers.client.controller.test.js:
--------------------------------------------------------------------------------
1 | (function() {
2 | 'use strict';
3 | // Consumers Controller Spec
4 | describe('Consumers Controller Tests', function() {
5 | // Initialize global variables
6 | var ConsumersController,
7 | scope,
8 | $httpBackend,
9 | $stateParams,
10 | $location,
11 | $KONGURL,
12 | $localStorage;
13 |
14 | // The $resource service augments the response object with methods for updating and deleting the resource.
15 | // If we were to use the standard toEqual matcher, our tests would fail because the test values would not match
16 | // the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.
17 | // When the toEqualData matcher compares two objects, it takes only object properties into
18 | // account and ignores methods.
19 | beforeEach(function() {
20 | jasmine.addMatchers({
21 | toEqualData: function(util, customEqualityTesters) {
22 | return {
23 | compare: function(actual, expected) {
24 | return {
25 | pass: angular.equals(actual, expected)
26 | };
27 | }
28 | };
29 | }
30 | });
31 | });
32 |
33 | // Then we can start by loading the main application module
34 | beforeEach(module(ApplicationConfiguration.applicationModuleName));
35 |
36 | // The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).
37 | // This allows us to inject a service but then attach it to a variable
38 | // with the same name as the service.
39 | beforeEach(inject(function($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_, _KONGURL_, _$localStorage_) {
40 | // Set a new global scope
41 | scope = $rootScope.$new();
42 |
43 | // Point global variables to injected services
44 | $stateParams = _$stateParams_;
45 | $httpBackend = _$httpBackend_;
46 | $location = _$location_;
47 | $KONGURL = _KONGURL_;
48 | $localStorage = _$localStorage_;
49 | $localStorage.kongurl = _KONGURL_;
50 |
51 |
52 | $httpBackend.when('GET', 'modules/core/views/home.client.view.html').respond(200, {});
53 | // Initialize the Consumers controller.
54 | ConsumersController = $controller('ConsumersController', {
55 | $scope: scope
56 | });
57 | }));
58 |
59 | it('$scope.find() should create an array with at least findOne Consumer object fetched from XHR', inject(function(Consumers) {
60 | // Create sample Consumer using the Consumers service
61 | var sampleConsumer = new Consumers({
62 | username: 'New Consumer',
63 | custom_id: '4356'
64 | });
65 |
66 | // Create a sample Consumers array that includes the new Consumer
67 | var sampleConsumers = {data: [sampleConsumer]};
68 |
69 | // Set GET response
70 | $httpBackend.expectGET($KONGURL+'/consumers').respond(sampleConsumers);
71 |
72 | // Run controller functionality
73 | scope.find();
74 | $httpBackend.flush();
75 |
76 | // Test scope value
77 | expect(scope.consumers).toEqualData(sampleConsumers);
78 | }));
79 |
80 | it('$scope.findOne() should create an array with one Consumer object fetched from XHR using a consumerId URL parameter', inject(function(Consumers) {
81 | // Define a sample Consumer object
82 | var sampleConsumer = new Consumers({
83 | username: 'New Consumer',
84 | custom_id: '4356'
85 | });
86 |
87 | // Set the URL parameter
88 | $stateParams.consumerId = '525a8422f6d0f87f0e407a33';
89 |
90 | // Set GET response
91 | $httpBackend.expectGET(/http:\/\/localhost:8001\/consumers\/([0-9a-fA-F]{24})$/).respond(sampleConsumer);
92 |
93 | // Run controller functionality
94 | scope.findOne();
95 | $httpBackend.flush();
96 |
97 | // Test scope value
98 | expect(scope.consumer).toEqualData(sampleConsumer);
99 | }));
100 |
101 | it('$scope.create() with valid form data should send a POST request with the form input values and then locate to new object URL', inject(function(Consumers) {
102 | // Create a sample Consumer object
103 | var sampleConsumerPostData = new Consumers({
104 | username: 'New Consumer',
105 | custom_id: '4356'
106 | });
107 |
108 | // Create a sample Consumer response
109 | var sampleConsumerResponse = new Consumers({
110 | id: '525cf20451979dea2c000001',
111 | username: 'New Consumer',
112 | custom_id: '4356'
113 | });
114 |
115 | // Fixture mock form input values
116 | scope.username = 'New Consumer';
117 | scope.custom_id = '4356';
118 |
119 | // Set POST response
120 | $httpBackend.expectPOST($KONGURL+'/consumers', sampleConsumerPostData).respond(sampleConsumerResponse);
121 | $httpBackend.when('GET', 'modules/consumers/views/view-consumer.client.view.html').respond(200, {});
122 |
123 | // Run controller functionality
124 | scope.create();
125 | $httpBackend.flush();
126 |
127 | // Test form inputs are reset
128 | expect(scope.username).toEqual('');
129 |
130 | // Test URL redirection after the Consumer was created
131 | expect($location.path()).toBe('/consumers/' + sampleConsumerResponse.id);
132 | }));
133 |
134 | it('$scope.update() should update a valid Consumer', inject(function(Consumers) {
135 | // Define a sample Consumer put data
136 | var sampleConsumerPutData = new Consumers({
137 | id: '525cf20451979dea2c000001',
138 | username: 'New Consumer',
139 | custom_id: '4356'
140 | });
141 |
142 | // Mock Consumer in scope
143 | scope.consumer = sampleConsumerPutData;
144 |
145 | // Set PUT response
146 | $httpBackend.expectPATCH(/http:\/\/localhost:8001\/consumers\/([0-9a-fA-F]{24})$/).respond();
147 | $httpBackend.when('GET', 'modules/consumers/views/view-consumer.client.view.html').respond(200, {});
148 | // Run controller functionality
149 | scope.update();
150 | $httpBackend.flush();
151 |
152 | // Test URL location to new object
153 | expect($location.path()).toBe('/consumers/' + sampleConsumerPutData.id);
154 | }));
155 |
156 | it('$scope.remove() should send a DELETE request with a valid consumerId and remove the Consumer from the scope', inject(function(Consumers) {
157 | // Create new Consumer object
158 | var sampleConsumer = new Consumers({
159 | id: '525a8422f6d0f87f0e407a33'
160 | });
161 |
162 | // Create new Consumers array and include the Consumer
163 | scope.consumers = [sampleConsumer];
164 |
165 | // Set expected DELETE response
166 | $httpBackend.expectDELETE(/consumers\/([0-9a-fA-F]{24})$/).respond(204);
167 |
168 | // Run controller functionality
169 | scope.remove(sampleConsumer);
170 | $httpBackend.flush();
171 |
172 | // Test array after successful delete
173 | expect(scope.consumers.length).toBe(0);
174 | }));
175 | });
176 | }());
177 |
--------------------------------------------------------------------------------
/modules/consumers/views/create-consumer.client.view.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/modules/consumers/views/crud-plugin-consumer.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{field.label}} |
5 | Actions |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | {{ item[field.name] && "True" || "False" }}
23 |
24 |
25 | |
26 |
27 |
28 |
36 |
37 |
38 |
39 |
40 | |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/modules/consumers/views/edit-consumer.client.view.html:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/modules/consumers/views/list-consumers.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
12 |
13 |
14 | ID
15 |
16 |
17 | |
18 |
19 | Username
20 |
21 |
22 | |
23 |
24 | Custom ID
25 |
26 |
27 | |
28 | Action |
29 |
30 |
31 |
32 |
33 | {{consumer.id}}
34 | |
35 |
36 |
37 | {{consumer.username}}
38 |
39 | |
40 |
41 |
42 | {{consumer.custom_id}}
43 |
44 | |
45 |
46 |
54 |
60 | |
61 |
62 |
63 |
64 |
65 | No Consumers yet, why don't you
create one?
66 |
67 |
68 |
--------------------------------------------------------------------------------
/modules/consumers/views/view-api-plugin-consumer.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
14 |
15 |
16 | Attribute |
17 | Value |
18 |
19 |
20 | Api name |
21 |
22 | {{api.name}}
23 | |
24 |
25 |
26 | {{key}} |
27 | {{value}} |
28 |
29 |
30 |
31 |
32 |
33 |
34 | No active plugins found in the API, why don't you add one ?
35 |
36 |
37 |
--------------------------------------------------------------------------------
/modules/consumers/views/view-consumer.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
5 |
26 |
27 |
28 |
29 | Attribute |
30 | Value |
31 |
32 |
33 | username |
34 | {{consumer.username}} |
35 |
36 |
37 | custom_id |
38 | {{consumer.custom_id}} |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/modules/core/config/core.client.constants.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Setting up route
4 | angular.module('core').value('KONGURL', 'http://localhost:8001');
5 |
--------------------------------------------------------------------------------
/modules/core/config/core.client.routes.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Setting up route
4 | angular.module('core').config(['$stateProvider', '$urlRouterProvider',
5 | function($stateProvider, $urlRouterProvider) {
6 | // Redirect to home view when route not found
7 | $urlRouterProvider.otherwise('/');
8 |
9 | // Home state routing
10 | $stateProvider.
11 | state('home', {
12 | url: '/',
13 | templateUrl: 'modules/core/views/home.client.view.html',
14 | ncyBreadcrumb: {
15 | label: 'Home page'
16 | }
17 | });
18 | }
19 | ]);
--------------------------------------------------------------------------------
/modules/core/controllers/header.client.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | angular.module('core').controller('HeaderController', ['$scope', '$location', '$state', '$window', '$localStorage', '$http', 'Menus', 'KONGURL',
4 | function($scope, $location, $state, $window, $localStorage, $http, Menus, KONGURL) {
5 | $scope.isCollapsed = false;
6 | $scope.menu = Menus.getMenu('topbar');
7 | $scope.storage = $localStorage;
8 |
9 | $scope.toggleCollapsibleMenu = function() {
10 | $scope.isCollapsed = !$scope.isCollapsed;
11 | };
12 |
13 | // Collapsing the menu after navigation
14 | $scope.$on('$stateChangeSuccess', function() {
15 | $scope.isCollapsed = false;
16 | });
17 |
18 | $scope.setKongUrl = function() {
19 | var url = this.url = this.url.replace(/\/$/, '');
20 | $http.get(this.url).success(function(data, status){
21 | if (status === 200) {
22 | $localStorage.kongurl = url;
23 | setTimeout(function () {
24 | $window.location.reload();
25 | }, 1000);
26 | }
27 | }).
28 | error(function(data, status){
29 |
30 | });
31 |
32 | };
33 |
34 | $scope.removeKongUrl = function() {
35 | delete $localStorage.kongurl;
36 | $localStorage.hasDisconnected = true;
37 | setTimeout(function () {
38 | $window.location.reload();
39 | }, 1000);
40 | };
41 | }
42 | ]);
43 |
--------------------------------------------------------------------------------
/modules/core/controllers/home.client.controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | angular.module('core').controller('HomeController', ['$scope', '$http', '$localStorage', 'PLUGINSAVAILABLE', 'KONGURL',
5 | function($scope, $http, $localStorage, PLUGINSAVAILABLE, KONGURL) {
6 | $scope.storage = $localStorage;
7 | $scope.isArray = angular.isArray;
8 | $scope.pluginsAvailable = PLUGINSAVAILABLE;
9 | if ($localStorage.hasDisconnected !== true && typeof KONGURL !== 'undefined'){
10 | $localStorage.kongurl = KONGURL;
11 | }
12 | if ($localStorage.kongurl !== undefined) {
13 | $http.get($localStorage.kongurl).
14 | success(function(data, status){
15 | $scope.serverInfo = data;
16 | });
17 | }
18 | }
19 | ]);
20 |
--------------------------------------------------------------------------------
/modules/core/core.client.module.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Use Applicaion configuration module to register a new module
4 | ApplicationConfiguration.registerModule('core');
--------------------------------------------------------------------------------
/modules/core/css/core.css:
--------------------------------------------------------------------------------
1 | .content {
2 | margin-top: 50px;
3 | }
4 | .undecorated-link:hover {
5 | text-decoration: none;
6 | }
7 | [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
8 | display: none !important;
9 | }
10 | .ng-invalid.ng-dirty {
11 | border-color: #FA787E;
12 | }
13 | .ng-valid.ng-dirty {
14 | border-color: #78FA89;
15 | }
--------------------------------------------------------------------------------
/modules/core/img/brand/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsdevigo/jungle/4c4886955d9ab4feb08fb3abe4979f1eb12bbe62/modules/core/img/brand/favicon.ico
--------------------------------------------------------------------------------
/modules/core/img/brand/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsdevigo/jungle/4c4886955d9ab4feb08fb3abe4979f1eb12bbe62/modules/core/img/brand/logo.png
--------------------------------------------------------------------------------
/modules/core/img/loaders/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rsdevigo/jungle/4c4886955d9ab4feb08fb3abe4979f1eb12bbe62/modules/core/img/loaders/loader.gif
--------------------------------------------------------------------------------
/modules/core/services/menus.client.service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | //Menu service used for managing menus
4 | angular.module('core').service('Menus', [
5 |
6 | function() {
7 | // Define a set of default roles
8 | this.defaultRoles = ['*'];
9 |
10 | // Define the menus object
11 | this.menus = {};
12 |
13 | // A private function for rendering decision
14 | var shouldRender = function(user) {
15 | if (user) {
16 | if (!!~this.roles.indexOf('*')) {
17 | return true;
18 | } else {
19 | for (var userRoleIndex in user.roles) {
20 | for (var roleIndex in this.roles) {
21 | if (this.roles[roleIndex] === user.roles[userRoleIndex]) {
22 | return true;
23 | }
24 | }
25 | }
26 | }
27 | } else {
28 | return this.isPublic;
29 | }
30 |
31 | return false;
32 | };
33 |
34 | // Validate menu existance
35 | this.validateMenuExistance = function(menuId) {
36 | if (menuId && menuId.length) {
37 | if (this.menus[menuId]) {
38 | return true;
39 | } else {
40 | throw new Error('Menu does not exists');
41 | }
42 | } else {
43 | throw new Error('MenuId was not provided');
44 | }
45 |
46 | return false;
47 | };
48 |
49 | // Get the menu object by menu id
50 | this.getMenu = function(menuId) {
51 | // Validate that the menu exists
52 | this.validateMenuExistance(menuId);
53 |
54 | // Return the menu object
55 | return this.menus[menuId];
56 | };
57 |
58 | // Add new menu object by menu id
59 | this.addMenu = function(menuId, isPublic, roles) {
60 | // Create the new menu
61 | this.menus[menuId] = {
62 | isPublic: isPublic || false,
63 | roles: roles || this.defaultRoles,
64 | items: [],
65 | shouldRender: shouldRender
66 | };
67 |
68 | // Return the menu object
69 | return this.menus[menuId];
70 | };
71 |
72 | // Remove existing menu object by menu id
73 | this.removeMenu = function(menuId) {
74 | // Validate that the menu exists
75 | this.validateMenuExistance(menuId);
76 |
77 | // Return the menu object
78 | delete this.menus[menuId];
79 | };
80 |
81 | // Add menu item object
82 | this.addMenuItem = function(menuId, menuItemTitle, menuItemURL, menuItemType, menuItemUIRoute, isPublic, roles, position) {
83 | // Validate that the menu exists
84 | this.validateMenuExistance(menuId);
85 |
86 | // Push new menu item
87 | this.menus[menuId].items.push({
88 | title: menuItemTitle,
89 | link: menuItemURL,
90 | menuItemType: menuItemType || 'item',
91 | menuItemClass: menuItemType,
92 | uiRoute: menuItemUIRoute || ('/' + menuItemURL),
93 | isPublic: ((isPublic === null || typeof isPublic === 'undefined') ? this.menus[menuId].isPublic : isPublic),
94 | roles: ((roles === null || typeof roles === 'undefined') ? this.menus[menuId].roles : roles),
95 | position: position || 0,
96 | items: [],
97 | shouldRender: shouldRender
98 | });
99 |
100 | // Return the menu object
101 | return this.menus[menuId];
102 | };
103 |
104 | // Add submenu item object
105 | this.addSubMenuItem = function(menuId, rootMenuItemURL, menuItemTitle, menuItemURL, menuItemUIRoute, isPublic, roles, position) {
106 | // Validate that the menu exists
107 | this.validateMenuExistance(menuId);
108 |
109 | // Search for menu item
110 | for (var itemIndex in this.menus[menuId].items) {
111 | if (this.menus[menuId].items[itemIndex].link === rootMenuItemURL) {
112 | // Push new submenu item
113 | this.menus[menuId].items[itemIndex].items.push({
114 | title: menuItemTitle,
115 | link: menuItemURL,
116 | uiRoute: menuItemUIRoute || ('/' + menuItemURL),
117 | isPublic: ((isPublic === null || typeof isPublic === 'undefined') ? this.menus[menuId].items[itemIndex].isPublic : isPublic),
118 | roles: ((roles === null || typeof roles === 'undefined') ? this.menus[menuId].items[itemIndex].roles : roles),
119 | position: position || 0,
120 | shouldRender: shouldRender
121 | });
122 | }
123 | }
124 |
125 | // Return the menu object
126 | return this.menus[menuId];
127 | };
128 |
129 | // Remove existing menu object by menu id
130 | this.removeMenuItem = function(menuId, menuItemURL) {
131 | // Validate that the menu exists
132 | this.validateMenuExistance(menuId);
133 |
134 | // Search for menu item to remove
135 | for (var itemIndex in this.menus[menuId].items) {
136 | if (this.menus[menuId].items[itemIndex].link === menuItemURL) {
137 | this.menus[menuId].items.splice(itemIndex, 1);
138 | }
139 | }
140 |
141 | // Return the menu object
142 | return this.menus[menuId];
143 | };
144 |
145 | // Remove existing menu object by menu id
146 | this.removeSubMenuItem = function(menuId, submenuItemURL) {
147 | // Validate that the menu exists
148 | this.validateMenuExistance(menuId);
149 |
150 | // Search for menu item to remove
151 | for (var itemIndex in this.menus[menuId].items) {
152 | for (var subitemIndex in this.menus[menuId].items[itemIndex].items) {
153 | if (this.menus[menuId].items[itemIndex].items[subitemIndex].link === submenuItemURL) {
154 | this.menus[menuId].items[itemIndex].items.splice(subitemIndex, 1);
155 | }
156 | }
157 | }
158 |
159 | // Return the menu object
160 | return this.menus[menuId];
161 | };
162 |
163 | //Adding the topbar menu
164 | this.addMenu('topbar');
165 | }
166 | ]);
--------------------------------------------------------------------------------
/modules/core/tests/header.client.controller.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | (function() {
4 | describe('HeaderController', function() {
5 | //Initialize global variables
6 | var scope,
7 | HeaderController;
8 |
9 | // Load the main application module
10 | beforeEach(module(ApplicationConfiguration.applicationModuleName));
11 |
12 | beforeEach(inject(function($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 |
15 | HeaderController = $controller('HeaderController', {
16 | $scope: scope
17 | });
18 | }));
19 |
20 |
21 | });
22 | })();
--------------------------------------------------------------------------------
/modules/core/tests/home.client.controller.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | (function() {
4 | describe('HomeController', function() {
5 | //Initialize global variables
6 | var scope,
7 | HomeController;
8 |
9 | // Load the main application module
10 | beforeEach(module(ApplicationConfiguration.applicationModuleName));
11 |
12 | beforeEach(inject(function($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 |
15 | HomeController = $controller('HomeController', {
16 | $scope: scope
17 | });
18 | }));
19 |
20 |
21 | });
22 | })();
--------------------------------------------------------------------------------
/modules/core/views/header.client.view.html:
--------------------------------------------------------------------------------
1 |
20 |
21 |
22 |
34 |
--------------------------------------------------------------------------------
/modules/core/views/home.client.view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Not connected to any Kong's instance.
4 | Please connect to an instance by entering the address in the form above.
5 |
6 |
7 |
8 |
9 |
How to connect your KONG instance
10 |
11 |
12 | - Add your Kong Admin API in the your Kong apis list
13 |
14 | - First add your Kong Admin API in the Kong
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
Server info
24 |
25 |
26 |
27 | Host |
28 | {{storage.kongurl}} |
29 |
30 |
31 | Hostname |
32 | {{serverInfo.hostname}} |
33 |
34 |
35 | Kong version |
36 | {{serverInfo.version}} |
37 |
38 |
39 | Lua version |
40 | {{serverInfo.lua_version}} |
41 |
42 |
43 | Plugins |
44 |
45 |
46 |
47 | {{plugin}}
48 | {{plugin}}
49 |
50 |
51 |
52 | |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |

63 |
64 |
65 |
66 |
67 |
68 | Open-Source User Interface for KONG
69 |
70 |
71 |
76 |
77 |
78 |
79 |
KONG Documentation
80 |
81 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jungle",
3 | "version": "0.0.1",
4 | "dependencies": {},
5 | "repository": {},
6 | "devDependencies": {
7 | "grunt": "^0.4.5",
8 | "grunt-autoprefixer": "^2.0.0",
9 | "grunt-concurrent": "^1.0.0",
10 | "grunt-contrib-clean": "^0.6.0",
11 | "grunt-contrib-concat": "^0.5.0",
12 | "grunt-contrib-connect": "^0.9.0",
13 | "grunt-contrib-copy": "^0.7.0",
14 | "grunt-contrib-cssmin": "^0.12.0",
15 | "grunt-contrib-htmlmin": "^0.4.0",
16 | "grunt-contrib-imagemin": "^0.9.2",
17 | "grunt-contrib-jshint": "~0.10.0",
18 | "grunt-contrib-csslint": "^0.3.1",
19 | "grunt-contrib-uglify": "^0.7.0",
20 | "grunt-contrib-watch": "^0.6.1",
21 | "grunt-filerev": "^2.1.2",
22 | "grunt-google-cdn": "^0.4.3",
23 | "grunt-karma": "*",
24 | "grunt-newer": "^1.1.0",
25 | "grunt-ng-annotate": "^0.9.2",
26 | "grunt-svgmin": "^2.0.0",
27 | "grunt-usemin": "^3.0.0",
28 | "grunt-wiredep": "^2.0.0",
29 | "jshint-stylish": "^1.0.0",
30 | "karma-jasmine": "*",
31 | "karma-phantomjs-launcher": "*",
32 | "karma-chrome-launcher": "*",
33 | "phantomjs": "*",
34 | "karma": "*",
35 | "load-grunt-tasks": "^3.1.0",
36 | "time-grunt": "^1.0.0",
37 | "glob": "~4.0.5",
38 | "lodash": "~2.4.1",
39 | "async": "~0.9.0",
40 | "grunt-env": "~0.4.1",
41 | "chalk": "~0.5",
42 | "grunt-htmlrefs": "*"
43 | },
44 | "engines": {
45 | "node": ">=0.10.0"
46 | },
47 | "scripts": {
48 | "test": "grunt test"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/robots.txt:
--------------------------------------------------------------------------------
1 | # robotstxt.org/
2 |
3 | User-agent: *
4 |
--------------------------------------------------------------------------------
/test/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "node": true,
3 | "browser": true,
4 | "esnext": true,
5 | "bitwise": true,
6 | "camelcase": true,
7 | "curly": true,
8 | "eqeqeq": true,
9 | "immed": true,
10 | "indent": 2,
11 | "latedef": true,
12 | "newcap": true,
13 | "noarg": true,
14 | "quotmark": "single",
15 | "regexp": true,
16 | "undef": true,
17 | "unused": true,
18 | "strict": true,
19 | "trailing": true,
20 | "smarttabs": true,
21 | "jasmine": true,
22 | "globals": {
23 | "angular": false,
24 | "browser": false,
25 | "inject": false
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // http://karma-runner.github.io/0.12/config/configuration-file.html
3 | // Generated on 2015-06-10 using
4 | // generator-karma 1.0.0
5 |
6 | module.exports = function(config) {
7 | 'use strict';
8 |
9 | config.set({
10 | // enable / disable watching file and executing tests whenever any file changes
11 | autoWatch: true,
12 |
13 | // base path, that will be used to resolve files and exclude
14 | basePath: '../',
15 |
16 | // testing framework to use (jasmine/mocha/qunit/...)
17 | // as well as any additional frameworks (requirejs/chai/sinon/...)
18 | frameworks: [
19 | "jasmine"
20 | ],
21 |
22 | // list of files / patterns to load in the browser
23 | files: [
24 | // bower:js
25 | 'lib/jquery/dist/jquery.js',
26 | 'lib/bootstrap/dist/js/bootstrap.js',
27 | 'lib/angular/angular.js',
28 | 'lib/angular-resource/angular-resource.js',
29 | 'lib/angular-mocks/angular-mocks.js',
30 | 'lib/angular-cookies/angular-cookies.js',
31 | 'lib/angular-animate/angular-animate.js',
32 | 'lib/angular-touch/angular-touch.js',
33 | 'lib/angular-sanitize/angular-sanitize.js',
34 | 'lib/angular-bootstrap/ui-bootstrap-tpls.js',
35 | 'lib/angular-ui-utils/ui-utils.js',
36 | 'lib/angular-ui-router/release/angular-ui-router.js',
37 | 'lib/angular-breadcrumb/release/angular-breadcrumb.js',
38 | // endbower
39 | "app/scripts/**/*.js",
40 | "test/mock/**/*.js",
41 | "test/spec/**/*.js"
42 | ],
43 |
44 | // list of files / patterns to exclude
45 | exclude: [
46 | ],
47 |
48 | // web server port
49 | port: 8080,
50 |
51 | // Start these browsers, currently available:
52 | // - Chrome
53 | // - ChromeCanary
54 | // - Firefox
55 | // - Opera
56 | // - Safari (only Mac)
57 | // - PhantomJS
58 | // - IE (only Windows)
59 | browsers: [
60 | "PhantomJS"
61 | ],
62 |
63 | // Which plugins to enable
64 | plugins: [
65 | "karma-phantomjs-launcher",
66 | "karma-jasmine"
67 | ],
68 |
69 | // Continuous Integration mode
70 | // if true, it capture browsers, run tests and exit
71 | singleRun: false,
72 |
73 | colors: true,
74 |
75 | // level of logging
76 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
77 | logLevel: config.LOG_INFO,
78 |
79 | // Uncomment the following lines if you are using grunt's server to run the tests
80 | // proxies: {
81 | // '/': 'http://localhost:9000/'
82 | // },
83 | // URL root prevent conflicts with the site root
84 | // urlRoot: '_karma_'
85 | });
86 | };
87 |
--------------------------------------------------------------------------------
/test/spec/controllers/about.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: AboutCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('jungleApp'));
7 |
8 | var AboutCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | AboutCtrl = $controller('AboutCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/spec/controllers/apis.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: ApisCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('jungleApp'));
7 |
8 | var ApisCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | ApisCtrl = $controller('ApisCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/spec/controllers/consumers.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: ConsumersCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('jungleApp'));
7 |
8 | var ConsumersCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | ConsumersCtrl = $controller('ConsumersCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/spec/controllers/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | describe('Controller: MainCtrl', function () {
4 |
5 | // load the controller's module
6 | beforeEach(module('jungleApp'));
7 |
8 | var MainCtrl,
9 | scope;
10 |
11 | // Initialize the controller and a mock scope
12 | beforeEach(inject(function ($controller, $rootScope) {
13 | scope = $rootScope.$new();
14 | MainCtrl = $controller('MainCtrl', {
15 | $scope: scope
16 | });
17 | }));
18 |
19 | it('should attach a list of awesomeThings to the scope', function () {
20 | expect(scope.awesomeThings.length).toBe(3);
21 | });
22 | });
23 |
--------------------------------------------------------------------------------