From 07e7d5d35e4bb0f3068c7f2643b7c47487694550 Mon Sep 17 00:00:00 2001 From: bryk Date: Tue, 13 Sep 2016 12:29:10 +0200 Subject: [PATCH] Config category page And a few fixes to the pagination in admin page. --- i18n/messages-en.xtb | 3 + i18n/messages-ja.xtb | 3 + src/app/backend/handler/apihandler.go | 24 ++++++ .../resource/common/resourcechannels.go | 38 ++++++++- src/app/backend/resource/config/config.go | 83 +++++++++++++++++++ src/app/backend/resource/secret/secretlist.go | 17 +++- src/app/externs/backendapi.js | 11 ++- src/app/frontend/admin/admin.html | 6 +- src/app/frontend/chrome/nav/nav_component.js | 3 + src/app/frontend/config/config.html | 43 ++++++++++ src/app/frontend/config/controller.js | 54 ++++++++++++ src/app/frontend/config/module.js | 45 ++++++++++ src/app/frontend/config/state.js | 19 +++++ src/app/frontend/config/stateconfig.js | 64 ++++++++++++++ src/app/frontend/index_module.js | 2 + .../servicesanddiscovery.html | 4 +- src/test/frontend/config/controller_test.js | 58 +++++++++++++ src/test/frontend/config/module_test.js | 25 ++++++ src/test/frontend/config/stateconfig_test.js | 40 +++++++++ 19 files changed, 534 insertions(+), 8 deletions(-) create mode 100644 src/app/backend/resource/config/config.go create mode 100644 src/app/frontend/config/config.html create mode 100644 src/app/frontend/config/controller.js create mode 100644 src/app/frontend/config/module.js create mode 100644 src/app/frontend/config/state.js create mode 100644 src/app/frontend/config/stateconfig.js create mode 100644 src/test/frontend/config/controller_test.js create mode 100644 src/test/frontend/config/module_test.js create mode 100644 src/test/frontend/config/stateconfig_test.js diff --git a/i18n/messages-en.xtb b/i18n/messages-en.xtb index b3ae07738..d3e3f60bd 100644 --- a/i18n/messages-en.xtb +++ b/i18n/messages-en.xtb @@ -755,4 +755,7 @@ Services and discovery Ingress Services + Config + Config Maps + Secrets \ No newline at end of file diff --git a/i18n/messages-ja.xtb b/i18n/messages-ja.xtb index e1a1bd900..930f10755 100644 --- a/i18n/messages-ja.xtb +++ b/i18n/messages-ja.xtb @@ -948,4 +948,7 @@ Services and discovery Ingress Services + Config + Config Maps + Secrets \ No newline at end of file diff --git a/src/app/backend/handler/apihandler.go b/src/app/backend/handler/apihandler.go index e751ddfe2..f4c7ea676 100644 --- a/src/app/backend/handler/apihandler.go +++ b/src/app/backend/handler/apihandler.go @@ -26,6 +26,7 @@ import ( "github.com/kubernetes/dashboard/src/app/backend/client" "github.com/kubernetes/dashboard/src/app/backend/resource/admin" "github.com/kubernetes/dashboard/src/app/backend/resource/common" + "github.com/kubernetes/dashboard/src/app/backend/resource/config" "github.com/kubernetes/dashboard/src/app/backend/resource/configmap" "github.com/kubernetes/dashboard/src/app/backend/resource/container" "github.com/kubernetes/dashboard/src/app/backend/resource/daemonset" @@ -198,6 +199,15 @@ func CreateHTTPAPIHandler(client *clientK8s.Client, heapsterClient client.Heapst To(apiHandler.handleGetServicesAndDiscovery). Writes(servicesanddiscovery.ServicesAndDiscovery{})) + apiV1Ws.Route( + apiV1Ws.GET("/config"). + To(apiHandler.handleGetConfig). + Writes(config.Config{})) + apiV1Ws.Route( + apiV1Ws.GET("/config/{namespace}"). + To(apiHandler.handleGetConfig). + Writes(config.Config{})) + apiV1Ws.Route( apiV1Ws.GET("/replicaset"). To(apiHandler.handleGetReplicaSets). @@ -805,6 +815,19 @@ func (apiHandler *APIHandler) handleGetServicesAndDiscovery( response.WriteHeaderAndEntity(http.StatusCreated, result) } +func (apiHandler *APIHandler) handleGetConfig( + request *restful.Request, response *restful.Response) { + + namespace := parseNamespacePathParameter(request) + result, err := config.GetConfig(apiHandler.client, namespace) + if err != nil { + handleInternalError(response, err) + return + } + + response.WriteHeaderAndEntity(http.StatusCreated, result) +} + // Handles get Replica Sets list API call. func (apiHandler *APIHandler) handleGetReplicaSets( request *restful.Request, response *restful.Response) { @@ -1266,6 +1289,7 @@ func (apiHandler *APIHandler) handleGetResourceQuotaDetail(request *restful.Requ } response.WriteHeaderAndEntity(http.StatusCreated, result) } + // Handles log API call. func (apiHandler *APIHandler) handleLogs(request *restful.Request, response *restful.Response) { namespace := request.PathParameter("namespace") diff --git a/src/app/backend/resource/common/resourcechannels.go b/src/app/backend/resource/common/resourcechannels.go index cc2ef2c5d..24d9f120a 100644 --- a/src/app/backend/resource/common/resourcechannels.go +++ b/src/app/backend/resource/common/resourcechannels.go @@ -74,9 +74,12 @@ type ResourceChannels struct { // List and error channels to PetSets. PetSetList PetSetListChannel - // List and error channels to PetSets. + // List and error channels to ConfigMaps. ConfigMapList ConfigMapListChannel + // List and error channels to Secrets. + SecretList SecretListChannel + // List and error channels to PodMetrics. PodMetrics PodMetricsChannel @@ -526,6 +529,39 @@ func GetConfigMapListChannel(client client.ConfigMapsNamespacer, nsQuery *Namesp return channel } +// SecretListChannel is a list and error channels to Secrets. +type SecretListChannel struct { + List chan *api.SecretList + Error chan error +} + +// GetSecretListChannel returns a pair of channels to a Secret list and errors that +// both must be read numReads times. +func GetSecretListChannel(client client.SecretsNamespacer, nsQuery *NamespaceQuery, numReads int) SecretListChannel { + + channel := SecretListChannel{ + List: make(chan *api.SecretList, numReads), + Error: make(chan error, numReads), + } + + go func() { + list, err := client.Secrets(nsQuery.ToRequestParam()).List(listEverything) + var filteredItems []api.Secret + for _, item := range list.Items { + if nsQuery.Matches(item.ObjectMeta.Namespace) { + filteredItems = append(filteredItems, item) + } + } + list.Items = filteredItems + for i := 0; i < numReads; i++ { + channel.List <- list + channel.Error <- err + } + }() + + return channel +} + // PersistentVolumeListChannel is a list and error channels to PersistentVolumes. type PersistentVolumeListChannel struct { List chan *api.PersistentVolumeList diff --git a/src/app/backend/resource/config/config.go b/src/app/backend/resource/config/config.go new file mode 100644 index 000000000..3b5837042 --- /dev/null +++ b/src/app/backend/resource/config/config.go @@ -0,0 +1,83 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package config + +import ( + "log" + + "github.com/kubernetes/dashboard/src/app/backend/resource/common" + "github.com/kubernetes/dashboard/src/app/backend/resource/configmap" + "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" + "github.com/kubernetes/dashboard/src/app/backend/resource/secret" + k8sClient "k8s.io/kubernetes/pkg/client/unversioned" +) + +// Config structure contains all resource lists grouped into the config category. +type Config struct { + ConfigMapList configmap.ConfigMapList `json:"configMapList"` + + SecretList secret.SecretList `json:"secretList"` +} + +// GetConfig returns a list of all config resources in the cluster. +func GetConfig(client *k8sClient.Client, nsQuery *common.NamespaceQuery) ( + *Config, error) { + + log.Printf("Getting config category") + channels := &common.ResourceChannels{ + ConfigMapList: common.GetConfigMapListChannel(client, nsQuery, 1), + SecretList: common.GetSecretListChannel(client, nsQuery, 1), + } + + return GetConfigFromChannels(channels) +} + +// GetConfigFromChannels returns a list of all config in the cluster, from the +// channel sources. +func GetConfigFromChannels(channels *common.ResourceChannels) ( + *Config, error) { + + configMapChan := make(chan *configmap.ConfigMapList) + secretChan := make(chan *secret.SecretList) + numErrs := 2 + errChan := make(chan error, numErrs) + + go func() { + items, err := configmap.GetConfigMapListFromChannels(channels, + dataselect.DefaultDataSelect) + errChan <- err + configMapChan <- items + }() + + go func() { + items, err := secret.GetSecretListFromChannels(channels, dataselect.DefaultDataSelect) + errChan <- err + secretChan <- items + }() + + for i := 0; i < numErrs; i++ { + err := <-errChan + if err != nil { + return nil, err + } + } + + config := &Config{ + ConfigMapList: *(<-configMapChan), + SecretList: *(<-secretChan), + } + + return config, nil +} diff --git a/src/app/backend/resource/secret/secretlist.go b/src/app/backend/resource/secret/secretlist.go index 39438136f..6d0c24f68 100644 --- a/src/app/backend/resource/secret/secretlist.go +++ b/src/app/backend/resource/secret/secretlist.go @@ -16,11 +16,11 @@ package secret import ( "github.com/kubernetes/dashboard/src/app/backend/resource/common" + "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" "k8s.io/kubernetes/pkg/fields" "k8s.io/kubernetes/pkg/labels" - "github.com/kubernetes/dashboard/src/app/backend/resource/dataselect" ) // SecretSpec - common interface for the specification of different secrets. @@ -86,6 +86,21 @@ func GetSecretList(client *client.Client, namespace *common.NamespaceQuery, return NewSecretList(secretList.Items, dsQuery), err } +// GetSecretListFromChannels returns a list of all Config Maps in the cluster +// reading required resource list once from the channels. +func GetSecretListFromChannels(channels *common.ResourceChannels, dsQuery *dataselect.DataSelectQuery) ( + *SecretList, error) { + + list := <-channels.SecretList.List + if err := <-channels.SecretList.Error; err != nil { + return nil, err + } + + result := NewSecretList(list.Items, dsQuery) + + return result, nil +} + // CreateSecret - create a single secret using the cluster API client func CreateSecret(client *client.Client, spec SecretSpec) (*Secret, error) { namespace := spec.GetNamespace() diff --git a/src/app/externs/backendapi.js b/src/app/externs/backendapi.js index 9e703dacc..b2687f839 100644 --- a/src/app/externs/backendapi.js +++ b/src/app/externs/backendapi.js @@ -156,6 +156,14 @@ backendApi.Admin; */ backendApi.ServicesAndDiscovery; +/** + * @typedef {{ + * configMapList: !backendApi.ConfigMapList, + * secretList: !backendApi.SecretList, + * }} + */ +backendApi.Config; + /** * @typedef {{ * timestamp: string, @@ -919,7 +927,8 @@ backendApi.Secret; /** * @typedef {{ - * secrets: !Array + * secrets: !Array, + * listMeta: !backendApi.ListMeta * }} */ backendApi.SecretList; diff --git a/src/app/frontend/admin/admin.html b/src/app/frontend/admin/admin.html index 32d864bc6..63ae6e847 100644 --- a/src/app/frontend/admin/admin.html +++ b/src/app/frontend/admin/admin.html @@ -23,7 +23,7 @@ limitations under the License. + namespace-list-resource="$ctrl.kdNamespaceListResource"> @@ -35,7 +35,7 @@ limitations under the License. + node-list-resource="$ctrl.kdNodeListResource"> @@ -48,7 +48,7 @@ limitations under the License. + persistent-volume-list-resource="$ctrl.kdPersistentVolumeListResource"> diff --git a/src/app/frontend/chrome/nav/nav_component.js b/src/app/frontend/chrome/nav/nav_component.js index 1f95c91df..013a45ea8 100644 --- a/src/app/frontend/chrome/nav/nav_component.js +++ b/src/app/frontend/chrome/nav/nav_component.js @@ -13,6 +13,7 @@ // limitations under the License. import {stateName as adminState} from 'admin/state'; +import {stateName as configState} from 'config/state'; import {stateName as configMapState} from 'configmaplist/configmaplist_state'; import {stateName as daemonSetState} from 'daemonsetlist/daemonsetlist_state'; import {stateName as deploymentState} from 'deploymentlist/deploymentlist_state'; @@ -31,6 +32,7 @@ import {stateName as serviceState} from 'servicelist/servicelist_state'; import {stateName as servicesanddiscoveryState} from 'servicesanddiscovery/state'; import {stateName as workloadState} from 'workloads/workloads_state'; + /** * @final */ @@ -66,6 +68,7 @@ export class NavController { 'configMap': configMapState, 'ingress': ingressState, 'serviceDiscovery': servicesanddiscoveryState, + 'config': configState, }; /** @export */ diff --git a/src/app/frontend/config/config.html b/src/app/frontend/config/config.html new file mode 100644 index 000000000..7eda70b97 --- /dev/null +++ b/src/app/frontend/config/config.html @@ -0,0 +1,43 @@ + + +
+ + + + {{::$ctrl.i18n.MSG_CONFIG_SECRETS_LABEL}} + + + + + + + + + + + {{::$ctrl.i18n.MSG_CONFIG_CONFIG_MAPS_LABEL}} + + + + + + + +
+ diff --git a/src/app/frontend/config/controller.js b/src/app/frontend/config/controller.js new file mode 100644 index 000000000..511d336c9 --- /dev/null +++ b/src/app/frontend/config/controller.js @@ -0,0 +1,54 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @final + */ +export class ConfigController { + /** + * @param {!backendApi.Config} config + * @param {!angular.Resource} kdConfigMapListResource + * @param {!angular.Resource} kdSecretListResource + * @ngInject + */ + constructor(config, kdConfigMapListResource, kdSecretListResource) { + /** @export {!backendApi.Config} */ + this.config = config; + /** @export {!angular.Resource} */ + this.kdConfigMapListResource = kdConfigMapListResource; + /** @export {!angular.Resource} */ + this.kdSecretListResource = kdSecretListResource; + /** @export */ + this.i18n = i18n; + } + + /** + * @return {boolean} + * @export + */ + shouldShowZeroState() { + /** @type {number} */ + let resourcesLength = + this.config.configMapList.listMeta.totalItems + this.config.secretList.listMeta.totalItems; + + return resourcesLength === 0; + } +} + +const i18n = { + /** @export {string} @desc Label that appears above the list of resources. */ + MSG_CONFIG_CONFIG_MAPS_LABEL: goog.getMsg('Config Maps'), + /** @export {string} @desc Label that appears above the list of resources. */ + MSG_CONFIG_SECRETS_LABEL: goog.getMsg('Secrets'), +}; diff --git a/src/app/frontend/config/module.js b/src/app/frontend/config/module.js new file mode 100644 index 000000000..2ef97fe6f --- /dev/null +++ b/src/app/frontend/config/module.js @@ -0,0 +1,45 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import chromeModule from 'chrome/chrome_module'; +import configMapModule from 'configmaplist/configmaplist_module'; +import secretModule from 'secretlist/module'; + +import stateConfig from './stateconfig'; + +/** + * Module for the config category. + */ +export default angular + .module( + 'kubernetesDashboard.config', + [ + 'ngMaterial', + 'ngResource', + 'ui.router', + chromeModule.name, + configMapModule.name, + secretModule.name, + ]) + .config(stateConfig) + .factory('kdConfigResource', resource); + +/** + * @param {!angular.$resource} $resource + * @return {!angular.Resource} + * @ngInject + */ +function resource($resource) { + return $resource('api/v1/config/:namespace'); +} diff --git a/src/app/frontend/config/state.js b/src/app/frontend/config/state.js new file mode 100644 index 000000000..f2fcf9986 --- /dev/null +++ b/src/app/frontend/config/state.js @@ -0,0 +1,19 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** Name of the state. Can be used in, e.g., $state.go method. */ +export const stateName = 'config'; + +/** Absolute URL of the state. */ +export const stateUrl = '/config'; diff --git a/src/app/frontend/config/stateconfig.js b/src/app/frontend/config/stateconfig.js new file mode 100644 index 000000000..722ed904c --- /dev/null +++ b/src/app/frontend/config/stateconfig.js @@ -0,0 +1,64 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {stateName as chromeStateName} from 'chrome/chrome_state'; +import {breadcrumbsConfig} from 'common/components/breadcrumbs/breadcrumbs_service'; + +import {ConfigController} from './controller'; +import {stateName} from './state'; +import {stateUrl} from './state'; + +/** + * @param {!ui.router.$stateProvider} $stateProvider + * @ngInject + */ +export default function stateConfig($stateProvider) { + $stateProvider.state(stateName, { + url: stateUrl, + parent: chromeStateName, + resolve: { + 'config': resolveResource, + }, + data: { + [breadcrumbsConfig]: { + 'label': i18n.MSG_BREADCRUMBS_CONFIG_LABEL, + }, + }, + views: { + '': { + controller: ConfigController, + controllerAs: '$ctrl', + templateUrl: 'config/config.html', + }, + }, + }); +} + +/** + * @param {!angular.$resource} kdConfigResource + * @param {!./../chrome/chrome_state.StateParams} $stateParams + * @param {!./../common/pagination/pagination_service.PaginationService} kdPaginationService + * @return {!angular.$q.Promise} + * @ngInject + */ +export function resolveResource(kdConfigResource, $stateParams, kdPaginationService) { + let paginationQuery = kdPaginationService.getDefaultResourceQuery($stateParams.namespace); + return kdConfigResource.get(paginationQuery).$promise; +} + +const i18n = { + /** @type {string} @desc Label 'Config' that appears as a breadcrumbs on the + action bar. */ + MSG_BREADCRUMBS_CONFIG_LABEL: goog.getMsg('Config'), +}; diff --git a/src/app/frontend/index_module.js b/src/app/frontend/index_module.js index 6b666680c..eb78f06bf 100644 --- a/src/app/frontend/index_module.js +++ b/src/app/frontend/index_module.js @@ -18,6 +18,7 @@ */ import adminModule from './admin/module'; import chromeModule from './chrome/chrome_module'; +import configModule from './config/module'; import configMapDetailModule from './configmapdetail/configmapdetail_module'; import configMapListModule from './configmaplist/configmaplist_module'; import deployModule from './deploy/deploy_module'; @@ -91,6 +92,7 @@ export default angular ingressListModule.name, ingressDetailModule.name, servicesanddiscoveryModule.name, + configModule.name, ]) .config(indexConfig) .config(routeConfig); diff --git a/src/app/frontend/servicesanddiscovery/servicesanddiscovery.html b/src/app/frontend/servicesanddiscovery/servicesanddiscovery.html index 0e446fe9f..b37382162 100644 --- a/src/app/frontend/servicesanddiscovery/servicesanddiscovery.html +++ b/src/app/frontend/servicesanddiscovery/servicesanddiscovery.html @@ -23,7 +23,7 @@ limitations under the License. + service-list-resource="$ctrl.kdServiceListResource"> @@ -35,7 +35,7 @@ limitations under the License. + ingress-list-resource="$ctrl.kdIngressListResource"> diff --git a/src/test/frontend/config/controller_test.js b/src/test/frontend/config/controller_test.js new file mode 100644 index 000000000..ea38d8abd --- /dev/null +++ b/src/test/frontend/config/controller_test.js @@ -0,0 +1,58 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import {ConfigController} from 'config/controller'; +import module from 'config/module'; + +describe('Config list controller', () => { + /** @type {!config/config_controller.ConfigController} + */ + let ctrl; + + beforeEach(() => { + angular.mock.module(module.name); + + angular.mock.inject( + ($controller) => { ctrl = $controller(ConfigController, {config: {config: []}}); }); + }); + + it('should initialize config', angular.mock.inject(($controller) => { + let config = {config: 'foo-bar'}; + /** @type {!ConfigController} */ + let ctrl = $controller(ConfigController, {config: config}); + + expect(ctrl.config).toBe(config); + })); + + it('should show zero state', () => { + // given + ctrl.config = { + secretList: {listMeta: {totalItems: 0}}, + configMapList: {listMeta: {totalItems: 0}}, + }; + + expect(ctrl.shouldShowZeroState()).toBe(true); + }); + + it('should hide zero state', () => { + // given + ctrl.config = { + secretList: {listMeta: {totalItems: 0}}, + configMapList: {listMeta: {totalItems: 1}}, + }; + + // then + expect(ctrl.shouldShowZeroState()).toBe(false); + }); +}); diff --git a/src/test/frontend/config/module_test.js b/src/test/frontend/config/module_test.js new file mode 100644 index 000000000..420b3fc50 --- /dev/null +++ b/src/test/frontend/config/module_test.js @@ -0,0 +1,25 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import module from 'config/module'; + +describe('Config module ', () => { + beforeEach(() => { angular.mock.module(module.name); }); + + it('should provide kdConfigResource', angular.mock.inject((kdConfigResource, $httpBackend) => { + kdConfigResource.get(); + $httpBackend.expectGET('api/v1/config').respond(); + $httpBackend.flush(); + })); +}); diff --git a/src/test/frontend/config/stateconfig_test.js b/src/test/frontend/config/stateconfig_test.js new file mode 100644 index 000000000..47f278707 --- /dev/null +++ b/src/test/frontend/config/stateconfig_test.js @@ -0,0 +1,40 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import module from 'config/module'; +import {resolveResource} from 'config/stateconfig'; + +describe('StateConfig for config list', () => { + /** @type {!common/pagination/pagination_service.PaginationService} */ + let kdPaginationService; + + beforeEach(() => { + angular.mock.module(module.name); + angular.mock.inject( + (_kdPaginationService_) => { kdPaginationService = _kdPaginationService_; }); + }); + + it('should resolve config', angular.mock.inject(($q) => { + let promise = $q.defer().promise; + + let resource = jasmine.createSpyObj('$resource', ['get']); + resource.get.and.callFake(function() { return {$promise: promise}; }); + + let actual = resolveResource(resource, {namespace: 'foo-ns'}, kdPaginationService); + + expect(resource.get) + .toHaveBeenCalledWith(kdPaginationService.getDefaultResourceQuery('foo-ns')); + expect(actual).toBe(promise); + })); +}); -- GitLab